Решение на Hangman от Михаил Младенов

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

Към профила на Михаил Младенов

Резултати

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

Код

#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
use std::fmt::{self, Display};
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&GameError::ParseError(ref s) => write!(f,"Parse error: {}!\n", s),
&GameError::BadGuess(ref s) => write!(f,"Error: {}!\n", s),
&GameError::InvalidSolution(ref s) => write!(f,"Error: {}!\n", s),
&GameError::GameOver => write!(f,"Error: Omae Wa Mou Shindeiru!\n"),
// Не всички терминали поддържат utf-8, така че 'お前はもう死んでいる' не става.
}
}
}
use std::str::FromStr;
use std::option::Option;
#[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 mut it = s.split_whitespace();
let first_word = it.next();
let second_word = it.next();
let third_word = it.next();
match first_word {
Some(first_word) => parse_first_word(first_word, second_word, third_word),
None => Err(GameError::ParseError(String::from("invalid command"))),
}
}
}
fn parse_first_word(first_word: &str, second_word: Option<&str>, third_word: Option<&str>) -> Result<Command, GameError> {
match first_word.chars().next().unwrap().to_lowercase().next().unwrap() {
'h' => Ok(Command::Help),
'i' => Ok(Command::Info),
'q' => Ok(Command::Quit),
't' => {
match second_word {
Some(second_word) => parse_second_word(second_word, third_word),
None => Err(GameError::ParseError(String::from("invalid command"))),
}
}
_ => Err(GameError::ParseError(String::from("invalid command"))),
}
}
fn parse_second_word(second_word: &str, third_word: Option<&str>) ->Result<Command, GameError> {
match second_word.chars().next().unwrap().to_lowercase().next().unwrap() {
'l' => {
match third_word {
Some(third_word) => parse_letter(third_word),
None => Err(GameError::ParseError(String::from("command expected a letter got nothing"))),
}
}
'w' => {
match third_word {
Some(third_word) => Ok(Command::TryWord(String::from(third_word))),
None => Err(GameError::ParseError(String::from("command expected a word got nothing"))),
}
}
_ => Err(GameError::ParseError(String::from("invalid command"))),
}
}
fn parse_letter(letter: &str) -> Result<Command, GameError> {
match letter.chars().count() {
1 => Ok(Command::TryLetter(letter.chars().next().unwrap())),
_ => Err(GameError::ParseError(String::from("command expected a letter got a word"))),
}
}
use std::collections::HashSet;
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
solution: String,
guessed_letters: u32,
running: bool,
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
if solution.is_empty() {
Err(GameError::InvalidSolution(String::from("empty solution")))
}
else if solution.contains(|c: char| !c.is_alphabetic()) {
Err(GameError::InvalidSolution(String::from("non-alphabetic character")))
}
else {
Ok(Game {
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
solution: String::from(solution),
guessed_letters: 0,
running: if attempts > 0 {true} else {false},
})
}
}
}
impl Game {
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
if self.is_over() {
Err(GameError::GameOver)
}
else if self.attempted_letters.contains(&guess) {
Err(GameError::BadGuess(String::from("letter already tested")))
}
else {
self.attempted_letters.insert(guess);
let letters_in_solution = self.solution.chars().filter(|c| c == &guess).count();
if letters_in_solution > 0 {
self.guessed_letters += letters_in_solution as u32;
if self.guessed_letters == self.solution.chars().count() as u32 {
self.running = false;
}
Ok(true)
}
else {
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.running = false;
}
Ok(false)
}
}
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.is_over() {
Err(GameError::GameOver)
}
else if self.attempted_words.contains(guess) {
Err(GameError::BadGuess(String::from("word already tested")))
}
else {
self.attempted_words.insert(String::from(guess));
if guess == self.solution {
self.running = false;
Ok(true)
}
else {
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.running = false;
}
Ok(false)
}
}
}
pub fn is_over(&self) -> bool {
!self.running
}
}
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.running {
let mut guess = String::with_capacity(self.solution.len()*4);
for i in self.solution.chars() {
if self.attempted_letters.contains(&i) {
guess.push(i);
}
else {
guess.push('_');
}
guess.push(' ');
}
write!(f, "Attempts remaining: {}\nGuess: {}\n",self.attempts_remaining, guess)
}
else {
if self.attempts_remaining > 0 {
write!(f, "You won! :(\nThe word was: {}",self.solution)
}
else {
write!(f, "You lost! :D\nThe word was: {}",self.solution)
}
}
}
}

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

