From ee38f1a0df1163c5e966bd8dbe56983e62fd389f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20B=20Nagy?= <20251272+BNAndras@users.noreply.github.com> Date: Wed, 13 Mar 2024 21:53:17 -0700 Subject: [PATCH] Add robot-simulator (#176) --- config.json | 10 + .../robot-simulator/.docs/instructions.md | 25 +++ .../robot-simulator/.meta/config.json | 18 ++ .../robot-simulator/.meta/example.arr | 51 +++++ .../practice/robot-simulator/.meta/tests.toml | 64 ++++++ .../robot-simulator/robot-simulator-test.arr | 182 ++++++++++++++++++ .../robot-simulator/robot-simulator.arr | 11 ++ 7 files changed, 361 insertions(+) create mode 100644 exercises/practice/robot-simulator/.docs/instructions.md create mode 100644 exercises/practice/robot-simulator/.meta/config.json create mode 100644 exercises/practice/robot-simulator/.meta/example.arr create mode 100644 exercises/practice/robot-simulator/.meta/tests.toml create mode 100644 exercises/practice/robot-simulator/robot-simulator-test.arr create mode 100644 exercises/practice/robot-simulator/robot-simulator.arr diff --git a/config.json b/config.json index 2018e37..ce81354 100644 --- a/config.json +++ b/config.json @@ -411,6 +411,16 @@ "prerequisites": [], "difficulty": 2 }, + { + "slug": "robot-simulator", + "name": "Robot Simulator", + "uuid": "e81a517a-16be-4760-980a-21ee549c24ea", + "practices": [ + "strings" + ], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "roman-numerals", "name": "Roman Numerals", diff --git a/exercises/practice/robot-simulator/.docs/instructions.md b/exercises/practice/robot-simulator/.docs/instructions.md new file mode 100644 index 0000000..0ac96ce --- /dev/null +++ b/exercises/practice/robot-simulator/.docs/instructions.md @@ -0,0 +1,25 @@ +# Instructions + +Write a robot simulator. + +A robot factory's test facility needs a program to verify robot movements. + +The robots have three possible movements: + +- turn right +- turn left +- advance + +Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, +e.g., {3,8}, with coordinates increasing to the north and east. + +The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing. + +- The letter-string "RAALAL" means: + - Turn right + - Advance twice + - Turn left + - Advance once + - Turn left yet again +- Say a robot starts at {7, 3} facing north. + Then running this stream of instructions should leave it at {9, 4} facing west. diff --git a/exercises/practice/robot-simulator/.meta/config.json b/exercises/practice/robot-simulator/.meta/config.json new file mode 100644 index 0000000..bb6454a --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "BNAndras" + ], + "files": { + "solution": [ + "robot-simulator.arr" + ], + "test": [ + "robot-simulator-test.arr" + ], + "example": [ + ".meta/example.arr" + ] + }, + "blurb": "Write a robot simulator.", + "source": "Inspired by an interview question at a famous company." +} diff --git a/exercises/practice/robot-simulator/.meta/example.arr b/exercises/practice/robot-simulator/.meta/example.arr new file mode 100644 index 0000000..1c0fcf7 --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/example.arr @@ -0,0 +1,51 @@ +use context essentials2020 + +provide-types * + +include string-dict +import lists as L + +data Axis: axis(label, left, right) end + +cardinal_axes = [string-dict: + 'north', axis('north', 'west', 'east'), + 'south', axis('south', 'east', 'west'), + 'east', axis('east', 'north','south'), + 'west', axis('west','south', 'north'), +] + +data Robot: + | robot(x :: NumInteger, y :: NumInteger, direction :: String) with: + method move(self, directions :: String) -> Robot: + L.foldl( + lam(acc, elt): + direction = + ask: + | elt == 'L' then: + cardinal_axes.get-value(acc.direction).left + | elt == 'R' then: + cardinal_axes.get-value(acc.direction).right + | otherwise: + acc.direction + end + {x; y} = + if elt == 'A': + ask: + | direction == 'north' then: + {acc.x; acc.y + 1} + | direction == 'south' then: + {acc.x; acc.y - 1} + | direction == 'east' then: + {acc.x + 1; acc.y} + | direction == 'west' then: + {acc.x - 1; acc.y} + end + else: + {acc.x; acc.y} + end + robot(x, y, direction) + end, + self, + string-explode(directions)) + end +end diff --git a/exercises/practice/robot-simulator/.meta/tests.toml b/exercises/practice/robot-simulator/.meta/tests.toml new file mode 100644 index 0000000..16da03d --- /dev/null +++ b/exercises/practice/robot-simulator/.meta/tests.toml @@ -0,0 +1,64 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[c557c16d-26c1-4e06-827c-f6602cd0785c] +description = "Create robot -> at origin facing north" + +[bf0dffce-f11c-4cdb-8a5e-2c89d8a5a67d] +description = "Create robot -> at negative position facing south" + +[8cbd0086-6392-4680-b9b9-73cf491e67e5] +description = "Rotating clockwise -> changes north to east" + +[8abc87fc-eab2-4276-93b7-9c009e866ba1] +description = "Rotating clockwise -> changes east to south" + +[3cfe1b85-bbf2-4bae-b54d-d73e7e93617a] +description = "Rotating clockwise -> changes south to west" + +[5ea9fb99-3f2c-47bd-86f7-46b7d8c3c716] +description = "Rotating clockwise -> changes west to north" + +[fa0c40f5-6ba3-443d-a4b3-58cbd6cb8d63] +description = "Rotating counter-clockwise -> changes north to west" + +[da33d734-831f-445c-9907-d66d7d2a92e2] +description = "Rotating counter-clockwise -> changes west to south" + +[bd1ca4b9-4548-45f4-b32e-900fc7c19389] +description = "Rotating counter-clockwise -> changes south to east" + +[2de27b67-a25c-4b59-9883-bc03b1b55bba] +description = "Rotating counter-clockwise -> changes east to north" + +[f0dc2388-cddc-4f83-9bed-bcf46b8fc7b8] +description = "Moving forward one -> facing north increments Y" + +[2786cf80-5bbf-44b0-9503-a89a9c5789da] +description = "Moving forward one -> facing south decrements Y" + +[84bf3c8c-241f-434d-883d-69817dbd6a48] +description = "Moving forward one -> facing east increments X" + +[bb69c4a7-3bbf-4f64-b415-666fa72d7b04] +description = "Moving forward one -> facing west decrements X" + +[e34ac672-4ed4-4be3-a0b8-d9af259cbaa1] +description = "Follow series of instructions -> moving east and north from README" + +[f30e4955-4b47-4aa3-8b39-ae98cfbd515b] +description = "Follow series of instructions -> moving west and north" + +[3e466bf6-20ab-4d79-8b51-264165182fca] +description = "Follow series of instructions -> moving west and south" + +[41f0bb96-c617-4e6b-acff-a4b279d44514] +description = "Follow series of instructions -> moving east and north" diff --git a/exercises/practice/robot-simulator/robot-simulator-test.arr b/exercises/practice/robot-simulator/robot-simulator-test.arr new file mode 100644 index 0000000..2b60306 --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator-test.arr @@ -0,0 +1,182 @@ +use context essentials2020 + +include file("robot-simulator.arr") + +#| + When working offline, all tests except the first one are skipped by default. + Once you get the first test running, unskip the next one until all tests pass locally. + Check the block comment below for further details. +|# + +fun create-robot-at-origin-facing-north(): + check "Create robot at origin facing north": + r = robot(0, 0, 'north') + expected = robot(0, 0, 'north') + r is expected + end +end + +fun create-robot-at-negative-position-facing-south(): + check "Create robot at negative position facing south": + r = robot(-1, -1, 'south') + expected = robot(-1, -1, 'south') + r is expected + end +end + +fun rotating-clockwise-changes-north-to-east(): + check "Rotating clockwise changes north to east": + r = robot(0, 0, 'north') + expected = robot(0, 0, 'east') + r.move('R') is expected + end +end + +fun rotating-clockwise-changes-east-to-south(): + check "Rotating clockwise changes east to south": + r = robot(0, 0, 'east') + expected = robot(0, 0, 'south') + r.move('R') is expected + end +end + +fun rotating-clockwise-changes-south-to-west(): + check "Rotating clockwise changes south to west": + r = robot(0, 0, 'south') + expected = robot(0, 0, 'west') + r.move('R') is expected + end +end + +fun rotating-clockwise-changes-west-to-north(): + check "Rotating clockwise changes west to north": + r = robot(0, 0, 'west') + expected = robot(0, 0, 'north') + r.move('R') is expected + end +end + +fun rotating-counter-clockwise-changes-north-to-west(): + check "Rotating counter-clockwise changes north to west": + r = robot(0, 0, 'north') + expected = robot(0, 0, 'west') + r.move('L') is expected + end +end + +fun rotating-counter-clockwise-changes-west-to-south(): + check "Rotating counter-clockwise changes west to south": + r = robot(0, 0, 'west') + expected = robot(0, 0, 'south') + r.move('L') is expected + end +end + +fun rotating-counter-clockwise-changes-south-to-east(): + check "Rotating counter-clockwise changes south to east": + r = robot(0, 0, 'south') + expected = robot(0, 0, 'east') + r.move('L') is expected + end +end + +fun rotating-counter-clockwise-changes-east-to-north(): + check "Rotating counter-clockwise changes east to north": + r = robot(0, 0, 'east') + expected = robot(0, 0, 'north') + r.move('L') is expected + end +end + +fun moving-forward-one-facing-north-increments-Y(): + check "Moving forward one facing north increments Y": + r = robot(0, 0, 'north') + expected = robot(0, 1, 'north') + r.move('A') is expected + end +end + +fun moving-forward-one-facing-south-decrements-Y(): + check "Moving forward one facing south decrements Y": + r = robot(0, 0, 'south') + expected = robot(0, -1, 'south') + r.move('A') is expected + end +end + +fun moving-forward-one-facing-east-increments-X(): + check "Moving forward one facing east increments X": + r = robot(0, 0, 'east') + expected = robot(1, 0, 'east') + r.move('A') is expected + end +end + +fun moving-forward-one-facing-west-decrements-X(): + check "Moving forward one facing west decrements X": + r = robot(0, 0, 'west') + expected = robot(-1, 0, 'west') + r.move('A') is expected + end +end + +fun follow-series-of-directions-moving-east-and-north-from-readme(): + check "Follow series of directions moving east and north from README": + r = robot(7, 3, 'north') + expected = robot(9, 4, 'west') + r.move('RAALAL') is expected + end +end + +fun follow-series-of-directions-moving-west-and-north(): + check "Follow series of directions moving west and north": + r = robot(0, 0, 'north') + expected = robot(-4, 1, 'west') + r.move('LAAARALA') is expected + end +end + +fun follow-series-of-directions-moving-west-and-south(): + check "Follow series of directions moving west and south": + r = robot(2, -7, 'east') + expected = robot(-3, -8, 'south') + r.move('RRAAAAALA') is expected + end +end + +fun follow-series-of-directions-moving-east-and-north(): + check "Follow series of directions moving east and north": + r = robot(8, 4, 'south') + expected = robot(11, 5, 'north') + r.move('LAAARRRALLLL') is expected + end +end + +#| + Code to run each test. Each line corresponds to a test above and whether it should be run. + To mark a test to be run, replace `false` with `true` on that same line after the comma. + test(test-a, true) will be run. test(test-a, false) will be skipped. +|# + +data TestRun: test(run, active) end + +[list: + test(create-robot-at-origin-facing-north, true), + test(create-robot-at-negative-position-facing-south, false), + test(rotating-clockwise-changes-north-to-east, false), + test(rotating-clockwise-changes-east-to-south, false), + test(rotating-clockwise-changes-south-to-west, false), + test(rotating-clockwise-changes-west-to-north, false), + test(rotating-counter-clockwise-changes-north-to-west, false), + test(rotating-counter-clockwise-changes-west-to-south, false), + test(rotating-counter-clockwise-changes-south-to-east, false), + test(rotating-counter-clockwise-changes-east-to-north, false), + test(moving-forward-one-facing-north-increments-Y, false), + test(moving-forward-one-facing-south-decrements-Y, false), + test(moving-forward-one-facing-east-increments-X, false), + test(moving-forward-one-facing-west-decrements-X, false), + test(follow-series-of-directions-moving-east-and-north-from-readme, false), + test(follow-series-of-directions-moving-west-and-north, false), + test(follow-series-of-directions-moving-west-and-south, false), + test(follow-series-of-directions-moving-east-and-north, false) +].each(lam(t): when t.active: t.run() end end) diff --git a/exercises/practice/robot-simulator/robot-simulator.arr b/exercises/practice/robot-simulator/robot-simulator.arr new file mode 100644 index 0000000..9246927 --- /dev/null +++ b/exercises/practice/robot-simulator/robot-simulator.arr @@ -0,0 +1,11 @@ +use context essentials2020 # Don't delete this line when using Pyret on Exercism + +provide-types * + +data Robot: + | robot() + with: + method move(self, directions :: String) -> Robot: + ... + end +end