♻️ refactor: refactored a lot and added some features too, whoops

main
_ 2021-10-24 20:24:39 +00:00
parent 9303fe4897
commit 574300cdad
1 changed files with 145 additions and 81 deletions

View File

@ -1,9 +1,3 @@
use std::io::{
Write,
stdin,
stdout,
};
/// As a dare to myself, I won't use any error-handling crates. /// As a dare to myself, I won't use any error-handling crates.
#[derive (Debug)] #[derive (Debug)]
@ -34,27 +28,16 @@ fn print (mut s: &str) {
println! ("{}", s); println! ("{}", s);
} }
fn print_help () {
print ("All commands are ASCII and case-insensitive.");
print ("Commands should start with a verb like LOOK.");
print ("e.g. `look table`");
print ("Single-word verbs are better, e.g. prefer `hint` over `give me a hint`");
print ("When in doubt, try generic verbs like `look` or `use` over specific verbs like `actuate`, `type`, or `consolidate`.");
}
fn print_undetected_item () {
print ("That ITEM does not exist in this ROOM, or you have not detected it.");
}
fn read_input () -> Result <String> { fn read_input () -> Result <String> {
{ {
let mut stdout = stdout (); use std::io::Write;
let mut stdout = std::io::stdout ();
stdout.write_all (b"> ")?; stdout.write_all (b"> ")?;
stdout.flush ()?; stdout.flush ()?;
} }
let mut buffer = String::new (); let mut buffer = String::new ();
stdin ().read_line (&mut buffer)?; std::io::stdin ().read_line (&mut buffer)?;
// I don't know why I need the type annotation here, but I do. // I don't know why I need the type annotation here, but I do.
let x: &[_] = &['\r', '\n']; let x: &[_] = &['\r', '\n'];
@ -63,6 +46,54 @@ fn read_input () -> Result <String> {
Ok (buffer) Ok (buffer)
} }
trait Io {
fn print (&mut self, s: &str);
fn read_input (&mut self) -> Result <String>;
fn sleep (&mut self, milliseconds: u32);
fn print_many <'a, I: IntoIterator <Item=&'a str>> (&mut self, lines: I) {
for line in lines {
self.print (line);
}
}
}
#[derive (Default)]
struct Stdio {}
impl Io for Stdio {
fn print (&mut self, s: &str) {
print (s)
}
fn read_input (&mut self) -> Result <String> {
read_input ()
}
fn sleep (&mut self, milliseconds: u32) {
use std::{
thread::sleep,
time::Duration,
};
sleep (Duration::from_millis (milliseconds.into ()));
}
}
fn print_help <I: Io> (io: &mut I) {
io.print_many ([
"All commands are ASCII and case-insensitive.",
"Commands should start with a verb like LOOK.",
"e.g. `look table`",
"Single-word verbs are better, e.g. prefer `hint` over `give me a hint`",
"When in doubt, try generic verbs like `look` or `use` over specific verbs like `actuate`, `type`, or `consolidate`.",
]);
}
fn print_undetected_item <I: Io> (io: &mut I) {
io.print ("That ITEM does not exist in this ROOM, or you have not detected it.");
}
#[derive (Debug, PartialEq)] #[derive (Debug, PartialEq)]
enum PlayerAction { enum PlayerAction {
Quit, Quit,
@ -72,6 +103,7 @@ enum PlayerAction {
Wait, Wait,
Look (ItemName), Look (ItemName),
LookAround, LookAround,
Use (ItemName),
} }
#[derive (Clone, Copy, Debug, PartialEq)] #[derive (Clone, Copy, Debug, PartialEq)]
@ -85,15 +117,15 @@ enum ItemName {
Table, Table,
} }
fn item_name_display (x: ItemName) -> &'static str { fn _item_name_display (x: ItemName) -> &'static str {
match x { match x {
Nonsense => "NONSENSE", ItemName::Nonsense => "NONSENSE",
Door => "DOOR", ItemName::Door => "DOOR",
EmergencyExit => "EMERGENCY EXIT", ItemName::EmergencyExit => "EMERGENCY EXIT",
Keypad => "KEYPAD", ItemName::Keypad => "KEYPAD",
Note => "NOTE", ItemName::Note => "NOTE",
Table => "TABLE", ItemName::Table => "TABLE",
} }
} }
@ -124,6 +156,14 @@ fn parse_input (s: &str) -> PlayerAction {
return PlayerAction::Look (parse_item_name (rest)); return PlayerAction::Look (parse_item_name (rest));
} }
if let Some (rest) = s.strip_prefix ("use the ") {
return PlayerAction::Use (parse_item_name (rest));
}
if let Some (rest) = s.strip_prefix ("use ") {
return PlayerAction::Use (parse_item_name (rest));
}
if s == "do nothing" { if s == "do nothing" {
return PlayerAction::Wait; return PlayerAction::Wait;
} }
@ -165,92 +205,112 @@ fn parse_item_name (s: &str) -> ItemName {
ItemName::Nonsense ItemName::Nonsense
} }
#[derive (Default)] #[derive (Clone, Default)]
struct StateRoom1 { struct StateRoom1 {
detected_keypad: bool, detected_keypad: bool,
detected_note: bool, detected_note: bool,
} }
#[derive (Default)] #[derive (Clone, Default)]
struct State { struct State {
quitting: bool, quitting: bool,
room_1: StateRoom1, room_1: StateRoom1,
} }
fn room_1 (state: &mut State) -> Result <()> { fn room_1 <I: Io> (io: &mut I, state: &mut State) -> Result <()> {
let input = read_input ()?; let input = io.read_input ()?;
let action = parse_input (&input); let action = parse_input (&input);
match action { match action {
PlayerAction::Quit => { PlayerAction::Quit => {
print ("Bye."); io.print ("Bye.");
state.quitting = true; state.quitting = true;
} }
PlayerAction::Help => { PlayerAction::Help => {
print_help (); print_help (io);
}, },
PlayerAction::Hint => { PlayerAction::Hint => {
print ("Hint for this room: Try using the `help` command."); io.print ("Hint for this room: Try using the `help` command.");
}, },
PlayerAction::Nonsense => { PlayerAction::Nonsense => {
print ("I couldn't understand that. Try `help` or `hint`."); io.print ("I couldn't understand that. Try `help` or `hint`.");
print ("`hint` may contain spoilers. `help` will not."); io.print ("`hint` may contain spoilers. `help` will not.");
}, },
PlayerAction::Wait => { PlayerAction::Wait => {
print ("You wait around a bit. You can hear humming from the electrical lights, and the distant rumble of the building's HVAC system. The room smells faintly of fresh paint. Nothing has changed."); io.print ("You wait around a bit. You can hear humming from the electrical lights, and the distant rumble of the building's HVAC system. The room smells faintly of fresh paint. Nothing has changed.");
}, },
PlayerAction::LookAround => { PlayerAction::LookAround => {
print ("You are in a small room. In one corner is a TABLE. Obvious exits are a locked DOOR, and an EMERGENCY EXIT."); io.print ("You are in a small room. In one corner is a TABLE. Obvious exits are a locked DOOR, and an EMERGENCY EXIT.");
} }
PlayerAction::Look (item_name) => { PlayerAction::Look (item_name) => {
match item_name { match item_name {
ItemName::Nonsense => {
print_undetected_item ();
},
ItemName::Door => { ItemName::Door => {
print ("You examine the DOOR. It is firmly locked, and you don't have any lock-picking tools. On the DOOR is an electronic KEYPAD."); io.print ("You examine the DOOR. It is firmly locked, and you don't have any lock-picking tools. On the DOOR is an electronic KEYPAD.");
state.room_1.detected_keypad = true; state.room_1.detected_keypad = true;
}, },
ItemName::EmergencyExit => { ItemName::EmergencyExit => {
print ("The EMERGENCY EXIT reads, \"Emergency exit. Push bar to open. Alarm will sound. Door will unlock in 15 seconds.\". The EMERGENCY EXIT is period-accurate for an American Wal-Mart c. 2020 C.E."); io.print ("The EMERGENCY EXIT reads, \"Emergency exit. Push bar to open. Alarm will sound. Door will unlock in 10 seconds.\". The EMERGENCY EXIT is period-accurate for an American Wal-Mart c. 2020 C.E.");
}, },
ItemName::Keypad => { ItemName::Keypad => {
if state.room_1.detected_keypad { if ! state.room_1.detected_keypad {
print ("The DOOR is locked by an electronic KEYPAD. A soft amber power light indicates that the KEYPAD is likely functional. The KEYPAD buttons are the digits 0-9, Enter, and Clear. Experience tells you that the key code is likely 4 or 5 digits long."); print_undetected_item (io);
} return Ok (());
else {
print_undetected_item ();
} }
io.print ("The DOOR is locked by an electronic KEYPAD. A soft amber power light indicates that the KEYPAD is likely functional. The KEYPAD buttons are the digits 0-9, Enter, and Clear. Experience tells you that the key code is likely 4 or 5 digits long.");
}, },
ItemName::Note => { ItemName::Note => {
if state.room_1.detected_note { if ! state.room_1.detected_note {
for x in [ print_undetected_item (io);
"You pick up the NOTE and read it.", return Ok (());
"",
"Welcome to SEROTONIN DEPOSITORY.",
"As you play, keep in mind:",
"- LOOKing at ITEMS is not always safe",
"- TAKEing an item may be bad long-term",
"- WAITing counts as an action",
"- LOOKing AROUND is always safe",
"- Other NOTEs may contain non-truths",
"The code for this first KEYPAD is 1234.",
"",
" -- Phayle Sayf",
"",
"You notice that the NOTE is _not_ period-accurate.",
].into_iter () {
print (x);
}
}
else {
print_undetected_item ();
} }
io.print_many ([
"You pick up the NOTE and read it.",
"",
"Welcome to SEROTONIN DEPOSITORY.",
"As you play, keep in mind:",
"- LOOKing at ITEMS is not always safe",
"- TAKEing an item may be bad long-term",
"- WAITing counts as an action",
"- LOOKing AROUND is always safe",
"- Other NOTEs may contain non-truths",
"The code for this first KEYPAD is 1234.",
"",
" -- Phayle Sayf",
"",
"You notice that the NOTE is _not_ period-accurate.",
])
}, },
ItemName::Table => { ItemName::Table => {
print ("You look at the TABLE. Your instincts tell you that it is period-accurate. Upon the TABLE sits a NOTE."); io.print ("You look at the TABLE. Your instincts tell you that it is period-accurate. Upon the TABLE sits a NOTE.");
state.room_1.detected_note = true; state.room_1.detected_note = true;
}, },
_ => {
print_undetected_item (io);
},
}
},
PlayerAction::Use (item_name) => {
match item_name {
ItemName::Door => {
io.print ("You can't USE the DOOR, it is locked.");
if ! state.room_1.detected_keypad {
io.print ("You notice an electronic KEYPAD on the DOOR.");
state.room_1.detected_keypad = true;
}
},
ItemName ::EmergencyExit => {
io.print ("You push on the emergency exit. An alarm starts sounding. Your ADVENTURE GAME ENTHUSIAST friend is going to be very mad at you.");
io.sleep (5000);
io.print ("The alarm is still sounding. You are getting embarrassed, but you have committed to this path of action.");
io.sleep (5000);
io.print ("The emergency exit unlocks, and you walk out of the game. Bye.");
state.quitting = true;
},
_ => {
print_undetected_item (io);
},
} }
}, },
} }
@ -259,26 +319,30 @@ fn room_1 (state: &mut State) -> Result <()> {
} }
fn main () -> Result <()> { fn main () -> Result <()> {
print ("Welcome to SEROTONIN DEPOSITORY, the only adventure game ever made."); let mut io = Stdio::default ();
print ("");
print ("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.");
print ("");
print ("Press ENTER if you dare to begin.");
let input = read_input ()?; io.print_many ([
"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.",
]);
let input = io.read_input ()?;
if ! input.is_empty () { if ! input.is_empty () {
print ("That was more than just ENTER but OKAY, overachiever."); io.print ("That was more than just ENTER but OKAY, overachiever.");
} }
print (""); io.print ("");
let mut state = State::default (); let mut state = State::default ();
print ("You are in a small room. In one corner is a TABLE."); io.print ("You are in a small room. In one corner is a TABLE.");
while ! state.quitting { while ! state.quitting {
room_1 (&mut state)?; room_1 (&mut io, &mut state)?;
} }
Ok (()) Ok (())