Compiling solution v0.1.0 (file:///tmp/d20171210-6053-1dk6z4c/solution)
    Finished dev [unoptimized + debuginfo] target(s) in 5.30 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 ... ok
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 ... ok
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

test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests solution

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

История (4 версии и 6 коментара)

Михаил качи първо решение на 05.12.2017 04:08 (преди почти 8 години)

Михаил качи решение на 05.12.2017 04:19 (преди почти 8 години)

#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
use std::fmt::{self, Display};
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
- &GameError::ParseError(ref s) => write!(f,"Parse error: {}!\n", s),
- &GameError::BadGuess(ref s) => write!(f,"Error: {}!\n", s),
- &GameError::InvalidSolution(ref s) => write!(f,"Error: {}!\n", s),
- &GameError::GameOver => write!(f,"Error: Omae Wa Mou Shindeiru!\n"),
- // Не всички терминали поддържат utf-8, така че 'お前はもう死んでいる' не става.
- }
+ &GameError::ParseError(ref s) => write!(f,"Parse error: {}!\n", s),
+ &GameError::BadGuess(ref s) => write!(f,"Error: {}!\n", s),
+ &GameError::InvalidSolution(ref s) => write!(f,"Error: {}!\n", s),
+ &GameError::GameOver => write!(f,"Error: Omae Wa Mou Shindeiru!\n"),
+ // Не всички терминали поддържат utf-8, така че 'お前はもう死んでいる' не става.
+ }
}
}
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 mut it = s.split_whitespace();
-
- let first_word = it.next();
- let second_word = it.next();
- let third_word = it.next();
-
- match first_word {
- Some(first_word) => {
- match first_word.chars().next().unwrap().to_lowercase().next().unwrap() {
- 'h' => Ok(Command::Help),
- 'i' => Ok(Command::Info),
- 'q' => Ok(Command::Quit),
-
- 't' => {
- match second_word {
- Some(second_word) => {
- match second_word.chars().next().unwrap().to_lowercase().next().unwrap() {
- 'l' => {
- match third_word {
- Some(third_word) => {
- match third_word.len() {
- 1 => Ok(Command::TryLetter(third_word.chars().next().unwrap())),
- _ => Err(GameError::ParseError(String::from("command expected a letter got a word"))),
- }
- }
-
- None => Err(GameError::ParseError(String::from("command expected a letter got nothing"))),
- }
- }
-
- 'w' => {
- match third_word {
- Some(third_word) => Ok(Command::TryWord(String::from(third_word))),
- None => Err(GameError::ParseError(String::from("command expected a word got nothing"))),
- }
- }
-
- _ => Err(GameError::ParseError(String::from("invalid command"))),
- }
- }
-
- None => Err(GameError::ParseError(String::from("invalid command"))),
- }
- }
-
- _ => Err(GameError::ParseError(String::from("invalid command"))),
- }
- }
-
- None => Err(GameError::ParseError(String::from("invalid command"))),
- }
+
+ let mut it = s.split_whitespace();
+
+ let first_word = it.next();
+ let second_word = it.next();
+ let third_word = it.next();
+
+ match first_word {
+ Some(first_word) => {
+ match first_word.chars().next().unwrap().to_lowercase().next().unwrap() {
+ 'h' => Ok(Command::Help),
+ 'i' => Ok(Command::Info),
+ 'q' => Ok(Command::Quit),
+
+ 't' => {

Много бързах за това домашно и като цяло не съм отделил много време за мислене (исках просто да подкарам нещо), за това стана толкова неграмотно. Но да това е добра идея. Утре ще пробвам да го пооправя (също този път не съм правил никакви тестове за коректност освен предоставените базови, което значи, че сигурно ми гърми на много места...).

Не на толкова много. Ако обърнеш внимание на това изречение от условието, например : "Тоест, ако решението е "крокодил", и сме пробвали буквата "к" и буквата "о", може да извадите това: ...", има hint, какво би било проблем :)

+ match second_word {
+ Some(second_word) => {
+ match second_word.chars().next().unwrap().to_lowercase().next().unwrap() {
+ 'l' => {
+ match third_word {
+ Some(third_word) => {
+ match third_word.len() {
+ 1 => Ok(Command::TryLetter(third_word.chars().next().unwrap())),
+ _ => Err(GameError::ParseError(String::from("command expected a letter got a word"))),
+ }
+ }
+
+ None => Err(GameError::ParseError(String::from("command expected a letter got nothing"))),
+ }
+ }
+
+ 'w' => {
+ match third_word {
+ Some(third_word) => Ok(Command::TryWord(String::from(third_word))),
+ None => Err(GameError::ParseError(String::from("command expected a word got nothing"))),
+ }
+ }
+
+ _ => Err(GameError::ParseError(String::from("invalid command"))),
+ }
+ }
+
+ None => Err(GameError::ParseError(String::from("invalid command"))),
+ }
+ }
+
+ _ => Err(GameError::ParseError(String::from("invalid command"))),
+ }
+ }
+
+ None => Err(GameError::ParseError(String::from("invalid command"))),
+ }
}
}
use std::collections::HashSet;
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
solution: String,
guessed_letters: u32,
running: bool,
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
if solution.is_empty() {
- Err(GameError::InvalidSolution(String::from("empty solution")))
- }
- else if solution.contains(|c: char| !c.is_alphabetic()) {
- Err(GameError::InvalidSolution(String::from("non-alphabetic character")))
- }
- else {
- Ok(Game {
- attempted_letters: HashSet::new(),
- attempted_words: HashSet::new(),
- attempts_remaining: attempts,
- solution: String::from(solution),
- guessed_letters: 0,
- running: if attempts > 0 {true} else {false},
- })
- }
+ Err(GameError::InvalidSolution(String::from("empty solution")))
+ }
+ else if solution.contains(|c: char| !c.is_alphabetic()) {
+ Err(GameError::InvalidSolution(String::from("non-alphabetic character")))
+ }
+ else {
+ Ok(Game {
+ attempted_letters: HashSet::new(),
+ attempted_words: HashSet::new(),
+ attempts_remaining: attempts,
+ solution: String::from(solution),
+ guessed_letters: 0,
+ running: if attempts > 0 {true} else {false},
+ })
+ }
}
}
impl Game {
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
- if self.is_over() {
- Err(GameError::GameOver)
- }
- else if self.attempted_letters.contains(&guess) {
- Err(GameError::BadGuess(String::from("letter already tested")))
- }
- else {
- self.attempted_letters.insert(guess);
- let letters_in_solution = self.solution.chars().filter(|c| c == &guess).count();
- if letters_in_solution > 0 {
- self.guessed_letters += letters_in_solution as u32;
-
- if self.guessed_letters == self.solution.len() as u32 {
- self.running = false;
- }
-
- Ok(true)
- }
- else {
- self.attempts_remaining -= 1;
-
- if self.attempts_remaining == 0 {
- self.running = false;
- }
-
- Ok(false)
- }
- }
+ if self.is_over() {
+ Err(GameError::GameOver)
+ }
+ else if self.attempted_letters.contains(&guess) {
+ Err(GameError::BadGuess(String::from("letter already tested")))
+ }
+ else {
+ self.attempted_letters.insert(guess);
+ let letters_in_solution = self.solution.chars().filter(|c| c == &guess).count();
+ if letters_in_solution > 0 {
+ self.guessed_letters += letters_in_solution as u32;
+
+ if self.guessed_letters == self.solution.len() as u32 {
+ self.running = false;
+ }
+
+ Ok(true)
+ }
+ else {
+ self.attempts_remaining -= 1;
+
+ if self.attempts_remaining == 0 {
+ self.running = false;
+ }
+
+ Ok(false)
+ }
+ }
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
- if self.is_over() {
- Err(GameError::GameOver)
- }
- else if self.attempted_words.contains(guess) {
- Err(GameError::BadGuess(String::from("word already tested")))
- }
- else {
- self.attempted_words.insert(String::from(guess));
-
- if guess == self.solution {
- self.running = false;
- Ok(true)
- }
- else {
- self.attempts_remaining -= 1;
-
- if self.attempts_remaining == 0 {
- self.running = false;
- }
-
- Ok(false)
- }
- }
+ if self.is_over() {
+ Err(GameError::GameOver)
+ }
+ else if self.attempted_words.contains(guess) {
+ Err(GameError::BadGuess(String::from("word already tested")))
+ }
+ else {
+ self.attempted_words.insert(String::from(guess));
+
+ if guess == self.solution {
+ self.running = false;
+ Ok(true)
+ }
+ else {
+ self.attempts_remaining -= 1;
+
+ if self.attempts_remaining == 0 {
+ self.running = false;
+ }
+
+ Ok(false)
+ }
+ }
}
pub fn is_over(&self) -> bool {
!self.running
}
}
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.running {
- let mut guess = String::with_capacity(self.solution.len()*4);
-
- for i in self.solution.chars() {
- if self.attempted_letters.contains(&i) {
- guess.push(i);
- }
- else {
- guess.push('_');
- }
-
- guess.push(' ');
- }
-
- write!(f, "Attempts remaining: {}\nGuess: {}\n",self.attempts_remaining, guess)
- }
- else {
- if self.attempts_remaining > 0 {
- write!(f, "You won! :(\nThe word was: {}",self.solution)
- }
- else {
- write!(f, "You lost! :D\nThe word was: {}",self.solution)
- }
- }
+ let mut guess = String::with_capacity(self.solution.len()*4);
+
+ for i in self.solution.chars() {
+ if self.attempted_letters.contains(&i) {
+ guess.push(i);
+ }
+ else {
+ guess.push('_');
+ }
+
+ guess.push(' ');
+ }
+
+ write!(f, "Attempts remaining: {}\nGuess: {}\n",self.attempts_remaining, guess)
+ }
+ else {
+ if self.attempts_remaining > 0 {
+ write!(f, "You won! :(\nThe word was: {}",self.solution)
+ }
+ else {
+ write!(f, "You lost! :D\nThe word was: {}",self.solution)
+ }
+ }
}
}

