├── .gitignore ├── words.txt ├── Cargo.toml ├── README.md ├── Cargo.lock └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /words.txt: -------------------------------------------------------------------------------- 1 | dcode,history,microphone,headset,vehicle,computer 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hangman-demo" 3 | version = "0.1.0" 4 | authors = ["Dom "] 5 | 6 | [dependencies] 7 | rand = "0.3" 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hangman-rust 2 | A simple and likely improvable Hangman Game created using the Rust Programming Language. 3 | 4 | I have uploaded a tutorial series for this application on YouTube, you can find it here: 5 | 6 | Part 1 - Reading the Data File 7 | https://www.youtube.com/watch?v=omLBlUWfxO0 8 | 9 | Part 2 - Creating the Letters 10 | https://www.youtube.com/watch?v=qydW0LVC4Mg 11 | 12 | Part 3 - Reading User Input & Logic 13 | https://www.youtube.com/watch?v=xbLI4cJcENk 14 | 15 | Part 4 - Finishing Up 16 | https://www.youtube.com/watch?v=XYyBf9rA408 17 | 18 | Entire Playlist 19 | https://www.youtube.com/watch?v=omLBlUWfxO0&list=PLVvjrrRCBy2Igh_kCtvRr2Np4fMRawn6x 20 | 21 | Remember to subscribe while you're over there 22 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "hangman-demo" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", 6 | ] 7 | 8 | [[package]] 9 | name = "bitflags" 10 | version = "1.0.1" 11 | source = "registry+https://github.com/rust-lang/crates.io-index" 12 | 13 | [[package]] 14 | name = "fuchsia-zircon" 15 | version = "0.3.2" 16 | source = "registry+https://github.com/rust-lang/crates.io-index" 17 | dependencies = [ 18 | "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", 19 | "fuchsia-zircon-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 20 | ] 21 | 22 | [[package]] 23 | name = "fuchsia-zircon-sys" 24 | version = "0.3.2" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | 27 | [[package]] 28 | name = "libc" 29 | version = "0.2.34" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | 32 | [[package]] 33 | name = "rand" 34 | version = "0.3.19" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | dependencies = [ 37 | "fuchsia-zircon 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 38 | "libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", 39 | ] 40 | 41 | [metadata] 42 | "checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" 43 | "checksum fuchsia-zircon 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd510087c325af53ba24f3be8f1c081b0982319adcb8b03cad764512923ccc19" 44 | "checksum fuchsia-zircon-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "08b3a6f13ad6b96572b53ce7af74543132f1a7055ccceb6d073dd36c54481859" 45 | "checksum libc 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "36fbc8a8929c632868295d0178dd8f63fc423fd7537ad0738372bd010b3ac9b0" 46 | "checksum rand 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "9e7944d95d25ace8f377da3ac7068ce517e4c646754c43a1b1849177bbf72e59" 47 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | /* Subscribe to dcode on YouTube! */ 2 | 3 | /* Random Numbers */ 4 | extern crate rand; 5 | use rand::Rng; 6 | 7 | /* File Handling */ 8 | use std::fs::File; 9 | use std::io::prelude::*; 10 | 11 | /* User Input */ 12 | use std::io; 13 | 14 | const ALLOWED_ATTEMPTS: u8 = 5; 15 | 16 | struct Letter { 17 | character: char, 18 | revealed: bool 19 | } 20 | 21 | enum GameProgress { 22 | InProgress, 23 | Won, 24 | Lost 25 | } 26 | 27 | fn main() { 28 | let mut turns_left = ALLOWED_ATTEMPTS; 29 | let selected_word = select_word(); 30 | let mut letters = create_letters(&selected_word); 31 | 32 | println!("Welcome to Hangman!"); 33 | 34 | loop { 35 | println!("\nYou have {} turns left.", turns_left); 36 | display_progress(&letters); 37 | 38 | println!("\nPlease enter a letter to guess:"); 39 | let user_char = read_user_input_character(); 40 | 41 | /* Exit if user enters an asterisk ('*') */ 42 | if user_char == '*' { 43 | break; 44 | } 45 | 46 | /* Update the 'revealed' state of each letter. If the user 47 | has guessed a correct letter, at_least_one_revealed is changed 48 | to true */ 49 | let mut at_least_one_revealed = false; 50 | for letter in letters.iter_mut() { 51 | if letter.character == user_char { 52 | letter.revealed = true; 53 | at_least_one_revealed = true; 54 | } 55 | } 56 | 57 | /* Lose a turn if you make an incorrect guess */ 58 | if !at_least_one_revealed { 59 | turns_left -= 1; 60 | } 61 | 62 | /* Check game progress */ 63 | match check_progress(turns_left, &letters) { 64 | GameProgress::InProgress => continue, 65 | GameProgress::Won => { 66 | println!("\nCongrats! You won! ☺"); 67 | break; 68 | } 69 | GameProgress::Lost => { 70 | println!("\nYou lost! ☹"); 71 | break; 72 | } 73 | } 74 | } 75 | 76 | println!("\nGoodbye!"); 77 | } 78 | 79 | /* Open the file containing list of words and select one at random, returning it 80 | as a String */ 81 | fn select_word() -> String { 82 | let mut file = File::open("words.txt").expect("Could not open file!"); 83 | 84 | let mut file_contents = String::new(); 85 | file.read_to_string(&mut file_contents).expect("An error occured while reading the file!"); 86 | 87 | let available_words: Vec<&str> = file_contents.split(',').collect(); 88 | 89 | /* Select word at random */ 90 | let random_index = rand::thread_rng().gen_range(0, available_words.len()); 91 | 92 | return String::from(available_words[random_index]); 93 | } 94 | 95 | /* Given a word (type String), create a Vector of Letter's from it with default 96 | members and return it */ 97 | fn create_letters(word: &String) -> Vec { 98 | let mut letters: Vec = Vec::new(); 99 | 100 | for c in word.chars() { 101 | letters.push(Letter { 102 | character: c, 103 | revealed: false 104 | }); 105 | } 106 | 107 | return letters; 108 | } 109 | 110 | /* Displays the progress of the game based off Vec 111 | Example output: l _ n g _ _ g _ */ 112 | fn display_progress(letters: &Vec) { 113 | let mut display_string = String::from("Progress:"); // Example: Progress: _ a _ a _ y 114 | 115 | for letter in letters { 116 | display_string.push(' '); 117 | 118 | if letter.revealed { 119 | display_string.push(letter.character); 120 | } else { 121 | display_string.push('_'); 122 | } 123 | 124 | display_string.push(' '); 125 | } 126 | 127 | println!("{}", display_string); 128 | } 129 | 130 | /* Reads a character from user input. If multiple characters are given, 131 | character at first index is returned. In any problematic cases, return 132 | an asterisk (*) */ 133 | fn read_user_input_character() -> char { 134 | let mut user_input = String::new(); 135 | 136 | match io::stdin().read_line(&mut user_input) { 137 | Ok(_) => { 138 | match user_input.chars().next() { 139 | Some(c) => { return c; } 140 | None => { return '*'; } 141 | } 142 | } 143 | Err(_) => { return '*'; } 144 | } 145 | } 146 | 147 | /* Checks the current state (progress) of the game and returns the appropriate 148 | GameProgress member */ 149 | fn check_progress(turns_left: u8, letters: &Vec) -> GameProgress { 150 | /* Determine if all letters have been revealed */ 151 | let mut all_revealed = true; 152 | for letter in letters { 153 | if !letter.revealed { 154 | all_revealed = false; 155 | } 156 | } 157 | 158 | if all_revealed { 159 | return GameProgress::Won; 160 | } 161 | 162 | if turns_left > 0 { 163 | return GameProgress::InProgress; 164 | } 165 | 166 | return GameProgress::Lost; 167 | } 168 | --------------------------------------------------------------------------------