Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add robot name exercise #123

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "robot-name",
"name": "Robot Name",
"uuid": "88eac1aa-a4ae-405a-88ab-0dedd5ac7ae2",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "acronym",
"name": "Acronym",
Expand Down
2 changes: 2 additions & 0 deletions config/generator_macros.j2
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ app [main] {
json: "https://github.com/lukewilliamboswell/roc-json/releases/download/0.10.2/FH4N0Sw-JSFXJfG3j54VEDPtXOoN-6I9v_IA8S18IGk.tar.br"
{%- elif name == "parser" -%}
parser: "https://github.com/lukewilliamboswell/roc-parser/releases/download/0.7.2/1usTzOOACTpnkarBX0ED3gFESzR4ROdAlt1Llf4WFzo.tar.br"
{%- elif name == "rand" -%}
rand: "https://github.com/lukewilliamboswell/roc-random/releases/download/0.3.0/hPlOciYUhWMU7BefqNzL89g84-30fTE6l2_6Y3cxIcE.tar.br"
{%- endif -%}
{%- endfor -%}
{%- endif %}
Expand Down
14 changes: 14 additions & 0 deletions exercises/practice/robot-name/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Instructions

Manage robot factory settings.

When a robot comes off the factory floor, it has no name.

The first time you turn on a robot, a random name is generated in the format of two uppercase letters followed by three digits, such as RX837 or BC811.

Every once in a while we need to reset a robot to its factory settings, which means that its name gets wiped.
The next time you ask, that robot will respond with a new random name.

The names must be random: they should not follow a predictable sequence.
Using random names means a risk of collisions.
Your solution must ensure that every existing robot has a unique name.
89 changes: 89 additions & 0 deletions exercises/practice/robot-name/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
module [createFactory, createRobot, boot, reset, getName, getFactory]

import rand.Random

## A factory is used to create robots, and hold state such as the existing robot
## names and the current random state
Factory := {
existingNames : Set Str,
state : Random.State,
}

## A robot must either have no name or a name composed of two letters followed
## by three digits
Robot := {
maybeName : Result Str [NoName],
factory : Factory,
}

createFactory : { seed : U32 } -> Factory
createFactory = \{ seed } ->
@Factory { state: Random.seed seed, existingNames: Set.empty {} }

createRobot : Factory -> Robot
createRobot = \factory ->
@Robot { maybeName: Err NoName, factory }

boot : Robot -> Robot
boot = \robot ->
when robot |> getName is
Ok _ -> robot
Err NoName -> robot |> generateRandomName

reset : Robot -> Robot
reset = \robot ->
resetRobot =
when robot |> getName is
Err NoName -> robot
Ok nameToRemove ->
factory = robot |> getFactory |> removeName nameToRemove
@Robot { maybeName: Err NoName, factory }

resetRobot |> boot

getName : Robot -> Result Str [NoName]
getName = \@Robot { maybeName } ->
maybeName

getFactory : Robot -> Factory
getFactory = \@Robot { factory } ->
factory

generateRandomName : Robot -> Robot
generateRandomName = \@Robot { maybeName, factory } ->
(@Factory { state, existingNames }) = factory
{ updatedState, string: twoLetters } = randomString { state, generator: Random.boundedU32 'A' 'Z', length: 2 }
{ updatedState: updatedState2, string: threeDigits } = randomString { state: updatedState, generator: Random.boundedU32 '0' '9', length: 3 }
possibleName = "$(twoLetters)$(threeDigits)"

if existingNames |> Set.contains possibleName then
numberOfPossibleNames = 26 * 26 * 10 * 10 * 10
if existingNames |> Set.len == numberOfPossibleNames then
# better crash than run into an infinite loop
crash "Too many robots, we have run out of possible names!"
else
updatedFactory = @Factory { existingNames, state: updatedState2 }
generateRandomName (@Robot { maybeName, factory: updatedFactory })
else
updatedFactory = @Factory {
existingNames: existingNames |> Set.insert possibleName,
state: updatedState2,
}
@Robot { maybeName: Ok possibleName, factory: updatedFactory }

removeName : Factory, Str -> Factory
removeName = \@Factory { state, existingNames }, robotName ->
@Factory { state, existingNames: existingNames |> Set.remove robotName }

randomString : { state : Random.State, generator : Random.Generator U32, length : U64 } -> { updatedState : Random.State, string : Str }
randomString = \{ state, generator, length } ->
List.range { start: At 0, end: Before length }
|> List.walk { state, characters: [] } \walk, _ ->
random = generator walk.state
updatedState = random.state
characters = walk.characters |> List.append (random.value |> Num.toU8)
{ state: updatedState, characters }
|> \{ state: updatedState, characters } ->
when characters |> Str.fromUtf8 is
Ok string -> { updatedState, string }
Err (BadUtf8 _ _) -> crash "Unreachable: characters are all ASCII"
18 changes: 18 additions & 0 deletions exercises/practice/robot-name/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"authors": [
"ageron"
],
"files": {
"solution": [
"RobotName.roc"
],
"test": [
"robot-name-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"blurb": "Manage robot factory settings.",
"source": "A debugging session with Paul Blackwell at gSchool."
}
47 changes: 47 additions & 0 deletions exercises/practice/robot-name/RobotName.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module [createFactory, createRobot, boot, reset, getName, getFactory]

import rand.Random

## A factory is used to create robots, and hold state such as the existing robot
## names and the current random state
Factory := {
# TODO: change this opaque type however you need
todo1 : U64,
todo2 : U64,
todo3 : U64,
# etc.
}

## A robot must either have no name or a name composed of two letters followed
## by three digits
Robot := {
# TODO: change this opaque type however you need
todo4 : U64,
todo5 : U64,
todo6 : U64,
# etc.
}

createFactory : { seed : U32 } -> Factory
createFactory = \{ seed } ->
crash "Please implement the 'createFactory' function"

createRobot : Factory -> Robot
createRobot = \factory ->
crash "Please implement the 'createRobot' function"

boot : Robot -> Robot
boot = \robot ->
crash "Please implement the 'boot' function"

reset : Robot -> Robot
reset = \robot ->
crash "Please implement the 'reset' function"

getName : Robot -> Result Str _
getName = \robot ->
crash "Please implement the 'getName' function"

getFactory : Robot -> Factory
getFactory = \robot ->
crash "Please implement the 'getFactory' function"
Loading
Loading