Михаил качи решение на 07.12.2017 15:56 (преди почти 8 години)

#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
use std::fmt::{self, Display};
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&GameError::ParseError(ref s) => write!(f,"Parse error: {}!\n", s),
&GameError::BadGuess(ref s) => write!(f,"Error: {}!\n", s),
&GameError::InvalidSolution(ref s) => write!(f,"Error: {}!\n", s),
&GameError::GameOver => write!(f,"Error: Omae Wa Mou Shindeiru!\n"),
// Не всички терминали поддържат utf-8, така че 'お前はもう死んでいる' не става.
}
}
}
use std::str::FromStr;
+use std::option::Option;
#[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 mut it = s.split_whitespace();
let first_word = it.next();
let second_word = it.next();
let third_word = it.next();
match first_word {
- Some(first_word) => {
- match first_word.chars().next().unwrap().to_lowercase().next().unwrap() {
- 'h' => Ok(Command::Help),
- 'i' => Ok(Command::Info),
- 'q' => Ok(Command::Quit),
-
- 't' => {
- match second_word {
- Some(second_word) => {
- match second_word.chars().next().unwrap().to_lowercase().next().unwrap() {
- 'l' => {
- match third_word {
- Some(third_word) => {
- match third_word.len() {
- 1 => Ok(Command::TryLetter(third_word.chars().next().unwrap())),
- _ => Err(GameError::ParseError(String::from("command expected a letter got a word"))),
- }
- }
-
- None => Err(GameError::ParseError(String::from("command expected a letter got nothing"))),
- }
- }
-
- 'w' => {
- match third_word {
- Some(third_word) => Ok(Command::TryWord(String::from(third_word))),
- None => Err(GameError::ParseError(String::from("command expected a word got nothing"))),
- }
- }
-
- _ => Err(GameError::ParseError(String::from("invalid command"))),
- }
- }
-
- None => Err(GameError::ParseError(String::from("invalid command"))),
- }
- }
-
- _ => Err(GameError::ParseError(String::from("invalid command"))),
- }
- }
-
+ Some(first_word) => parse_first_word(first_word, second_word, third_word),
None => Err(GameError::ParseError(String::from("invalid command"))),
}
}
+
+
}
+
+fn parse_first_word(first_word: &str, second_word: Option<&str>, third_word: Option<&str>) -> Result<Command, GameError> {
+ match first_word.chars().next().unwrap().to_lowercase().next().unwrap() {
+ 'h' => Ok(Command::Help),
+ 'i' => Ok(Command::Info),
+ 'q' => Ok(Command::Quit),
+
+ 't' => {
+ match second_word {
+ Some(second_word) => parse_second_word(second_word, third_word),
+ None => Err(GameError::ParseError(String::from("invalid command"))),
+ }
+ }
+
+ _ => Err(GameError::ParseError(String::from("invalid command"))),
+ }
+}
+
+fn parse_second_word(second_word: &str, third_word: Option<&str>) ->Result<Command, GameError> {
+ match second_word.chars().next().unwrap().to_lowercase().next().unwrap() {
+ 'l' => {
+ match third_word {
+ Some(third_word) => parse_letter(third_word),
+ None => Err(GameError::ParseError(String::from("command expected a letter got nothing"))),
+ }
+ }
+
+ 'w' => {
+ match third_word {
+ Some(third_word) => Ok(Command::TryWord(String::from(third_word))),
+ None => Err(GameError::ParseError(String::from("command expected a word got nothing"))),
+ }
+ }
+
+ _ => Err(GameError::ParseError(String::from("invalid command"))),
+ }
+}
+
+fn parse_letter(letter: &str) -> Result<Command, GameError> {
+ match letter.chars().count() {
+ 1 => Ok(Command::TryLetter(letter.chars().next().unwrap())),
+ _ => Err(GameError::ParseError(String::from("command expected a letter got a word"))),
+ }
+}
+
use std::collections::HashSet;
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
solution: String,
guessed_letters: u32,
running: bool,
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
if solution.is_empty() {
Err(GameError::InvalidSolution(String::from("empty solution")))
}
else if solution.contains(|c: char| !c.is_alphabetic()) {
Err(GameError::InvalidSolution(String::from("non-alphabetic character")))
}
else {
Ok(Game {
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
solution: String::from(solution),
guessed_letters: 0,
running: if attempts > 0 {true} else {false},
})
}
}
}
impl Game {
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
if self.is_over() {
Err(GameError::GameOver)
}
else if self.attempted_letters.contains(&guess) {
Err(GameError::BadGuess(String::from("letter already tested")))
}
else {
self.attempted_letters.insert(guess);
let letters_in_solution = self.solution.chars().filter(|c| c == &guess).count();
if letters_in_solution > 0 {
self.guessed_letters += letters_in_solution as u32;
- if self.guessed_letters == self.solution.len() as u32 {
+ if self.guessed_letters == self.solution.chars().count() as u32 {
self.running = false;
}
Ok(true)
}
else {
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.running = false;
}
Ok(false)
}
}
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.is_over() {
Err(GameError::GameOver)
}
else if self.attempted_words.contains(guess) {
Err(GameError::BadGuess(String::from("word already tested")))
}
else {
self.attempted_words.insert(String::from(guess));
if guess == self.solution {
self.running = false;
Ok(true)
}
else {
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.running = false;
}
Ok(false)
}
}
}
pub fn is_over(&self) -> bool {
!self.running
}
}
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.running {
let mut guess = String::with_capacity(self.solution.len()*4);
for i in self.solution.chars() {
if self.attempted_letters.contains(&i) {
guess.push(i);
}
else {
guess.push('_');
}
guess.push(' ');
}
write!(f, "Attempts remaining: {}\nGuess: {}\n",self.attempts_remaining, guess)
}
else {
if self.attempts_remaining > 0 {
write!(f, "You won! :(\nThe word was: {}",self.solution)
}
else {
write!(f, "You lost! :D\nThe word was: {}",self.solution)
}
}
}
}

