Plan C - Add More Drawings

Plan C - Add More Drawings

Click Here if you would like to see a complete code listing to this point.

Click here to return to "Plan C - Add Tweaks".

We already have some of the code for animating a boat and a plane in our program. Now let's actually add those images.

The original "sl" program doesn't have these images, and I'm not an artist, so I went Googling for ASCII art boats, and came up with this:

        |\          
        | \         
        |  \        
        |___\       
     \--|----/      
      \_____/        

There was lots of artwork to choose from, but I wanted to keep it simple. I would have preferred that the sail pointed to the left instead of to the right, but beggars can't afford to be starving artists, right? Or something like that.

However, rather than add this as a constant to our existing "images.rs" file as you might expect us to do, there's a new trick I just learned, which I'd like to try.

Put this image into its own text file, named "boat.txt" (making sure to get the spacing in the lines even, like in the image below:

Spacing around ASCII-art boat.

You'll recall that earlier we decided against putting each drawing in its own text file, because those text files would then have to follow the program and be find-able by the program, which increased the chances that the program would work as we wanted it to. But with this new technique, using the "include_str!()" macro, we can read a text file at compile time, which means the text file only has to be with the source code, and find-able, when the program is compiled. Once compiled, the executable is standalone, and no longer needs access to the text file. It's a Win-Win!

So now that we have the art in its own file, "boat.txt", adding it to our program is ridiculously easy:

get_options.rs
/* get_options.rs */

use crate::images::*;
const BOAT: &str = include_str!(r"boat.txt");     // The "r" here, for "raw", seems to be optional.
...
    // Set "kind".
    let (kind, image_string, tail) = match (&switches.kind.to_uppercase()).as_str() {
        "D" | "D51" => ("D51".to_string(), D51.to_string(), COALCAR.to_string()),
        "C" | "C51" => (String::from("C51"), String::from(C51), COALCAR.to_string()),
        "L" | "LITTLE" => ("LITTLE".to_owned(), LITTLE.to_owned(), "".to_string()),
        "J" | "JACK" => (String::from("JACK"), JACK.to_string(), "".to_string()),
        "B" | "BOAT" => (String::from("BOAT"), BOAT.to_string(), "".to_string()),
...

Run the program with a command like $ cargo run -- --kind=boat and watch the craft sail across your screen.

Notice, however, that if you don't have a blank line at the top of the drawing, then the top of the sail is broken. This is because our other images have a newline at the top of the image, which our program then strips out by stripping out the first character of the string that it reads as a constant. Our program doesn't know not to strip the first character out of this constant. The easy fix is just to add a blank line to the top of this image:

boat.txt
  
        |\          
        | \         
        |  \        
        |___\       
     \--|----/      
      \_____/        

Also notice that there are not side-delimiting single-quote marks. The way our program is written, these are optional. The program will strip them out if they're found, and will do nothing if they're not found. I prefer to have them in the original art; it makes it easier to see the limits of the drawing:

boat.txt

'   |\    '
'   | \   '
'   |  \  '
'   |___\ '
'\--|----/'
' \_____/ '

Also notice that if you don't have a newline after the final single-quote, our program does strip out that final single-quote, leaving a "blip" in the animation. Let's add that space, and a correction to our instructions to the ASCII-artists (with text-justification adjustments in blue.

boat.txt

'   |\    '
'   | \   '
'   |  \  '
'   |___\ '
'\--|----/'
' \_____/ ' 

images.rs
...
 To add an art image, just follow the pattern of the images below, making sure to give
 a unique name as the constant value, and making sure to put a blank line between each
 animation frame, and after the final frame, and making sure to enclose each side of
 each line of each frame in single-quotes (which will be converted by the program into
 spaces). Then add an entry for your constant in both the documentation string of the
 "What kind of object" arg in the "struct Args" section of the "get_options.rs" file,
 and in the "Set 'kind'." section of the "parse_opts()" function in that same file.
 ...

Adding a plane is just as easy. Start by finding a suitable ASCII-art image, and saving it to its own file, such as "plane.txt". I found the first frame below on line, and then duplicated that for the other three frames, and made adjustments to the propellor on frames 2 and 3 for animation purposes.

plane.txt

'   ____       _  '
'| __\_\_o____/_| '
'<[___\_\_-----<  '
'|  o'            '
'                 '
'                 '

'   ____       _  '
'/ __\_\_o____/_| '
'<[___\_\_-----<  '
'\  o'            '
'                 '
'                 '

'   ____       _  '
'| __\_\_o____/_| '
'<[___\_\_-----<  '
'|  o'            '
'                 '
'                 '

'   ____       _  '
'\ __\_\_o____/_| '
'<[___\_\_-----<  '
'/  o'            '
'                 '
'                 '

and add the appropriate changes to "get_options.rs":

get_options.rs
/* get_options.rs */

use crate::images::*;
const BOAT: &str = include_str!(r"boat.txt");     // The "r" here, for "raw", seems to be optional.
const PLANE: &str = include_str!(r"plane.txt");
...
    // Set "kind".
    let (kind, image_string, tail) = match (&switches.kind.to_uppercase()).as_str() {
        "D" | "D51" => ("D51".to_string(), D51.to_string(), COALCAR.to_string()),
        "C" | "C51" => (String::from("C51"), String::from(C51), COALCAR.to_string()),
        "L" | "LITTLE" => ("LITTLE".to_owned(), LITTLE.to_owned(), "".to_string()),
        "J" | "JACK" => (String::from("JACK"), JACK.to_string(), "".to_string()),
        "B" | "BOAT" => (String::from("BOAT"), BOAT.to_string(), "".to_string()),
        "P" | "PLANE" => (String::from("BOAT"), PLANE.to_string(), "".to_string()),
...

and then fly to your heart's content.

You can even get fancier if you desire. Notice I did not add the single-quote marks to the image that I found on the 'Net. Again, I found the first image, added some propeller "action" marks, then duplicated it three more times, and then "tweaked" the propeller action-marks in the last three to make them distinct.

twinengine.txt

                                                                .____   __ _      
   __o__   _______ _ _  _                                     /     /             
   \    ~\                                                  /      /              
     \     '\                                         ..../      .'               
      . ' ' . ~\                                      ' /       /                 
     .  |    .  ~ \  .+~\~ ~ ' ' " " ' ' ~ - - - - - -''_      /                  
    .  <#  .  - - -/' . ' \  __                          '~ - \                   
     .. -           ~-.._ / |__|  ( )  ( )  ( )  0  o    _ _    ~ .               
   .-'                                               .- ~    '-.    -.            
  <                      . ~ ' ' .             . - ~             ~ -.__~_. _ _    
    ~- .       N121PP  .       /  . . . . ,- ~                                    
          ' ~ - - - - =.   <#>    .         \.._                                  
                      .     ~      ____ _ .. ..  .- .                             
                       .  /      '        ~ -.        ~ -.                        
                         ' . . '               ~ - .       ~-.                    
                                                     ~ - .      ~ .               
                                                            ~ -...0..~. ____      

                                                                .____   __ _      
   __o__   _______ _ _  _                                     /     /             
   \    ~\                                                  /      /              
     \     '\                                         ..../      .'               
      . ' ' . ~\                                      ' /       /                 
     .  _    .  ~ \  .+~\~ ~ ' ' " " ' ' ~ - - - - - -''_      /                  
    .  <# -.  - - -/' . ' \  __                          '~ - \                   
     .. -           ~-.._ / |__|  ( )  ( )  ( )  0  o    _ _    ~ .               
   .-'                                               .- ~    '-.    -.            
  <                      . ~ ' ' .             . - ~             ~ -.__~_. _ _    
    ~- .       N121PP  .          . . . . ,- ~                                    
          ' ~ - - - - =. - <#> -  .         \.._                                  
                      .     ~      ____ _ .. ..  .- .                             
                       .         '        ~ -.        ~ -.                        
                         ' . . '               ~ - .       ~-.                    
                                                     ~ - .      ~ .               
                                                            ~ -...0..~. ____      

                                                                .____   __ _      
   __o__   _______ _ _  _                                     /     /             
   \    ~\                                                  /      /              
     \     '\                                         ..../      .'               
      . ' ' . ~\                                      ' /       /                 
     .    /  .  ~ \  .+~\~ ~ ' ' " " ' ' ~ - - - - - -''_      /                  
    . -<#  .  - - -/' . ' \  __                          '~ - \                   
     .. -           ~-.._ / |__|  ( )  ( )  ( )  0  o    _ _    ~ .               
   .-'                                               .- ~    '-.    -.            
  <                      . ~ ' ' .             . - ~             ~ -.__~_. _ _    
    ~- .       N121PP  . \        . . . . ,- ~                                    
          ' ~ - - - - =.   <#>    .         \.._                                  
                      .     ~      ____ _ .. ..  .- .                             
                       .      \  '        ~ -.        ~ -.                        
                         ' . . '               ~ - .       ~-.                    
                                                     ~ - .      ~ .               
                                                            ~ -...0..~. ____      

                                                                .____   __ _      
   __o__   _______ _ _  _                                     /     /             
   \    ~\                                                  /      /              
     \     '\                                         ..../      .'               
      . ' ' . ~\                                      ' /       /                 
     .  _    .  ~ \  .+~\~ ~ ' ' " " ' ' ~ - - - - - -''_      /                  
    .  <# -.  - - -/' . ' \  __                          '~ - \                   
     .. -           ~-.._ / |__|  ( )  ( )  ( )  0  o    _ _    ~ .               
   .-'                                               .- ~    '-.    -.            
  <                      . ~ ' ' .             . - ~             ~ -.__~_. _ _    
    ~- .       N121PP  .          . . . . ,- ~                                    
          ' ~ - - - - =. - <#> -  .         \.._                                  
                      .     ~      ____ _ .. ..  .- .                             
                       .         '        ~ -.        ~ -.                        
                         ' . . '               ~ - .       ~-.                    
                                                     ~ - .      ~ .               
                                                            ~ -...0..~. ____      

get_options.rs
/* get_options.rs */

use crate::images::*;
const BOAT: &str = include_str!(r"boat.txt");     // The "r" here, for "raw", seems to be optional.
const PLANE: &str = include_str!(r"plane.txt");
const TWIN: &str = include_str!(r"twinengine.txt");
...
    // Set "kind".
    let (kind, image_string, tail) = match (&switches.kind.to_uppercase()).as_str() {
        "D" | "D51" => ("D51".to_string(), D51.to_string(), COALCAR.to_string()),
        "C" | "C51" => (String::from("C51"), String::from(C51), COALCAR.to_string()),
        "L" | "LITTLE" => ("LITTLE".to_owned(), LITTLE.to_owned(), "".to_string()),
        "J" | "JACK" => (String::from("JACK"), JACK.to_string(), "".to_string()),
        "B" | "BOAT" => (String::from("BOAT"), BOAT.to_string(), "".to_string()),
        "P" | "PLANE" => (String::from("BOAT"), PLANE.to_string(), "".to_string()),
        "T" | "TWIN" | "TWINENGINE" => (String::from("TWIN"), TWIN.to_string(), "".to_string()),
...

Even though we did not plan ahead to make the twin-engine plane an option on the command-line, in our "Args" struct, the program still honors the option. This is because the "Args" struct merely defines the argument and the type of data it can handle, but it does not define what the contents can be, and our "Set 'kind'" match statement is able to handle the otherwise-unknown "t" option. There's really nothing in the "Args" struct that would limit what can be entered in as the "kind"; we could use a command like cargo run -- --kind gorilla, and the Clap system would happily pass that on to our program; it's our "Set 'kind'" match section that really determines what can and can't be entered. But still, if we want the help screens to be accurate, we should touch the doc-line in the "Args" struct:

get_options.rs
...
/// What Kind of object? D51 | C51 | Little | Jack | Boat | Plane | Twin
#[arg(short, long, default_value_t = String::from("D51"))]
kind: String,
...

See if you can add this large and detailed motorcycle:

motorcycle.txt

                         _                                                                  
                       ,S/  .e.##ee                                                         
                     ,SS/_ /#####""                                                         
                   ,SSSSSS`|##|                                                             
                 ,'|SSSSSS/%##|                                                             
                 | ;SSSSS/%%%/ .-""-._                           __..ooo._.sSSSSSSSSS"7.    
                 |/SSSSS/%%%/.'       `._ __               _.od888888888888"'  '"SSSSS"     
             ___  `"SSS/%%%/"-.,sSSs._   8888o._    __.o888888888""SS""    `-._    `7Sb     
      _.sssSSSSSSSSSSS/`%%/ ,sSSSSSSSSS-.888888888888888888888"'.S"         ,SSS""   `7b    
   ,+""       ```""SS/%%./,sSSSSSSSS".   `"888888888888888"'.sSS"         ,SS"'        `S.  
                    /%%%/sSSSSSSSS"   `s.   `"88888888"'.sSSSS"         ,S"'             7  
                   /%%%/ `SSSSSSS$$,..sSS`-.   `"88'.sSSSSSSSS._     ,-'                    
                  /%%%/    `SSSS$$$$SSS",\\\`-.   `"SSSSSS"  8"""7.-'                       
                  /`%/      `SS$$$SSS,dP,s.\\//`-.   `SS" ,'`8       ,ee888888ee.           
        ,oo8888oo/ /         `"""",d88Pd8888./,-'/`.  `,-._`d'    ,d88888888888888b.        
     ,d888888888/ /8b.          d8888Pd8888888bd88b`.  :_._`8   ,888888"'    '""88888.      
   ,888P'      / /"888b.       d88888`8888888Pd8888 :  :_`-.( ,d8888.__           7888b.    
  d88P        / /   `788b     (88888:. `8888Pd88888 ;  ; `-._ 8888P_Z_.>-""--.._   `8888    
 d88'     ,--/ /      `88b     `88888b`. `8P 78888";  ;      `"*88_,"   s88s.       `888b   
d88'    ,',$/ /$$$$.   `88b      `8888b `. `"'88"_/_,'`-._         `-.d8"88"8P.      `888.  
888    ; ,$$$$$$$$$'    888        `"8'   `---------------`-.._      8888888888       888'  
888    : `$$$$$$$':     888                                 '888b`-._8s888888"P      .888'  
788.   `  `$$$$'  ;     88P                                  8888.   "8878888P'      d888   
 88b    `.  `"' ,'     d88'                                  '888b     '88s8"'     .d888'   
 `88b.    `-..-'      d88'                                    '888b.             .dd888'    
   788b.            ,888'                                       7888b._       _.d8888P      
    `7888oo..__..oo888'                                          `"8888888888888888"'       
      `"7888888888P"'                                               `"788 mGk 8P"'          

Hint: For the match statement, you might try: "M" | "CYCLE" | "MOTORCYCLE" => (String::from("CYCLE"), CYCLE.to_string(), "".to_string()), .

My first attempt crashed, because I had two blank lines at the start of the image, instead of just one, and my second attempt crashed, because there are two missing spaces at the end of the last line. Ideally, our program would check for dirty data like these two things, but we're not going to do that in this tutorial.

This project is pretty close to complete. Let's work on just a few more things: making the tailing coalcar optional, implementing an accident mode, and creating some smoke patterns. Let's start by Making the Coalcar Optional.