In the last lesson, Add the C51 Train, we created a new "c51.rs" file to read the ASCII-art in the "c51.txt" file, which then calls the "draw_image()" function to draw an animated C51 train across the screen. We can generalize that "c51.rs" code to be used for other ASCII-art text files.
If you need to see the entire program up until this point, click here.
Copy all this text and Cut it out of "src/c51.rs", and paste it into "src/extras.rs" (perhaps at the bottom of the file; location really is not all that important), and then make the following edits on the function now in "src/extras.rs":
fn get_c51()image(filename:&str) -> Vec<Vec<String>> { /* This function reads a text file containing a series of ASCII-art image "frames". This gets put into a variable as one long string of UTF-8 characters (think of 'em as ASCII chars, though). This string is then broken down into separate lines, breaking at the newline/end-of-line characters, and each line is pushed onto an inner vector. When a blank line is found, we skip on to the next loop, and create a new inner vector at the same time, and then repeat the above process. Eventually, each of the image frames is put into its own inner vector, and all those inner vectors are put into an outer vector named "outer_vec". */ let mut outer_vec: Vec<Vec<String>> = Vec::new(); // Outer vec holding all the inner frame vecs. // Read the file containing the image, into a variable named "image_string". let image_string = std::fs::read_to_string("src/c51.txt"filename ).expect("Error opening file."); let mut inner_vec: usize = 0; // To keep track of which frame/inner vector we're working with. outer_vec.push(Vec::new()); // Create first frame vec in outer vec, named "frames". // Process each line of the text file that's been read into memory. for each_line in image_string.lines() { // Break-up the single String variable "image_string" at newlines. if each_line != "" { // If the line is not blank, let line_string = each_line.replace("\"", " ").to_string(); // replace quotes with spaces, & convert the &str variable to a String variable outer_vec[inner_vec].push(line_string); // Then copy that String into the current inner frame vec. } else { // Else, if the line is blank, then we're between frames, so ignore the blan line, and inner_vec += 1; // prep for a new inner frame vec, and then outer_vec.push(Vec::new()); // create that new inner frame vec, } // before looping around to the next line. } return outer_vec;} // end of get_c51()} // end of get_image()
Then, in that same "src/extras.rs" file, in the "draw_image()" function, make the following edits:
... // Load ASCII-art into "image" variable (a vector of vectors holding Strings). let image = match kind.as_str() { // Which image are we supposed to draw? "D51" => get_d51(),// D51 is the only one that currently works. // "C51" => get_c51(),"C51" => get_image("./c51.txt"), &_ => { // If the user enters an unknown image name (say, "racecar") ... println!( "Invalid kind. You entered '{}'; the default was used instead.", kind ); get_d51() } }; ...
Note that instead of specifying "src/c51.txt", I specified "./c51.txt". This is because the program, once it's installed, won't know about any "src" directory, but it will know about the current directory. This means that when we run the program using cargo run
, we'll need to be in the "src" directory, so you'll likely need to cd src
before testing the C51 option.
To test it, run cargo run
without any options to get the default train, the D51, then run it with the C51 option, like so: $ cargo run -- --kind c51"
.
Hey, it works!
Now in the next lesson, we'll Add the Little Train.