Михаил качи решение на 07.12.2017 15:58 (преди почти 8 години)

#[derive(Debug)]
pub enum GameError {
ParseError(String),
BadGuess(String),
InvalidSolution(String),
GameOver,
}
use std::fmt::{self, Display};
impl Display for GameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&GameError::ParseError(ref s) => write!(f,"Parse error: {}!\n", s),
&GameError::BadGuess(ref s) => write!(f,"Error: {}!\n", s),
&GameError::InvalidSolution(ref s) => write!(f,"Error: {}!\n", s),
&GameError::GameOver => write!(f,"Error: Omae Wa Mou Shindeiru!\n"),
// Не всички терминали поддържат utf-8, така че 'お前はもう死んでいる' не става.
}
}
}
use std::str::FromStr;
use std::option::Option;
#[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 mut it = s.split_whitespace();
let first_word = it.next();
let second_word = it.next();
let third_word = it.next();
match first_word {
Some(first_word) => parse_first_word(first_word, second_word, third_word),
None => Err(GameError::ParseError(String::from("invalid command"))),
}
}
}
fn parse_first_word(first_word: &str, second_word: Option<&str>, third_word: Option<&str>) -> Result<Command, GameError> {
- match first_word.chars().next().unwrap().to_lowercase().next().unwrap() {
- 'h' => Ok(Command::Help),
- 'i' => Ok(Command::Info),
- 'q' => Ok(Command::Quit),
-
- 't' => {
- match second_word {
- Some(second_word) => parse_second_word(second_word, third_word),
- None => Err(GameError::ParseError(String::from("invalid command"))),
- }
- }
-
- _ => Err(GameError::ParseError(String::from("invalid command"))),
- }
+ match first_word.chars().next().unwrap().to_lowercase().next().unwrap() {
+ 'h' => Ok(Command::Help),
+ 'i' => Ok(Command::Info),
+ 'q' => Ok(Command::Quit),
+
+ 't' => {
+ match second_word {
+ Some(second_word) => parse_second_word(second_word, third_word),
+ None => Err(GameError::ParseError(String::from("invalid command"))),
+ }
+ }
+
+ _ => Err(GameError::ParseError(String::from("invalid command"))),
+ }
}
fn parse_second_word(second_word: &str, third_word: Option<&str>) ->Result<Command, GameError> {
- match second_word.chars().next().unwrap().to_lowercase().next().unwrap() {
- 'l' => {
- match third_word {
- Some(third_word) => parse_letter(third_word),
- None => Err(GameError::ParseError(String::from("command expected a letter got nothing"))),
- }
- }
-
- 'w' => {
- match third_word {
- Some(third_word) => Ok(Command::TryWord(String::from(third_word))),
- None => Err(GameError::ParseError(String::from("command expected a word got nothing"))),
- }
- }
-
- _ => Err(GameError::ParseError(String::from("invalid command"))),
- }
+ match second_word.chars().next().unwrap().to_lowercase().next().unwrap() {
+ 'l' => {
+ match third_word {
+ Some(third_word) => parse_letter(third_word),
+ None => Err(GameError::ParseError(String::from("command expected a letter got nothing"))),
+ }
+ }
+
+ 'w' => {
+ match third_word {
+ Some(third_word) => Ok(Command::TryWord(String::from(third_word))),
+ None => Err(GameError::ParseError(String::from("command expected a word got nothing"))),
+ }
+ }
+
+ _ => Err(GameError::ParseError(String::from("invalid command"))),
+ }
}
fn parse_letter(letter: &str) -> Result<Command, GameError> {
- match letter.chars().count() {
- 1 => Ok(Command::TryLetter(letter.chars().next().unwrap())),
- _ => Err(GameError::ParseError(String::from("command expected a letter got a word"))),
- }
+ match letter.chars().count() {
+ 1 => Ok(Command::TryLetter(letter.chars().next().unwrap())),
+ _ => Err(GameError::ParseError(String::from("command expected a letter got a word"))),
+ }
}
use std::collections::HashSet;
pub struct Game {
pub attempted_letters: HashSet<char>,
pub attempted_words: HashSet<String>,
pub attempts_remaining: u32,
solution: String,
guessed_letters: u32,
running: bool,
}
impl Game {
pub fn new(solution: &str, attempts: u32) -> Result<Self, GameError> {
if solution.is_empty() {
Err(GameError::InvalidSolution(String::from("empty solution")))
}
else if solution.contains(|c: char| !c.is_alphabetic()) {
Err(GameError::InvalidSolution(String::from("non-alphabetic character")))
}
else {
Ok(Game {
attempted_letters: HashSet::new(),
attempted_words: HashSet::new(),
attempts_remaining: attempts,
solution: String::from(solution),
guessed_letters: 0,
running: if attempts > 0 {true} else {false},
})
}
}
}
impl Game {
pub fn guess_letter(&mut self, guess: char) -> Result<bool, GameError> {
if self.is_over() {
Err(GameError::GameOver)
}
else if self.attempted_letters.contains(&guess) {
Err(GameError::BadGuess(String::from("letter already tested")))
}
else {
self.attempted_letters.insert(guess);
let letters_in_solution = self.solution.chars().filter(|c| c == &guess).count();
if letters_in_solution > 0 {
self.guessed_letters += letters_in_solution as u32;
if self.guessed_letters == self.solution.chars().count() as u32 {
self.running = false;
}
Ok(true)
}
else {
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.running = false;
}
Ok(false)
}
}
}
pub fn guess_word(&mut self, guess: &str) -> Result<bool, GameError> {
if self.is_over() {
Err(GameError::GameOver)
}
else if self.attempted_words.contains(guess) {
Err(GameError::BadGuess(String::from("word already tested")))
}
else {
self.attempted_words.insert(String::from(guess));
if guess == self.solution {
self.running = false;
Ok(true)
}
else {
self.attempts_remaining -= 1;
if self.attempts_remaining == 0 {
self.running = false;
}
Ok(false)
}
}
}
pub fn is_over(&self) -> bool {
!self.running
}
}
impl Display for Game {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.running {
let mut guess = String::with_capacity(self.solution.len()*4);
for i in self.solution.chars() {
if self.attempted_letters.contains(&i) {
guess.push(i);
}
else {
guess.push('_');
}
guess.push(' ');
}
write!(f, "Attempts remaining: {}\nGuess: {}\n",self.attempts_remaining, guess)
}
else {
if self.attempts_remaining > 0 {
write!(f, "You won! :(\nThe word was: {}",self.solution)
}
else {
write!(f, "You lost! :D\nThe word was: {}",self.solution)
}
}
}
}

