How to make a Text Adventure game in Rust - II.1 - Commands
This content and all code is licensed under the MIT License.
Photo by Frank Cone |
2.1 - Better Command Processing
'go north'
would parse correctly, but 'Go North'
would not).String
representation of Command
with an enum based approach for Command
that should be more flexible going forward. While we're at it we'll modify the parser to deal with mixed case input from the player.Command
struct to an enum. Lets have a look there first.pub enum Command {
Look(String),
Go(String),
Quit,
Unknown(String),
}
pub fn parse(input_str: String) -> Command {
let lc_input_str = input_str.to_lowercase();
let mut split_input_iter = lc_input_str.trim().split_whitespace();
let verb = split_input_iter.next().unwrap_or_default().to_string();
let noun = split_input_iter.next().unwrap_or_default().to_string();
match verb.as_str() {
"look" => Command::Look(noun),
"go" => Command::Go(noun),
"quit" => Command::Quit,
_ => Command::Unknown(input_str.trim().to_string()),
}
}
Explanation
1 - Define Command
. Note that Command
is an enum now and not a struct.
2-5 - Enum values for 'Look', 'Go', 'Quit', and Unknown' command actions. The enum's values include an associated String to hold additional parsed input.
8-21 - Note that new()
is completely removed, and parse()
is no longer an implementation function on Command
.
8 - The modified parse()
function. The new version accepts a String
input and returns a Command
.
9 - Creates a lower-case version of the input string. The rest of the function operates on the lower-case value and enables parsing to work with mixed case input. Note that as a result of this approach, input is now completely case insensitive, so if we need that we'll need to revisit this line.
10 - 13 - Unchanged from the earlier implementation.
15 - 20 - Added match statement that processes the extracted verb strings. Because the user enters strings as input, we can't get away from verb strings completely, but we can isolate them in this function. Here we show how we abstracted the verb strings into enum values. In a future step, we might abstract the noun clauses as well.
Command
adjusted, we need to modify all the code locations that depend on the previous implementation. get_input()
and update_state()
need modifications.pub fn get_input() -> Command {
// Prompt
println!("");
print!("> ");
io::stdout().flush().unwrap();
let mut input_str = String::new();
io::stdin()
.read_line(&mut input_str)
.expect("Failed to read move");
println!("");
// Parse & Return
parse(input_str)
}
pub fn update_state(command: &Command) -> String {
let output: String;
match command {
Command::Look(_) => {
output = format!("It is very dark, you can see nothing but the flashing light.")
}
Command::Go(_) => output = format!("It is too dark to move."),
Command::Quit => output = format!("Quitting.\nThank you for playing!"),
Command::Unknown(input_str) => output = format!("I don't know how to '{}'.", input_str),
}
// Return
output
}
Explanation
1-16 Updated version of get_input()
. All lines are the same with the exception of the call to parse()
near the end.
15 - There are two simplifications in the updated version. First is that we don't need to create a Command
struct first. The updated version of parse()
creates and returns the Command
enum. The second change is that since the function returns the Command
object directly, we don't need the separate return line that the prior version had. So, get_input()
is shorter and simpler with the updated implementation.
18-31 - Updated version of update_state()
. The change in the modified implementation is in the match
statement.
22-27 - Modified match
statement with arms described for each of the Command
enum values.
main()
.fn main() {
//
// Introduction and Setup
//
println!("Welcome to Reentry. A space adventure.");
println!("");
println!("You awake in darkness with a pounding headache.");
println!("An alarm is flashing and beeping loudly. This doesn't help your headache.");
println!("");
let mut command: rlib::Command;
let mut output: String;
//
// Main Loop
//
loop {
command = rlib::get_input();
output = rlib::update_state(&command);
rlib::update_screen(output);
if matches!(command, rlib::Command::Quit) {
break;
}
}
//
// Shutdown and Exit
//
println!("Bye!");
}
Explanation
11 - Modified declaration for command that pre-declares the variable name. New Command
enum values will be created by the call to get_input()
on line 18.
17-25 - Modified main loop. This implementation will loop forever until break is called on line 23.
18 - Call to get_input()
. No change from the prior implementation, but this line returns a new Command
each loop.
22-24 - New addition to the main loop to exit when Command::Quit
is parsed.
Progress
parse()
function.
Comments
Post a Comment