Browse Source

Fixed the Rc problem with RefCell, but some rolls are not being throw in the next frame, for some reason

master
Julio Biason 4 years ago
parent
commit
0c620675ad
  1. 230
      rust/bowling/src/lib.rs
  2. 6
      rust/bowling/tests/bowling.rs

230
rust/bowling/src/lib.rs

@ -92,37 +92,12 @@ mod throw {
)
}
}
#[cfg(test)]
mod throw_tests {
#[test]
fn make_free() {
let throw = super::Throw::new_free(1);
assert!(throw.is_free_throw());
assert_eq!("1-Free", format!("{:?}", throw));
}
#[test]
fn make_strike() {
let throw = super::Throw::new(1, 10);
assert!(!throw.is_free_throw());
assert_eq!("1-10", format!("{:?}", throw));
assert!(throw.is_strike());
}
#[test]
fn update() {
let mut throw = super::Throw::new_free(1);
assert_eq!(throw.score(), None);
throw.update(5);
assert_eq!(throw.score(), Some(5));
}
}
}
/// A game frame
mod frame {
#[deny(warnings, missing_docs)]
use std::cell::RefCell;
use std::fmt;
use std::rc::Rc;
@ -130,7 +105,7 @@ mod frame {
pub struct Frame {
id: usize,
throws: Vec<Rc<Throw>>,
throws: Vec<Rc<RefCell<Throw>>>,
}
impl Frame {
@ -141,8 +116,8 @@ mod frame {
}
}
pub fn add_throw(&mut self, throw: &Rc<Throw>) {
self.throws.push(throw.clone());
pub fn add_throw(&mut self, throw: &Rc<RefCell<Throw>>) {
self.throws.push(Rc::clone(throw));
}
pub fn possible_spare(&self, pins: u16) -> bool {
@ -150,15 +125,39 @@ mod frame {
}
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.borrow().is_free_throw())
{
None
} else {
Some(self.throws.iter().map(|throw| throw.score().unwrap()).sum())
Some(
self.throws
.iter()
.map(|throw| throw.borrow().score().unwrap())
.sum(),
)
}
}
pub fn is_closed(&self) -> bool {
self.throws.len() >= 2 && self.throws.iter().all(|throw| !throw.is_free_throw())
!self.has_free_throws() || self.is_a_strike() || self.throws.len() >= 2
}
pub fn accept_more_throws(&self) -> bool {
self.throws.len() != 3
}
fn has_free_throws(&self) -> bool {
self.throws
.iter()
.all(|throw| !throw.borrow().is_free_throw())
}
fn is_a_strike(&self) -> bool {
self.throws.len() == 1 && self.throws[0].borrow().is_strike()
}
}
@ -181,56 +180,14 @@ mod frame {
)
}
}
#[cfg(test)]
mod frame_tests {
#[test]
fn no_score() {
let frame = super::Frame::new(1);
assert_eq!(frame.score(), None);
}
#[test]
fn score() {
let mut frame = super::Frame::new(1);
let throw = super::Throw::new(1, 10);
frame.add_throw(&super::Rc::new(throw));
assert_eq!(frame.score(), Some(10));
}
#[test]
fn no_score_with_free_throws() {
let mut frame = super::Frame::new(1);
let throw1 = super::Throw::new(1, 5);
let throw2 = super::Throw::new_free(2);
frame.add_throw(&super::Rc::new(throw1));
frame.add_throw(&super::Rc::new(throw2));
assert_eq!(frame.score(), None);
}
#[test]
fn debug() {
let mut frame = super::Frame::new(1);
let throw1 = super::Throw::new(1, 5);
let throw2 = super::Throw::new_free(2);
frame.add_throw(&super::Rc::new(throw1));
frame.add_throw(&super::Rc::new(throw2));
assert_eq!("1-?? (1-5,2-Free)", format!("{:?}", frame));
}
}
}
use std::cell::RefCell;
use std::rc::Rc;
pub struct BowlingGame {
pub frames: Vec<frame::Frame>,
pub throws: Vec<Rc<throw::Throw>>,
pub throws: Vec<Rc<RefCell<throw::Throw>>>,
}
impl BowlingGame {
@ -247,22 +204,23 @@ impl BowlingGame {
} else if pins > 10 {
Err(Error::NotEnoughPinsLeft)
} else {
let mut frame_throws: Vec<Rc<throw::Throw>> = Vec::new();
let mut frame_throws: Vec<Rc<RefCell<throw::Throw>>> = Vec::new();
if !self.has_free_throws() {
let throw = self.add_new_throw(pins);
frame_throws.push(throw);
} else {
self.reuse_free_throw(pins);
let reused_throw = self.reuse_free_throw(pins);
frame_throws.push(reused_throw);
}
if pins == 10 {
// On a strike, the player get two "free" throws.
let last_id = self.throws.len();
frame_throws.push(Rc::new(throw::Throw::new_free(last_id + 1)));
frame_throws.push(Rc::new(throw::Throw::new_free(last_id + 2)));
frame_throws.push(Rc::new(RefCell::new(throw::Throw::new_free(last_id + 1))));
frame_throws.push(Rc::new(RefCell::new(throw::Throw::new_free(last_id + 2))));
} else if self.spare(pins) {
let last_id = self.throws.len();
frame_throws.push(Rc::new(throw::Throw::new_free(last_id + 1)));
frame_throws.push(Rc::new(RefCell::new(throw::Throw::new_free(last_id + 1))));
}
self.push_throws_to_last_frame(frame_throws.as_slice());
@ -276,47 +234,50 @@ impl BowlingGame {
}
/// Add a new throw to the throw list.
pub fn add_new_throw(&mut self, pins: u16) -> Rc<throw::Throw> {
let last_id = self.throws.len();
let new_throw = Rc::new(throw::Throw::new(last_id, pins));
self.throws.push(new_throw.clone());
new_throw.clone()
pub fn add_new_throw(&mut self, pins: u16) -> Rc<RefCell<throw::Throw>> {
let last_id = self.throws.len() + 1;
let new_throw = Rc::new(RefCell::new(throw::Throw::new(last_id, pins)));
self.throws.push(Rc::clone(&new_throw));
Rc::clone(&new_throw)
}
/// Reuse on the free throws in the throw list instead of creating a new one.
pub fn reuse_free_throw(&mut self, pins: u16) {
(*Rc::get_mut(
self.throws
.iter_mut()
.filter(|throw| throw.is_free_throw())
.take(1)
.next()
.expect("There are no free throws for reuse"),
)
.expect("Failed to get the mutable reference to the free throw"))
.update(pins);
pub fn reuse_free_throw(&self, pins: u16) -> Rc<RefCell<throw::Throw>> {
let throw = self
.throws
.iter()
.filter(|throw| throw.borrow().is_free_throw())
.take(1)
.next()
.expect("There are no free throws for reuse");
throw.borrow_mut().update(pins);
throw.clone()
}
/// 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() {
pub fn push_throws_to_last_frame(&mut self, throws: &[Rc<RefCell<throw::Throw>>]) {
if self.frames.len() == 0
|| (self.frames.last().unwrap().is_closed() && self.frames.len() < 10)
{
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());
new_frame.add_throw(&Rc::clone(&throw));
}
self.frames.push(new_frame);
} else {
} else if self.frames.last().unwrap().accept_more_throws() {
let last_frame = self.frames.last_mut().unwrap();
for throw in throws {
last_frame.add_throw(&throw.clone());
last_frame.add_throw(&Rc::clone(&throw));
}
}
}
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());
pub fn push_free_throws(&mut self, throws: &[Rc<RefCell<throw::Throw>>]) {
for throw in throws.iter().filter(|throw| throw.borrow().is_free_throw()) {
self.throws.push(Rc::clone(throw));
}
}
@ -347,61 +308,8 @@ impl BowlingGame {
/// Check if there are any free throws available.
pub fn has_free_throws(&self) -> bool {
self.throws.iter().any(|throw| throw.is_free_throw())
}
}
#[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());
self.throws
.iter()
.any(|throw| throw.borrow().is_free_throw())
}
}

