Browse Source

Fixed the problem with the mutable uses, but now Rc can't get a mutable reference to the throws

master
Julio Biason 5 years ago
parent
commit
612fde25b8
  1. 1
      .gitignore
  2. 192
      rust/bowling/src/lib.rs
  3. 9
      rust/bowling/tests/bowling.rs

1
.gitignore vendored

@ -0,0 +1 @@
*.sw?

192
rust/bowling/src/lib.rs

@ -145,6 +145,10 @@ mod frame {
self.throws.push(throw.clone()); self.throws.push(throw.clone());
} }
pub fn possible_spare(&self, pins: u16) -> bool {
self.throws.len() == 1 && self.score().unwrap() + pins == 10
}
pub fn score(&self) -> Option<u16> { pub fn score(&self) -> Option<u16> {
if self.throws.len() == 0 || self.throws.iter().any(|throw| throw.is_free_throw()) { if self.throws.len() == 0 || self.throws.iter().any(|throw| throw.is_free_throw()) {
None None
@ -153,8 +157,8 @@ mod frame {
} }
} }
pub fn is_spare(&self) -> bool { pub fn is_closed(&self) -> bool {
self.throws.len() == 2 && self.score() == Some(10) self.throws.len() >= 2 && self.throws.iter().all(|throw| !throw.is_free_throw())
} }
} }
@ -225,8 +229,8 @@ mod frame {
use std::rc::Rc; use std::rc::Rc;
pub struct BowlingGame { pub struct BowlingGame {
frames: Vec<frame::Frame>, pub frames: Vec<frame::Frame>,
throws: Vec<Rc<throw::Throw>>, pub throws: Vec<Rc<throw::Throw>>,
} }
impl BowlingGame { impl BowlingGame {
@ -240,76 +244,164 @@ impl BowlingGame {
pub fn roll(&mut self, pins: u16) -> Result<(), Error> { pub fn roll(&mut self, pins: u16) -> Result<(), Error> {
if !self.has_free_throws() && self.is_finished() { if !self.has_free_throws() && self.is_finished() {
Err(Error::GameComplete) Err(Error::GameComplete)
} else if pins > 10 {
Err(Error::NotEnoughPinsLeft)
} else { } else {
let throw = self.make_throw(pins); let mut frame_throws: Vec<Rc<throw::Throw>> = Vec::new();
let last_id = self.throws.len(); if !self.has_free_throws() {
let frame = self.last_frame(); let throw = self.add_new_throw(pins);
frame.add_throw(&throw); frame_throws.push(throw);
} else {
self.reuse_free_throw(pins);
}
// check if the player gained any free throws.
if pins == 10 { if pins == 10 {
// a strike, you get 2 free throws // On a strike, the player get two "free" throws.
let new_throw = Rc::new(throw::Throw::new_free(last_id + 1)); let last_id = self.throws.len();
self.throws.push(new_throw.clone()); frame_throws.push(Rc::new(throw::Throw::new_free(last_id + 1)));
frame.add_throw(&new_throw); frame_throws.push(Rc::new(throw::Throw::new_free(last_id + 2)));
} else if self.spare(pins) {
let new_throw = Rc::new(throw::Throw::new_free(last_id + 2)); let last_id = self.throws.len();
self.throws.push(new_throw.clone()); frame_throws.push(Rc::new(throw::Throw::new_free(last_id + 1)));
frame.add_throw(&new_throw);
} else if frame.is_spare() {
// in a spare, just one throw
let new_throw = Rc::new(throw::Throw::new_free(last_id + 1));
self.throws.push(new_throw.clone());
frame.add_throw(&new_throw);
} }
self.push_throws_to_last_frame(frame_throws.as_slice());
self.push_free_throws(frame_throws.as_slice());
dbg!(&self.throws); dbg!(&self.throws);
dbg!(&self.frames); dbg!(&self.frames);
Ok(()) Ok(())
} }
} }
pub fn score(&self) -> Option<u16> { /// Add a new throw to the throw list.
None pub fn add_new_throw(&mut self, pins: u16) -> Rc<throw::Throw> {
// if !self.is_finished() { let last_id = self.throws.len();
// None let new_throw = Rc::new(throw::Throw::new(last_id, pins));
// } else { self.throws.push(new_throw.clone());
// Some(self.frames.iter().map(|x| x.score()).sum()) new_throw.clone()
// }
} }
/// If there are free throws, update the most recent one; if there are none, create a new /// Reuse on the free throws in the throw list instead of creating a new one.
/// throw. pub fn reuse_free_throw(&mut self, pins: u16) {
fn make_throw(&mut self, pins: u16) -> Rc<throw::Throw> { (*Rc::get_mut(
let new_id = self.throws.len() + 1;
self.throws self.throws
.iter_mut() .iter_mut()
.filter(|x| x.is_free_throw()) .filter(|throw| throw.is_free_throw())
.take(1) .take(1)
.next() .next()
.map_or(Rc::new(throw::Throw::new_free(new_id)), |x| { .expect("There are no free throws for reuse"),
let throw = Rc::get_mut(x).unwrap(); )
throw.update(pins); .expect("Failed to get the mutable reference to the free throw"))
x.clone() .update(pins);
}) }
/// A a throw to the last available frame.
pub fn push_throws_to_last_frame(&mut self, throws: &[Rc<throw::Throw>]) {
if self.frames.len() == 0 || self.frames.last().unwrap().is_closed() {
let last_id = self.frames.len();
let mut new_frame = frame::Frame::new(last_id + 1);
for throw in throws {
new_frame.add_throw(&throw.clone());
}
self.frames.push(new_frame);
} else {
let last_frame = self.frames.last_mut().unwrap();
for throw in throws {
last_frame.add_throw(&throw.clone());
}
}
}
pub fn push_free_throws(&mut self, throws: &[Rc<throw::Throw>]) {
for throw in throws.iter().filter(|throw| throw.is_free_throw()) {
self.throws.push(throw.clone());
}
} }
/// Check if the list of throws in the game there are at least one free. /// Return the game score, but only if the game is fininshed.
fn has_free_throws(&self) -> bool { pub fn score(&self) -> Option<u16> {
self.throws.iter().any(|x| x.is_free_throw()) if !self.is_finished() {
None
} else {
Some(self.frames.iter().map(|x| x.score().unwrap_or(0)).sum())
}
} }
/// The game is over when there are 10 frames of scores. /// Check if the game is fininshed.
fn is_finished(&self) -> bool { pub fn is_finished(&self) -> bool {
self.frames.len() == 10 self.frames.len() == 10
&& self
.frames
.iter()
.last()
.map_or(false, |frame| frame.is_closed())
}
pub fn spare(&self, pins: u16) -> bool {
self.frames
.last()
.map_or(false, |frame| frame.possible_spare(pins))
} }
/// Get the last/current frame. /// Check if there are any free throws available.
fn last_frame(&mut self) -> &mut frame::Frame { pub fn has_free_throws(&self) -> bool {
if self.frames.len() == 0 { self.throws.iter().any(|throw| throw.is_free_throw())
let new_id = self.frames.len() + 1;
self.frames.push(frame::Frame::new(new_id));
} }
self.frames.iter_mut().last().unwrap() }
#[cfg(test)]
mod game_internal_tests {
use std::rc::Rc;
#[test]
pub fn unfinished_game_not_enough_frames() {
let game = super::BowlingGame::new();
assert!(!game.is_finished());
}
#[test]
pub fn unfinished_game_free_throws_last_frame() {
let mut game = super::BowlingGame::new();
game.frames.push(super::frame::Frame::new(1));
game.frames.push(super::frame::Frame::new(2));
game.frames.push(super::frame::Frame::new(3));
game.frames.push(super::frame::Frame::new(4));
game.frames.push(super::frame::Frame::new(5));
game.frames.push(super::frame::Frame::new(6));
game.frames.push(super::frame::Frame::new(7));
game.frames.push(super::frame::Frame::new(8));
game.frames.push(super::frame::Frame::new(9));
let mut final_frame = super::frame::Frame::new(10);
final_frame.add_throw(&Rc::new(super::throw::Throw::new(1, 5)));
final_frame.add_throw(&Rc::new(super::throw::Throw::new_free(2)));
game.frames.push(final_frame);
assert!(!game.is_finished());
}
#[test]
pub fn finished_game() {
let mut game = super::BowlingGame::new();
game.frames.push(super::frame::Frame::new(1));
game.frames.push(super::frame::Frame::new(2));
game.frames.push(super::frame::Frame::new(3));
game.frames.push(super::frame::Frame::new(4));
game.frames.push(super::frame::Frame::new(5));
game.frames.push(super::frame::Frame::new(6));
game.frames.push(super::frame::Frame::new(7));
game.frames.push(super::frame::Frame::new(8));
game.frames.push(super::frame::Frame::new(9));
let mut final_frame = super::frame::Frame::new(10);
final_frame.add_throw(&Rc::new(super::throw::Throw::new(1, 5)));
final_frame.add_throw(&Rc::new(super::throw::Throw::new(2, 3)));
game.frames.push(final_frame);
assert!(game.is_finished());
} }
} }

9
rust/bowling/tests/bowling.rs

@ -1,21 +1,18 @@
use bowling::*; use bowling::*;
#[test] #[test]
#[ignore]
fn roll_returns_a_result() { fn roll_returns_a_result() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();
assert!(game.roll(0).is_ok()); assert!(game.roll(0).is_ok());
} }
#[test] #[test]
#[ignore]
fn you_cannot_roll_more_than_ten_pins_in_a_single_roll() { fn you_cannot_roll_more_than_ten_pins_in_a_single_roll() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();
assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft)); assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft));
} }
#[test] #[test]
#[ignore]
fn a_game_score_is_some_if_ten_frames_have_been_rolled() { fn a_game_score_is_some_if_ten_frames_have_been_rolled() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();
@ -28,14 +25,12 @@ fn a_game_score_is_some_if_ten_frames_have_been_rolled() {
} }
#[test] #[test]
#[ignore]
fn you_cannot_score_a_game_with_no_rolls() { fn you_cannot_score_a_game_with_no_rolls() {
let game = BowlingGame::new(); let game = BowlingGame::new();
assert_eq!(game.score(), None); assert_eq!(game.score(), None);
} }
#[test] #[test]
#[ignore]
fn a_game_score_is_none_if_fewer_than_ten_frames_have_been_rolled() { fn a_game_score_is_none_if_fewer_than_ten_frames_have_been_rolled() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();
@ -48,7 +43,6 @@ fn a_game_score_is_none_if_fewer_than_ten_frames_have_been_rolled() {
} }
#[test] #[test]
#[ignore]
fn a_roll_is_err_if_the_game_is_done() { fn a_roll_is_err_if_the_game_is_done() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();
@ -61,7 +55,6 @@ fn a_roll_is_err_if_the_game_is_done() {
} }
#[test] #[test]
#[ignore]
fn twenty_zero_pin_rolls_scores_zero() { fn twenty_zero_pin_rolls_scores_zero() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();
@ -73,7 +66,6 @@ fn twenty_zero_pin_rolls_scores_zero() {
} }
#[test] #[test]
#[ignore]
fn ten_frames_without_a_strike_or_spare() { fn ten_frames_without_a_strike_or_spare() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();
@ -86,7 +78,6 @@ fn ten_frames_without_a_strike_or_spare() {
} }
#[test] #[test]
#[ignore]
fn spare_in_the_first_frame_followed_by_zeros() { fn spare_in_the_first_frame_followed_by_zeros() {
let mut game = BowlingGame::new(); let mut game = BowlingGame::new();

Loading…
Cancel
Save