Решение на Hangman от Йоанна Николова
Към профила на Йоанна Николова
Резултати
- 13 точки от тестове
- 0 бонус точки
- 13 точки общо
- 13 успешни тест(а)
- 2 неуспешни тест(а)
Код
Лог от изпълнението
Compiling solution v0.1.0 (file:///tmp/d20171210-6053-12lxlx0/solution) Finished dev [unoptimized + debuginfo] target(s) in 5.47 secs Running target/debug/deps/solution-3f98bfa5c86a5dd9 running 0 tests test result: ok. 0 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 ... ok 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("Invalid command!"))', 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("Invalid command!"))', tests/solution_test.rs:194:4 failures: solution_test::test_command_parsing_cyrillic solution_test::test_command_parsing_special test result: FAILED. 13 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--test solution_test'
История (4 версии и 4 коментара)
Йоанна качи решение на 06.12.2017 12:26 (преди почти 8 години)
#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
-
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self{
GameError::ParseError(ref msg) =>
write!(f, "ParseError: {}", msg),
GameError::BadGuess(ref msg) =>
write!(f, "BadGuess: {} ", msg),
GameError::InvalidSolution(ref msg) =>
write!(f, "InvalidSolution: {}", msg),
GameError::GameOver =>
write!(f, "GameOver! Sorry you lost. :("),
}
}
}
-
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> {
let command = s.to_lowercase();
let mut command_iter = command.split_whitespace();
let first = command_iter.next();
let second = command_iter.next();
let third = command_iter.next();
-
match (first, second, third){
(None, None, None) =>
Err(GameError::ParseError(String::from("There's no command"))),
-
(Some(command), None, None) =>
match_command(command),
-
(Some(_), Some(_), None) =>
Err(GameError::ParseError(String::from("There's no command with one argument"))),
-
(Some(command), Some(attr), Some(param)) =>
match_command_with_arg(command, attr, param),
-
_ => panic!(),
}
}
}
-
fn match_command(command: &str) -> Result<Command, GameError>{
if "info".starts_with(command) {
Ok(Command::Info)
}else if "help".starts_with(command) {
Ok(Command::Help)
}else if "quit".starts_with(command) {
Ok(Command::Quit)
}else {
Err(GameError::ParseError(String::from("Invalid command!")))
}
}
-
fn match_command_with_arg(command:&str, attr: &str, param: &str)
-> Result<Command, GameError>{
if "try".starts_with(command) {
if "word".starts_with(attr){
return Ok(Command::TryWord(String::from(param)));
}else if "letter".starts_with(attr) {
if param.len() == 1 {
return Ok(Command::TryLetter(param.chars().next().unwrap()));
}
}
}
Err(GameError::ParseError(String::from("Invalid command!")))
}
-
use std::collections::HashSet;
-
#[derive(Debug)]
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
word: String,
unguessed: HashSet<char>,
}
-
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
let solution = solution.trim();
let is_valid_word : bool = !solution.is_empty() &&
solution.chars().fold(true, {
|res, symbol|
symbol.is_alphabetic() & res
});
if !is_valid_word {
Err(GameError::InvalidSolution(
String::from("No word to be guess. Read the rules of game!!")
))
}
else{
Ok(Game{
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
word: String::from(solution),
unguessed: solution.chars().collect(),
})
}
}
-
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
if self.attempts_remaining == 0{
Err(GameError::GameOver)
}else if self.attempted_letters.contains(&guess){
Err(GameError::BadGuess(
String::from("The letter is already guessed")))
}else if self.unguessed.contains(&guess) {
self.unguessed.remove(&guess);
self.attempted_letters.insert(guess);
Ok(true)
}else{
self.attempts_remaining -= 1;
self.attempted_letters.insert(guess);
Ok(false)
}
}
-
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.attempts_remaining == 0{
Err(GameError::GameOver)
}else if self.attempted_words.contains(guess){
Err(GameError::BadGuess(String::from("You already try this word")))
- }else if self.word.contains(guess){
+ }else if self.word == guess{
self.unguessed= self.unguessed
.difference(&guess.chars().collect())
.map(|&x| x). collect();
self.attempted_words.insert(String::from(guess));
Ok(true)
}else {
self.attempts_remaining -= 1;
self.attempted_words.insert(String::from(guess));
Ok(false)
}
}
-
pub fn is_over(&self) -> bool {
self.attempts_remaining == 0 || self.unguessed.is_empty()
}
}
-
use std::fmt::{self, Display};
-
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.unguessed.is_empty() {
write!(f, "You won.\nThe word is: {}", self.word)
}else if self.attempts_remaining == 0 {
write!(f, "You lost.\nThe word was: {}", self.word)
}else{
write!(f, "Attempts remaining: {}\nGuess:{}",
self.attempts_remaining,
self.word.chars().map(|x|
if self.attempted_letters.contains(&x){
format!(" {}", x)
}
else {
" _".to_string()
}
).collect::<String>()
)
}
}
}
Всичко ли е като по условието?
Йоанна качи решение на 08.12.2017 03:38 (преди почти 8 години)
#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
+
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self{
GameError::ParseError(ref msg) =>
write!(f, "ParseError: {}", msg),
GameError::BadGuess(ref msg) =>
write!(f, "BadGuess: {} ", msg),
GameError::InvalidSolution(ref msg) =>
write!(f, "InvalidSolution: {}", msg),
GameError::GameOver =>
write!(f, "GameOver! Sorry you lost. :("),
}
}
}
+
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> {
- let command = s.to_lowercase();
- let mut command_iter = command.split_whitespace();
- let first = command_iter.next();
- let second = command_iter.next();
- let third = command_iter.next();
- match (first, second, third){
- (None, None, None) =>
- Err(GameError::ParseError(String::from("There's no command"))),
- (Some(command), None, None) =>
- match_command(command),
- (Some(_), Some(_), None) =>
- Err(GameError::ParseError(String::from("There's no command with one argument"))),
- (Some(command), Some(attr), Some(param)) =>
- match_command_with_arg(command, attr, param),
- _ => panic!(),
+ let command_norm = s.to_lowercase();
+ let mut command =command_norm.split_whitespace();
+
+ if equal_command(["info"].iter(), &command) {
+ Ok(Command::Info)
+ }else if equal_command(["help"].iter(), &command){
+ Ok(Command::Help)
+ }else if equal_command(["quit"].iter(), &command){
+ Ok(Command::Quit)
+ }else if command.clone().count() >= 3 &&
+ (equal_command(["try", "letter"].iter(), &command)) {
+ match command.nth(2){
+ None => Err(GameError::ParseError(String::from("Invalid command!"))),
+ Some(val) =>{
+ if val.len() == 1{
+ Ok(Command::TryLetter(val.chars().next()
+ .unwrap()))
+ }else{
+ Err(GameError::ParseError(String::from("Invalid command!")))
+ }
+ }
+ }
+
+ }else if command.clone().count() >= 3 &&
+ (equal_command(["try", "word"].iter(), &command)) {
+ match command.nth(2){
+ None => Err(GameError::ParseError(String::from("Invalid command!"))),
+ Some(val) => Ok(Command::TryWord(val.to_string())),
+ }
+ }else{
+ Err(GameError::ParseError(String::from("Invalid command!")))
}
}
+
+
}
-fn match_command(command: &str) -> Result<Command, GameError>{
- if "info".starts_with(command) {
- Ok(Command::Info)
- }else if "help".starts_with(command) {
- Ok(Command::Help)
- }else if "quit".starts_with(command) {
- Ok(Command::Quit)
- }else {
- Err(GameError::ParseError(String::from("Invalid command!")))
- }
+
+fn equal_command(lhs_command: std::slice::Iter<&str> ,
+ rhs_command: &std::str::SplitWhitespace) -> bool{
+ lhs_command.zip(rhs_command.clone()).all( |(l_command, r_command)|
+ l_command.starts_with(r_command)
+ )
}
-fn match_command_with_arg(command:&str, attr: &str, param: &str)
- -> Result<Command, GameError>{
- if "try".starts_with(command) {
- if "word".starts_with(attr){
- return Ok(Command::TryWord(String::from(param)));
- }else if "letter".starts_with(attr) {
- if param.len() == 1 {
- return Ok(Command::TryLetter(param.chars().next().unwrap()));
- }
- }
- }
- Err(GameError::ParseError(String::from("Invalid command!")))
-}
+
use std::collections::HashSet;
-#[derive(Debug)]
+
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
word: String,
unguessed: HashSet<char>,
}
+
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
let solution = solution.trim();
let is_valid_word : bool = !solution.is_empty() &&
- solution.chars().fold(true, {
- |res, symbol|
- symbol.is_alphabetic() & res
- });
+ solution.chars().all(|x| x.is_alphabetic());
if !is_valid_word {
Err(GameError::InvalidSolution(
String::from("No word to be guess. Read the rules of game!!")
))
}
else{
Ok(Game{
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
word: String::from(solution),
unguessed: solution.chars().collect(),
})
}
}
+
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
if self.attempts_remaining == 0{
Err(GameError::GameOver)
}else if self.attempted_letters.contains(&guess){
Err(GameError::BadGuess(
String::from("The letter is already guessed")))
}else if self.unguessed.contains(&guess) {
self.unguessed.remove(&guess);
self.attempted_letters.insert(guess);
Ok(true)
}else{
self.attempts_remaining -= 1;
self.attempted_letters.insert(guess);
Ok(false)
}
}
+
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.attempts_remaining == 0{
Err(GameError::GameOver)
}else if self.attempted_words.contains(guess){
Err(GameError::BadGuess(String::from("You already try this word")))
}else if self.word == guess{
- self.unguessed= self.unguessed
- .difference(&guess.chars().collect())
- .map(|&x| x). collect();
+ self.unguessed.clear();
self.attempted_words.insert(String::from(guess));
Ok(true)
}else {
self.attempts_remaining -= 1;
self.attempted_words.insert(String::from(guess));
Ok(false)
}
}
+
pub fn is_over(&self) -> bool {
self.attempts_remaining == 0 || self.unguessed.is_empty()
}
}
+
use std::fmt::{self, Display};
+
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.unguessed.is_empty() {
- write!(f, "You won.\nThe word is: {}", self.word)
+ write!(f, "You won.\n The word is: {}", self.word)
}else if self.attempts_remaining == 0 {
- write!(f, "You lost.\nThe word was: {}", self.word)
+ write!(f, "You lost.\n The word was: {}", self.word)
}else{
write!(f, "Attempts remaining: {}\nGuess:{}",
self.attempts_remaining,
self.word.chars().map(|x|
if self.attempted_letters.contains(&x){
format!(" {}", x)
}
else {
" _".to_string()
}
).collect::<String>()
)
}
}
-}
+}
Видях едно разминаване ама не съм сигурна, че е само едно :D
Йоанна качи решение на 08.12.2017 15:51 (преди почти 8 години)
#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self{
GameError::ParseError(ref msg) =>
write!(f, "ParseError: {}", msg),
GameError::BadGuess(ref msg) =>
write!(f, "BadGuess: {} ", msg),
GameError::InvalidSolution(ref msg) =>
write!(f, "InvalidSolution: {}", msg),
GameError::GameOver =>
write!(f, "GameOver! Sorry you lost. :("),
}
}
}
use std::str::FromStr;
#[derive(Debug)]
pub enum Command {
TryLetter(char),
TryWord(String),
Info,
Help,
Quit,
}
impl FromStr for Command {
type Err = GameError;
/// Това е хипер грозно |(
Мда, и на мен ми харесваше повече това с pattern-matching-а, ама какво да се прави. Колкото и да е красиво, като е грешно, трябва да се пренапише :). Разгледай останалите решения за вдъхновение за по-красиви варианти.
fn from_str(s: &str) -> Result<Self, Self::Err> {
let command_norm = s.to_lowercase();
let mut command =command_norm.split_whitespace();
if equal_command(["info"].iter(), &command) {
Ok(Command::Info)
}else if equal_command(["help"].iter(), &command){
Ok(Command::Help)
}else if equal_command(["quit"].iter(), &command){
Ok(Command::Quit)
}else if command.clone().count() >= 3 &&
(equal_command(["try", "letter"].iter(), &command)) {
- match command.nth(2){
- None => Err(GameError::ParseError(String::from("Invalid command!"))),
- Some(val) =>{
- if val.len() == 1{
- Ok(Command::TryLetter(val.chars().next()
- .unwrap()))
- }else{
- Err(GameError::ParseError(String::from("Invalid command!")))
- }
- }
- }
-
+ let val = command.nth(2).unwrap();
+ if val.len() == 1{
Метода len
брои байтове, а не char-ове: https://doc.rust-lang.org/std/primitive.str.html#method.len. Можеше и да го кажем експлицитно на лекции, I guess. Ама пък можеше и да тествате с кирилица, така че мисля, че вината е 50/50 :)
+ Ok(Command::TryLetter(val.to_lowercase().chars().next()
+ .unwrap()))
+ }else{
+ Err(GameError::ParseError(String::from("Invalid command!")))
+ }
}else if command.clone().count() >= 3 &&
(equal_command(["try", "word"].iter(), &command)) {
- match command.nth(2){
- None => Err(GameError::ParseError(String::from("Invalid command!"))),
- Some(val) => Ok(Command::TryWord(val.to_string())),
- }
+ let val = command.nth(2).unwrap();
+ Ok(Command::TryWord(val.to_lowercase()))
}else{
Err(GameError::ParseError(String::from("Invalid command!")))
}
}
}
fn equal_command(lhs_command: std::slice::Iter<&str> ,
rhs_command: &std::str::SplitWhitespace) -> bool{
lhs_command.zip(rhs_command.clone()).all( |(l_command, r_command)|
l_command.starts_with(r_command)
)
}
use std::collections::HashSet;
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
word: String,
unguessed: HashSet<char>,
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
let solution = solution.trim();
let is_valid_word : bool = !solution.is_empty() &&
solution.chars().all(|x| x.is_alphabetic());
if !is_valid_word {
Err(GameError::InvalidSolution(
String::from("No word to be guess. Read the rules of game!!")
))
}
else{
Ok(Game{
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
word: String::from(solution),
unguessed: solution.chars().collect(),
})
}
}
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
- if self.attempts_remaining == 0{
+ let guess = guess.to_lowercase().next().unwrap();
+ if self.is_over(){
Err(GameError::GameOver)
}else if self.attempted_letters.contains(&guess){
Err(GameError::BadGuess(
String::from("The letter is already guessed")))
}else if self.unguessed.contains(&guess) {
self.unguessed.remove(&guess);
self.attempted_letters.insert(guess);
Ok(true)
}else{
self.attempts_remaining -= 1;
self.attempted_letters.insert(guess);
Ok(false)
}
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
- if self.attempts_remaining == 0{
+ let guess = guess.to_lowercase();
+ if self.is_over(){
Err(GameError::GameOver)
- }else if self.attempted_words.contains(guess){
+ }else if self.attempted_words.contains(&guess){
Err(GameError::BadGuess(String::from("You already try this word")))
}else if self.word == guess{
self.unguessed.clear();
self.attempted_words.insert(String::from(guess));
Ok(true)
}else {
self.attempts_remaining -= 1;
self.attempted_words.insert(String::from(guess));
Ok(false)
}
}
pub fn is_over(&self) -> bool {
self.attempts_remaining == 0 || self.unguessed.is_empty()
}
}
use std::fmt::{self, Display};
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.unguessed.is_empty() {
write!(f, "You won.\n The word is: {}", self.word)
}else if self.attempts_remaining == 0 {
write!(f, "You lost.\n The word was: {}", self.word)
}else{
write!(f, "Attempts remaining: {}\nGuess:{}",
self.attempts_remaining,
self.word.chars().map(|x|
if self.attempted_letters.contains(&x){
format!(" {}", x)
}
else {
" _".to_string()
}
).collect::<String>()
)
}
}
}
Мда, и на мен ми харесваше повече това с pattern-matching-а, ама какво да се прави. Колкото и да е красиво, като е грешно, трябва да се пренапише :). Разгледай останалите решения за вдъхновение за по-красиви варианти.
Метода
len
брои байтове, а не char-ове: https://doc.rust-lang.org/std/primitive.str.html#method.len. Можеше и да го кажем експлицитно на лекции, I guess. Ама пък можеше и да тествате с кирилица, така че мисля, че вината е 50/50 :)