Skip to content

Commit dd4e120

Browse files
committed
[gleam] complete bowling
1 parent 4d26c02 commit dd4e120

File tree

7 files changed

+473
-0
lines changed

7 files changed

+473
-0
lines changed

gleam/bowling/.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.beam
2+
*.ez
3+
build
4+
erl_crash.dump

gleam/bowling/HELP.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Help
2+
3+
## Running the tests
4+
5+
To run the tests, run the command `gleam test` from within the exercise directory.
6+
7+
## Submitting your solution
8+
9+
You can submit your solution using the `exercism submit src/bowling.gleam` command.
10+
This command will upload your solution to the Exercism website and print the solution page's URL.
11+
12+
It's possible to submit an incomplete solution which allows you to:
13+
14+
- See how others have completed the exercise
15+
- Request help from a mentor
16+
17+
## Need to get help?
18+
19+
If you'd like help solving the exercise, check the following pages:
20+
21+
- The [Gleam track's documentation](https://exercism.org/docs/tracks/gleam)
22+
- The [Gleam track's programming category on the forum](https://forum.exercism.org/c/programming/gleam)
23+
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
24+
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
25+
26+
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
27+
28+
To get help if you're having trouble, you can use one of the following resources:
29+
30+
- [gleam.run](https://gleam.run/documentation/) is the gleam official documentation.
31+
- [Discord](https://discord.gg/Fm8Pwmy) is the discord channel.
32+
- [StackOverflow](https://stackoverflow.com/questions/tagged/gleam) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.

gleam/bowling/README.md

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Bowling
2+
3+
Welcome to Bowling on Exercism's Gleam Track.
4+
If you need help running the tests or submitting your code, check out `HELP.md`.
5+
6+
## Instructions
7+
8+
Score a bowling game.
9+
10+
Bowling is a game where players roll a heavy ball to knock down pins arranged in a triangle.
11+
Write code to keep track of the score of a game of bowling.
12+
13+
## Scoring Bowling
14+
15+
The game consists of 10 frames.
16+
A frame is composed of one or two ball throws with 10 pins standing at frame initialization.
17+
There are three cases for the tabulation of a frame.
18+
19+
- An open frame is where a score of less than 10 is recorded for the frame.
20+
In this case the score for the frame is the number of pins knocked down.
21+
22+
- A spare is where all ten pins are knocked down by the second throw.
23+
The total value of a spare is 10 plus the number of pins knocked down in their next throw.
24+
25+
- A strike is where all ten pins are knocked down by the first throw.
26+
The total value of a strike is 10 plus the number of pins knocked down in the next two throws.
27+
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.
28+
29+
Here is a three frame example:
30+
31+
| Frame 1 | Frame 2 | Frame 3 |
32+
| :--------: | :--------: | :--------------: |
33+
| X (strike) | 5/ (spare) | 9 0 (open frame) |
34+
35+
Frame 1 is (10 + 5 + 5) = 20
36+
37+
Frame 2 is (5 + 5 + 9) = 19
38+
39+
Frame 3 is (9 + 0) = 9
40+
41+
This means the current running total is 48.
42+
43+
The tenth frame in the game is a special case.
44+
If someone throws a spare or a strike then they get one or two fill balls respectively.
45+
Fill balls exist to calculate the total of the 10th frame.
46+
Scoring a strike or spare on the fill ball does not give the player more fill balls.
47+
The total value of the 10th frame is the total number of pins knocked down.
48+
49+
For a tenth frame of X1/ (strike and a spare), the total value is 20.
50+
51+
For a tenth frame of XXX (three strikes), the total value is 30.
52+
53+
## Requirements
54+
55+
Write code to keep track of the score of a game of bowling.
56+
It should support two operations:
57+
58+
- `roll(pins : int)` is called each time the player rolls a ball.
59+
The argument is the number of pins knocked down.
60+
- `score() : int` is called only at the very end of the game.
61+
It returns the total score for that game.
62+
63+
## Source
64+
65+
### Created by
66+
67+
- @massivefermion
68+
69+
### Contributed to by
70+
71+
- @lpil
72+
73+
### Based on
74+
75+
The Bowling Game Kata from UncleBob - https://web.archive.org/web/20221001111000/http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata

gleam/bowling/gleam.toml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name = "bowling"
2+
version = "0.1.0"
3+
4+
[dependencies]
5+
gleam_bitwise = "~> 1.2"
6+
gleam_otp = "~> 0.7 or ~> 1.0"
7+
gleam_stdlib = "~> 0.32 or ~> 1.0"
8+
simplifile = "~> 1.0"
9+
gleam_erlang = ">= 0.25.0 and < 1.0.0"
10+
11+
[dev-dependencies]
12+
exercism_test_runner = "~> 1.4"

gleam/bowling/manifest.toml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This file was generated by Gleam
2+
# You typically do not need to edit this file
3+
4+
packages = [
5+
{ name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
6+
{ name = "exercism_test_runner", version = "1.8.0", build_tools = ["gleam"], requirements = ["argv", "gap", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_json", "gleam_stdlib", "simplifile"], otp_app = "exercism_test_runner", source = "hex", outer_checksum = "B944D89A9D049897DF28C63D595D89CB54D8C407D06EFFCE4CDA8C3EC1C9F51E" },
7+
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
8+
{ name = "gap", version = "1.1.3", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "6EF5E3B523FDFBC317E9EA28D5163EE04744A97C007106F90207569789612291" },
9+
{ name = "glance", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "1510D4A03C28880E62974389E5BF1A5A185036BA07392F1D769620706A9E042F" },
10+
{ name = "gleam_bitwise", version = "1.3.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_bitwise", source = "hex", outer_checksum = "B36E1D3188D7F594C7FD4F43D0D2CE17561DE896202017548578B16FE1FE9EFC" },
11+
{ name = "gleam_community_ansi", version = "1.4.1", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "4CD513FC62523053E62ED7BAC2F36136EC17D6A8942728250A9A00A15E340E4B" },
12+
{ name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" },
13+
{ name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" },
14+
{ name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" },
15+
{ name = "gleam_otp", version = "0.12.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BFACC1513410DF5A1617169A9CD7EA334973AC71D860A17574BA7B2EADD89A6F" },
16+
{ name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" },
17+
{ name = "glexer", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "BD477AD657C2B637FEF75F2405FAEFFA533F277A74EF1A5E17B55B1178C228FB" },
18+
{ name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" },
19+
{ name = "thoas", version = "1.2.1", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "E38697EDFFD6E91BD12CEA41B155115282630075C2A727E7A6B2947F5408B86A" },
20+
]
21+
22+
[requirements]
23+
exercism_test_runner = { version = "~> 1.4" }
24+
gleam_bitwise = { version = "~> 1.2" }
25+
gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" }
26+
gleam_otp = { version = "~> 0.7 or ~> 1.0" }
27+
gleam_stdlib = { version = "~> 0.32 or ~> 1.0" }
28+
simplifile = { version = "~> 1.0" }

gleam/bowling/src/bowling.gleam

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import gleam/bool
2+
import gleam/int
3+
import gleam/list
4+
5+
pub opaque type Frame {
6+
Frame(rolls: List(Int), bonus: List(Int))
7+
}
8+
9+
pub type Game {
10+
Game(frames: List(Frame))
11+
}
12+
13+
pub type Error {
14+
InvalidPinCount
15+
GameComplete
16+
GameNotComplete
17+
}
18+
19+
pub fn roll(game: Game, pins: Int) -> Result(Game, Error) {
20+
use <- bool.guard(pins < 0 || pins > 10, Error(InvalidPinCount))
21+
22+
case game.frames {
23+
[Frame([a], _), ..] if a + pins > 10 && a != 10 -> Error(InvalidPinCount)
24+
25+
[Frame([10], _), Frame([10], f), ..rest] ->
26+
Ok(
27+
Game([
28+
Frame([pins], []),
29+
Frame([10], [pins]),
30+
Frame([10], [pins, ..f]),
31+
..rest
32+
]),
33+
)
34+
35+
[Frame([10], bonus), ..rest] ->
36+
Ok(Game([Frame([pins], []), Frame([10], [pins, ..bonus]), ..rest]))
37+
38+
[Frame([a], _), Frame([10], bonus), ..rest] ->
39+
Ok(Game([Frame([pins, a], []), Frame([10], [pins, ..bonus]), ..rest]))
40+
41+
[Frame([a, b], _), ..rest] if a + b == 10 ->
42+
Ok(Game([Frame([pins], []), Frame([a, b], [pins]), ..rest]))
43+
44+
[_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11] -> Error(GameComplete)
45+
[Frame([_, _], _), _2, _3, _4, _5, _6, _7, _8, _9, _10] ->
46+
Error(GameComplete)
47+
48+
[Frame([a, b], bonus), ..rest] ->
49+
Ok(Game([Frame([pins], []), Frame([a, b], bonus), ..rest]))
50+
51+
[Frame([a], bonus), ..rest] -> Ok(Game([Frame([a, pins], bonus), ..rest]))
52+
53+
_ -> Ok(Game([Frame([pins], []), ..game.frames]))
54+
}
55+
}
56+
57+
pub fn score(game: Game) -> Result(Int, Error) {
58+
use <- bool.guard(list.length(game.frames) < 10, Error(GameNotComplete))
59+
60+
case game.frames {
61+
[Frame([_], _), Frame([10], _), _3, _4, _5, _6, _7, _8, _9, _10, _11] ->
62+
Error(GameNotComplete)
63+
[Frame([a, b], _), _1, _2, _3, _4, _5, _6, _7, _8, _9] if a + b == 10 ->
64+
Error(GameNotComplete)
65+
[Frame([10], _), _1, _2, _3, _4, _5, _6, _7, _8, _9] ->
66+
Error(GameNotComplete)
67+
_ -> Ok(sum_rolls(list.reverse(game.frames) |> list.take(10), 0))
68+
}
69+
}
70+
71+
fn sum_rolls(frames: List(Frame), total: Int) -> Int {
72+
case frames {
73+
[] -> total
74+
[Frame(a, b), ..rest] -> sum_rolls(rest, total + int.sum(a) + int.sum(b))
75+
}
76+
}

0 commit comments

Comments
 (0)