aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
Diffstat (limited to 'rust')
-rw-r--r--rust/bowling/.exercism/metadata.json1
-rw-r--r--rust/bowling/.gitignore8
-rw-r--r--rust/bowling/Cargo.toml4
-rw-r--r--rust/bowling/README.md141
-rw-r--r--rust/bowling/src/lib.rs45
-rw-r--r--rust/bowling/tests/bowling.rs418
6 files changed, 617 insertions, 0 deletions
diff --git a/rust/bowling/.exercism/metadata.json b/rust/bowling/.exercism/metadata.json
new file mode 100644
index 0000000..832eced
--- /dev/null
+++ b/rust/bowling/.exercism/metadata.json
@@ -0,0 +1 @@
{"track":"rust","exercise":"bowling","id":"b330b3979d1e4e18ab421b2a06d22671","url":"https://exercism.io/my/solutions/b330b3979d1e4e18ab421b2a06d22671","handle":"dyamon","is_requester":true,"auto_approve":false} \ No newline at end of file
diff --git a/rust/bowling/.gitignore b/rust/bowling/.gitignore
new file mode 100644
index 0000000..db7f315
--- /dev/null
+++ b/rust/bowling/.gitignore
@@ -0,0 +1,8 @@
1# Generated by Cargo
2# will have compiled files and executables
3/target/
4**/*.rs.bk
5
6# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
8Cargo.lock
diff --git a/rust/bowling/Cargo.toml b/rust/bowling/Cargo.toml
new file mode 100644
index 0000000..b14350e
--- /dev/null
+++ b/rust/bowling/Cargo.toml
@@ -0,0 +1,4 @@
1[package]
2edition = "2018"
3name = "bowling"
4version = "1.2.0"
diff --git a/rust/bowling/README.md b/rust/bowling/README.md
new file mode 100644
index 0000000..6c08b38
--- /dev/null
+++ b/rust/bowling/README.md
@@ -0,0 +1,141 @@
1# Bowling
2
3Score a bowling game.
4
5Bowling is a game where players roll a heavy ball to knock down pins
6arranged in a triangle. Write code to keep track of the score
7of a game of bowling.
8
9## Scoring Bowling
10
11The game consists of 10 frames. A frame is composed of one or two ball
12throws with 10 pins standing at frame initialization. There are three
13cases for the tabulation of a frame.
14
15* An open frame is where a score of less than 10 is recorded for the
16 frame. In this case the score for the frame is the number of pins
17 knocked down.
18
19* A spare is where all ten pins are knocked down by the second
20 throw. The total value of a spare is 10 plus the number of pins
21 knocked down in their next throw.
22
23* A strike is where all ten pins are knocked down by the first
24 throw. The total value of a strike is 10 plus the number of pins
25 knocked down in the next two throws. If a strike is immediately
26 followed by a second strike, then the value of the first strike
27 cannot be determined until the ball is thrown one more time.
28
29Here is a three frame example:
30
31| Frame 1 | Frame 2 | Frame 3 |
32| :-------------: |:-------------:| :---------------------:|
33| X (strike) | 5/ (spare) | 9 0 (open frame) |
34
35Frame 1 is (10 + 5 + 5) = 20
36
37Frame 2 is (5 + 5 + 9) = 19
38
39Frame 3 is (9 + 0) = 9
40
41This means the current running total is 48.
42
43The tenth frame in the game is a special case. If someone throws a
44strike or a spare then they get a fill ball. Fill balls exist to
45calculate the total of the 10th frame. Scoring a strike or spare on
46the fill ball does not give the player more fill balls. The total
47value of the 10th frame is the total number of pins knocked down.
48
49For a tenth frame of X1/ (strike and a spare), the total value is 20.
50
51For a tenth frame of XXX (three strikes), the total value is 30.
52
53## Requirements
54
55Write code to keep track of the score of a game of bowling. It should
56support two operations:
57
58* `roll(pins : int)` is called each time the player rolls a ball. The
59 argument is the number of pins knocked down.
60* `score() : int` is called only at the very end of the game. It
61 returns the total score for that game.
62
63## Rust Installation
64
65Refer to the [exercism help page][help-page] for Rust installation and learning
66resources.
67
68## Writing the Code
69
70Execute the tests with:
71
72```bash
73$ cargo test
74```
75
76All but the first test have been ignored. After you get the first test to
77pass, open the tests source file which is located in the `tests` directory
78and remove the `#[ignore]` flag from the next test and get the tests to pass
79again. Each separate test is a function with `#[test]` flag above it.
80Continue, until you pass every test.
81
82If you wish to run all ignored tests without editing the tests source file, use:
83
84```bash
85$ cargo test -- --ignored
86```
87
88To run a specific test, for example `some_test`, you can use:
89
90```bash
91$ cargo test some_test
92```
93
94If the specific test is ignored use:
95
96```bash
97$ cargo test some_test -- --ignored
98```
99
100To learn more about Rust tests refer to the [online test documentation][rust-tests]
101
102Make sure to read the [Modules][modules] chapter if you
103haven't already, it will help you with organizing your files.
104
105## Further improvements
106
107After you have solved the exercise, please consider using the additional utilities, described in the [installation guide](https://exercism.io/tracks/rust/installation), to further refine your final solution.
108
109To format your solution, inside the solution directory use
110
111```bash
112cargo fmt
113```
114
115To see, if your solution contains some common ineffective use cases, inside the solution directory use
116
117```bash
118cargo clippy --all-targets
119```
120
121## Submitting the solution
122
123Generally you should submit all files in which you implemented your solution (`src/lib.rs` in most cases). If you are using any external crates, please consider submitting the `Cargo.toml` file. This will make the review process faster and clearer.
124
125## Feedback, Issues, Pull Requests
126
127The [exercism/rust](https://github.com/exercism/rust) repository on GitHub is the home for all of the Rust exercises. If you have feedback about an exercise, or want to help implement new exercises, head over there and create an issue. Members of the rust track team are happy to help!
128
129If you want to know more about Exercism, take a look at the [contribution guide](https://github.com/exercism/docs/blob/master/contributing-to-language-tracks/README.md).
130
131[help-page]: https://exercism.io/tracks/rust/learning
132[modules]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
133[cargo]: https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html
134[rust-tests]: https://doc.rust-lang.org/book/ch11-02-running-tests.html
135
136## Source
137
138The Bowling Game Kata at but UncleBob [http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata](http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata)
139
140## Submitting Incomplete Solutions
141It's possible to submit an incomplete solution so you can see how others have completed the exercise.
diff --git a/rust/bowling/src/lib.rs b/rust/bowling/src/lib.rs
new file mode 100644
index 0000000..bd517a5
--- /dev/null
+++ b/rust/bowling/src/lib.rs
@@ -0,0 +1,45 @@
1#[derive(Debug, PartialEq)]
2pub enum Error {
3 NotEnoughPinsLeft,
4 GameComplete,
5}
6
7#[derive(Debug, Default)]
8pub struct BowlingGame {
9 throws: Vec<u16>,
10 prev: u16
11}
12
13impl BowlingGame {
14 pub fn new() -> Self {
15 Default::default()
16 }
17
18 pub fn roll(&mut self, pins: u16) -> Result<(), Error> {
19 if pins + self.prev > 10 {
20 Err(Error::NotEnoughPinsLeft)
21 } else if self.score().is_some() {
22 Err(Error::GameComplete)
23 } else {
24 self.throws.push(pins);
25 self.prev = if self.prev + pins == 10 { 0 } else { pins };
26 Ok(())
27 }
28 }
29
30 pub fn score(&self) -> Option<u16> {
31 let mut score = 0;
32 let mut stage = 0;
33 for _ in 1..=10 {
34 let first = self.throws.get(stage)?;
35 let second = self.throws.get(stage+1)?;
36 score += first + second;
37 if first + second >= 10 {
38 let third = self.throws.get(stage+2)?;
39 score += third;
40 }
41 stage += if *first == 10 { 1 } else { 2 };
42 }
43 Some(score)
44 }
45}
diff --git a/rust/bowling/tests/bowling.rs b/rust/bowling/tests/bowling.rs
new file mode 100644
index 0000000..92f646f
--- /dev/null
+++ b/rust/bowling/tests/bowling.rs
@@ -0,0 +1,418 @@
1use bowling::*;
2
3#[test]
4fn roll_returns_a_result() {
5 let mut game = BowlingGame::new();
6 assert!(game.roll(0).is_ok());
7}
8
9#[test]
10fn you_cannot_roll_more_than_ten_pins_in_a_single_roll() {
11 let mut game = BowlingGame::new();
12
13 assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft));
14}
15
16#[test]
17fn a_game_score_is_some_if_ten_frames_have_been_rolled() {
18 let mut game = BowlingGame::new();
19
20 for _ in 0..10 {
21 let _ = game.roll(0);
22 let _ = game.roll(0);
23 }
24
25 assert!(game.score().is_some());
26}
27
28#[test]
29fn you_cannot_score_a_game_with_no_rolls() {
30 let game = BowlingGame::new();
31
32 assert_eq!(game.score(), None);
33}
34
35#[test]
36fn a_game_score_is_none_if_fewer_than_ten_frames_have_been_rolled() {
37 let mut game = BowlingGame::new();
38
39 for _ in 0..9 {
40 let _ = game.roll(0);
41 let _ = game.roll(0);
42 }
43
44 assert_eq!(game.score(), None);
45}
46
47#[test]
48fn a_roll_is_err_if_the_game_is_done() {
49 let mut game = BowlingGame::new();
50
51 for _ in 0..10 {
52 let _ = game.roll(0);
53 let _ = game.roll(0);
54 }
55
56 assert_eq!(game.roll(0), Err(Error::GameComplete));
57}
58
59#[test]
60fn twenty_zero_pin_rolls_scores_zero() {
61 let mut game = BowlingGame::new();
62
63 for _ in 0..20 {
64 let _ = game.roll(0);
65 }
66
67 assert_eq!(game.score(), Some(0));
68}
69
70#[test]
71fn ten_frames_without_a_strike_or_spare() {
72 let mut game = BowlingGame::new();
73
74 for _ in 0..10 {
75 let _ = game.roll(3);
76 let _ = game.roll(6);
77 }
78
79 assert_eq!(game.score(), Some(90));
80}
81
82#[test]
83fn spare_in_the_first_frame_followed_by_zeros() {
84 let mut game = BowlingGame::new();
85
86 let _ = game.roll(6);
87 let _ = game.roll(4);
88
89 for _ in 0..18 {
90 let _ = game.roll(0);
91 }
92
93 assert_eq!(game.score(), Some(10));
94}
95
96#[test]
97fn points_scored_in_the_roll_after_a_spare_are_counted_twice_as_a_bonus() {
98 let mut game = BowlingGame::new();
99
100 let _ = game.roll(6);
101 let _ = game.roll(4);
102 let _ = game.roll(3);
103
104 for _ in 0..17 {
105 let _ = game.roll(0);
106 }
107
108 assert_eq!(game.score(), Some(16));
109}
110
111#[test]
112fn consecutive_spares_each_get_a_one_roll_bonus() {
113 let mut game = BowlingGame::new();
114
115 let _ = game.roll(5);
116 let _ = game.roll(5);
117 let _ = game.roll(3);
118 let _ = game.roll(7);
119 let _ = game.roll(4);
120
121 for _ in 0..15 {
122 let _ = game.roll(0);
123 }
124
125 assert_eq!(game.score(), Some(31));
126}
127
128#[test]
129fn if_the_last_frame_is_a_spare_you_get_one_extra_roll_that_is_scored_once() {
130 let mut game = BowlingGame::new();
131
132 for _ in 0..18 {
133 let _ = game.roll(0);
134 }
135
136 let _ = game.roll(5);
137 let _ = game.roll(5);
138 let _ = game.roll(7);
139
140 assert_eq!(game.score(), Some(17));
141}
142
143#[test]
144fn a_strike_earns_ten_points_in_a_frame_with_a_single_roll() {
145 let mut game = BowlingGame::new();
146
147 let _ = game.roll(10);
148
149 for _ in 0..18 {
150 let _ = game.roll(0);
151 }
152
153 assert_eq!(game.score(), Some(10));
154}
155
156#[test]
157fn points_scored_in_the_two_rolls_after_a_strike_are_counted_twice_as_a_bonus() {
158 let mut game = BowlingGame::new();
159
160 let _ = game.roll(10);
161 let _ = game.roll(5);
162 let _ = game.roll(3);
163
164 for _ in 0..16 {
165 let _ = game.roll(0);
166 }
167
168 assert_eq!(game.score(), Some(26));
169}
170
171#[test]
172fn consecutive_strikes_each_get_the_two_roll_bonus() {
173 let mut game = BowlingGame::new();
174
175 let _ = game.roll(10);
176 let _ = game.roll(10);
177 let _ = game.roll(10);
178 let _ = game.roll(5);
179 let _ = game.roll(3);
180
181 for _ in 0..12 {
182 let _ = game.roll(0);
183 }
184
185 assert_eq!(game.score(), Some(81));
186}
187
188#[test]
189fn a_strike_in_the_last_frame_earns_a_two_roll_bonus_that_is_counted_once() {
190 let mut game = BowlingGame::new();
191
192 for _ in 0..18 {
193 let _ = game.roll(0);
194 }
195
196 let _ = game.roll(10);
197 let _ = game.roll(7);
198 let _ = game.roll(1);
199
200 assert_eq!(game.score(), Some(18));
201}
202
203#[test]
204fn a_spare_with_the_two_roll_bonus_does_not_get_a_bonus_roll() {
205 let mut game = BowlingGame::new();
206
207 for _ in 0..18 {
208 let _ = game.roll(0);
209 }
210
211 let _ = game.roll(10);
212 let _ = game.roll(7);
213 let _ = game.roll(3);
214
215 assert_eq!(game.score(), Some(20));
216}
217
218#[test]
219fn strikes_with_the_two_roll_bonus_do_not_get_a_bonus_roll() {
220 let mut game = BowlingGame::new();
221
222 for _ in 0..18 {
223 let _ = game.roll(0);
224 }
225
226 let _ = game.roll(10);
227 let _ = game.roll(10);
228 let _ = game.roll(10);
229
230 assert_eq!(game.score(), Some(30));
231}
232
233#[test]
234fn a_strike_with_the_one_roll_bonus_after_a_spare_in_the_last_frame_does_not_get_a_bonus() {
235 let mut game = BowlingGame::new();
236
237 for _ in 0..18 {
238 let _ = game.roll(0);
239 }
240
241 let _ = game.roll(7);
242 let _ = game.roll(3);
243 let _ = game.roll(10);
244
245 assert_eq!(game.score(), Some(20));
246}
247
248#[test]
249fn all_strikes_is_a_perfect_score_of_300() {
250 let mut game = BowlingGame::new();
251
252 for _ in 0..12 {
253 let _ = game.roll(10);
254 }
255
256 assert_eq!(game.score(), Some(300));
257}
258
259#[test]
260fn you_cannot_roll_more_than_ten_pins_in_a_single_frame() {
261 let mut game = BowlingGame::new();
262
263 assert!(game.roll(5).is_ok());
264 assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft));
265}
266
267#[test]
268fn first_bonus_ball_after_a_final_strike_cannot_score_an_invalid_number_of_pins() {
269 let mut game = BowlingGame::new();
270
271 for _ in 0..18 {
272 let _ = game.roll(0);
273 }
274
275 let _ = game.roll(10);
276
277 assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft));
278}
279
280#[test]
281fn the_two_balls_after_a_final_strike_cannot_score_an_invalid_number_of_pins() {
282 let mut game = BowlingGame::new();
283
284 for _ in 0..18 {
285 let _ = game.roll(0);
286 }
287
288 let _ = game.roll(10);
289
290 assert!(game.roll(5).is_ok());
291 assert_eq!(game.roll(6), Err(Error::NotEnoughPinsLeft));
292}
293
294#[test]
295fn the_two_balls_after_a_final_strike_can_be_a_strike_and_non_strike() {
296 let mut game = BowlingGame::new();
297
298 for _ in 0..18 {
299 let _ = game.roll(0);
300 }
301
302 let _ = game.roll(10);
303
304 assert!(game.roll(10).is_ok());
305 assert!(game.roll(6).is_ok());
306}
307
308#[test]
309fn the_two_balls_after_a_final_strike_cannot_be_a_non_strike_followed_by_a_strike() {
310 let mut game = BowlingGame::new();
311
312 for _ in 0..18 {
313 let _ = game.roll(0);
314 }
315
316 let _ = game.roll(10);
317
318 assert!(game.roll(6).is_ok());
319 assert_eq!(game.roll(10), Err(Error::NotEnoughPinsLeft));
320}
321
322#[test]
323fn second_bonus_ball_after_a_final_strike_cannot_score_an_invalid_number_of_pins_even_if_first_is_strike(
324) {
325 let mut game = BowlingGame::new();
326
327 for _ in 0..18 {
328 let _ = game.roll(0);
329 }
330
331 let _ = game.roll(10);
332
333 assert!(game.roll(10).is_ok());
334 assert_eq!(game.roll(11), Err(Error::NotEnoughPinsLeft));
335}
336
337#[test]
338fn if_the_last_frame_is_a_strike_you_cannot_score_before_the_extra_rolls_are_taken() {
339 let mut game = BowlingGame::new();
340
341 for _ in 0..18 {
342 let _ = game.roll(0);
343 }
344
345 let _ = game.roll(10);
346
347 assert_eq!(game.score(), None);
348
349 let _ = game.roll(10);
350
351 assert_eq!(game.score(), None);
352
353 let _ = game.roll(10);
354
355 assert!(game.score().is_some());
356}
357
358#[test]
359fn if_the_last_frame_is_a_spare_you_cannot_create_a_score_before_extra_roll_is_taken() {
360 let mut game = BowlingGame::new();
361
362 for _ in 0..18 {
363 let _ = game.roll(0);
364 }
365
366 let _ = game.roll(5);
367 let _ = game.roll(5);
368
369 assert_eq!(game.score(), None);
370
371 let _ = game.roll(10);
372
373 assert!(game.score().is_some());
374}
375
376#[test]
377fn cannot_roll_after_bonus_roll_for_spare() {
378 let mut game = BowlingGame::new();
379
380 for _ in 0..9 {
381 let _ = game.roll(0);
382 let _ = game.roll(0);
383 }
384 let _ = game.roll(7);
385 let _ = game.roll(3);
386 assert!(game.roll(2).is_ok());
387
388 assert_eq!(game.roll(2), Err(Error::GameComplete));
389}
390
391#[test]
392fn cannot_roll_after_bonus_roll_for_strike() {
393 let mut game = BowlingGame::new();
394
395 for _ in 0..9 {
396 let _ = game.roll(0);
397 let _ = game.roll(0);
398 }
399 let _ = game.roll(10);
400 let _ = game.roll(3);
401 assert!(game.roll(2).is_ok());
402
403 assert_eq!(game.roll(2), Err(Error::GameComplete));
404}
405
406#[test]
407fn last_two_strikes_followed_by_only_last_bonus_with_non_strike_points() {
408 let mut game = BowlingGame::new();
409 for _ in 0..16 {
410 let _ = game.roll(0);
411 }
412 let _ = game.roll(10);
413 let _ = game.roll(10);
414 let _ = game.roll(0);
415 let _ = game.roll(1);
416
417 assert_eq!(game.score(), Some(31));
418}