Решение на Wordle от Николай Паев

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

Към профила на Николай Паев

Резултати

  • 15 точки от тестове
  • 0 бонус точки
  • 15 точки общо
  • 11 успешни тест(а)
  • 4 неуспешни тест(а)

Код

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GameStatus {
InProgress,
Won,
Lost,
}
#[derive(Debug, PartialEq)]
pub enum GameError {
NotInAlphabet(char),
WrongLength { expected: usize, actual: usize },
GameIsOver(GameStatus),
}
#[derive(Debug)]
pub struct Game {
pub status: GameStatus,
pub attempts: u8,
word: String,
word_char_count: usize,
alphabet: HashSet<char>,
history: Vec<Word>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Word {
letters: Vec<Letter>,
}
#[derive(Debug, Clone, PartialEq)]
enum Letter {
Unknown,
FullMatch(char),
PartialMatch(char),
NoMatch(char),
}
impl Game {
pub fn new(alphabet: &str, word: &str) -> Result<Self, GameError> {
let mut set = HashSet::with_capacity(alphabet.len());
alphabet.chars().for_each(|c| -> () {
set.insert(c);
});
for c in word.chars() {
if !set.contains(&c) {
return Err(GameError::NotInAlphabet(c));
}
}
let word = word.to_string();
let word_char_count = word.chars().count();
let letters = vec![Letter::Unknown; word.len()];
Ok(Game {
status: GameStatus::InProgress,
attempts: 0,
word,
word_char_count,
alphabet: set,
history: vec![Word { letters }],
})
}
pub fn guess_word(&mut self, guess: &str) -> Result<Word, GameError> {
match self.status {
GameStatus::Won | GameStatus::Lost => Err(GameError::GameIsOver(self.status)),
GameStatus::InProgress => {
let guess_char_count = guess.chars().count();
if guess_char_count != self.word_char_count {
return Err(GameError::WrongLength {
expected: self.word_char_count,
actual: guess_char_count,
});
}
if guess == self.word {
self.status = GameStatus::Won;
} else if self.attempts == 5 {
self.status = GameStatus::Lost;
}
self.attempts += 1;
let mut letters = Vec::new();
for (guess_char, word_char) in guess.chars().zip(self.word.chars()) {
if !self.alphabet.contains(&guess_char) {
return Err(GameError::NotInAlphabet(guess_char));
}
if guess_char == word_char {
letters.push(Letter::FullMatch(guess_char));
} else if self.word.contains(guess_char) {
letters.push(Letter::PartialMatch(guess_char));
} else {
letters.push(Letter::NoMatch(guess_char));
}
}
let word = Word { letters };
self.history.push(word.clone());
Ok(word)
}
}
}
}
use std::{collections::HashSet, fmt};
fn combine(br1: &str, ch: &char, br2: &str) -> String {
br1.chars()
.chain(ch.to_uppercase())
.chain(br2.chars())
.collect::<String>()
}
impl fmt::Display for Letter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let result = match self {
Letter::Unknown => String::from("|_|"),
Letter::FullMatch(ch) => combine("[", ch, "]"),
Letter::PartialMatch(ch) => combine("(", ch, ")"),
Letter::NoMatch(ch) => combine(">", ch, "<"),
};
write!(f, "{}", result)
}
}
impl fmt::Display for Word {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let result = self
.letters
.iter()
.map(|letter| -> String { format!("{}", letter) })
.fold(String::new(), |acc, s| acc + &s);
write!(f, "{}", result)
}
}
impl fmt::Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.history.iter();
// Game history should not be empty:
let first = format!("{}", iter.next().unwrap());
let rest = iter
.map(|word| format!("\n{}", word))
.fold(String::new(), |acc, s| acc + &s);
write!(f, "{}{}", first, rest)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_basic() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
// Конструираме по два различни начина, just in case -- няма причина да не работи и с двата.
assert!(Game::new(english_letters, "!!!").is_err());
let mut game = Game::new(&String::from(english_letters), "abc").unwrap();
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 0);
assert_eq!(game.to_string(), "|_||_||_|");
assert_eq!(game.guess_word("abc").unwrap().to_string(), "[A][B][C]");
}
#[test]
fn test_english_win() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "scone").unwrap();
assert_eq!(game.to_string(), "|_||_||_||_||_|");
assert_eq!(
game.guess_word("paddy").unwrap().to_string(),
">P<>A<>D<>D<>Y<"
);
assert_eq!(game.to_string(), "|_||_||_||_||_|\n>P<>A<>D<>D<>Y<");
assert_eq!(game.status, GameStatus::InProgress);
//Errors should not be considered as attempts
assert_eq!(
game.guess_word("longer"),
Err(GameError::WrongLength {
expected: 5,
actual: 6
})
);
assert_eq!(game.guess_word("лотус"), Err(GameError::NotInAlphabet('л')));
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(
game.guess_word("scent").unwrap().to_string(),
"[S][C](E)[N]>T<"
);
assert_eq!(
game.to_string(),
"|_||_||_||_||_|\n>P<>A<>D<>D<>Y<\n[S][C](E)[N]>T<"
);
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(
game.guess_word("scone").unwrap().to_string(),
"[S][C][O][N][E]"
);
assert_eq!(
game.to_string(),
"|_||_||_||_||_|\n>P<>A<>D<>D<>Y<\n[S][C](E)[N]>T<\n[S][C][O][N][E]"
);
assert_eq!(game.status, GameStatus::Won);
// Try to guess 6 times
assert_eq!(
game.guess_word("random"),
Err(GameError::GameIsOver(GameStatus::Won))
);
assert_eq!(
game.guess_word("random"),
Err(GameError::GameIsOver(GameStatus::Won))
);
assert_eq!(
game.guess_word("random"),
Err(GameError::GameIsOver(GameStatus::Won))
);
assert_eq!(game.status, GameStatus::Won);
}
#[test]
fn test_english_lost() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
assert_eq!(game.to_string(), "|_||_||_||_||_|");
assert_eq!(
game.guess_word("route").unwrap().to_string(),
"[R]>O<(U)>T<(E)"
);
assert_eq!(game.to_string(), "|_||_||_||_||_|\n[R]>O<(U)>T<(E)");
assert_eq!(game.status, GameStatus::InProgress);
//Errors should not be considered as attempts
assert_eq!(
game.guess_word("longer"),
Err(GameError::WrongLength {
expected: 5,
actual: 6
})
);
assert_eq!(game.guess_word("лoute"), Err(GameError::NotInAlphabet('л')));
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(
game.guess_word("rogue").unwrap().to_string(),
"[R]>O<>G<[U](E)"
);
assert_eq!(
game.to_string(),
"|_||_||_||_||_|\n[R]>O<(U)>T<(E)\n[R]>O<>G<[U](E)"
);
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(
game.guess_word("range").unwrap().to_string(),
"[R]>A<>N<>G<(E)"
);
assert_eq!(
game.to_string(),
"|_||_||_||_||_|\n[R]>O<(U)>T<(E)\n[R]>O<>G<[U](E)\n[R]>A<>N<>G<(E)"
);
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(
game.guess_word("linux").unwrap().to_string(),
">L<>I<>N<[U]>X<"
);
assert_eq!(
game.to_string(),
"|_||_||_||_||_|\n[R]>O<(U)>T<(E)\n[R]>O<>G<[U](E)\n[R]>A<>N<>G<(E)\n>L<>I<>N<[U]>X<"
);
assert_eq!(game.status, GameStatus::InProgress);
//duplicate letters
assert_eq!(
game.guess_word("rrage").unwrap().to_string(),
"[R](R)>A<>G<(E)"
);
assert_eq!(game.to_string(),"|_||_||_||_||_|\n[R]>O<(U)>T<(E)\n[R]>O<>G<[U](E)\n[R]>A<>N<>G<(E)\n>L<>I<>N<[U]>X<\n[R](R)>A<>G<(E)");
assert_eq!(game.status, GameStatus::Lost);
// Try to guess 6 times
assert_eq!(
game.guess_word("random"),
Err(GameError::GameIsOver(GameStatus::Lost))
);
assert_eq!(
game.guess_word("raнdoм"),
Err(GameError::GameIsOver(GameStatus::Lost))
);
assert_eq!(game.status, GameStatus::Lost);
}
#[test]
fn test_german_uppercase() {
let german_letters = "abcdefghijklmnopqrstuvwxyzäöüß";
let mut game = Game::new(german_letters, "süß").unwrap();
assert_eq!(game.guess_word("füß").unwrap().to_string(), ">F<[Ü][SS]");
assert_eq!(game.status, GameStatus::InProgress);
}
#[test]
fn test_bulgarian() {
let bulgarian_letters = "абвгдежзийклмнопрстуфхцчьъюя";
let mut game = Game::new(bulgarian_letters, "свят").unwrap();
assert_eq!(game.guess_word("word"), Err(GameError::NotInAlphabet('w')));
assert_eq!(
game.guess_word("световно"),
Err(GameError::WrongLength {
expected: 4,
actual: 8
})
);
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(game.guess_word("сито").unwrap().to_string(), "[С]>И<(Т)>О<");
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(game.guess_word("свят").unwrap().to_string(), "[С][В][Я][Т]");
assert_eq!(game.status, GameStatus::Won);
}
#[test]
fn test_empty() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "").unwrap();
assert_eq!(game.to_string(), "");
assert_eq!(
game.guess_word("random"),
Err(GameError::WrongLength {
expected: 0,
actual: 6
})
);
assert_eq!(game.status, GameStatus::InProgress);
assert_eq!(game.guess_word("").unwrap().to_string(), "");
assert_eq!(game.status, GameStatus::Won);
assert_eq!(game.to_string(), "\n");
}
}

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

