Skip to main content

bowling_rs/
score_provider.rs

1use crate::{Frame, Game, Score};
2
3/// Trait for all [ScoreProvider]s.
4pub trait ScoreProvider: Clone + Copy {
5    /// Calculate the score for each [Frame] of a [Game].
6    fn computed(&self, game: &Game) -> Vec<usize>;
7
8    /// Caculate the current score for a [Game].
9    fn score(&self, game: &Game) -> usize {
10        let computed = self.computed(game);
11
12        if let Some(score) = computed.last() {
13            return *score;
14        }
15
16        0
17    }
18
19    /// Calculate the maximum score achievable for a [Game].
20    fn max(&self, game: &Game, provider: &impl ScoreProvider) -> usize;
21}
22
23/// [ScoreProvider] for classic 10-pin bowling.
24#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
25pub struct TenPinScoreProvider;
26
27impl ScoreProvider for TenPinScoreProvider {
28    fn computed(&self, game: &Game) -> Vec<usize> {
29        let mut frames = vec![];
30        let mut score = 0;
31
32        for i in 0..game.frame_count {
33            if let Some(frame) = game.frame(i) {
34                // Add frame's score
35                score += frame.value();
36
37                // Add bonus points
38                if i < game.frame_count - 1 {
39                    if let Some(next) = game.frame(i + 1) {
40                        if frame.is_spare() {
41                            score += next.first.value();
42                        } else if frame.is_strike() {
43                            score += next.first.value();
44                            
45                            if next.is_strike() {
46                                if i == game.frame_count - 2 {
47                                    score += next.second.value();
48                                } else {
49                                    if let Some(next2) = game.frame(i + 2) {
50                                        score += next2.first.value();
51                                    }
52                                }
53                            } else if next.is_spare() {
54                                score += 10 - next.first.value();
55                            } else {
56                                score += next.second.value();
57                            }
58                        }
59                    }
60                }
61
62                println!("({}): {} -> {}", i, frame, score);
63
64                frames.push(score);
65            }
66        }
67
68        frames
69    }
70
71    fn max(&self, game: &Game, provider: &impl ScoreProvider) -> usize {
72        let mut frames = vec![];
73
74        for i in 0..game.frame_count {
75            if let Some(frame) = game.frame(i) {
76                if i == game.frame_count - 1 {
77                    let mut f = frame.clone();
78
79                    if f.first == Score::EMPTY {
80                        f.first = Score::STRIKE;
81                    }
82
83                    if f.second == Score::EMPTY {
84                        if f.first == Score::STRIKE {
85                            f.second = Score::STRIKE;
86                        } else {
87                            f.second = Score::SPARE;
88                        }
89                    }
90
91                    if let Some(bonus) = frame.bonus {
92                        if bonus == Score::EMPTY {
93                            if f.second.is_strike() {
94                                f.bonus = Some(Score::STRIKE);
95                            } else {
96                                f.bonus = Some(Score::SPARE);
97                            }
98                        }
99                    } else {
100                        if f.second.is_strike() {
101                            f.bonus = Some(Score::STRIKE);
102                        } else {
103                            f.bonus = Some(Score::SPARE);
104                        }
105                    }
106
107                    frames.push(f);
108                } else {
109                    if frame.first.is_strike() {
110                        frames.push(Frame::strike());
111                    } else if frame.second == Score::EMPTY {
112                        frames.push(Frame::spare(frame.first.value()))
113                    } else {
114                        frames.push(frame.clone());
115                    }
116                }
117            } else {
118                if i == game.frame_count - 1 {
119                    frames.push(Frame::triple_strike());
120                } else {
121                    frames.push(Frame::strike());
122                }
123            }
124        }
125
126        Game::with_frame_count(game.frame_count, frames).score_with_provider(*provider)
127    }
128}