Супер става!

Последен hint; Всичко ли е изпълнено от условитето тук?

Ще се наложи да обработите входа и да прецените коя команда с какви параметри ще извадите. Ще дадем доста свобода на входа, за доброто на потребителя:

    * Малки или големи букви нямат значение. Командите help, Help и HELP са еквивалентни
    * Начален и краен whitespace няма значение. Низа try letter x е еквивалентен на try letter x
    * Може командите да бъдат съкратени, примерно t l x и tr le x са еквивалентни на try letter x. (Hint: може да проверявате първите буквички, няма да тестваме с неща като "hlep" или "ifno")
    * Всичко след правилната команда се игнорира. Тоест, help i need somebody се парси просто до Command::Help, a try word one two three се парси до Command::TryWord(String::from("one"))
    * Няма да тестваме с повече от един интервал между компонентите. Чувствайте се свободни да разбивате входа по само един интервал. (Макар че hint: типа &str си има вече метод за тая цел)
    * Няма да тестваме за валидност на символа или думата. По-долу, когато инициализирате играта, ще проверявате дали решението е валидно, но тук няма нужда (макар че ако искате, няма да попречи).

Решението е стабилно, единствения бъг е свързан с case-insensitivity-то на буквата, която подаваш. Командата "Try Letter Z" я превеждаш до TryLetter('Z'), а в тестовете я превеждаме до TryLetter('z'), понеже downcase-ваме всичко.

Точно ти ме пита за case-sensitivity, и те уверих, че е не проблем. Така че ще пробвам да променя теста и да ги пусна отново, ако не проработи, ще ти дам една бонус точка, за да negate-на това.