role-playing-game

This commit is contained in:
Alexander Heldt
2025-11-02 21:22:44 +01:00
parent 5fb5492306
commit 1de1e2fcb6
10 changed files with 409 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
{
"authors": [
"lpil"
],
"files": {
"solution": [
"src/role_playing_game.gleam"
],
"test": [
"test/role_playing_game_test.gleam"
],
"exemplar": [
".meta/example.gleam"
],
"invalidator": [
"gleam.toml",
"manifest.toml"
]
},
"forked_from": [
"elm/role-playing-game"
],
"blurb": "Learn the Option type by designing game mechanics"
}

View File

@@ -0,0 +1 @@
{"track":"gleam","exercise":"role-playing-game","id":"50edb689de054e5daf0a42cd9efb7176","url":"https://exercism.org/tracks/gleam/exercises/role-playing-game","handle":"fw353qwgs","is_requester":true,"auto_approve":false}

4
role-playing-game/.gitignore vendored Normal file
View File

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

32
role-playing-game/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/role_playing_game.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.

View File

@@ -0,0 +1,15 @@
# Hints
## 1. Introduce yourself
- The [`option.unwrap` function][unwrap] can be used to get the value from an option, or a default value if there is no value.
## 2. Implement the revive mechanic
- Option values can be created using the `Some` and `None` constructors from the `gleam/option` module.
## 3. Implement the spell casting mechanic
- The [`int.max` function][max] can be used to make sure the health does not go below 0.
[max]: https://hexdocs.pm/gleam_stdlib/gleam/int.html#max

122
role-playing-game/README.md Normal file
View File

@@ -0,0 +1,122 @@
# Role Playing Game
Welcome to Role Playing Game 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
## Options
The `Option` type is used to represent values that can either be absent or present.
It is defined in the `gleam/option` module as follows:
```gleam
type Option(a) {
Some(a)
None
}
```
The `Some` constructor is used to wrap a value when it's present, and the `None` constructor is used to represent the absence of a value.
Accessing the content of a `Option` is often done via pattern matching.
```gleam
import gleam/option.{type Option, None, Some}
pub fn say_hello(person: Option(String)) -> String {
case person {
Some(name) -> "Hello, " <> name <> "!"
None -> "Hello, Friend!"
}
}
```
```gleam
say_hello(Some("Matthieu"))
// -> "Hello, Matthieu!"
say_hello(None)
// -> "Hello, Friend!"
```
The `gleam/option` module also defines a number of useful function for working with `Option` types, such as `unwrap`, which returns the content of an `Option` or a default value if it is `None`.
```gleam
import gleam/option.{type Option}
pub fn say_hello_again(person: Option(String)) -> String {
let name = option.unwrap(person, "Friend")
"Hello, " <> name <> "!"
}
```
## Instructions
Josh is working on a new role-playing game and needs your help implementing some of the mechanics.
## 1. Introduce yourself
Implement the `introduce` function.
Stealthy players may be hiding their name and will be introduced as `"Mighty Magician"`.
Otherwise, just use the player's name to introduce them.
```gleam
introduce(Player(name: None, level: 2, health: 8, mana: None))
// -> "Mighty Magician"
introduce(Player(name: Some("Merlin"), level: 2, health: 8, mana: None))
// -> "Merlin"
```
## 2. Implement the revive mechanic
The `revive` function should check that the player's character is indeed dead (their health has reached 0).
If they are, it should return a new `Player` instance with 100 health.
Otherwise, if the player's character isn't dead, the `revive` function returns `None`.
If the player's level is 10 or above, they should also be revived with 100 mana.
If the player's level is below 10, their mana should be untouched.
The `revive` function should preserve the player's level.
```gleam
let dead_player = Player(name: None, level: 2, health: 0, mana: None)
revive(dead_player)
// -> Some(Player(name: None, level: 2, health: 100, mana: None))
```
If the `revive` method is called on a player whose health is 1 or above, then the function should return `None`.
```gleam
let alive_player = Player(name: None, level: 2, health: 42, mana: None)
revive(alive_player)
// -> None
```
## 3. Implement the spell casting mechanic
The `cast_spell` function takes as arguments an `Int` indicating how much mana the spell costs as well as a `Player`.
It returns the updated player, as well as the amount of damage that the cast spell performs.
A successful spell cast does damage equal to two times the mana cost of the spell.
However, if the player has insufficient mana, nothing happens, the player is unchanged and no damage is done.
If the player does not even have a mana pool, attempting to cast the spell must decrease their health by the mana cost of the spell and does no damage.
Be aware that the players health cannot be below zero (0).
```gleam
let wizard = Player(name: None, level: 18, health: 123, mana: Some(30))
let #(wizard, damage) = cast_spell(wizard, 14)
wizard.mana // -> Some 16
damage // -> 28
```
## Source
### Created by
- @lpil