Compiling solution v0.1.0 (/tmp/d20230111-3772066-ke8tsv/solution)
    Finished test [unoptimized + debuginfo] target(s) in 0.96s
     Running tests/solution_test.rs (target/debug/deps/solution_test-0edbea2040daef01)

running 15 tests
test solution_test::test_game_display_cyrillic ... FAILED
test solution_test::test_game_display ... ok
test solution_test::test_game_state_1 ... ok
test solution_test::test_game_display_german ... FAILED
test solution_test::test_game_state_2 ... FAILED
test solution_test::test_game_state_3 ... ok
test solution_test::test_word_display ... ok
test solution_test::test_word_display_bulgarian ... ok
test solution_test::test_word_display_german ... FAILED
test solution_test::test_word_not_in_alphabet_on_construction ... ok
test solution_test::test_word_display_with_repetitions ... ok
test solution_test::test_word_not_in_alphabet_on_guess ... ok
test solution_test::test_word_not_in_alphabet_on_construction_cyrrilic ... ok
test solution_test::test_wrong_length ... ok
test solution_test::test_word_not_in_alphabet_on_guess_cyrillic ... ok

failures:

---- solution_test::test_game_display_cyrillic stdout ----
thread 'solution_test::test_game_display_cyrillic' panicked at 'assertion failed: `(left == right)`
  left: `"|_||_||_||_||_||_|"`,
 right: `"|_||_||_|"`', tests/solution_test.rs:119:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- solution_test::test_game_display_german stdout ----
thread 'solution_test::test_game_display_german' panicked at 'assertion failed: `(left == right)`
  left: `"|_||_||_||_||_|"`,
 right: `"|_||_||_|"`', tests/solution_test.rs:133:5

---- solution_test::test_game_state_2 stdout ----
thread 'solution_test::test_game_state_2' panicked at 'Expression InProgress does not match the pattern "GameStatus::Lost"', tests/solution_test.rs:159:5

---- solution_test::test_word_display_german stdout ----
thread 'solution_test::test_word_display_german' panicked at 'assertion failed: `(left == right)`
  left: `"|_||_||_||_||_|"`,
 right: `"|_||_||_|"`', tests/solution_test.rs:87:5


failures:
    solution_test::test_game_display_cyrillic
    solution_test::test_game_display_german
    solution_test::test_game_state_2
    solution_test::test_word_display_german

test result: FAILED. 11 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

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

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

Николай качи първо решение на 19.11.2022 18:01 (преди 3 месеца)