Решение на Hangman от Биляна Добрева

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

Към профила на Биляна Добрева

Резултати

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

Код

#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
impl std::fmt::Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let error = match *self {
GameError::ParseError(ref s) => format!("Error with parsing {}!",s),
GameError::BadGuess(ref s) => format!("Error: {} is a bad guess!",s),
GameError::InvalidSolution(ref s) => format!("Error: {} is invalid solution!",s),
GameError::GameOver => String::from("Game Over!!!"),
};
write!(f,"{}",error)
}
}
use std::str::FromStr;
#[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> {
if s == ""||s == " " {
return Err(GameError::ParseError(s.to_string()));
}
let v : Vec<_> = s.trim().split_whitespace().collect();
if v[0].starts_with("Q")||v[0].starts_with("q") {
return Ok(Command::Quit);
}
if v[0].starts_with("I")||v[0].starts_with("i") {
return Ok(Command::Info);
}
if v[0].starts_with("H")||v[0].starts_with("h") {
return Ok(Command::Help);
}
if (v[0].starts_with("T")||v[0].starts_with("t"))&&(v.len()>=3)&&(v[1].starts_with("W")||v[1].starts_with("w")) {
return Ok(Command::TryWord(v[2].to_string()));
}
if (v[0].starts_with("T")||v[0].starts_with("t"))&&(v.len()>=3)&&(v[1].starts_with("L")||v[1].starts_with("l")) {
if v[2].len()==1 {

Метода len брои байтове, а не char-ове: https://doc.rust-lang.org/std/primitive.str.html#method.len. Това значи, че буква на кирилица би имала дължина 2.

Освен това, едно подобрение на този код би било да lowercase-неш командите предварително -- така не би ти се налагало да проверяваш ръчно за малки и главни букви (което би могла лесно да сбъркаш в един момент).

for c in v[2].chars() {
return Ok(Command::TryLetter(c));
}
}
else {
return Err(GameError::ParseError(s.to_string()));
}
}
Err(GameError::ParseError(s.to_string()))
}
}
use std::collections::HashSet;
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
pub answer : Vec<(char,char)>,
pub won : bool,
pub lost :bool,
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
if solution=="" {
return Err(GameError::InvalidSolution(solution.to_string()));
}
let flag :bool = true;
for c in solution.clone().chars(){
if !c.is_alphabetic() {
flag == false;
}
}
if flag == false {
return Err(GameError::InvalidSolution(solution.to_string()));
}

Хм, мисля, че тук ти е другата грешка. Забележи, че в цикъла не мутираш променливата flag. Не си написала flag = false, а flag == false. Още повече, че не мисля, че дори ти трябва този флаг -- ако намериш символ, който не е азбучен, директно можеш да return-неш грешката. Не би трябвало и да ти трябва този clone, понеже итерираш през символите през итератора, който ти връща .chars(), не консумираш низа.

Пробвай да опростиш този код, мисля, че ще стане доста по-четим.

let mut v: Vec<char> = Vec::new();
for c in solution.chars(){
v.push(c);
}
let v1 : Vec<(char,char)> = v.into_iter().map(|x| (x,'_')).collect();
Ok( Game { attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts, answer: v1,
won: false, lost: false, } )
}
}
impl 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) {
return Err(GameError::BadGuess(guess.to_string()));
}
if !self.answer.clone().into_iter().any(|x| x.0==guess) {
self.attempted_letters.insert(guess);
self.attempts_remaining= self.attempts_remaining- 1;
if self.attempts_remaining == 0 {
self.lost=true;
}
return Ok(false);
}
else {
self.attempted_letters.insert(guess);
let mut answer: Vec<(char,char)> = Vec::new();
for x in self.answer.clone().iter(){
if x.0 == guess {
answer.push((x.0,guess));
}
else {
answer.push((x.0,x.1));
}
}
self.answer=answer;
if self.answer.clone().into_iter().all(|(x,y)| x==y) {
self.won=true;
}
return Ok(true);
}
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.is_over() {
return Err(GameError::GameOver);
}
let g= guess.clone().to_string();
if self.attempted_words.contains(&g) {
return Err(GameError::BadGuess(g));
}
let v : (Vec<char>, Vec<char>) = self.answer.clone().iter().cloned().unzip();
let mut v1 :Vec<char> = Vec::new();
for c in guess.chars() {
v1.push(c);
}
let v2 : Vec<(char,char)> =v.0.into_iter().zip(v1.into_iter()).collect();
if !v2.clone().into_iter().all(|(x,y)| x==y) {
self.attempted_words.insert(g);
self.attempts_remaining= self.attempts_remaining- 1;
if self.attempts_remaining == 0 {
self.lost=true;
}
return Ok(false);
}
else {
self.attempted_words.insert(g);
self.answer=v2;
self.won=true;
return Ok(true);
}
}
pub fn is_over(&self) -> bool {
if self.won == true||self.lost == true {
return true;
}
return false;
}
}
use std::fmt::{self, Display};
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s:String ;
if self.is_over() {
let s1 :(String,String)=self.answer.clone().into_iter().unzip();
if self.won==true {
s=format!("You won! ^_^\nThe word was: {} ",s1.0);
}
else {
s= format!("You lost! :/\nThe word was: {} ",s1.0);
}
}
else {
let mut s1= self.answer[0].1.to_string().to_owned();
let mut i=1;
while i<=self.answer.len()-1{
s1.push_str(" ");
s1.push_str(&self.answer[i].1.to_string());
i = i +1;
}
s= format!("Attempts remaining: {}\nGuess: {}",self.attempts_remaining,s1);
}
write!(f,"{}",s)
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
}
}

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

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

running 1 test
test tests::it_works ... ok

test result: ok. 1 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 ... FAILED
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

---- solution_test::test_game_error stdout ----
	thread 'solution_test::test_game_error' panicked at 'Expected Some(GameError::InvalidSolution(_)) to match None', tests/solution_test.rs:38:4


failures:
    solution_test::test_command_parsing_cyrillic
    solution_test::test_command_parsing_special
    solution_test::test_game_error

test result: FAILED. 12 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out

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

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