2021-10-29 17:09:11 +00:00
mod player_actions ;
mod responses ;
mod room_1 ;
2021-10-29 16:58:53 +00:00
mod state ;
2021-10-29 17:09:11 +00:00
use player_actions ::* ;
use responses ::* ;
2021-10-24 23:56:50 +00:00
2021-10-29 17:09:11 +00:00
pub use responses ::Response ;
pub use state ::State ;
2021-10-24 23:56:50 +00:00
fn _item_name_display ( x : ItemName ) -> & 'static str {
match x {
ItemName ::Nonsense = > " NONSENSE " ,
ItemName ::Door = > " DOOR " ,
ItemName ::EmergencyExit = > " EMERGENCY EXIT " ,
ItemName ::Keypad = > " KEYPAD " ,
ItemName ::Note = > " NOTE " ,
ItemName ::Table = > " TABLE " ,
}
}
fn parse_input ( s : & str ) -> PlayerAction {
use PlayerAction ::* ;
use PlayerActionRoomSpecific ::* ;
2021-10-25 01:06:09 +00:00
let s = s . trim ( ) . to_lowercase ( ) ;
2021-10-24 23:56:50 +00:00
let look = | rest | RoomSpecific ( Look ( parse_item_name ( rest ) ) ) ;
let activate = | rest | RoomSpecific ( Use ( parse_item_name ( rest ) ) ) ;
if s = = " quit " | | s = = " quit game " {
Quit
}
else if s = = " help " | | s = = " help me " {
Help
}
else if s = = " look " | | s = = " look around " {
RoomSpecific ( LookAround )
}
else if let Some ( rest ) = s . strip_prefix ( " look at the " ) {
look ( rest )
}
else if let Some ( rest ) = s . strip_prefix ( " look at " ) {
look ( rest )
}
else if let Some ( rest ) = s . strip_prefix ( " look " ) {
look ( rest )
}
else if let Some ( rest ) = s . strip_prefix ( " examine " ) {
look ( rest )
}
else if let Some ( rest ) = s . strip_prefix ( " use the " ) {
activate ( rest )
}
else if let Some ( rest ) = s . strip_prefix ( " use " ) {
activate ( rest )
}
else if
s = = " do nothing " | |
s = = " wait "
{
RoomSpecific ( Wait )
}
else if s = = " hint " {
RoomSpecific ( Hint )
}
else {
Nonsense
}
}
fn parse_item_name ( s : & str ) -> ItemName {
let s = s . trim ( ) ;
if s = = " door " {
ItemName ::Door
}
else if s = = " emergency exit " {
ItemName ::EmergencyExit
}
else if s = = " keypad " {
ItemName ::Keypad
}
else if s = = " note " {
ItemName ::Note
}
else if s = = " table " {
ItemName ::Table
}
else {
ItemName ::Nonsense
}
}
2021-10-29 17:09:11 +00:00
#[ macro_export ]
2021-10-24 23:56:50 +00:00
macro_rules ! require_detection {
( $condition :expr $(, ) ? ) = > {
if ! $condition {
return vec! [
Response ::FailedDetectionCheck ,
undetected_item ( ) ,
] ;
}
} ;
}
impl State {
2021-10-29 16:32:20 +00:00
pub fn cheat ( & mut self ) {
2021-10-29 16:58:53 +00:00
self . current_room = state ::RoomName ::SortingRoom ;
2021-10-29 16:32:20 +00:00
}
2021-10-24 23:56:50 +00:00
/// Send a line of player input (e.g. "look table") into the game and return
/// a Vec of Responses. The runtime should process these responses in order.
pub fn step ( & mut self , input : & str ) -> Vec < Response > {
2021-10-29 16:58:53 +00:00
use state ::{
IntroState ,
RoomName ::* ,
} ;
2021-10-29 16:32:20 +00:00
2021-10-24 23:56:50 +00:00
match self . intro_state {
IntroState ::Stage1 = > {
self . intro_state = IntroState ::Stage2 ;
return just ( Response ::PrintMany ( vec! [
" Welcome to SEROTONIN DEPOSITORY, the only adventure game ever made. " ,
" " ,
" You have been consensually kidnapped by a diabolical ADVENTURE GAME ENTHUSIAST and encouraged to solve PUZZLES for their sick PLEASURE. The only winning move is to solve all the PUZZLES. " ,
" " ,
" Press ENTER if you dare to begin. " ,
] ) ) ;
} ,
IntroState ::Stage2 = > {
self . intro_state = IntroState ::Stage3 ;
let mut output = vec! [ ] ;
if ! input . is_empty ( ) {
output . push ( line_response ( " That was more than just ENTER but OKAY, overachiever. " ) ) ;
}
output . push ( line_response ( " " ) ) ;
output . push ( line_response ( " You are in a small room. In one corner is a TABLE. " ) ) ;
return output ;
} ,
_ = > ( ) ,
}
let action = parse_input ( input ) ;
match action {
PlayerAction ::Quit = > vec! [
line_response ( " Bye. " ) ,
Response ::Quit ,
] ,
PlayerAction ::Help = > just ( print_help ( ) ) ,
PlayerAction ::Nonsense = > vec! [
line_response ( " I couldn't understand that. Try `help` or `hint`. " ) ,
line_response ( " `hint` may contain spoilers. `help` will not. " ) ,
2021-10-25 01:06:09 +00:00
Response ::Nonsense ,
2021-10-24 23:56:50 +00:00
] ,
2021-10-29 16:32:20 +00:00
PlayerAction ::RoomSpecific ( x ) = > match self . current_room {
Room1 = > self . room_1 ( x ) ,
2021-10-29 16:41:14 +00:00
SortingRoom = > self . sorting_room ( x ) ,
2021-10-29 16:32:20 +00:00
_ = > just ( line_response ( " ERR: Invalid current room " ) ) ,
}
2021-10-24 23:56:50 +00:00
}
}
2021-10-29 16:41:14 +00:00
fn sorting_room ( & mut self , action : PlayerActionRoomSpecific ) -> Vec < Response >
{
use PlayerActionRoomSpecific ::* ;
match action {
Hint = > {
just ( line_response ( " The books are out of order " ) )
} ,
Wait = > {
just ( line_response ( " You wait. Nothing is moving. The room smells like 5 books. " ) )
} ,
LookAround = > {
vec! [
line_response ( " You see a small bookshelf with 5 books on it, and a MACHINE. It looks like the MACHINE is able to grab books and swap them around with a robotic arm. The bookshelf is behind a pane of reinforced glass, but a control panel near you has buttons labelled LEFT, RIGHT, and SWAP. " ) ,
]
} ,
Look ( item_name ) = > {
just ( undetected_item ( ) )
} ,
Use ( item_name ) = > {
just ( undetected_item ( ) )
} ,
}
}
2021-10-24 23:56:50 +00:00
}
#[ cfg (test) ]
mod test {
use super ::{
Response ,
State ,
} ;
2021-10-25 00:54:17 +00:00
fn skip_intro ( ) -> State {
let mut x = State ::default ( ) ;
x . step ( " " ) ;
x . step ( " " ) ;
x
}
2021-10-24 23:56:50 +00:00
#[ test ]
fn parse_input ( ) {
use super ::{
ItemName ,
PlayerAction ::* ,
PlayerActionRoomSpecific ::* ,
} ;
for ( input , expected ) in [
( " " , Nonsense ) ,
( " help " , Help ) ,
( " look at the table " , RoomSpecific ( Look ( ItemName ::Table ) ) ) ,
( " look at table " , RoomSpecific ( Look ( ItemName ::Table ) ) ) ,
( " look table " , RoomSpecific ( Look ( ItemName ::Table ) ) ) ,
( " look note " , RoomSpecific ( Look ( ItemName ::Note ) ) ) ,
( " LOOK TABLE " , RoomSpecific ( Look ( ItemName ::Table ) ) ) ,
2021-10-25 01:06:09 +00:00
( " look " , RoomSpecific ( LookAround ) ) ,
2021-10-24 23:56:50 +00:00
( " wait " , RoomSpecific ( Wait ) ) ,
( " hint " , RoomSpecific ( Hint ) ) ,
] . into_iter ( ) {
let actual = super ::parse_input ( input ) ;
assert_eq! ( actual , expected ) ;
}
}
#[ test ]
fn joke_ending ( ) {
2021-10-25 00:54:17 +00:00
let mut state = skip_intro ( ) ;
2021-10-24 23:56:50 +00:00
let responses = state . step ( " use emergency exit " ) ;
assert! ( responses . contains ( & Response ::Quit ) ) ;
assert! ( responses . contains ( & Response ::JokeEnding ) ) ;
}
#[ test ]
fn detection_check ( ) {
2021-10-25 00:54:17 +00:00
let mut state = skip_intro ( ) ;
2021-10-24 23:56:50 +00:00
let responses = state . step ( " look keypad " ) ;
assert! ( responses . contains ( & Response ::FailedDetectionCheck ) ) ;
state . step ( " look door " ) ;
let responses = state . step ( " look keypad " ) ;
assert! ( ! responses . contains ( & Response ::FailedDetectionCheck ) ) ;
}
#[ test ]
fn happy_path ( ) {
2021-10-25 00:54:17 +00:00
let mut state = skip_intro ( ) ;
2021-10-24 23:56:50 +00:00
for line in [
" look table " ,
" look note " ,
" look door " ,
] {
let responses = state . step ( line ) ;
assert! ( ! responses . contains ( & Response ::PlayerVictory ) ) ;
}
let responses = state . step ( " use keypad " ) ;
assert! ( responses . contains ( & Response ::PlayerVictory ) ) ;
}
}