From 9f97edfedf1f4e3b6de24292dd4eaa8770adc4be Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Tue, 28 Apr 2026 10:05:05 -0400 Subject: [PATCH] Add rectangles --- config.json | 8 ++ exercises/practice/rectangles/.busted | 5 + .../practice/rectangles/.docs/instructions.md | 63 ++++++++++ .../practice/rectangles/.meta/config.json | 17 +++ .../practice/rectangles/.meta/example.moon | 52 ++++++++ .../rectangles/.meta/spec_generator.moon | 13 ++ .../practice/rectangles/.meta/tests.toml | 52 ++++++++ exercises/practice/rectangles/rectangles.moon | 4 + .../practice/rectangles/rectangles_spec.moon | 119 ++++++++++++++++++ .../shared/templates/spec_generator.moon | 7 +- lib/test_helpers.moon | 5 +- 11 files changed, 340 insertions(+), 5 deletions(-) create mode 100644 exercises/practice/rectangles/.busted create mode 100644 exercises/practice/rectangles/.docs/instructions.md create mode 100644 exercises/practice/rectangles/.meta/config.json create mode 100644 exercises/practice/rectangles/.meta/example.moon create mode 100644 exercises/practice/rectangles/.meta/spec_generator.moon create mode 100644 exercises/practice/rectangles/.meta/tests.toml create mode 100644 exercises/practice/rectangles/rectangles.moon create mode 100644 exercises/practice/rectangles/rectangles_spec.moon diff --git a/config.json b/config.json index 51df31e..2102592 100644 --- a/config.json +++ b/config.json @@ -866,6 +866,14 @@ "prerequisites": [], "difficulty": 7 }, + { + "slug": "rectangles", + "name": "Rectangles", + "uuid": "c63979dc-1b48-4fbf-ae84-c10ba5a10304", + "practices": [], + "prerequisites": [], + "difficulty": 7 + }, { "slug": "rest-api", "name": "REST API", diff --git a/exercises/practice/rectangles/.busted b/exercises/practice/rectangles/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/rectangles/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/rectangles/.docs/instructions.md b/exercises/practice/rectangles/.docs/instructions.md new file mode 100644 index 0000000..8eb4ed4 --- /dev/null +++ b/exercises/practice/rectangles/.docs/instructions.md @@ -0,0 +1,63 @@ +# Instructions + +Count the rectangles in an ASCII diagram like the one below. + +```text + +--+ + ++ | ++-++--+ +| | | ++--+--+ +``` + +The above diagram contains these 6 rectangles: + +```text + + ++-----+ +| | ++-----+ +``` + +```text + +--+ + | | + | | + | | + +--+ +``` + +```text + +--+ + | | + +--+ + + +``` + +```text + + + +--+ + | | + +--+ +``` + +```text + + ++--+ +| | ++--+ +``` + +```text + + ++ + ++ + + +``` + +You may assume that the input is always a proper rectangle (i.e. the length of every line equals the length of the first line). diff --git a/exercises/practice/rectangles/.meta/config.json b/exercises/practice/rectangles/.meta/config.json new file mode 100644 index 0000000..1ef9375 --- /dev/null +++ b/exercises/practice/rectangles/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "rectangles.moon" + ], + "test": [ + "rectangles_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Count the rectangles in an ASCII diagram." +} diff --git a/exercises/practice/rectangles/.meta/example.moon b/exercises/practice/rectangles/.meta/example.moon new file mode 100644 index 0000000..0424528 --- /dev/null +++ b/exercises/practice/rectangles/.meta/example.moon @@ -0,0 +1,52 @@ +contains = (vertices, vertex) -> + for v in *vertices + return true if v == vertex + false + +-- ---------------------------------------- +class Vertex + new: (@row, @col) => + __eq: (other) => @row == other.row and @col == other.col + +vertices = (input) -> + [Vertex row, col for row = 1, #input for col = 1, #input[1] when input[row][col] == '+'] + +verticesRightOf = (vertex, vertices) -> + [v for v in *vertices when v.row == vertex.row and v.col > vertex.col] + +verticesBelow = (vertex, vertices) -> + [v for v in *vertices when v.col == vertex.col and v.row > vertex.row] + +-- ---------------------------------------- +isRectangle = (input, topLeft, topRight, bottomLeft, bottomRight) -> + horizontalLine = (left, right) -> + for col = left.col + 1, right.col - 1 + return false if not (input[left.row][col] == '-' or input[left.row][col] == '+') + true + + verticalLine = (top, bottom) -> + for row = top.row + 1, bottom.row - 1 + return false if not (input[row][top.col] == '|' or input[row][top.col] == '+') + true + + return false if not horizontalLine topLeft, topRight + return false if not horizontalLine bottomLeft, bottomRight + return false if not verticalLine topLeft, bottomLeft + return false if not verticalLine topRight, bottomRight + true + +-- ---------------------------------------- +rectangles = (input) -> + input = [ [c for c in line\gmatch '.'] for line in *input] + vs = vertices input + count = 0 + for topLeft in *vs + for topRight in *verticesRightOf topLeft, vs + for bottomLeft in *verticesBelow topLeft, vs + bottomRight = Vertex bottomLeft.row, topRight.col + if contains vs, bottomRight + if isRectangle input, topLeft, topRight, bottomLeft, bottomRight + count += 1 + count + +{ :rectangles } diff --git a/exercises/practice/rectangles/.meta/spec_generator.moon b/exercises/practice/rectangles/.meta/spec_generator.moon new file mode 100644 index 0000000..ab9a797 --- /dev/null +++ b/exercises/practice/rectangles/.meta/spec_generator.moon @@ -0,0 +1,13 @@ +test_helpers = require 'test_helpers' +import string_list from test_helpers + +{ + module_imports: {'rectangles'}, + + generate_test: (case, level) -> + lines = { + "input = #{string_list case.input.strings, level, {inline: 1}}", + "assert.are.equal #{case.expected}, rectangles input" + } + table.concat [indent line, level for line in *lines], '\n' +} diff --git a/exercises/practice/rectangles/.meta/tests.toml b/exercises/practice/rectangles/.meta/tests.toml new file mode 100644 index 0000000..2820150 --- /dev/null +++ b/exercises/practice/rectangles/.meta/tests.toml @@ -0,0 +1,52 @@ +# 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. + +[485b7bab-4150-40aa-a8db-73013427d08c] +description = "no rows" + +[076929ed-27e8-45dc-b14b-08279944dc49] +description = "no columns" + +[0a8abbd1-a0a4-4180-aa4e-65c1b1a073fa] +description = "no rectangles" + +[a4ba42e9-4e7f-4973-b7c7-4ce0760ac6cd] +description = "one rectangle" + +[ced06550-83da-4d23-98b7-d24152e0db93] +description = "two rectangles without shared parts" + +[5942d69a-a07c-41c8-8b93-2d13877c706a] +description = "five rectangles with shared parts" + +[82d70be4-ab37-4bf2-a433-e33778d3bbf1] +description = "rectangle of height 1 is counted" + +[57f1bc0e-2782-401e-ab12-7c01d8bfc2e0] +description = "rectangle of width 1 is counted" + +[ef0bb65c-bd80-4561-9535-efc4067054f9] +description = "1x1 square is counted" + +[e1e1d444-e926-4d30-9bf3-7d8ec9a9e330] +description = "only complete rectangles are counted" + +[ca021a84-1281-4a56-9b9b-af14113933a4] +description = "rectangles can be of different sizes" + +[51f689a7-ef3f-41ae-aa2f-5ea09ad897ff] +description = "corner is required for a rectangle to be complete" + +[d78fe379-8c1b-4d3c-bdf7-29bfb6f6dc66] +description = "large input with many rectangles" + +[6ef24e0f-d191-46da-b929-4faca24b4cd2] +description = "rectangles must have four sides" diff --git a/exercises/practice/rectangles/rectangles.moon b/exercises/practice/rectangles/rectangles.moon new file mode 100644 index 0000000..f2cf1f7 --- /dev/null +++ b/exercises/practice/rectangles/rectangles.moon @@ -0,0 +1,4 @@ +{ + rectangles: (input) -> + error 'Implement me' +} diff --git a/exercises/practice/rectangles/rectangles_spec.moon b/exercises/practice/rectangles/rectangles_spec.moon new file mode 100644 index 0000000..693d61d --- /dev/null +++ b/exercises/practice/rectangles/rectangles_spec.moon @@ -0,0 +1,119 @@ +import rectangles from require 'rectangles' + +describe 'rectangles', -> + it 'no rows', -> + input = {} + assert.are.equal 0, rectangles input + + pending 'no columns', -> + input = {''} + assert.are.equal 0, rectangles input + + pending 'no rectangles', -> + input = {' '} + assert.are.equal 0, rectangles input + + pending 'one rectangle', -> + input = { + '+-+', + '| |', + '+-+', + } + assert.are.equal 1, rectangles input + + pending 'two rectangles without shared parts', -> + input = { + ' +-+', + ' | |', + '+-+-+', + '| | ', + '+-+ ', + } + assert.are.equal 2, rectangles input + + pending 'five rectangles with shared parts', -> + input = { + ' +-+', + ' | |', + '+-+-+', + '| | |', + '+-+-+', + } + assert.are.equal 5, rectangles input + + pending 'rectangle of height 1 is counted', -> + input = { + '+--+', + '+--+', + } + assert.are.equal 1, rectangles input + + pending 'rectangle of width 1 is counted', -> + input = { + '++', + '||', + '++', + } + assert.are.equal 1, rectangles input + + pending '1x1 square is counted', -> + input = { + '++', + '++', + } + assert.are.equal 1, rectangles input + + pending 'only complete rectangles are counted', -> + input = { + ' +-+', + ' |', + '+-+-+', + '| | -', + '+-+-+', + } + assert.are.equal 1, rectangles input + + pending 'rectangles can be of different sizes', -> + input = { + '+------+----+', + '| | |', + '+---+--+ |', + '| | |', + '+---+-------+', + } + assert.are.equal 3, rectangles input + + pending 'corner is required for a rectangle to be complete', -> + input = { + '+------+----+', + '| | |', + '+------+ |', + '| | |', + '+---+-------+', + } + assert.are.equal 2, rectangles input + + pending 'large input with many rectangles', -> + input = { + '+---+--+----+', + '| +--+----+', + '+---+--+ |', + '| +--+----+', + '+---+--+--+-+', + '+---+--+--+-+', + '+------+ | |', + ' +-+', + } + assert.are.equal 60, rectangles input + + pending 'rectangles must have four sides', -> + input = { + '+-+ +-+', + '| | | |', + '+-+-+-+', + ' | | ', + '+-+-+-+', + '| | | |', + '+-+ +-+', + } + assert.are.equal 5, rectangles input diff --git a/exercises/shared/templates/spec_generator.moon b/exercises/shared/templates/spec_generator.moon index 7bd104e..78f65ae 100644 --- a/exercises/shared/templates/spec_generator.moon +++ b/exercises/shared/templates/spec_generator.moon @@ -1,9 +1,10 @@ -test_helpers = require 'test_helpers' -import int_list, word_list from test_helpers +import int_list, word_list from require 'test_helpers' { + -- one of: module_name: '${camel_slug}', - -- or, module_imports: {'func1', 'func2', ...}, + -- or + -- module_imports: {'func1', 'func2', ...}, generate_test: (case, level) -> local lines diff --git a/lib/test_helpers.moon b/lib/test_helpers.moon index 7d883d0..075e1d9 100644 --- a/lib/test_helpers.moon +++ b/lib/test_helpers.moon @@ -54,9 +54,10 @@ word_list = (list) -> -- This returns a multi-line string without the first line indented. -- The returned string will be added to another string in the test case body, -- and then that string (without regard to internal newlines) will later be indented. -string_list = (list, level) -> +string_list = (list, level, opts) -> error 'Provide a level for `string_list`', 2 if not level - if #list <= 2 + opts = opts or {inline: 2} + if #list <= opts.inline "{#{table.concat [quote elem for elem in *list], ', '}}" else lines = [indent quote(elem) .. ',', level + 1 for elem in *list]