Skip to content

Commit 226be73

Browse files
feat: Add bowling exercise
1 parent f91afda commit 226be73

File tree

7 files changed

+632
-0
lines changed

7 files changed

+632
-0
lines changed

config.json

+8
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,14 @@
153153
"prerequisites": [],
154154
"difficulty": 3
155155
},
156+
{
157+
"slug": "bowling",
158+
"name": "Bowling",
159+
"uuid": "a62ffd58-12ae-414d-a5ba-fa970de81d02",
160+
"practices": [],
161+
"prerequisites": [],
162+
"difficulty": 6
163+
},
156164
{
157165
"uuid": "c7b387ae-3ea2-40d2-9792-5f83d14f316e",
158166
"slug": "list-ops",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Instructions
2+
3+
Score a bowling game.
4+
5+
Bowling is a game where players roll a heavy ball to knock down pins arranged in a triangle.
6+
Write code to keep track of the score of a game of bowling.
7+
8+
## Scoring Bowling
9+
10+
The game consists of 10 frames.
11+
A frame is composed of one or two ball throws with 10 pins standing at frame initialization.
12+
There are three cases for the tabulation of a frame.
13+
14+
- An open frame is where a score of less than 10 is recorded for the frame.
15+
In this case the score for the frame is the number of pins knocked down.
16+
17+
- A spare is where all ten pins are knocked down by the second throw.
18+
The total value of a spare is 10 plus the number of pins knocked down in their next throw.
19+
20+
- A strike is where all ten pins are knocked down by the first throw.
21+
The total value of a strike is 10 plus the number of pins knocked down in the next two throws.
22+
If a strike is immediately followed by a second strike, then the value of the first strike cannot be determined until the ball is thrown one more time.
23+
24+
Here is a three frame example:
25+
26+
| Frame 1 | Frame 2 | Frame 3 |
27+
| :--------: | :--------: | :--------------: |
28+
| X (strike) | 5/ (spare) | 9 0 (open frame) |
29+
30+
Frame 1 is (10 + 5 + 5) = 20
31+
32+
Frame 2 is (5 + 5 + 9) = 19
33+
34+
Frame 3 is (9 + 0) = 9
35+
36+
This means the current running total is 48.
37+
38+
The tenth frame in the game is a special case.
39+
If someone throws a spare or a strike then they get one or two fill balls respectively.
40+
Fill balls exist to calculate the total of the 10th frame.
41+
Scoring a strike or spare on the fill ball does not give the player more fill balls.
42+
The total value of the 10th frame is the total number of pins knocked down.
43+
44+
For a tenth frame of X1/ (strike and a spare), the total value is 20.
45+
46+
For a tenth frame of XXX (three strikes), the total value is 30.
47+
48+
## Requirements
49+
50+
Write code to keep track of the score of a game of bowling.
51+
It should support two operations:
52+
53+
- `roll(pins : int)` is called each time the player rolls a ball.
54+
The argument is the number of pins knocked down.
55+
- `score() : int` is called only at the very end of the game.
56+
It returns the total score for that game.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"keiravillekode"
4+
],
5+
"files": {
6+
"solution": [
7+
"bowling.v"
8+
],
9+
"test": [
10+
"run_test.v"
11+
],
12+
"example": [
13+
".meta/example.v"
14+
]
15+
},
16+
"blurb": "Score a bowling game.",
17+
"source": "The Bowling Game Kata from UncleBob",
18+
"source_url": "https://web.archive.org/web/20221001111000/http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata"
19+
}
+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
module main
2+
3+
enum State {
4+
double // Previous two frames were strikes.
5+
strike // Previous frame was a strike.
6+
spare // Previous frame was a spare.
7+
open // Previous frame was not a strike or a spare.
8+
}
9+
10+
struct Game {
11+
mut:
12+
score int
13+
pins int
14+
frames int
15+
state State = .open
16+
}
17+
18+
// build a new Game
19+
fn Game.new() Game {
20+
return Game{}
21+
}
22+
23+
pub fn (g Game) is_over() bool {
24+
return match g.state {
25+
.double {
26+
if g.pins == 0 {
27+
g.frames == 12
28+
} else {
29+
g.frames == 11
30+
}
31+
}
32+
.strike {
33+
g.frames == 11
34+
}
35+
.spare {
36+
if g.pins == 0 {
37+
g.frames == 11
38+
} else {
39+
g.frames == 10
40+
}
41+
}
42+
.open {
43+
g.frames >= 10
44+
}
45+
}
46+
}
47+
48+
pub fn (mut g Game) roll(pins int) ! {
49+
if g.is_over() {
50+
return error('Cannot roll after game is over')
51+
}
52+
53+
if pins < 0 {
54+
return error('Negative roll is invalid')
55+
}
56+
57+
if pins > 10 || (g.pins > 0 && pins > g.pins) {
58+
return error('Pin count exceeds pins on the lane')
59+
}
60+
61+
if g.frames < 10 {
62+
g.score += pins
63+
}
64+
65+
if g.state != .open && (g.state != .spare || g.pins == 0) {
66+
g.score += pins
67+
if g.state == .double && g.pins == 0 && g.frames < 11 {
68+
g.score += pins
69+
}
70+
}
71+
72+
if g.pins == 0 {
73+
g.pins = 10 - pins
74+
if g.pins == 0 {
75+
if g.state == .double || g.state == .strike {
76+
g.state = .double
77+
} else {
78+
g.state = .strike
79+
}
80+
}
81+
} else {
82+
if pins == g.pins {
83+
g.state = .spare
84+
} else {
85+
g.state = .open
86+
}
87+
g.pins = 0
88+
}
89+
90+
if g.pins == 0 {
91+
g.frames++
92+
}
93+
}
94+
95+
pub fn (g Game) score() !int {
96+
if !g.is_over() {
97+
return error('Score cannot be taken until the end of the game')
98+
}
99+
return g.score
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# This is an auto-generated file. Regular comments will be removed when this
2+
# file is regenerated. Regenerating will not touch any manually added keys,
3+
# so comments can be added in a "comment" key.
4+
5+
[656ae006-25c2-438c-a549-f338e7ec7441]
6+
description = "should be able to score a game with all zeros"
7+
8+
[f85dcc56-cd6b-4875-81b3-e50921e3597b]
9+
description = "should be able to score a game with no strikes or spares"
10+
11+
[d1f56305-3ac2-4fe0-8645-0b37e3073e20]
12+
description = "a spare followed by zeros is worth ten points"
13+
14+
[0b8c8bb7-764a-4287-801a-f9e9012f8be4]
15+
description = "points scored in the roll after a spare are counted twice"
16+
17+
[4d54d502-1565-4691-84cd-f29a09c65bea]
18+
description = "consecutive spares each get a one roll bonus"
19+
20+
[e5c9cf3d-abbe-4b74-ad48-34051b2b08c0]
21+
description = "a spare in the last frame gets a one roll bonus that is counted once"
22+
23+
[75269642-2b34-4b72-95a4-9be28ab16902]
24+
description = "a strike earns ten points in a frame with a single roll"
25+
26+
[037f978c-5d01-4e49-bdeb-9e20a2e6f9a6]
27+
description = "points scored in the two rolls after a strike are counted twice as a bonus"
28+
29+
[1635e82b-14ec-4cd1-bce4-4ea14bd13a49]
30+
description = "consecutive strikes each get the two roll bonus"
31+
32+
[e483e8b6-cb4b-4959-b310-e3982030d766]
33+
description = "a strike in the last frame gets a two roll bonus that is counted once"
34+
35+
[9d5c87db-84bc-4e01-8e95-53350c8af1f8]
36+
description = "rolling a spare with the two roll bonus does not get a bonus roll"
37+
38+
[576faac1-7cff-4029-ad72-c16bcada79b5]
39+
description = "strikes with the two roll bonus do not get bonus rolls"
40+
41+
[efb426ec-7e15-42e6-9b96-b4fca3ec2359]
42+
description = "last two strikes followed by only last bonus with non strike points"
43+
44+
[72e24404-b6c6-46af-b188-875514c0377b]
45+
description = "a strike with the one roll bonus after a spare in the last frame does not get a bonus"
46+
47+
[62ee4c72-8ee8-4250-b794-234f1fec17b1]
48+
description = "all strikes is a perfect game"
49+
50+
[1245216b-19c6-422c-b34b-6e4012d7459f]
51+
description = "rolls cannot score negative points"
52+
53+
[5fcbd206-782c-4faa-8f3a-be5c538ba841]
54+
description = "a roll cannot score more than 10 points"
55+
56+
[fb023c31-d842-422d-ad7e-79ce1db23c21]
57+
description = "two rolls in a frame cannot score more than 10 points"
58+
59+
[6082d689-d677-4214-80d7-99940189381b]
60+
description = "bonus roll after a strike in the last frame cannot score more than 10 points"
61+
62+
[e9565fe6-510a-4675-ba6b-733a56767a45]
63+
description = "two bonus rolls after a strike in the last frame cannot score more than 10 points"
64+
65+
[2f6acf99-448e-4282-8103-0b9c7df99c3d]
66+
description = "two bonus rolls after a strike in the last frame can score more than 10 points if one is a strike"
67+
68+
[6380495a-8bc4-4cdb-a59f-5f0212dbed01]
69+
description = "the second bonus rolls after a strike in the last frame cannot be a strike if the first one is not a strike"
70+
71+
[2b2976ea-446c-47a3-9817-42777f09fe7e]
72+
description = "second bonus roll after a strike in the last frame cannot score more than 10 points"
73+
74+
[29220245-ac8d-463d-bc19-98a94cfada8a]
75+
description = "an unstarted game cannot be scored"
76+
77+
[4473dc5d-1f86-486f-bf79-426a52ddc955]
78+
description = "an incomplete game cannot be scored"
79+
80+
[2ccb8980-1b37-4988-b7d1-e5701c317df3]
81+
description = "cannot roll if game already has ten frames"
82+
83+
[4864f09b-9df3-4b65-9924-c595ed236f1b]
84+
description = "bonus rolls for a strike in the last frame must be rolled before score can be calculated"
85+
86+
[537f4e37-4b51-4d1c-97e2-986eb37b2ac1]
87+
description = "both bonus rolls for a strike in the last frame must be rolled before score can be calculated"
88+
89+
[8134e8c1-4201-4197-bf9f-1431afcde4b9]
90+
description = "bonus roll for a spare in the last frame must be rolled before score can be calculated"
91+
92+
[9d4a9a55-134a-4bad-bae8-3babf84bd570]
93+
description = "cannot roll after bonus roll for spare"
94+
95+
[d3e02652-a799-4ae3-b53b-68582cc604be]
96+
description = "cannot roll after bonus rolls for strike"

exercises/practice/bowling/bowling.v

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module main
2+
3+
struct Game {}
4+
5+
// build a new Game
6+
fn Game.new() Game {
7+
}
8+
9+
pub fn (mut g Game) roll(pins int) ! {
10+
}
11+
12+
pub fn (g Game) score() !int {
13+
}

0 commit comments

Comments
 (0)