Решение на Wordle от Тодор Кюркчиев
Резултати
- 5 точки от тестове
- 0 бонус точки
- 5 точки общо
- 4 успешни тест(а)
- 11 неуспешни тест(а)
Код
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum GameStatus {
InProgress,
Won,
Lost,
}
#[derive(Debug)]
pub enum GameError {
NotInAlphabet(char),
WrongLength { expected: usize, actual: usize },
GameIsOver(GameStatus),
}
#[derive(Debug)]
pub struct Game {
pub status: GameStatus,
pub attempts: u8,
pub word: Word
}
#[derive(Debug)]
pub struct Word {
pub word: String,
pub guessed: Vec<bool>,
pub alphabet: Vec<char>
}
impl Word {
pub fn new(word: String) -> Word {
let mut guessed = Vec::new();
for _ in 0..word.len() {
guessed.push(false);
}
Word {
word: word,
guessed: guessed,
alphabet: "abcdefghijklmnopqrstuvwxyz".chars().collect()
}
}
pub fn guess(&mut self, c: char) -> Result<(), GameError> {
if !self.alphabet.contains(&c) {
return Err(GameError::NotInAlphabet(c));
}
let mut found = false;
for (i, ch) in self.word.chars().enumerate() {
if ch == c {
self.guessed[i] = true;
found = true;
}
}
if !found {
self.alphabet.retain(|&x| x != c);
}
Ok(())
}
pub fn is_guessed(&self) -> bool {
self.guessed.iter().all(|&x| x)
}
}
impl Game {
/// Конструира нова игра с думи/букви от дадената в `alphabet` азбука. Alphabet е просто низ,
/// в който всеки символ е отделна буква, който вероятно искате да си запазите някак за после.
///
/// Подадената дума с `word` трябва да има само букви от тази азбука. Иначе очакваме да върнете
/// `GameError::NotInAlphabet` грешка с първия символ в `word`, който не е от азбуката.
///
/// Началното състояние на играта е `InProgress` а началния брой опити `attempts` е 0.
///
pub fn new(alphabet: &str, word: &str) -> Result<Self, GameError> {
let mut word_vec = Vec::new();
let mut alphabet_vec = Vec::new();
for c in word.chars() {
if !alphabet.contains(c) {
return Err(GameError::NotInAlphabet(c));
}
word_vec.push(c);
}
for c in alphabet.chars() {
alphabet_vec.push(c);
}
Ok(Game {
status: GameStatus::InProgress,
attempts: 0,
word: Word {
word: word_vec.into_iter().collect(),
guessed: vec![false; word.len()],
alphabet: alphabet_vec
}
})
}
/// Опитва се да познае търсената дума. Опита е в `guess`.
///
/// Ако играта е приключила, тоест статуса ѝ е `Won` или `Lost`, очакваме да върнете
/// `GameIsOver` със статуса, с който е приключила.
///
/// Ако `guess` има различен брой букви от търсената дума, очакваме да върнете
/// `GameError::WrongLength`. Полето `expected` на грешката трябва да съдържа броя букви на
/// търсената дума, а `actual` да е броя букви на опита `guess`.
///
/// Ако `guess` има правилния брой букви, но има буква, която не е от азбуката на играта,
/// очакваме `GameError::NotInAlphabet` както по-горе, с първия символ от `guess`, който не е
/// от азбуката.
///
/// Метода приема `&mut self`, защото всеки валиден опит (такъв, който не връща грешка) се
/// запазва в играта за по-нататък. Метода връща `Word`, което описва освен самите символи на
/// `guess`, и как тези символи са се напаснали на търсената дума. Също така инкрементира
/// `attempts` с 1.
///
/// След опита за напасване на думата, ако всички букви са уцелени на правилните места,
/// очакваме `state` полето да се промени на `Won`. Иначе, ако `attempts` са станали 5,
/// състоянието трябва да е `Lost`.
///
pub fn guess_word(&mut self, guess: &str) -> Result<Word, GameError> {
if self.status == GameStatus::Won || self.status == GameStatus::Lost {
return Err(GameError::GameIsOver(self.status));
}
else if guess.len() != self.word.word.len() {
return Err(GameError::WrongLength {
expected: self.word.word.len(),
actual: guess.len()
});
}
else if guess.chars().any(|c| !self.word.alphabet.contains(&c)) {
return Err(GameError::NotInAlphabet(guess.chars().find(|c| !self.word.alphabet.contains(c)).unwrap()));
}
let mut word = Word {
word: guess.chars().collect(),
guessed: vec![false; guess.len()],
alphabet: self.word.alphabet.clone()
};
for (i, c) in guess.chars().enumerate() {
if self.word.word.contains(*&c) {
word.guessed[i] = true;
}
}
Ok(word)
}
}
use std::fmt;
impl fmt::Display for Word {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut word = String::new();
for (i, c) in self.word.chars().enumerate() {
if self.guessed[i] {
word.push_str(&format!("[{}]", c.to_uppercase()));
}
else if self.word.contains(c) {
word.push_str(&format!("({})", c.to_uppercase()));
}
else {
word.push_str(&format!(">{}<", c.to_uppercase()));
}
}
write!(f, "{}", word)
}
}
impl fmt::Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.word)
}
}
#[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]");
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20230111-3772066-12lqa8j/solution) Finished test [unoptimized + debuginfo] target(s) in 0.82s Running tests/solution_test.rs (target/debug/deps/solution_test-0edbea2040daef01) running 15 tests test solution_test::test_game_display ... FAILED test solution_test::test_game_display_cyrillic ... FAILED test solution_test::test_game_display_german ... FAILED test solution_test::test_game_state_1 ... FAILED test solution_test::test_game_state_2 ... FAILED test solution_test::test_game_state_3 ... FAILED test solution_test::test_word_display ... FAILED test solution_test::test_word_display_bulgarian ... FAILED 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 ... FAILED test solution_test::test_word_not_in_alphabet_on_construction_cyrrilic ... ok test solution_test::test_word_not_in_alphabet_on_guess ... ok test solution_test::test_word_not_in_alphabet_on_guess_cyrillic ... FAILED test solution_test::test_wrong_length ... ok failures: ---- solution_test::test_game_display stdout ---- thread 'solution_test::test_game_display' panicked at 'assertion failed: `(left == right)` left: `"(C)(A)(P)(E)"`, right: `"|_||_||_||_|"`', tests/solution_test.rs:107:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- 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 ---- solution_test::test_game_display_german stdout ---- thread 'solution_test::test_game_display_german' panicked at 'assertion failed: `(left == right)` left: `"(S)(Ü)(SS)"`, right: `"|_||_||_|"`', tests/solution_test.rs:133:5 ---- solution_test::test_game_state_1 stdout ---- thread 'solution_test::test_game_state_1' panicked at 'Expression InProgress does not match the pattern "GameStatus::Won"', tests/solution_test.rs:145: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_game_state_3 stdout ---- thread 'solution_test::test_game_state_3' panicked at 'assertion failed: `(left == right)` left: `0`, right: `1`', tests/solution_test.rs:172:9 ---- solution_test::test_word_display stdout ---- thread 'solution_test::test_word_display' panicked at 'assertion failed: `(left == right)` left: `"[O][P](S)"`, right: `"(O)(P)>S<"`', tests/solution_test.rs:57:5 ---- solution_test::test_word_display_bulgarian stdout ---- thread 'solution_test::test_word_display_bulgarian' panicked at 'assertion failed: `(left == right)` left: `"[Л](А)[Л](Е)"`, right: `"(Л)>А<(Л)>Е<"`', tests/solution_test.rs:77:5 ---- solution_test::test_word_display_german stdout ---- thread 'solution_test::test_word_display_german' panicked at 'assertion failed: `(left == right)` left: `"(S)(Ü)(SS)"`, right: `"|_||_||_|"`', tests/solution_test.rs:87:5 ---- solution_test::test_word_display_with_repetitions stdout ---- thread 'solution_test::test_word_display_with_repetitions' panicked at 'assertion failed: `(left == right)` left: `"[O][O][P](S)"`, right: `"(O)[O](P)>S<"`', tests/solution_test.rs:67:5 ---- solution_test::test_word_not_in_alphabet_on_guess_cyrillic stdout ---- thread 'solution_test::test_word_not_in_alphabet_on_guess_cyrillic' panicked at 'Expression Err(WrongLength { expected: 4, actual: 8 }) does not match the pattern "Err(GameError::NotInAlphabet('х'))"', tests/solution_test.rs:46:5 failures: solution_test::test_game_display solution_test::test_game_display_cyrillic solution_test::test_game_display_german solution_test::test_game_state_1 solution_test::test_game_state_2 solution_test::test_game_state_3 solution_test::test_word_display solution_test::test_word_display_bulgarian solution_test::test_word_display_german solution_test::test_word_display_with_repetitions solution_test::test_word_not_in_alphabet_on_guess_cyrillic test result: FAILED. 4 passed; 11 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--test solution_test`