Решение на Wordle от Стойчо Кьосев

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

Към профила на Стойчо Кьосев

Резултати

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

Код

use std::fmt;
use std::collections::HashSet;
use std::collections::HashMap;
// use Box;
type DynHashsetT = Box<HashSet<i32>>;
type WordT = HashMap<char, DynHashsetT>;
type AlphabetT = HashSet<char>;
type WordListT = [Word; 6];
#[derive(Debug, 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,
cmp : WordComparator,
states : WordListT
}
#[derive(Debug)]
struct WordComparator {
alphabet: AlphabetT,
current_word : WordT,
current_word_len : usize,
}
struct WordComparatorData {
data : Word,
won : bool
}
impl WordComparator {
fn new(alph : &str, word: &str) -> Result<WordComparator, GameError> {
let mut alphabet: AlphabetT = AlphabetT::new();
let mut current_word : WordT = WordT::new();
let current_word_len: usize = word.chars().count();
for c in alph.chars() { alphabet.insert(c); }
for c in word.chars() {
if !alphabet.contains(&c) {
return Err(GameError::NotInAlphabet(c));
}
}
for c in word.chars().enumerate() {
match current_word.get_mut(&c.1) {
Some(current_hash_set) => {
current_hash_set.insert(c.0 as i32);
},
None => {
let mut to_insert : DynHashsetT = DynHashsetT::new(HashSet::new());
to_insert.insert(c.0 as i32);
current_word.insert(c.1, to_insert);
}
}
}
Ok ( WordComparator { alphabet, current_word, current_word_len } )
}
fn compare(&self, input_word: &str) -> Result<WordComparatorData, GameError> {
if input_word.chars().count() != self.current_word_len {
return Err ( GameError::WrongLength { expected: self.current_word_len, actual: input_word.len() } );
}
let mut result = String::from("");
let mut won = true;
// Провери дали всички букви от дувата са валидни
for c in input_word.chars() {
if !self.alphabet.contains(&c) {
return Err ( GameError::NotInAlphabet(c) );
}
}
// Ако са валидни строим изходния низ
for c in input_word.chars().enumerate() {
match self.current_word.get_key_value(&c.1) {
Some(current_hash_set) => {
let correct_place : bool = current_hash_set.1.contains(&(c.0 as i32));
if correct_place {
// На правилното място е
let p = format!("[{}]", char::to_uppercase(c.1));
result.push_str(&p);
}
else {
// В думата е, но не е на правилното място
let p = format!("({})", char::to_uppercase(c.1));
result.push_str(&p);
won = false;
}
},
None => {
// Тук няма символа в думата
let p = format!(">{}<", char::to_uppercase(c.1));
result.push_str(&p);
won = false;
}
}
}
let data = Word {data : result};
let to_return = WordComparatorData { data, won };
Ok (to_return)
}
}
#[derive(Debug, PartialEq)]
pub struct Word {
data : String,
}
impl Word {
fn new(len: usize) -> Word {
let mut data = String::new();
for _ in 0..len {
data.push_str("|_|");
}
Word { data }
}
fn empty() -> Word {
let data = String::from("");
Word { data }
}
fn empty_array() -> WordListT {
[Word::empty(), Word::empty(), Word::empty(), Word::empty(), Word::empty(), Word::empty()]
}
}
impl fmt::Display for Word {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.data)
}
}
impl fmt::Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut res = String::new();
for w in 0..self.attempts+1 {
let c_str = format!("{}\n", self.states[w as usize]);
res.push_str(&c_str);
}
// Махаме ненужния /n
res.pop();
write!(f, "{}", res)
}
}
impl Game {
pub fn new(alphabet: &str, word: &str) -> Result<Self, GameError> {
let cmp = WordComparator::new(alphabet, word)?;
let status = GameStatus::InProgress;
let attempts : u8 = 0;
let mut states: [Word; 6] = Word::empty_array();
states[0] = Word::new(word.len());
Ok (
Game {
status, attempts, cmp, states
}
)
}
pub fn guess_word(&mut self, guess: &str) -> Result<Word, GameError> {
if self.attempts > 4 {
self.status = GameStatus::Lost;
return Err( GameError::GameIsOver(GameStatus::Lost) );
}
match &self.status {
GameStatus::Won => { return Err( GameError::GameIsOver(GameStatus::Won) ); },
_ => {}
}
self.attempts += 1;
let comparator_result = self.cmp.compare(guess);
match comparator_result {
Ok(comparator_data) => {
let res = comparator_data.data;
self.states[self.attempts as usize] = Word { data: res.to_string() };
if comparator_data.won {
self.status = GameStatus::Won;
}
return Ok( res );
},
Err(a) => { return Err ( a ); }
}
}
}
// Накратко за решението
// Имам мап ( char -> Set<usize> )
// В сета държа всички индекси на които този чар се среща
// 1. Ако чара не е в мапа няма го в думата
// 2. Ако чара е в мапа но индексът му не е в сета значи не си е на мястото
// 3. Ако чара е в мапа и индексът му е в сета значи си е на мястото
#[cfg(test)]
mod creation_tests {
use crate::{Game, GameError};
#[test]
fn create_game_succ() {
let g = Game::new("abcdefg", "abb");
match g {
Ok(_) => {assert!(true);},
_ => {assert!(false);}
}
}
#[test]
fn create_game_not_in_alphabet() {
let g = Game::new("abcd", "cde");
match g {
Err ( GameError::NotInAlphabet('e') ) => {assert!(true);},
_ => {assert!(false);}
}
}
}
#[cfg(test)]
mod runtime_tests {
use crate::{Game, GameError, GameStatus};
#[test]
fn make_single_move() {
let mut g = Game::new("abcderfhigelo", "hello").unwrap();
assert_eq!(g.guess_word("hallo").unwrap().to_string(), "[H]>A<[L][L][O]");
}
#[test]
fn make_more_moves() {
let mut g = Game::new("abcderfhigelo", "hello").unwrap();
assert_eq!(g.guess_word("hallo").unwrap().to_string(), "[H]>A<[L][L][O]");
assert_eq!(g.guess_word("hella").unwrap().to_string(), "[H][E][L][L]>A<");
}
#[test]
fn stolen_test_one() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
assert_eq!(game.guess_word("route").unwrap().to_string(), "[R]>O<(U)>T<(E)");
println!("{}", game.guess_word("rules").unwrap().to_string());
assert_eq!(game.guess_word("rules").unwrap().to_string(), "[R](U)>L<(E)[S]");
assert_eq!(game.guess_word("rebus").unwrap().to_string(), "[R][E][B][U][S]");
}
#[test]
fn stolen_test_two() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "foobar").unwrap();
assert_eq!(game.guess_word("oopsie").unwrap().to_string(), "(O)[O]>P<>S<>I<>E<");
}
#[test]
fn stolen_test_three() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
assert_eq!(game.to_string(), "|_||_||_||_||_|");
game.guess_word("route");
assert_eq!(game.to_string(), "|_||_||_||_||_|\n[R]>O<(U)>T<(E)");
}
#[test]
fn not_in_alphabet() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
let g = game.guess_word("ребус");
match g {
Err ( GameError::NotInAlphabet (_) ) => {assert!(true);},
_ => {assert!(false);}
}
}
#[test]
fn wrong_len() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
let g = game.guess_word("word.len()");
match g {
Err ( GameError::WrongLength { expected: 5, actual: 10 } ) => {assert!(true);},
_ => {assert!(false);}
}
}
// Първата грешка която хващаме е, че имаме некоректна дължина.
// После гледаме азбуката
#[test]
fn return_wrong_len_first() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
let g = game.guess_word("Тва не е от азбуката ама трябва да хване дължината");
match g {
Err ( GameError::WrongLength { expected: _, actual: _ } ) => {assert!(true);},
_ => {assert!(false);}
}
}
#[test]
fn test_win() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
let x = game.guess_word("rebus");
assert_eq!(game.status, GameStatus::Won);
let y = game.guess_word("a");
match y {
Err (GameError::GameIsOver(GameStatus::Won)) => {assert!(true)},
_ => {assert!(false);}
}
}
#[test]
fn test_loss_and_in_progress() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(english_letters, "rebus").unwrap();
assert_eq!(game.status, GameStatus::InProgress);
game.guess_word("rebes");
assert_eq!(game.status, GameStatus::InProgress);
game.guess_word("rebes");
assert_eq!(game.status, GameStatus::InProgress);
game.guess_word("rebes");
assert_eq!(game.status, GameStatus::InProgress);
game.guess_word("rebes");
assert_eq!(game.status, GameStatus::InProgress);
game.guess_word("rebes");
assert_eq!(game.status, GameStatus::InProgress);
// Имам пет опита губя на шестия fair play
let y = game.guess_word("rebes");
assert_eq!(game.status, GameStatus::Lost);
match y {
Err ( GameError::GameIsOver(GameStatus::Lost) ) => {assert!(true);},
_ => {assert!(false);}
}
}
#[test]
fn test_against_different_lenguages() {
let bg = "абвгдежзийклмнопрстребус";
let mut game = Game::new(bg, "ребус").unwrap();
let y = game.guess_word("ребус");
assert_eq!(game.cmp.current_word_len, 5);
assert_eq!(game.status, GameStatus::Won);
game.guess_word("ааааа");
assert_eq!(game.status, GameStatus::Won);
}
#[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-ui95at/solution)
    Finished test [unoptimized + debuginfo] target(s) in 0.90s
     Running tests/solution_test.rs (target/debug/deps/solution_test-0edbea2040daef01)

running 15 tests
test solution_test::test_game_display ... ok
test solution_test::test_game_display_cyrillic ... FAILED
test solution_test::test_game_display_german ... FAILED
test solution_test::test_game_state_1 ... ok
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_german ... FAILED
test solution_test::test_word_display_bulgarian ... ok
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 коментара)

Стойчо качи първо решение на 24.11.2022 15:48 (преди почти 3 години)