View File

@@ -0,0 +1,14 @@
name = "role_playing_game"
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"

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" }

View File

@@ -0,0 +1,96 @@
import gleam/option.{type Option, None, Some}
pub type Player {
Player(name: Option(String), level: Int, health: Int, mana: Option(Int))
}
pub fn introduce(player: Player) -> String {
case player.name {
option.None -> "Mighty Magician"
option.Some(name) -> name
}
}
pub fn revive(player: Player) -> Option(Player) {
case player.health <= 0 {
False -> None
True -> {
case player.level >= 10 {
False ->
Some(Player(
name: player.name,
level: player.level,
health: 100,
mana: None,
))
True ->
Some(Player(
name: player.name,
level: player.level,
health: 100,
mana: Some(100),
))
}
}
}
}
// The `cast_spell` function takes as arguments an `Int` indicating how much mana the spell costs as well as a `Player`.
// It returns the updated player, as well as the amount of damage that the cast spell performs.
// A successful spell cast does damage equal to two times the mana cost of the spell.
// However, if the player has insufficient mana, nothing happens, the player is unchanged and no damage is done.
// If the player does not even have a mana pool, attempting to cast the spell must decrease their health by the mana cost of the spell and does no damage.
// Be aware that the players health cannot be below zero (0).
pub fn cast_spell(player: Player, cost: Int) -> #(Player, Int) {
let damage = cost * 2
case player.mana {
None -> {
let health = player.health - cost
case health {
h if h > 0 -> #(
Player(
name: player.name,
level: player.level,
health:,
mana: player.mana,
),
0,
)
_ -> #(
Player(
name: player.name,
level: player.level,
health: 0,
mana: player.mana,
),
0,
)
}
}
Some(mana) -> {
let mana_left = mana - cost
case mana_left {
ml if ml >= 0 -> #(
Player(
name: player.name,
level: player.level,
health: player.health,
mana: Some(ml),
),
damage,
)
_ -> #(player, 0)
}
// let health = player.health - damage
// case health {
// h if h > 0 ->
// Player(name: player.name, level: player.level, health:, mana: todo)
// _ ->
// Player(name: player.name, level: player.level, health: 0, mana: mana)
// }
}
}
}

View File

@@ -0,0 +1,70 @@
import exercism/should
import exercism/test_runner
import gleam/option.{None, Some}
import role_playing_game.{Player}
pub fn main() {
test_runner.main()
}
pub fn introduce_someone_with_their_name_test() {
Player(name: Some("Gandalf"), level: 1, health: 42, mana: None)
|> role_playing_game.introduce
|> should.equal("Gandalf")
}
pub fn introducing_an_unidentified_player_should_return_mighty_magician_test() {
Player(name: None, level: 1, health: 42, mana: None)
|> role_playing_game.introduce
|> should.equal("Mighty Magician")
}
pub fn revive_a_player_that_is_alive_should_return_none_test() {
Player(name: None, level: 12, health: 42, mana: Some(7))
|> role_playing_game.revive
|> should.equal(None)
}
pub fn reviving_a_low_level_player_resets_its_health_to_100_test() {
Player(name: None, level: 3, health: 0, mana: None)
|> role_playing_game.revive
|> should.equal(Some(Player(name: None, level: 3, health: 100, mana: None)))
}
pub fn reviving_a_high_level_player_resets_both_its_health_and_mana_test() {
Player(name: None, level: 10, health: 0, mana: Some(14))
|> role_playing_game.revive
|> should.equal(
Some(Player(name: None, level: 10, health: 100, mana: Some(100))),
)
}
pub fn cast_spell_causes_damage_of_double_the_mana_test() {
Player(name: None, level: 10, health: 69, mana: Some(20))
|> role_playing_game.cast_spell(9)
|> should.equal(#(
Player(name: None, level: 10, health: 69, mana: Some(11)),
18,
))
}
pub fn casting_a_spell_with_insufficient_mana_does_none_test() {
Player(name: None, level: 10, health: 69, mana: Some(20))
|> role_playing_game.cast_spell(39)
|> should.equal(#(
Player(name: None, level: 10, health: 69, mana: Some(20)),
0,
))
}
pub fn casting_a_spell_without_a_mana_pool_decreases_the_players_health_test() {
Player(name: None, level: 5, health: 58, mana: None)
|> role_playing_game.cast_spell(7)
|> should.equal(#(Player(name: None, level: 5, health: 51, mana: None), 0))
}
pub fn a_players_health_cannot_go_below_0_test() {
Player(name: None, level: 5, health: 6, mana: None)
|> role_playing_game.cast_spell(12)
|> should.equal(#(Player(name: None, level: 5, health: 0, mana: None), 0))
}