Решение на Hangman от Исмаил Алиджиков

Обратно към всички решения

Към профила на Исмаил Алиджиков

Резултати

  • 13 точки от тестове
  • 0 бонус точки
  • 13 точки общо
  • 13 успешни тест(а)
  • 2 неуспешни тест(а)

Код

use std::fmt::{self, Display};
use std::str::FromStr;
use std::collections::HashSet;
#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let message = match *self {
GameError::ParseError(ref msg) => msg,
GameError::BadGuess(ref msg) => msg,
GameError::InvalidSolution(ref msg) => msg,
GameError::GameOver => "The game is over.",
};
write!(f, "{}", message)
}
}
#[derive(Debug)]
pub enum Command {
TryLetter(char),
TryWord(String),
Info,
Help,
Quit,
}
impl FromStr for Command {
type Err = GameError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let lowercase = s.to_lowercase();
let mut tokens = lowercase.split_whitespace();
if let Some(command) = tokens.next() {
return match command.chars().next() {
Some('h') => Ok(Command::Help),
Some('i') => Ok(Command::Info),
Some('q') => Ok(Command::Quit),
Some('t') => {
if let Some(subcommand) = tokens.next() {
let argument = tokens.next();
if argument == None {
let message = format!("Missing guess for try command.");
return Err(GameError::ParseError(message));
}
match subcommand.chars().next() {
Some('l') => {
if argument.unwrap().len() > 1 {
let message = String::from("Cannot pass word to 'try letter' command.");
return Err(GameError::ParseError(message));
}
let guess = argument.unwrap().chars().next().unwrap();
Ok(Command::TryLetter(guess))
},
Some('w') => {
let guess = String::from(argument.unwrap());
Ok(Command::TryWord(guess))
},
Some(_) | None => {
let message = String::from("Unrecogninized command for 'try'. Try running 'help'.");
Err(GameError::ParseError(message))
},
}
} else {
let message = String::from("Missing command for 'try'. Try running 'help'.");
Err(GameError::ParseError(String::from(message)))
}
},
Some(_) | None => Err(GameError::ParseError(String::from(""))),
}
} else {
let message = String::from("Unrecognized command. Try running 'help'.");
Err(GameError::ParseError(message))
}
}
}

Стабилно решение, с добри проверки навсякъде. Особено ми харесва, че си избрал специфични съобщения за отделните грешки. Единствената грешка идва от това, че len метода брои байтове, а не символи: https://doc.rust-lang.org/std/primitive.str.html#method.len. Това означава, че не работи правилно за кирилица :).

#[derive(Debug)]
pub struct Game {
solution: String,
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
has_won: bool,
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
let solution_string = String::from(solution);
if solution_string.is_empty() {
return Err(GameError::InvalidSolution(String::from("The solution cannot be empty.")));
} else if solution_string.chars().any(|ch| !ch.is_alphabetic()) {
return Err(GameError::InvalidSolution(String::from("The solution cannot contain non alphabetic symbols.")));
}
let game = Game {
solution: solution_string,
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
has_won: false,
};
Ok(game)
}
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
if self.is_over() {
return Err(GameError::GameOver);
}
if self.attempted_letters.contains(&guess) {
let message = format!("The guess {} is already tried.", guess);
return Err(GameError::BadGuess(message));
}
self.attempted_letters.insert(guess);
if !self.solution.contains(guess) {
self.attempts_remaining -= 1;
return Ok(false);
}
self.has_won = self.solution.chars().all(|ch| self.attempted_letters.contains(&ch));
Ok(true)
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.is_over() {
return Err(GameError::GameOver);
}
if self.attempted_words.contains(guess) {
let message = format!("The guess {} is already tried.", guess);
return Err(GameError::BadGuess(message));
}
self.attempted_words.insert(String::from(guess));
if self.solution != guess {
self.attempts_remaining -= 1;
return Ok(false);
}
self.has_won = true;
Ok(true)
}
pub fn is_over(&self) -> bool {
self.has_lost() || self.has_won()
}
pub fn has_lost(&self) -> bool {
self.attempts_remaining == 0 && !self.has_won
}
pub fn has_won(&self) -> bool {
self.has_won
}
}
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.has_won() {
return write!(f, "You won! ^_^\nThe word was: {}", self.solution);
} else if self.has_lost() {
return write!(f, "You lost! :/\nThe word was: {}", self.solution);
}
let guess = self.solution.chars().map(|ch| {
if !self.attempted_letters.contains(&ch) {
return "_".to_string();
}
ch.to_string()
}).collect::<Vec<String>>().join(" ");
write!(f, "Attempts remaining: {}\nGuess: {}", self.attempts_remaining, guess)
}
}

Лог от изпълнението

Compiling solution v0.1.0 (file:///tmp/d20171210-6053-gb9pk3/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 6.33 secs
     Running target/debug/deps/solution-3f98bfa5c86a5dd9

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-3d9e4ea2eafbbc82

running 15 tests
test solution_test::test_command_parsing_cyrillic ... FAILED
test solution_test::test_command_parsing_extra_stuff ... ok
test solution_test::test_command_parsing_full_words ... ok
test solution_test::test_command_parsing_partial_words ... ok
test solution_test::test_command_parsing_spacing ... ok
test solution_test::test_command_parsing_special ... FAILED
test solution_test::test_game_basic ... ok
test solution_test::test_game_cyrillic ... ok
test solution_test::test_game_display ... ok
test solution_test::test_game_error ... ok
test solution_test::test_game_guess_basic ... ok
test solution_test::test_game_guess_state_lose ... ok
test solution_test::test_game_guess_state_won ... ok
test solution_test::test_game_guess_word ... ok
test solution_test::test_game_over_guesses ... ok

failures:

---- solution_test::test_command_parsing_cyrillic stdout ----
	thread 'solution_test::test_command_parsing_cyrillic' panicked at 'Expected Ok(Command::TryLetter('\u{44f}')) to match Err(ParseError("Cannot pass word to \'try letter\' command."))', tests/solution_test.rs:235:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

---- solution_test::test_command_parsing_special stdout ----
	thread 'solution_test::test_command_parsing_special' panicked at 'Expected Ok(Command::TryLetter('\u{44f}')) to match Err(ParseError("Cannot pass word to \'try letter\' command."))', tests/solution_test.rs:194:4


failures:
    solution_test::test_command_parsing_cyrillic
    solution_test::test_command_parsing_special

test result: FAILED. 13 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (1 версия и 3 коментара)

Исмаил качи първо решение на 07.12.2017 23:43 (преди почти 8 години)