Skip to content

Instantly share code, notes, and snippets.

@tomphp
Last active October 26, 2019 10:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomphp/9ba642eb21c121e80c6e45d9f296c28f to your computer and use it in GitHub Desktop.
Save tomphp/9ba642eb21c121e80c6e45d9f296c28f to your computer and use it in GitHub Desktop.
Rusty Bowling
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