Решение на Wordle от Никола Ласков
Резултати
- 19 точки от тестове
- 0 бонус точки
- 19 точки общо
- 14 успешни тест(а)
- 1 неуспешни тест(а)
Код
use std::fmt;
#[derive(Debug)]
pub enum GameStatus {
InProgress,
Won,
Lost,
}
#[derive(Debug)]
pub enum GameError {
NotInAlphabet(char),
WrongLength { expected: usize, actual: usize },
GameIsOver(GameStatus),
}
#[derive(Debug,Clone)]
pub enum LetterStatus {
Match,
Found,
NotFound,
None,
}
#[derive(Debug)]
pub struct Game {
pub status: GameStatus,
pub attempts: u8,
pub word: Word,
pub alphabet: String,
pub guesses: Vec<String>,
}
#[derive(Debug)]
pub struct Word {
pub letters: Vec<char>,
pub matches: Vec<LetterStatus>,
}
impl Word{
fn from_str(s: &str) -> Self{
let letters:Vec<char> = s.chars().collect();
let matches = vec![LetterStatus::None; letters.len()];
Word { letters , matches}
}
fn new_word(alphabet: &str, word: &str) -> Result<Self, GameError>{
let word_arr:Vec<char> = word.chars().collect();
for letter in word_arr.iter(){
match alphabet.find(*letter) {
Some(_) => continue,
None => return Err(GameError::NotInAlphabet(*letter)),
};
}
Ok(Word::from_str(word))
}
fn compare(&self,word: Word) -> Result<(Self,bool),GameError>{
let mut word = word;
let mut found = true;
if self.letters.len() != word.letters.len() {
let expected = self.letters.len();
let actual = word.letters.len();
return Err(GameError::WrongLength{expected,actual});
}
for i in 0..word.letters.len(){
if self.letters[i]==word.letters[i]{
word.matches[i] = LetterStatus::Match;
}
else if self.letters.contains(&word.letters[i]){
word.matches[i] = LetterStatus::Found;
found = false;
}
else {
word.matches[i] = LetterStatus::NotFound;
found = false;
}
}
Ok((word,found))
}
fn print(&self) ->String{
let mut result = String::from("");
for i in 0..self.letters.len(){
match self.matches[i]{
LetterStatus::Match =>{
result.push_str("[");
result.push_str(&self.letters[i].to_uppercase().to_string());
result.push_str("]");
}
LetterStatus::Found =>{
result.push_str("(");
result.push_str(&self.letters[i].to_uppercase().to_string());
result.push_str(")");
}
LetterStatus::NotFound =>{
result.push_str(">");
result.push_str(&self.letters[i].to_uppercase().to_string());
result.push_str("<");
}
LetterStatus::None =>{
result.push_str("|_|");
}
}
}
result
}
}
impl Game {
/// Конструира нова игра с думи/букви от дадената в `alphabet` азбука. Alphabet е просто низ,
/// в който всеки символ е отделна буква, който вероятно искате да си запазите някак за после.
///
/// Подадената дума с `word` трябва да има само букви от тази азбука. Иначе очакваме да върнете
/// `GameError::NotInAlphabet` грешка с първия символ в `word`, който не е от азбуката.
///
/// Началното състояние на играта е `InProgress` а началния брой опити `attempts` е 0.
///
pub fn new(alphabet: &str, word: &str) -> Result<Self, GameError> {
match Word::new_word(alphabet, word){
Ok(word) => {
let mut guesses=Vec::new();
let temp = word.print();
guesses.push(temp);
Ok(Game{status: GameStatus::InProgress,
attempts:0,
word:word,
alphabet:alphabet.to_string(),
guesses:guesses})
}
Err(e) => return Err(e),
}
}
/// Опитва се да познае търсената дума. Опита е в `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> {
match self.status{
GameStatus::Lost => return Err(GameError::GameIsOver(GameStatus::Lost)),
GameStatus::Won => return Err(GameError::GameIsOver(GameStatus::Won)),
_ => (),
}
let guess = match Word::new_word(self.alphabet.as_str(),guess){
Ok(word) => word,
Err(e) => return Err(e),
};
let (guess,found) = match self.word.compare(guess){
Ok((word,found)) => (word,found),
Err(e) => return Err(e),
};
let temp = guess.print();
self.guesses.push(temp);
self.attempts = self.attempts+1;
if found{
self.status = GameStatus::Won;
return Ok(guess);
}
if self.attempts == 5{
self.status = GameStatus::Lost;
return Ok(guess);
}
Ok(guess)
}
fn print(&self)->String{
let mut result = String::from("");
for i in 0..self.guesses.len(){
if i!=0 {
result.push_str("\n");
}
result.push_str(&self.guesses[i]);
}
result
}
}
impl fmt::Display for Word {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}" ,self.print())
}
}
impl fmt::Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}" ,self.print())
}
}
#[test]
fn latin_test() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
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]");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B][C]");
}
#[test]
fn cyrillic_test(){
let cyrillic_letters = "абвгдежзийклмнопрстуфхцчшщъьюя";
assert!(Game::new(cyrillic_letters, "!!!").is_err());
let mut game = Game::new(&String::from(cyrillic_letters), "абв").unwrap();
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 0);
assert_eq!(game.to_string(), "|_||_||_|");
assert_eq!(game.guess_word("абв").unwrap().to_string(), "[А][Б][В]");
assert_eq!(game.to_string(), "|_||_||_|\n[А][Б][В]");
}
#[test]
fn symbols_test(){
let symbols = "!.@?<>:'{}[]()*&^%$#@+=_-|/,ß∂僩¥";
assert!(Game::new(symbols, "abc").is_err());
let mut game = Game::new(&String::from(symbols), "]()").unwrap();
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 0);
assert_eq!(game.to_string(), "|_||_||_|");
assert_eq!(game.guess_word("]()").unwrap().to_string(), "[]][(][)]");
assert_eq!(game.to_string(), "|_||_||_|\n[]][(][)]");
}
#[test]
fn full_game_win_test(){
let english_letters = "abcdefghijklmnopqrstuvwxyz";
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("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 1);
assert_eq!(game.guess_word("adb").unwrap().to_string(), "[A]>D<(B)");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A]>D<(B)");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 2);
assert_eq!(game.guess_word("abc").unwrap().to_string(), "[A][B][C]");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A]>D<(B)\n[A][B][C]");
assert!(matches!(game.status, GameStatus::Won));
}
#[test]
fn full_game_lost(){
let english_letters = "abcdefghijklmnopqrstuvwxyz";
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.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 1);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 2);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 3);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 4);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::Lost));
}
#[test]
fn errors_test(){
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(&String::from(english_letters), "abc").unwrap();
//Wrong alphabet to create game
assert!(Game::new(english_letters, "!!!").is_err());
//Wrong alphabet to guess in the game
assert!(game.guess_word("абв").is_err());
//Wrong length of the word
assert!(game.guess_word("abcd").is_err());
let _ = game.guess_word("abc");
//Game is already over
assert!(game.guess_word("abg").is_err());
}
Лог от изпълнението
Compiling solution v0.1.0 (/tmp/d20230111-3772066-2kxgqk/solution) Finished test [unoptimized + debuginfo] target(s) in 0.78s 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 ... ok test solution_test::test_game_display_german ... ok test solution_test::test_game_state_1 ... ok test solution_test::test_game_state_2 ... ok test solution_test::test_game_state_3 ... ok test solution_test::test_word_display ... ok test solution_test::test_word_display_german ... ok test solution_test::test_word_display_bulgarian ... ok test solution_test::test_word_display_with_repetitions ... ok test solution_test::test_word_not_in_alphabet_on_construction ... ok 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 ... ok test solution_test::test_wrong_length ... FAILED failures: ---- solution_test::test_wrong_length stdout ---- thread 'solution_test::test_wrong_length' panicked at 'Expression Err(NotInAlphabet(' ')) does not match the pattern "Err(GameError::WrongLength { expected: 4, actual: 5 })"', tests/solution_test.rs:98:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: solution_test::test_wrong_length test result: FAILED. 14 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s error: test failed, to rerun pass `--test solution_test`
История (2 версии и 0 коментара)
Никола качи решение на 24.11.2022 15:52 (преди почти 3 години)
use std::fmt;
#[derive(Debug)]
pub enum GameStatus {
InProgress,
Won,
Lost,
}
#[derive(Debug)]
pub enum GameError {
NotInAlphabet(char),
WrongLength { expected: usize, actual: usize },
GameIsOver(GameStatus),
}
#[derive(Debug,Clone)]
pub enum LetterStatus {
Match,
Found,
NotFound,
None,
}
#[derive(Debug)]
pub struct Game {
pub status: GameStatus,
pub attempts: u8,
pub word: Word,
pub alphabet: String,
pub guesses: Vec<String>,
}
#[derive(Debug)]
pub struct Word {
pub letters: Vec<char>,
pub matches: Vec<LetterStatus>,
}
impl Word{
fn from_str(s: &str) -> Self{
let letters:Vec<char> = s.chars().collect();
let matches = vec![LetterStatus::None; letters.len()];
Word { letters , matches}
}
fn new_word(alphabet: &str, word: &str) -> Result<Self, GameError>{
let word_arr:Vec<char> = word.chars().collect();
for letter in word_arr.iter(){
match alphabet.find(*letter) {
Some(_) => continue,
None => return Err(GameError::NotInAlphabet(*letter)),
};
}
Ok(Word::from_str(word))
}
fn compare(&self,word: Word) -> Result<(Self,bool),GameError>{
let mut word = word;
let mut found = true;
if self.letters.len() != word.letters.len() {
let expected = self.letters.len();
let actual = word.letters.len();
return Err(GameError::WrongLength{expected,actual});
}
for i in 0..word.letters.len(){
if self.letters[i]==word.letters[i]{
word.matches[i] = LetterStatus::Match;
}
else if self.letters.contains(&word.letters[i]){
word.matches[i] = LetterStatus::Found;
found = false;
}
else {
word.matches[i] = LetterStatus::NotFound;
found = false;
}
}
Ok((word,found))
}
fn print(&self) ->String{
let mut result = String::from("");
for i in 0..self.letters.len(){
match self.matches[i]{
LetterStatus::Match =>{
result.push_str("[");
result.push_str(&self.letters[i].to_uppercase().to_string());
result.push_str("]");
}
LetterStatus::Found =>{
result.push_str("(");
result.push_str(&self.letters[i].to_uppercase().to_string());
result.push_str(")");
}
LetterStatus::NotFound =>{
result.push_str(">");
result.push_str(&self.letters[i].to_uppercase().to_string());
result.push_str("<");
}
LetterStatus::None =>{
result.push_str("|_|");
}
}
}
result
}
}
impl Game {
/// Конструира нова игра с думи/букви от дадената в `alphabet` азбука. Alphabet е просто низ,
/// в който всеки символ е отделна буква, който вероятно искате да си запазите някак за после.
///
/// Подадената дума с `word` трябва да има само букви от тази азбука. Иначе очакваме да върнете
/// `GameError::NotInAlphabet` грешка с първия символ в `word`, който не е от азбуката.
///
/// Началното състояние на играта е `InProgress` а началния брой опити `attempts` е 0.
///
pub fn new(alphabet: &str, word: &str) -> Result<Self, GameError> {
match Word::new_word(alphabet, word){
Ok(word) => {
let mut guesses=Vec::new();
let temp = word.print();
guesses.push(temp);
Ok(Game{status: GameStatus::InProgress,
attempts:0,
word:word,
alphabet:alphabet.to_string(),
guesses:guesses})
}
Err(e) => return Err(e),
}
}
/// Опитва се да познае търсената дума. Опита е в `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> {
match self.status{
GameStatus::Lost => return Err(GameError::GameIsOver(GameStatus::Lost)),
GameStatus::Won => return Err(GameError::GameIsOver(GameStatus::Won)),
_ => (),
}
let guess = match Word::new_word(self.alphabet.as_str(),guess){
Ok(word) => word,
Err(e) => return Err(e),
};
let (guess,found) = match self.word.compare(guess){
Ok((word,found)) => (word,found),
Err(e) => return Err(e),
};
let temp = guess.print();
self.guesses.push(temp);
+ self.attempts = self.attempts+1;
if found{
self.status = GameStatus::Won;
return Ok(guess);
}
- self.attempts = self.attempts+1;
+
if self.attempts == 5{
self.status = GameStatus::Lost;
return Ok(guess);
}
Ok(guess)
}
fn print(&self)->String{
let mut result = String::from("");
for i in 0..self.guesses.len(){
if i!=0 {
result.push_str("\n");
}
result.push_str(&self.guesses[i]);
}
result
}
}
impl fmt::Display for Word {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}" ,self.print())
}
}
impl fmt::Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}" ,self.print())
}
}
#[test]
fn latin_test() {
let english_letters = "abcdefghijklmnopqrstuvwxyz";
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]");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B][C]");
}
#[test]
fn cyrillic_test(){
let cyrillic_letters = "абвгдежзийклмнопрстуфхцчшщъьюя";
assert!(Game::new(cyrillic_letters, "!!!").is_err());
let mut game = Game::new(&String::from(cyrillic_letters), "абв").unwrap();
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 0);
assert_eq!(game.to_string(), "|_||_||_|");
assert_eq!(game.guess_word("абв").unwrap().to_string(), "[А][Б][В]");
assert_eq!(game.to_string(), "|_||_||_|\n[А][Б][В]");
}
#[test]
fn symbols_test(){
let symbols = "!.@?<>:'{}[]()*&^%$#@+=_-|/,ß∂僩¥";
assert!(Game::new(symbols, "abc").is_err());
let mut game = Game::new(&String::from(symbols), "]()").unwrap();
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 0);
assert_eq!(game.to_string(), "|_||_||_|");
assert_eq!(game.guess_word("]()").unwrap().to_string(), "[]][(][)]");
assert_eq!(game.to_string(), "|_||_||_|\n[]][(][)]");
}
#[test]
fn full_game_win_test(){
let english_letters = "abcdefghijklmnopqrstuvwxyz";
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("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 1);
assert_eq!(game.guess_word("adb").unwrap().to_string(), "[A]>D<(B)");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A]>D<(B)");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 2);
assert_eq!(game.guess_word("abc").unwrap().to_string(), "[A][B][C]");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A]>D<(B)\n[A][B][C]");
assert!(matches!(game.status, GameStatus::Won));
}
#[test]
fn full_game_lost(){
let english_letters = "abcdefghijklmnopqrstuvwxyz";
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.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 1);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 2);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 3);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::InProgress));
assert_eq!(game.attempts, 4);
assert_eq!(game.guess_word("abd").unwrap().to_string(), "[A][B]>D<");
assert_eq!(game.to_string(), "|_||_||_|\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<\n[A][B]>D<");
assert!(matches!(game.status, GameStatus::Lost));
}
#[test]
fn errors_test(){
let english_letters = "abcdefghijklmnopqrstuvwxyz";
let mut game = Game::new(&String::from(english_letters), "abc").unwrap();
//Wrong alphabet to create game
assert!(Game::new(english_letters, "!!!").is_err());
//Wrong alphabet to guess in the game
assert!(game.guess_word("абв").is_err());
- //Wrong lenght of the word
+ //Wrong length of the word
assert!(game.guess_word("abcd").is_err());
let _ = game.guess_word("abc");
//Game is already over
assert!(game.guess_word("abg").is_err());
}