Last active
October 26, 2019 10:25
-
-
Save tomphp/9ba642eb21c121e80c6e45d9f296c28f to your computer and use it in GitHub Desktop.
Rusty Bowling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const FRAMES_IN_GAME: i32 = 10; | |
const PINS_IN_FRAME: i32 = 10; | |
const ROLLS_IN_FRAME: usize = 2; | |
const ROLLS_IN_STRIKE_FRAME: usize = 1; | |
const ROLLS_IN_SCORE_DEFAULT: usize = 2; | |
const ROLLS_IN_SCORE_SPARE: usize = 3; | |
const ROLLS_IN_SCORE_STRIKE: usize = 3; | |
struct Game { | |
scores: Vec<i32>, | |
} | |
impl Game { | |
fn new() -> Game { | |
Game { scores: Vec::new() } | |
} | |
fn roll(&self, pins_knocked_down: i32) -> Game { | |
Game { | |
scores: vec![self.scores.clone(), vec![pins_knocked_down]].concat(), | |
} | |
} | |
fn score(&self) -> i32 { | |
let frame_zero = Frame { remaining_rolls: self.scores.clone(), current_score: 0 }; | |
(0..FRAMES_IN_GAME) | |
.fold(frame_zero, |frame, _| frame.score_and_return_next()) | |
.current_score | |
} | |
} | |
struct Frame { | |
remaining_rolls: Vec<i32>, | |
current_score: i32, | |
} | |
#[derive(Clone, Copy)] | |
struct FrameDetails { | |
rolls_scored: usize, | |
rolls_in_frame: usize, | |
} | |
impl Frame { | |
fn score_and_return_next(&self) -> Frame { | |
let frame_details = self.frame_details(); | |
let score = self.current_score + self.calculate_score(frame_details); | |
let next_rolls = self.move_to_next_frame(frame_details); | |
Frame { current_score: score, remaining_rolls: next_rolls } | |
} | |
fn frame_details(&self) -> FrameDetails { | |
if self.is_strike() { | |
FrameDetails { rolls_scored: ROLLS_IN_SCORE_STRIKE, rolls_in_frame: ROLLS_IN_STRIKE_FRAME } | |
} else if self.is_spare() { | |
FrameDetails { rolls_scored: ROLLS_IN_SCORE_SPARE, rolls_in_frame: ROLLS_IN_FRAME } | |
} else { | |
FrameDetails { rolls_scored: ROLLS_IN_SCORE_DEFAULT, rolls_in_frame: ROLLS_IN_FRAME } | |
} | |
} | |
fn is_strike(&self) -> bool { | |
self.remaining_rolls.get(0) == Some(&PINS_IN_FRAME) | |
} | |
fn is_spare(&self) -> bool { | |
let frame_score: i32 = self.remaining_rolls[..ROLLS_IN_FRAME].into_iter().sum(); | |
frame_score == PINS_IN_FRAME | |
} | |
fn calculate_score(&self, frame_details: FrameDetails) -> i32 { | |
self.remaining_rolls[..frame_details.rolls_scored].into_iter().sum() | |
} | |
fn move_to_next_frame(&self, frame_details: FrameDetails) -> Vec<i32> { | |
self.remaining_rolls[frame_details.rolls_in_frame..].to_vec() | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use crate::{Game, FRAMES_IN_GAME, PINS_IN_FRAME}; | |
impl Game { | |
fn roll_frame(&self, first_roll: i32, second_roll: i32) -> Game { | |
self.roll(first_roll).roll(second_roll) | |
} | |
fn roll_spare(&self) -> Game { | |
self.roll_frame(1, 9) | |
} | |
fn roll_strike(&self) -> Game { | |
self.roll(PINS_IN_FRAME) | |
} | |
} | |
#[test] | |
fn it_adds_together_all_rolls_in_game() { | |
let mut game = Game::new(); | |
for _ in 0..FRAMES_IN_GAME { | |
game = game.roll_frame(1, 2) | |
} | |
assert_eq!(game.score(), 30); | |
} | |
#[test] | |
fn gutter_ball_game() { | |
let mut game = Game::new(); | |
for _ in 0..FRAMES_IN_GAME { | |
game = game.roll_frame(0, 0) | |
} | |
assert_eq!(game.score(), 0); | |
} | |
#[test] | |
fn it_doubles_the_next_roll_on_a_spare() { | |
let mut game = Game::new(); | |
game = game.roll_spare(); | |
game = game.roll_frame(2, 3); | |
const ALREADY_ROLLED_FRAMES: i32 = 2; | |
for _ in 0..(FRAMES_IN_GAME - ALREADY_ROLLED_FRAMES) { | |
game = game.roll_frame(0, 0) | |
} | |
assert_eq!(game.score(), 17); | |
} | |
#[test] | |
fn it_doubles_the_next_two_rolls_on_a_strike() { | |
let mut game = Game::new(); | |
game = game.roll_strike(); | |
game = game.roll_frame(2, 3); | |
game = game.roll_frame(2, 3); | |
const ALREADY_ROLLED_FRAMES: i32 = 3; | |
for _ in 0..FRAMES_IN_GAME - ALREADY_ROLLED_FRAMES { | |
game = game.roll_frame(0, 0); | |
} | |
assert_eq!(game.score(), 25); | |
} | |
#[test] | |
fn perfect_game() { | |
let mut game = Game::new(); | |
const BONUS_ROLLS: i32 = 2; | |
for _ in 0..FRAMES_IN_GAME + BONUS_ROLLS { | |
game = game.roll_strike() | |
} | |
assert_eq!(game.score(), 300); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment