Skip to main content

bowling_rs/
game.rs

1use std::{fmt, slice::{Iter, IterMut}, vec::IntoIter};
2
3use crate::{Frame, ScoreProvider, TenPinScoreProvider};
4
5/// Contains the data for a bowling game.
6#[derive(Clone, Debug, Default, Hash)]
7pub struct Game {
8    /// The total number of frames allowed for the [Game].
9    pub frame_count: usize,
10    /// The frames of the [Game].
11    frames: Vec<Frame>
12}
13
14impl Game {
15    /// Create a new [Game].
16    pub fn new(frames: Vec<Frame>) -> Self {
17        Self {
18            frame_count: 10,
19            frames
20        }
21    }
22
23    /// Create a new [Game] with a custom frame count.
24    pub fn with_frame_count(frame_count: usize, frames: Vec<Frame>) -> Self {
25        Self {
26            frame_count,
27            frames
28        }
29    }
30
31    /// Returns an iterator for the [Frame]s of the [Game].
32    pub fn iter(&self) -> Iter<Frame> {
33        self.frames.iter()
34    }
35
36    /// Returns a mutable iterator for the [Frame]s of the [Game].
37    pub fn iter_mut(&mut self) -> IterMut<Frame> {
38        self.frames.iter_mut()
39    }
40
41    /// Creates a consuming iterator for the [Frame]s of the [Game].
42    pub fn into_iter(self) -> IntoIter<Frame> {
43        self.frames.into_iter()
44    }
45
46    /// Add a [Frame] to the [Game].
47    pub fn add_frame(&mut self, frame: Frame) {
48        self.frames.push(frame);
49    }
50
51    /// Remove a [Frame] to the [Game].
52    pub fn remove_frame(&mut self, index: usize) -> Frame {
53        self.frames.remove(index)
54    }
55
56    /// Get a [Frame] from the [Game].
57    pub fn frame(&self, index: usize) -> Option<&Frame> {
58        self.frames.get(index)
59    }
60
61    /// Set a [Frame] from the [Game].
62    pub fn set_frame(&mut self, index: usize, frame: Frame) {
63        self.frames[index] = frame;
64    }
65
66    /// Check if the [Game] is perfect (300).
67    pub fn is_perfect(&self) -> bool {
68        self.score() == 300
69    }
70
71    /// Calculate the score for each [Frame] of the [Game] using a [ScoreProvider].
72    pub fn computed_with_provider(&self, provider: impl ScoreProvider) -> Vec<usize> {
73        provider.computed(&self)
74    }
75
76    /// Calculate the score for each [Frame] of the [Game] using the [TenPinScoreProvider].
77    pub fn computed(&self) -> Vec<usize> {
78        self.computed_with_provider(TenPinScoreProvider)
79    }
80
81    /// Caculate the current score for the [Game] using a [ScoreProvider].
82    pub fn score_with_provider(&self, provider: impl ScoreProvider) -> usize {
83        provider.score(&self)
84    }
85
86    /// Caculate the current score for the [Game] using the [TenPinScoreProvider].
87    pub fn score(&self) -> usize {
88        self.score_with_provider(TenPinScoreProvider)
89    }
90
91    /// Calculate the maximum score achievable for the [Game] using a [ScoreProvider].
92    pub fn max_with_provider(&self, provider: impl ScoreProvider) -> usize {
93        provider.max(&self, &provider)
94    }
95
96    /// Calculate the maximum score achievable for the [Game] using the [TenPinScoreProvider].
97    pub fn max(&self) -> usize {
98        self.max_with_provider(TenPinScoreProvider)
99    }
100}
101
102// TODO: fix formatting for frames
103impl fmt::Display for Game {
104    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(formatter, "Game ({} frame{}) [\n{:#?}\n]", self.frame_count, if self.frame_count == 1 { "" } else { "s" }, self.frames)
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::{Frame, Game, Score};
112
113    #[test]
114    fn game_300() {
115        let mut game = Game::new(vec![Frame::strike(); 9]);
116        game.frames.push(Frame::triple_strike());
117        
118        assert_eq!(game.score(), 300);
119    }
120
121    #[test]
122    fn game_299() {
123        let mut game = Game::new(vec![Frame::strike(); 9]);
124        game.frames.push(Frame::with_bonus(Score::STRIKE, Score::STRIKE, Score::PINS(9)));
125        
126        assert_eq!(game.score(), 299);
127    }
128
129    #[test]
130    fn game_290_0() {
131        let mut game = Game::new(vec![Frame::strike(); 9]);
132        game.frames.push(Frame::with_bonus(Score::STRIKE, Score::STRIKE, Score::PINS(0)));
133        
134        assert_eq!(game.score(), 290);
135    }
136
137    #[test]
138    fn game_290_1() {
139        let mut game = Game::new(vec![Frame::spare(9)]);
140        game.frames.append(&mut vec![Frame::strike(); 8]);
141        game.frames.push(Frame::triple_strike());
142        
143        assert_eq!(game.score(), 290);
144    }
145
146    #[test]
147    fn game_289() {
148        let mut game = Game::new(vec![Frame::strike(); 9]);
149        game.frames.push(Frame::with_bonus(Score::STRIKE, Score::PINS(9), Score::SPARE));
150        
151        assert_eq!(game.score(), 289);
152    }
153
154    #[test]
155    fn game_280() {
156        let mut game = Game::new(vec![Frame::strike(), Frame::spare(9)]);
157        game.frames.append(&mut vec![Frame::strike(); 7]);
158        game.frames.push(Frame::triple_strike());
159        
160        assert_eq!(game.score(), 280);
161    }
162
163    #[test]
164    fn game_279_0() {
165        let mut game = Game::new(vec![Frame::strike(), Frame::strike(), Frame::spare(9)]);
166        game.frames.append(&mut vec![Frame::strike(); 6]);
167        game.frames.push(Frame::triple_strike());
168        
169        assert_eq!(game.score(), 279);
170    }
171
172    #[test]
173    fn game_279_1() {
174        let mut game = Game::new(vec![Frame::strike(); 9]);
175        game.frames.push(Frame::with_bonus(Score::PINS(9), Score::SPARE, Score::STRIKE));
176        
177        assert_eq!(game.score(), 279);
178    }
179
180    #[test]
181    fn game_245() {
182        let game = Game::new(vec![Frame::new(Score::PINS(7), Score::PINS(1)), Frame::spare(9), Frame::strike(), Frame::spare(8), Frame::strike(), Frame::strike(), Frame::strike(), Frame::strike(), Frame::strike(), Frame::with_bonus(Score::STRIKE, Score::STRIKE, Score::PINS(7))]);
183        assert_eq!(game.score(), 245);
184    }
185
186    #[test]
187    fn game_223() {
188        let game = Game::new(vec![Frame::spare(9), Frame::spare(8), Frame::strike(), Frame::strike(), Frame::strike(), Frame::strike(), Frame::spare(8), Frame::strike(), Frame::strike(), Frame::new(Score::PINS(9), Score::PINS(0))]);
189        assert_eq!(game.score(), 223);
190    }
191
192    #[test]
193    fn game_200() {
194        let game = Game::new(vec![Frame::spare(9), Frame::strike(), Frame::spare(9), Frame::strike(), Frame::spare(9), Frame::strike(), Frame::spare(9), Frame::strike(), Frame::spare(9), Frame::with_bonus(Score::STRIKE, Score::PINS(9), Score::SPARE)]);
195        assert_eq!(game.score(), 200);
196    }
197
198    #[test]
199    fn game_0() {
200        let game = Game::new(vec![]);
201        assert_eq!(game.score(), 0);
202    }
203
204    #[test]
205    fn spare_strike() {
206        let game = Game::new(vec![Frame::spare(9), Frame::strike()]);
207        assert_eq!(game.score(), 30);
208    }
209
210    #[test]
211    fn spare_5() {
212        let game = Game::new(vec![Frame::spare(9), Frame::new(Score::PINS(5), Score::PINS(0))]);
213        assert_eq!(game.score(), 20);
214    }
215
216    #[test]
217    fn strike_5_2_1() {
218        let game = Game::new(vec![Frame::strike(), Frame::new(Score::PINS(5), Score::PINS(2)), Frame::new(Score::PINS(1), Score::PINS(0))]);
219        assert_eq!(game.score(), 25);
220    }
221
222    #[test]
223    fn spare_strike_spare() {
224        let game = Game::new(vec![Frame::spare(9), Frame::strike(), Frame::spare(9)]);
225        assert_eq!(game.score(), 50);
226    }
227
228    #[test]
229    fn max_300() {
230        let game = Game::new(vec![]);
231        assert_eq!(game.max(), 300);
232    }
233
234    #[test]
235    fn max_290() {
236        let game = Game::new(vec![Frame::new(Score::PINS(9), Score::EMPTY)]);
237        assert_eq!(game.max(), 290);
238    }
239
240    #[test]
241    fn max_289() {
242        let mut game = Game::new(vec![Frame::strike(); 9]);
243        game.frames.push(Frame::with_bonus(Score::STRIKE, Score::PINS(9), Score::EMPTY));
244        
245        assert_eq!(game.max(), 289);
246    }
247
248    #[test]
249    fn max_279_0() {
250        let game = Game::new(vec![Frame::new(Score::PINS(9), Score::PINS(0))]);
251        assert_eq!(game.max(), 279);
252    }
253
254    #[test]
255    fn max_279_1() {
256        let game = Game::new(vec![Frame::strike(), Frame::strike(), Frame::new(Score::PINS(9), Score::EMPTY)]);
257        assert_eq!(game.max(), 279);
258    }
259}