use super::super::interpreter::*;
use crate::config::{Config, Verb};
#[cfg(test)]
use pretty_assertions::assert_eq;
#[test]
fn it_looks_at_room() {
let config = Config::from_path("fixtures/").unwrap();
let mut state = State::init(config);
let look_result1 = look_room(&state);
state.current_room = 7;
let look_result2 = look_room(&state);
assert!(look_result1.is_ok());
assert_eq!(
look_result1.unwrap(),
ParsingResult::Look(
"first room\n\nHere you see: \nan item1\nan item2\nsubject1".to_string()
)
);
assert!(look_result2.is_err());
assert_eq!(look_result2.unwrap_err().to_string(), NoRoom.to_string());
}
#[test]
fn it_looks_at_subject() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let subject1 = state.config.subjects[0].clone();
let subject2 = Subject {
id: 7,
name: "subject2".to_string(),
description: "a non-existing subject".to_string(),
default_text: "".to_string(),
};
let look_result1 = look_subject(&state, subject1);
let look_result2 = look_subject(&state, subject2);
assert!(look_result1.is_ok());
assert_eq!(
look_result1.unwrap(),
ParsingResult::Look("a subject description".to_string())
);
assert_eq!(
look_result2.unwrap(),
ParsingResult::Look("I can't see that here".to_string())
);
}
#[test]
fn it_looks_at_item() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let item1 = state.config.items[0].clone();
let item2 = Item {
id: 7,
name: "item2".to_string(),
description: "a non-existing item".to_string(),
can_pick: false,
};
let look_result1 = look_item(&state, item1);
let look_result2 = look_item(&state, item2);
assert!(look_result1.is_ok());
assert_eq!(
look_result1.unwrap(),
ParsingResult::Look("item 1 description".to_string())
);
assert!(look_result2.is_ok());
assert_eq!(
look_result2.unwrap(),
ParsingResult::Look("I can't see that here".to_string())
);
}
#[test]
fn it_picks_items() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let current_room_id = state.current_room;
let current_room = state
.rooms
.iter()
.find(|room| room.id == current_room_id)
.unwrap()
.clone();
let item1 = current_room.stash.items[0].clone();
let item2 = current_room.stash.items[1].clone();
let item3 = Item {
id: 7,
name: "item3".to_string(),
description: "a non-existing item".to_string(),
can_pick: false,
};
let pick_result1 = pick_item(&state, item1);
let pick_result2 = pick_item(&state, item2);
let pick_result3 = pick_item(&state, item3);
assert!(pick_result1.is_err());
assert_eq!(pick_result1.unwrap_err().to_string(), CantPick.to_string());
assert!(pick_result2.is_ok());
assert_eq!(
pick_result2.unwrap().1,
ParsingResult::NewItem("\nYou now have a item2\n".to_string())
);
assert!(&pick_result3.is_err());
let result3_message = match pick_result3 {
Ok(_) => panic!("pick_item should have failed"),
Err(e) => e,
};
assert_eq!(result3_message.to_string(), NoItem.to_string());
}
#[test]
fn it_drops_items() {
let config = Config::from_path("fixtures/").unwrap();
let mut state = State::init(config);
let current_room_id = &state.current_room;
let current_room = &state
.rooms
.iter()
.find(|room| &room.id == current_room_id)
.unwrap()
.clone();
let item1 = current_room.stash.items[0].clone();
let item2 = current_room.stash.items[1].clone();
state.player.inventory.items.push(item1.clone());
let drop_result1 = drop_item(&state, item1);
let drop_result2 = drop_item(&state, item2);
assert!(drop_result1.is_ok());
assert_eq!(
drop_result1.unwrap().1,
ParsingResult::DropItem("\nYou no longer have a item1\n".to_string())
);
assert!(&drop_result2.is_err());
let result2_message = match drop_result2 {
Ok(_) => panic!("pick_item should have failed"),
Err(e) => e,
};
assert_eq!(result2_message.to_string(), NoItem.to_string());
}
#[test]
fn it_shows_inventory() {
let config = Config::from_path("fixtures/").unwrap();
let mut state = State::init(config);
let inventory_result_1 = show_inventory(&state);
assert_eq!(
inventory_result_1.unwrap(),
ParsingResult::Inventory("You are not carrying anything.".to_string())
);
state.player.inventory.items.push(Item {
id: 7,
name: "item1".to_string(),
description: "item 1 description".to_string(),
can_pick: false,
});
let inventory_result2 = show_inventory(&state);
assert_eq!(
inventory_result2.unwrap(),
ParsingResult::Inventory("You are currently carrying: \n\nan item1".to_string())
);
state.player.inventory.items.push(Item {
id: 8,
name: "item2".to_string(),
description: "item 2 description".to_string(),
can_pick: false,
});
let inventory_result3 = show_inventory(&state);
assert_eq!(
inventory_result3.unwrap(),
ParsingResult::Inventory("You are currently carrying: \n\nan item1\nan item2".to_string())
);
}
#[test]
fn it_extracts_item_and_subject() {
let config = Config::from_path("fixtures/").unwrap();
let mut state = State::init(config);
let subject = Subject {
id: 1,
name: "subject1".to_string(),
description: "a subject description".to_string(),
default_text: "a subject default text".to_string(),
};
let item1 = Item {
id: 1,
name: "item1".to_string(),
description: "item 1 description".to_string(),
can_pick: false,
};
state.player.inventory.items.push(item1.clone());
let item2 = Item {
id: 2,
name: "item2".to_string(),
description: "item 2 description".to_string(),
can_pick: false,
};
let verb = Verb {
id: 1,
names: vec!["go".to_string()],
verb_function: VerbFunction::Normal,
};
let action1 = Action {
verb: Some(verb.clone()),
subject: Some(subject.clone()),
item: Some(item1.clone()),
movement: None,
command_tokens: vec![
"go".to_string(),
"subject1".to_string(),
"item1".to_string(),
],
input: "go subject1 item1".to_string(),
};
let action2 = Action {
verb: Some(verb),
subject: Some(subject.clone()),
item: Some(item2),
movement: None,
command_tokens: vec![
"go".to_string(),
"subject1".to_string(),
"item2".to_string(),
],
input: "go subject1 item2".to_string(),
};
let action3 = Action {
verb: None,
subject: None,
item: None,
movement: Some(Directions::North),
command_tokens: vec!["north".to_string()],
input: "north".to_string(),
};
let extract_result1 = extract_item_subject(&state, &action1);
let extract_result2 = extract_item_subject(&state, &action2);
let extract_result3 = extract_item_subject(&state, &action3);
assert_eq!(extract_result1, (Some(item1), Some(subject.clone())));
assert_eq!(extract_result2, (None, Some(subject)));
assert_eq!(extract_result3, (None, None));
}
#[test]
fn it_handles_movement() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let (new_state, movement_result1) = handle_movement(&state, Some(Directions::South)).unwrap();
let movement_result2 = handle_movement(&new_state, Some(Directions::South)).unwrap_err();
let movement_result3 = handle_movement(&new_state, None).unwrap_err();
let mut message_parts = HashMap::new();
message_parts.insert(MessageParts::RoomText, "this is a templated which exists in the game item3.\n\nthis is a templated subject that exists in the game subject2.".to_string());
message_parts.insert(
MessageParts::Exits,
"Exits:\nto the north you see first room".to_string(),
);
message_parts.insert(MessageParts::EventText, "".to_string());
assert_eq!(
movement_result1,
ParsingResult::EventSuccess(EventMessage{
message: "this is a templated which exists in the game item3.\n\nthis is a templated subject that exists in the game subject2.\n\n\nExits:\nto the north you see first room".to_string(),
templated_words: vec!["item3".to_string(), "subject2".to_string()],
message_parts,
})
);
assert_eq!(movement_result2.to_string(), InvalidMovement.to_string());
assert_eq!(movement_result3.to_string(), InvalidMovement.to_string());
}
#[test]
fn it_handles_verbs() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let action_north = Action::parse(&state, "north");
let action_look = Action::parse(&state, "look");
let action_inventory = Action::parse(&state, "i");
let action_quit = Action::parse(&state, "quit");
let action_help = Action::parse(&state, "help");
let verb_result1 = handle_verb(&state, action_north);
let verb_result2 = handle_verb(&state, action_look);
let verb_result3 = handle_verb(&state, action_inventory);
let verb_result4 = handle_verb(&state, action_quit);
let verb_result5 = handle_verb(&state, action_help);
assert_eq!(
verb_result1.unwrap_err().to_string(),
InvalidVerb.to_string()
);
assert_eq!(
verb_result2.unwrap().1,
ParsingResult::Look(String::from(
"first room\n\nHere you see: \nan item1\nan item2\nsubject1"
))
);
assert_eq!(
verb_result3.unwrap().1,
ParsingResult::Inventory(String::from("You are not carrying anything."))
);
assert_eq!(verb_result4.unwrap().1, ParsingResult::Quit);
assert_eq!(verb_result5.unwrap().1, ParsingResult::Help(String::from("\nTo play this game you type your commands and hit enter to execute them. Typically a command has at most three parts: a verb, a subject, and an item. A verb indicates an action you, the player, wants to execute. Many commands can be executed with just a verb such as look, help, quit. For more complex commands you will also need verb and either a subject or an item. A command can also have a verb, item, and subject. A complex command can be: look at dog, talk to person, pick the box, give the box to the dog.\n\nThe game will ignore words like 'to', 'the', 'at', 'from', so using them is optional. A valid command can be: talk person, pick box, go south, climb tree, use axe tree.\n\nValid verbs: quit, help, look, inventory, pick, drop, give, talk, hug")));
}
#[test]
fn it_handles_verb_items() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let action_look_item = Action::parse(&state, "look item1");
let action_pick_item = Action::parse(&state, "pick item2");
let action_cant_pick_item = Action::parse(&state, "pick item1");
let action_drop_item = Action::parse(&state, "drop item2");
let action_wrong_verb = Action::parse(&state, "quit item1");
// Returns the item description if item is in the room
let (new_state, action_look_item_result) = handle_verb_item(&state, action_look_item).unwrap();
// Adds the item to the player's inventory and returns a message if the item can be picked
let (new_state, action_pick_item_result) =
handle_verb_item(&new_state, action_pick_item).unwrap();
// Returns an error if the item can't be picked
let action_cant_pick_item_result =
handle_verb_item(&new_state, action_cant_pick_item).unwrap_err();
// Removes the item from the player's inventory and returns a message if the item is in the inventory
let (new_state, action_drop_item_result) =
handle_verb_item(&new_state, action_drop_item).unwrap();
// Returns an error if the verb is not valid
let action_wrong_verb_result = handle_verb_item(&new_state, action_wrong_verb).unwrap_err();
assert_eq!(
action_look_item_result,
ParsingResult::Look(String::from("item 1 description"))
);
assert_eq!(
action_pick_item_result,
ParsingResult::NewItem(String::from("\nYou now have a item2\n"))
);
assert_eq!(
action_cant_pick_item_result.to_string(),
CantPick.to_string()
);
assert_eq!(
action_drop_item_result,
ParsingResult::DropItem(String::from("\nYou no longer have a item2\n"))
);
assert_eq!(
action_wrong_verb_result.to_string(),
InvalidVerb.to_string()
);
}
#[test]
fn it_handles_verb_subjects() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let action_talk_subject = Action::parse(&state, "talk item1");
let action_talk_subject2 = Action::parse(&state, "talk subject1");
let action_look_subject = Action::parse(&state, "look subject1");
let action_give_subject = Action::parse(&state, "give subject1");
let action_talk_subject_result = handle_verb_subject(&state, action_talk_subject);
let action_talk_subject2_result = handle_verb_subject(&state, action_talk_subject2);
let action_look_subject_result = handle_verb_subject(&state, action_look_subject);
let action_give_subject_result = handle_verb_subject(&state, action_give_subject);
let mut message_parts = HashMap::new();
message_parts.insert(MessageParts::RoomText, "text".to_string());
message_parts.insert(
MessageParts::Exits,
"Exits:\nto the south you see second room".to_string(),
);
message_parts.insert(MessageParts::EventText, "".to_string());
assert_eq!(
action_talk_subject_result.unwrap_err().to_string(),
InvalidSubject.to_string()
);
// This is parsed as an event, so an EventMessage is returned
assert_eq!(
action_talk_subject2_result.unwrap().1,
ParsingResult::EventSuccess(EventMessage {
message: String::from("text\n\n\nExits:\nto the south you see second room"),
templated_words: vec![],
message_parts,
})
);
assert_eq!(
action_look_subject_result.unwrap().1,
ParsingResult::Look(String::from("a subject description"))
);
assert_eq!(
action_give_subject_result.unwrap().1,
ParsingResult::SubjectNoEvent("default text".to_string())
);
}
#[test]
fn it_handles_events() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let action_talk_subject = Action::parse(&state, "talk subject1");
let action_give_subject = Action::parse(&state, "give subject1");
let action_hug_subject = Action::parse(&state, "hug subject1");
let action_talk_subject2 = Action::parse(&state, "talk subject2");
let action_give_item = Action::parse(&state, "give item2 subject1");
// If we try to complete event 2 before event one it should return an error
let action_hug_subject_result = handle_event(&state, action_hug_subject.clone()).unwrap_err();
assert_eq!(
action_hug_subject_result.to_string(),
RequiredEventNotCompleted.to_string()
);
let (new_state, action_talk_subject_result) =
handle_event(&state, action_talk_subject).unwrap();
let mut message_parts = HashMap::new();
message_parts.insert(MessageParts::EventText, "".to_string());
message_parts.insert(MessageParts::RoomText, "text".to_string());
message_parts.insert(
MessageParts::Exits,
"Exits:\nto the south you see second room".to_string(),
);
assert_eq!(
action_talk_subject_result,
ParsingResult::EventSuccess(EventMessage {
message: String::from("text\n\n\nExits:\nto the south you see second room"),
templated_words: vec![],
message_parts,
})
);
assert_eq!(
new_state
.rooms
.iter()
.flat_map(|room| &room.events)
.filter(|e| e.completed)
.map(|e| e.id)
.collect::<Vec<u16>>(),
vec![4]
);
assert_ne!(
new_state
.rooms
.iter()
.flat_map(|room| &room.events)
.filter(|e| e.completed)
.map(|e| e.id)
.collect::<Vec<u16>>(),
vec![2, 4],
"Event 2 should not be completed {:#?}",
&new_state.config.events
);
let (new_state, action_give_subject_result) =
handle_event(&new_state, action_give_subject).unwrap();
assert_eq!(
action_give_subject_result,
ParsingResult::SubjectNoEvent("default text".to_string())
);
// Event 2 can be completed now
let (new_state, action_hug_subject_result) =
handle_event(&new_state, action_hug_subject).unwrap();
assert_eq!(
new_state
.rooms
.iter()
.flat_map(|room| &room.events)
.filter(|e| e.completed)
.map(|e| e.id)
.collect::<Vec<u16>>(),
vec![2, 4],
"Event 2 should be completed at this point. {:#?}",
&new_state.config.events
);
assert!(
new_state
.rooms
.iter()
.flat_map(|room| &room.events)
.find(|e| e.id == 2)
.unwrap()
.completed,
"Event 2 should be completed at this point. {:#?}",
&new_state.config.events.iter().find(|e| e.id == 2).unwrap()
);
// This event replaces the narrative, so we return a message to the front-end
// with the new narrative, but we leave the room narrative alone so we can
// display it again if the player wants to go back to the room.
let mut message_parts = HashMap::new();
message_parts.insert(
MessageParts::RoomText,
"this narrative should replace the old one.".to_string(),
);
message_parts.insert(
MessageParts::Exits,
"Exits:\nto the south you see second room".to_string(),
);
message_parts.insert(MessageParts::EventText, "".to_string());
assert_eq!(
action_hug_subject_result,
ParsingResult::EventSuccess(EventMessage {
message: String::from("this narrative should replace the old one.\n\n\nExits:\nto the south you see second room"),
templated_words: vec![],
message_parts,
})
);
// We moved south so we should receive an event message with the new room narrative
let (new_state, move_south_result) =
handle_movement(&new_state, Action::parse(&state, "south").movement).unwrap();
let mut message_parts = HashMap::new();
message_parts.insert(
MessageParts::RoomText,
"this is a templated which exists in the game item3.\n\nthis is a templated subject that exists in the game subject2.".to_string(),
);
message_parts.insert(
MessageParts::Exits,
"Exits:\nto the north you see first room".to_string(),
);
message_parts.insert(MessageParts::EventText, "".to_string());
assert_eq!(
move_south_result,
ParsingResult::EventSuccess(EventMessage {
message: String::from("this is a templated which exists in the game item3.\n\nthis is a templated subject that exists in the game subject2.\n\n\nExits:\nto the north you see first room"),
templated_words: vec!["item3".to_string(), "subject2".to_string()],
message_parts,
})
);
assert_eq!(new_state.current_room, 2);
// room 2 has subject2, so we should be able to talk to it. This event should
// return you to room one, and the message to be returned should be the one
// for that room. The narrative for the event should be appended to the room
// narrative, or replace it if the event requires that.
let (new_state, action_talk_subject2_result) =
handle_event(&new_state, action_talk_subject2).unwrap();
let mut message_parts = HashMap::new();
message_parts.insert(
MessageParts::RoomText,
"this is a templated which exists in the game item3.\n\nthis is a templated subject that exists in the game subject2.\n\nthis narrative should be returned along with the text of room 1.".to_string(),
);
message_parts.insert(
MessageParts::Exits,
"Exits:\nto the north you see first room".to_string(),
);
message_parts.insert(
MessageParts::EventText,
"\nYou now have a item2".to_string(),
);
assert_eq!(
action_talk_subject2_result,
ParsingResult::EventSuccess(EventMessage {
message: String::from("this is a templated which exists in the game item3.\n\nthis is a templated subject that exists in the game subject2.\n\nthis narrative should be returned along with the text of room 1.\n\nYou now have a item2\n\nExits:\nto the north you see first room"),
templated_words: vec!["item3".to_string(), "subject2".to_string()],
message_parts,
})
);
// event gives item2 to player so inventory should be updated
assert!(new_state.player.inventory.items.len() == 1);
assert_eq!(
&new_state.player.inventory.items[0].name, "item2",
"item2 should be in inventory",
);
assert!(new_state.current_room == 1);
//Since we left the first room and came back to it via the event,
//we should not be able to move north as room 1 only has one exit
//to the south
let move_north_result =
handle_movement(&new_state, Action::parse(&new_state, "north").movement);
assert_eq!(
move_north_result.unwrap_err().to_string(),
InvalidMovement.to_string()
);
assert_eq!(new_state.current_room, 1);
let (new_state, give_item_result) = handle_event(&new_state, action_give_item).unwrap();
let mut message_parts = HashMap::new();
message_parts.insert(
MessageParts::EventText,
"\nYou no longer have a item2".to_string(),
);
message_parts.insert(
MessageParts::Exits,
"Exits:\nto the south you see second room".to_string(),
);
message_parts.insert(
MessageParts::RoomText,
"text\n\nthis narrative should be returned along with the text of room 1.".to_string(),
);
assert_eq!(give_item_result, ParsingResult::EventSuccess(EventMessage {
message: String::from("text\n\nthis narrative should be returned along with the text of room 1.\n\nYou no longer have a item2\n\nExits:\nto the south you see second room"),
templated_words: vec![],
message_parts,
}));
assert_eq!(new_state.player.inventory.items.len(), 0);
}
#[test]
fn it_process_action() {
let config = Config::from_path("fixtures/").unwrap();
let state = State::init(config);
let action_talk_subject = Action::parse(&state, "talk subject1");
let action_give_subject = Action::parse(&state, "give subject1");
let action_hug_subject = Action::parse(&state, "hug subject1");
let action_talk_subject2 = Action::parse(&state, "talk subject2");
let action_give_item = Action::parse(&state, "give item2 subject1");
assert_eq!(action_talk_subject.action_type(), ActionType::VerbSubject);
assert_eq!(action_give_subject.action_type(), ActionType::VerbSubject);
assert_eq!(action_hug_subject.action_type(), ActionType::VerbSubject);
assert_eq!(action_talk_subject2.action_type(), ActionType::VerbSubject);
assert_eq!(action_give_item.action_type(), ActionType::VerbItemSubject);
assert!(action_talk_subject.is_valid());
assert!(action_give_subject.is_valid());
assert!(action_hug_subject.is_valid());
assert!(action_talk_subject2.is_valid());
assert!(action_give_item.is_valid());
assert!(action_give_subject.is_valid());
assert_eq!(format!("{}", action_talk_subject.verb.unwrap()), "talk");
assert_eq!(format!("{}", action_give_subject.verb.unwrap()), "give");
assert_eq!(format!("{}", action_hug_subject.verb.unwrap()), "hug");
assert_eq!(format!("{}", action_talk_subject2.verb.unwrap()), "talk");
assert_eq!(format!("{}", action_give_item.verb.unwrap()), "give");
assert_eq!(
format!("{}", action_talk_subject.subject.unwrap()),
"subject1",
);
assert_eq!(
format!("{}", action_give_subject.subject.unwrap()),
"subject1",
);
assert_eq!(
format!("{}", action_hug_subject.subject.unwrap()),
"subject1",
);
assert_eq!(
format!("{}", action_talk_subject2.subject.unwrap()),
"subject2",
);
assert_eq!(format!("{}", action_give_item.subject.unwrap()), "subject1");
}