Skip to content

Commit

Permalink
fix: single step not working as expected (#177)
Browse files Browse the repository at this point in the history
This PR: 
- fixes single-step quest not handling its graph and state properly
- fixes multiple starting points working as "multiple single steps"
  • Loading branch information
lauti7 authored Jun 11, 2024
1 parent 4f28c50 commit ca9e384
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 17 deletions.
71 changes: 54 additions & 17 deletions crates/protocol/src/quests/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,31 @@ fn build_graph_from_quest_definition(quest: &Quest) -> Dag<String, u32> {
let Some(definition) = &quest.definition else {
return dag;
};
for Connection {
ref step_from,
ref step_to,
} in &definition.connections
{
// Validate if steps are in defined in the quest
if quest.contains_step(step_from) && quest.contains_step(step_to) {
if let Some(node_from) = nodes.get(step_from) {
let (_, node_to) =
dag.add_child(*node_from, node_from.index() as u32, step_to.clone());
nodes.insert(step_to.clone(), node_to);
} else {
let node_from = dag.add_node(step_from.clone());
nodes.insert(step_from.clone(), node_from);
let (_, node_to) =
dag.add_child(node_from, node_from.index() as u32, step_to.clone());
nodes.insert(step_to.clone(), node_to);

if definition.connections.is_empty() {
for step in &definition.steps {
let node = dag.add_node(step.id.clone());
nodes.insert(step.id.clone(), node);
}
} else {
for Connection {
ref step_from,
ref step_to,
} in &definition.connections
{
// Validate if steps are in defined in the quest
if quest.contains_step(step_from) && quest.contains_step(step_to) {
if let Some(node_from) = nodes.get(step_from) {
let (_, node_to) =
dag.add_child(*node_from, node_from.index() as u32, step_to.clone());
nodes.insert(step_to.clone(), node_to);
} else {
let node_from = dag.add_node(step_from.clone());
nodes.insert(step_from.clone(), node_from);
let (_, node_to) =
dag.add_child(node_from, node_from.index() as u32, step_to.clone());
nodes.insert(step_to.clone(), node_to);
}
}
}
}
Expand Down Expand Up @@ -636,4 +644,33 @@ mod tests {
let result = matches_action(&Action::emote(Coordinates::new(1, 2), "ID"), &other_action);
assert!(result);
}

#[test]
fn quest_with_single_step_works_properly() {
let quest = Quest {
id: "1e9a8bbf-2223-4f51-b7e5-660d35cedef4".to_string(),
name: "CUSTOM_QUEST".to_string(),
description: "".to_string(),
creator_address: "0xB".to_string(),
definition: Some(QuestDefinition {
connections: vec![],
steps: vec![Step {
id: "A".to_string(),
description: "".to_string(),
tasks: vec![Task {
id: "A_1".to_string(),
description: "".to_string(),
action_items: vec![],
}],
}],
}),
..Default::default()
};

let graph: QuestGraph = (&quest).into();

let next = graph.next(START_STEP_ID).unwrap();
assert_eq!(next.len(), 1);
assert_eq!(next[0], "A");
}
}
2 changes: 2 additions & 0 deletions crates/protocol/src/quests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,8 @@ mod tests {
.build();

assert!(quest.is_valid().is_ok());
assert!(quest.get_steps_without_from().contains("A"));
assert!(quest.get_steps_without_to().contains("A"))
}

#[test]
Expand Down
113 changes: 113 additions & 0 deletions crates/protocol/src/quests/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,4 +704,117 @@ mod tests {
assert_eq!(state.current_steps.len(), 0);
assert!(state.is_completed())
}

#[test]
fn quest_graph_single_step_apply_event_works() {
let quest = Quest {
id: "".to_string(),
name: "CUSTOM_QUEST".to_string(),
description: "".to_string(),
creator_address: "0xB".to_string(),
definition: Some(QuestDefinition {
connections: vec![],
steps: vec![Step {
id: "A1".to_string(),
description: "".to_string(),
tasks: vec![Task {
id: "A1_1".to_string(),
description: "".to_string(),
action_items: vec![Action::custom("A1_1_ID")],
}],
}],
}),
..Default::default()
};
let quest_graph = QuestGraph::from(&quest);
let mut events = vec![Event {
// A1_1
id: uuid::Uuid::new_v4().to_string(),
address: "0xA".to_string(),
action: Some(Action::custom("A1_1_ID")),
}];

let mut state = QuestState::from(&quest_graph);
assert!(state.current_steps.contains_key("A1")); // branch 1
assert_eq!(state.current_steps.len(), 1);
assert!(state.steps_completed.is_empty());
assert_eq!(state.steps_left, 1);
assert!(state.required_steps.contains(&"A1".to_string()));

state = state.apply_event(&quest_graph, &events.remove(0));
assert!(state.current_steps.is_empty());
assert!(state.steps_completed.contains(&"A1".to_string()));
assert_eq!(state.steps_left, 0);
assert!(state.is_completed());
}

#[test]
fn quest_graph_multiple_starting_points_as_single_steps_apply_event_works() {
let quest = Quest {
id: "".to_string(),
name: "CUSTOM_QUEST".to_string(),
description: "".to_string(),
creator_address: "0xB".to_string(),
definition: Some(QuestDefinition {
connections: vec![],
steps: vec![
Step {
id: "A1".to_string(),
description: "".to_string(),
tasks: vec![Task {
id: "A1_1".to_string(),
description: "".to_string(),
action_items: vec![Action::custom("A1_1_ID")],
}],
},
Step {
id: "B1".to_string(),
description: "".to_string(),
tasks: vec![Task {
id: "B1_1".to_string(),
description: "".to_string(),
action_items: vec![Action::custom("B1_1_ID")],
}],
},
],
}),
..Default::default()
};
let quest_graph = QuestGraph::from(&quest);
let mut events = vec![
Event {
// A1_1
id: uuid::Uuid::new_v4().to_string(),
address: "0xA".to_string(),
action: Some(Action::custom("A1_1_ID")),
},
Event {
// A1_1
id: uuid::Uuid::new_v4().to_string(),
address: "0xA".to_string(),
action: Some(Action::custom("B1_1_ID")),
},
];

let mut state = QuestState::from(&quest_graph);
assert!(state.current_steps.contains_key("A1")); // branch 1
assert!(state.current_steps.contains_key("B1")); // branch 2
assert_eq!(state.current_steps.len(), 2);
assert!(state.steps_completed.is_empty());
assert_eq!(state.steps_left, 2);
assert!(state.required_steps.contains(&"A1".to_string()));
assert!(state.required_steps.contains(&"B1".to_string()));

state = state.apply_event(&quest_graph, &events.remove(0));
assert_eq!(state.current_steps.len(), 1);
assert!(state.current_steps.contains_key("B1"));
assert!(state.steps_completed.contains(&"A1".to_string()));
assert_eq!(state.steps_left, 1);

state = state.apply_event(&quest_graph, &events.remove(0));
assert_eq!(state.current_steps.len(), 0);
assert!(state.steps_completed.contains(&"A1".to_string()));
assert!(state.steps_completed.contains(&"B1".to_string()));
assert!(state.is_completed())
}
}

0 comments on commit ca9e384

Please sign in to comment.