6
rust/bowling/tests/bowling.rs

@ -92,7 +92,6 @@ fn spare_in_the_first_frame_followed_by_zeros() {
}
#[test]
#[ignore]
fn points_scored_in_the_roll_after_a_spare_are_counted_twice_as_a_bonus() {
let mut game = BowlingGame::new();
@ -108,7 +107,6 @@ fn points_scored_in_the_roll_after_a_spare_are_counted_twice_as_a_bonus() {
}
#[test]
#[ignore]
fn consecutive_spares_each_get_a_one_roll_bonus() {
let mut game = BowlingGame::new();
@ -126,7 +124,6 @@ fn consecutive_spares_each_get_a_one_roll_bonus() {
}
#[test]
#[ignore]
fn if_the_last_frame_is_a_spare_you_get_one_extra_roll_that_is_scored_once() {
let mut game = BowlingGame::new();
@ -142,7 +139,6 @@ fn if_the_last_frame_is_a_spare_you_get_one_extra_roll_that_is_scored_once() {
}
#[test]
#[ignore]
fn a_strike_earns_ten_points_in_a_frame_with_a_single_roll() {
let mut game = BowlingGame::new();
@ -156,7 +152,6 @@ fn a_strike_earns_ten_points_in_a_frame_with_a_single_roll() {
}
#[test]
#[ignore]
fn points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus() {
let mut game = BowlingGame::new();
@ -172,7 +167,6 @@ fn points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus()
}
#[test]
#[ignore]
fn consecutive_strikes_each_get_the_two_roll_bonus() {
let mut game = BowlingGame::new();

Loading…
Cancel
Save