Решение на Hangman от Емилиан Станков

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

Към профила на Емилиан Станков

Резултати

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

Код

use std::fmt;
use std::fmt::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 {
match &self {
&&GameError::ParseError(ref s) => write!(f, "Command parse failed! Commang: {}", s),
&&GameError::BadGuess(ref s) => write!(f, "You have already guessed that! Guess: {}", s),
&&GameError::InvalidSolution(ref s) => write!(f, "You have provided an invalid solution! Solution: {}", s),
&&GameError::GameOver => write!(f, "The game has ended!")
}
}
}
#[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 lowercased = s.trim().to_lowercase();
let command: Vec<&str> = lowercased.split_whitespace().collect();
if command[0].starts_with("h") {
return Ok(Command::Help);
} else if command[0].starts_with("i") {
return Ok(Command::Info);
} else if command[0].starts_with("q") {
return Ok(Command::Quit);
} else if command[0].starts_with("t") {
if command.len() > 2 {
if command[1].starts_with("l") {
if command[2].len() == 1 {

Метода len брои байтове, не char-ове: https://doc.rust-lang.org/std/primitive.str.html#method.len

Това ти е едната грешка. Дори да беше тествал с кирилица обаче, и да го беше хванал, пак щеше да имаш 14 точки, понеже не проверяваш за празен низ. Тоест, какво става, ако няма command[0]? Паника.

По принцип в Rust е за предпочитане да използваш методи като get, които вземат елемент на индекс или връщат None. В случая, спокйно можеше да вземеш let first_char = command.get(0).and_then(|word| word.chars().get(0)), примерно. Ако ти е трудно да изпарсиш този ред, пробвай да потърсиш из документацията за непознатите методи. Може да погледнеш и моето решение като вдъхновение (аз съм го направил подобно на теб): https://fmi.rust-lang.bg/tasks/4/solutions/43

return Ok(Command::TryLetter(command[2].chars().nth(0).unwrap()));
}
} else if command[1].starts_with("w") {
return Ok(Command::TryWord(String::from(command[2])));
}
}
}
Err(GameError::ParseError(String::from(s)))
}
}
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
solution: String,
ended: bool
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
if solution.is_empty() {
return Err(GameError::InvalidSolution(String::from(solution)));
}
let chars: Vec<char> = solution.chars().collect();
for c in chars {
if !c.is_alphabetic() {
return Err(GameError::InvalidSolution(String::from(solution)));
}
};
Ok(Game {
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
solution: String::from(solution),
ended: false
})
}
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
if self.is_over() {
return Err(GameError::GameOver);
} else if self.attempted_letters.contains(&guess) {
return Err(GameError::BadGuess(guess.to_string()));
} else if self.solution.contains(guess) {
self.attempted_letters.insert(guess);
let chars: Vec<char> = self.solution.chars().collect();
let mut won: bool = true;
for c in chars {
if !self.attempted_letters.contains(&c) {
won = false;
}
};
self.ended = won;
return Ok(true);
} else {
self.attempted_letters.insert(guess);
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.ended = true;
}
return Ok(false);
}
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.is_over() {
return Err(GameError::GameOver);
} else if self.attempted_words.contains(guess) {
return Err(GameError::BadGuess(String::from(guess)));
} else if self.solution == String::from(guess) {
self.attempted_words.insert(String::from(guess));
self.ended = true;
return Ok(true);
} else {
self.attempted_words.insert(String::from(guess));
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.ended = true;
}
return Ok(false);
}
}
pub fn is_over(&self) -> bool {
self.ended
}
}
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_over() && self.attempts_remaining != 0 {
write!(f, "You won! ^_^\nThe word was: {}", &self.solution)
} else if self.is_over() && self.attempts_remaining == 0 {
write!(f, "You lost! :/\nThe word was: {}", &self.solution)
} else {
let mut state: String = String::new();
let chars: Vec<char> = self.solution.chars().collect();
for c in chars {
if self.attempted_letters.contains(&c) {
state.push(c);
} else {
state.push('_');
}
state.push(' ');
};
state = String::from(state.trim());
write!(f, "Attempts remaining: {}\nGuess: {}", &self.attempts_remaining, state)
}
}
}

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

Compiling solution v0.1.0 (file:///tmp/d20171210-6053-nm0x1x/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 5.73 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("try letter я"))', 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("try letter я"))', 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 версия и 5 коментара)