This commit is contained in:
Alexander Heldt
2025-11-08 14:31:07 +01:00
parent 05a0ab003b
commit 74e8c6d6dc
10 changed files with 360 additions and 0 deletions

24
go/.exercism/config.json Normal file
View File

@@ -0,0 +1,24 @@
{
"authors": [
"lpil"
],
"files": {
"solution": [
"src/go.gleam"
],
"test": [
"test/go_test.gleam"
],
"exemplar": [
".meta/example.gleam"
],
"invalidator": [
"gleam.toml",
"manifest.toml"
]
},
"forked_from": [
"elm/go"
],
"blurb": "Learn usage of Result by applying the rules of the game of Go"
}

View File

@@ -0,0 +1 @@
{"track":"gleam","exercise":"go","id":"5f92ded7f0d94054b5eb825d783c3245","url":"https://exercism.org/tracks/gleam/exercises/go","handle":"fw353qwgs","is_requester":true,"auto_approve":false}

4
go/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.beam
*.ez
build
erl_crash.dump

32
go/HELP.md Normal file
View File

@@ -0,0 +1,32 @@
# Help
## Running the tests
To run the tests, run the command `gleam test` from within the exercise directory.
## Submitting your solution
You can submit your solution using the `exercism submit src/go.gleam` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Gleam track's documentation](https://exercism.org/docs/tracks/gleam)
- The [Gleam track's programming category on the forum](https://forum.exercism.org/c/programming/gleam)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [gleam.run](https://gleam.run/documentation/) is the gleam official documentation.
- [Discord](https://discord.gg/Fm8Pwmy) is the discord channel.
- [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.

5
go/HINTS.md Normal file
View File

@@ -0,0 +1,5 @@
# Hints
- The [Result module][result-module] has some useful functions for working with the Result type, such as `result.map` and `result.try`.
[result-module]: https://hexdocs.pm/gleam_stdlib/gleam/result.html

107
go/README.md Normal file
View File

@@ -0,0 +1,107 @@
# Go
Welcome to Go on Exercism's Gleam Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
## Results
Gleam doesn't use exceptions for error handling, instead the generic `Result` type is returned by functions that can either succeed or fail.
The `Result` type is built into the language so you don't need to define or import it, but if you were to define it yourself it would look like this:
```gleam
pub type Result(value, error) {
Ok(value)
Error(error)
}
```
The `Ok` variant is returned when a function succeeds, and the `Error` variant is returned when a function fails.
Results are very common in Gleam, and Gleam programmers will commonly use the [`gleam/result` module](https://hexdocs.pm/gleam_stdlib/gleam/result.html) to make working with them easier.
The `result.map` function can be used to call a function on the value inside a result if it is an `Ok`, or to pass through an `Error` unchanged.
```gleam
Ok(1)
|> result.map(fn(x) { x + 1 })
// -> Ok(2)
Error("Oh no!")
|> result.map(fn(x) { x + 1 })
// -> Error("Oh no!")
```
The `result.try` function is similar, but the callback function is expected to return a result. This is useful for chaining together multiple functions that return results.
```gleam
Ok(1)
|> result.try(fn(x) { Ok(x + 1) })
// -> Ok(2)
Ok(1)
|> result.try(fn(x) { Error("Nope!") })
// -> Error("Nope!")
```
## Instructions
In this exercise, you'll be applying the [rules of the game of Go](https://matmoore.github.io/learngo/). The rules themselves are already written, you just need to apply them in order to update the Game and to handle any errors / violations of the rules.
The game is represented as follows:
```gleam
pub type Player {
Black
White
}
pub type Game {
Game(
white_captured_stones: Int,
black_captured_stones: Int,
player: Player,
error: String,
)
}
```
There are 4 rules in the game:
- Each point can only have one stone.
- Opposition stones can be captured.
- You can not place a stone where it would capture itself.
- You can not use the same point twice.
## 1. Apply the rules
Write the content of the `apply_rules` function, which takes an initial `Game` and a set of rules, and returns the new `Game` after the rules have been applied.
Three of the rules all check for violations of the rules and may return an error, and so have the return type of `Result(Game, String)`. If any of these rules fail, the original game should be returned, but with the `error` field updated with the relevant error.
The other rule does not check for violations and so cannot fail (although it can return a changed `Game`), and so has the return type of `Game`.
If all the rules pass, then any changes to `Game` from the rules should be kept, and the player should be changed.
```gleam
pub fn apply_rules(
game: Game,
rule1: fn(Game) -> Result(Game, String),
rule2: fn(Game) -> Game,
rule3: fn(Game) -> Result(Game, String),
rule4: fn(Game) -> Result(Game, String),
) -> Game {
// -> If all rules pass, return a `Game` with all changes from the rules applied, and change player
// -> If any rule fails, return the original Game, but with the error field set
}
```
## Source
### Created by
- @lpil

14
go/gleam.toml Normal file
View File

@@ -0,0 +1,14 @@
name = "go"
version = "0.1.0"
[dependencies]
gleam_otp = "~> 0.7 or ~> 1.0"
gleam_stdlib = ">= 0.54.0 or ~> 1.0"
simplifile = "~> 1.0"
gleam_erlang = ">= 0.25.0 and < 1.0.0"
gleam_yielder = ">= 1.1.0 and < 2.0.0"
gleam_regexp = ">= 1.1.0 and < 2.0.0"
gleam_deque = ">= 1.0.0 and < 2.0.0"
[dev-dependencies]
exercism_test_runner = "~> 1.9"

31
go/manifest.toml Normal file
View File

@@ -0,0 +1,31 @@
# This file was generated by Gleam
# You typically do not need to edit this file
packages = [
{ name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" },
{ name = "exercism_test_runner", version = "1.9.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 = "0B17BB25F2FF1E60266467C24FE0CA04005410306AA05E9A4B41B1852D72865C" },
{ name = "filepath", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "67A6D15FB39EEB69DD31F8C145BB5A421790581BD6AA14B33D64D5A55DBD6587" },
{ name = "gap", version = "1.1.3", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib"], otp_app = "gap", source = "hex", outer_checksum = "6EF5E3B523FDFBC317E9EA28D5163EE04744A97C007106F90207569789612291" },
{ name = "glance", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "E155BA1A787FD11827048355021C0390D2FE9A518485526F631A9D472858CC6D" },
{ name = "gleam_community_ansi", version = "1.4.3", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "8A62AE9CC6EA65BEA630D95016D6C07E4F9973565FA3D0DE68DC4200D8E0DD27" },
{ name = "gleam_community_colour", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "FDD6AC62C6EC8506C005949A4FCEF032038191D5EAAEC3C9A203CD53AE956ACA" },
{ name = "gleam_deque", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_deque", source = "hex", outer_checksum = "64D77068931338CF0D0CB5D37522C3E3CCA7CB7D6C5BACB41648B519CC0133C7" },
{ name = "gleam_erlang", version = "0.34.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "0C38F2A128BAA0CEF17C3000BD2097EB80634E239CE31A86400C4416A5D0FDCC" },
{ name = "gleam_json", version = "2.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "C55C5C2B318533A8072D221C5E06E5A75711C129E420DD1CE463342106012E5D" },
{ name = "gleam_otp", version = "0.16.1", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "50DA1539FC8E8FA09924EB36A67A2BBB0AD6B27BCDED5A7EF627057CF69D035E" },
{ name = "gleam_regexp", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "7F5E0C0BBEB3C58E57C9CB05FA9002F970C85AD4A63BA1E55CBCB35C15809179" },
{ name = "gleam_stdlib", version = "0.55.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "32D8F4AE03771516950047813A9E359249BD9FBA5C33463FDB7B953D6F8E896B" },
{ name = "gleam_yielder", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_yielder", source = "hex", outer_checksum = "8E4E4ECFA7982859F430C57F549200C7749823C106759F4A19A78AEA6687717A" },
{ name = "glexer", version = "2.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "5C235CBDF4DA5203AD5EAB1D6D8B456ED8162C5424FE2309CFFB7EF438B7C269" },
{ name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" },
]
[requirements]
exercism_test_runner = { version = "~> 1.9" }
gleam_deque = { version = ">= 1.0.0 and < 2.0.0" }
gleam_erlang = { version = ">= 0.25.0 and < 1.0.0" }
gleam_otp = { version = "~> 0.7 or ~> 1.0" }
gleam_regexp = { version = ">= 1.1.0 and < 2.0.0" }
gleam_stdlib = { version = ">= 0.54.0 or ~> 1.0" }
gleam_yielder = { version = ">= 1.1.0 and < 2.0.0" }
simplifile = { version = "~> 1.0" }

32
go/src/go.gleam Normal file
View File

@@ -0,0 +1,32 @@
import gleam/result
pub type Player {
Black
White
}
pub type Game {
Game(
white_captured_stones: Int,
black_captured_stones: Int,
player: Player,
error: String,
)
}
pub fn apply_rules(
game: Game,
rule1: fn(Game) -> Result(Game, String),
rule2: fn(Game) -> Game,
rule3: fn(Game) -> Result(Game, String),
rule4: fn(Game) -> Result(Game, String),
) -> Game {
case rule2(game) |> rule1 |> result.try(rule3) |> result.try(rule4) {
Error(err) -> Game(..game, error: err)
Ok(g) ->
case g.player {
Black -> Game(..g, player: White)
White -> Game(..g, player: Black)
}
}
}

110
go/test/go_test.gleam Normal file
View File

@@ -0,0 +1,110 @@
import exercism/should
import exercism/test_runner
import go.{type Game, Black, Game, White}
pub fn main() {
test_runner.main()
}
fn identity(x: Game) -> Game {
x
}
fn identity_rule(game: Game) -> Result(Game, String) {
Ok(game)
}
fn error_rule(_game: Game, message: String) -> Result(Game, String) {
Error(message)
}
fn new_game() -> Game {
Game(0, 0, White, "")
}
fn ko_rule() -> String {
"Cannot repeat a previously played board position"
}
fn liberty_rule() -> String {
"Cannot place a stone with no liberties"
}
fn one_stone_per_point_rule() -> String {
"You can't put a stone on top of another stone"
}
fn add_white_captured_stone(game: Game) -> Game {
Game(..game, white_captured_stones: 1)
}
pub fn change_player_if_all_rules_pass_test() {
new_game()
|> go.apply_rules(identity_rule, identity, identity_rule, identity_rule)
|> should.equal(change_player(new_game()))
}
pub fn retain_error_and_player_if_ko_rule_fails_test() {
new_game()
|> go.apply_rules(identity_rule, identity, identity_rule, error_rule(
_,
ko_rule(),
))
|> should.equal(Game(..new_game(), error: ko_rule()))
}
pub fn retain_error_and_player_if_liberty_rule_fails_test() {
new_game()
|> go.apply_rules(
identity_rule,
identity,
error_rule(_, liberty_rule()),
identity_rule,
)
|> should.equal(Game(..new_game(), error: liberty_rule()))
}
pub fn retain_error_and_player_if_one_stone_per_point_rule_fails_test() {
new_game()
|> go.apply_rules(
error_rule(_, one_stone_per_point_rule()),
identity,
identity_rule,
identity_rule,
)
|> should.equal(Game(..new_game(), error: one_stone_per_point_rule()))
}
pub fn retain_changes_from_capture_rule_and_change_player_test() {
new_game()
|> go.apply_rules(
identity_rule,
add_white_captured_stone,
identity_rule,
identity_rule,
)
|> should.equal(
new_game()
|> add_white_captured_stone
|> change_player,
)
}
pub fn discard_changes_from_capture_rule_if_subsequent_rule_fails_test() {
new_game()
|> go.apply_rules(
identity_rule,
add_white_captured_stone,
identity_rule,
error_rule(_, ko_rule()),
)
|> should.equal(Game(..new_game(), error: ko_rule()))
}
fn change_player(game: Game) -> Game {
let new_player = case game.player {
White -> Black
Black -> White
}
Game(..game, player: new_player)
}