From ffb8842ad60c41e14f3501d48cd16d9825e24acc Mon Sep 17 00:00:00 2001 From: Glenn Jackman Date: Sat, 22 Aug 2026 13:47:13 -0400 Subject: [PATCH] Add ocr-numbers --- config.json | 8 + exercises/practice/ocr-numbers/.busted | 5 + .../ocr-numbers/.docs/instructions.md | 47 +++++ .../ocr-numbers/.docs/introduction.md | 6 + .../practice/ocr-numbers/.meta/config.json | 19 ++ .../practice/ocr-numbers/.meta/example.moon | 31 ++++ .../practice/ocr-numbers/.meta/generator.tmpl | 14 ++ .../practice/ocr-numbers/.meta/tests.toml | 61 +++++++ .../practice/ocr-numbers/ocr_numbers.moon | 4 + .../ocr-numbers/ocr_numbers_spec.moon | 165 ++++++++++++++++++ 10 files changed, 360 insertions(+) create mode 100644 exercises/practice/ocr-numbers/.busted create mode 100644 exercises/practice/ocr-numbers/.docs/instructions.md create mode 100644 exercises/practice/ocr-numbers/.docs/introduction.md create mode 100644 exercises/practice/ocr-numbers/.meta/config.json create mode 100644 exercises/practice/ocr-numbers/.meta/example.moon create mode 100644 exercises/practice/ocr-numbers/.meta/generator.tmpl create mode 100644 exercises/practice/ocr-numbers/.meta/tests.toml create mode 100644 exercises/practice/ocr-numbers/ocr_numbers.moon create mode 100644 exercises/practice/ocr-numbers/ocr_numbers_spec.moon diff --git a/config.json b/config.json index f3e2122..d8bcdc0 100644 --- a/config.json +++ b/config.json @@ -794,6 +794,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "ocr-numbers", + "name": "OCR Numbers", + "uuid": "52d9b8d1-7dcd-4056-8a13-cd74a65b6187", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "pig-latin", "name": "Pig Latin", diff --git a/exercises/practice/ocr-numbers/.busted b/exercises/practice/ocr-numbers/.busted new file mode 100644 index 0000000..86b84e7 --- /dev/null +++ b/exercises/practice/ocr-numbers/.busted @@ -0,0 +1,5 @@ +return { + default = { + ROOT = { '.' } + } +} diff --git a/exercises/practice/ocr-numbers/.docs/instructions.md b/exercises/practice/ocr-numbers/.docs/instructions.md new file mode 100644 index 0000000..8a391ce --- /dev/null +++ b/exercises/practice/ocr-numbers/.docs/instructions.md @@ -0,0 +1,47 @@ +# Instructions + +Optical Character Recognition or OCR is software that converts images of text into machine-readable text. +Given a grid of characters representing some digits, convert the grid to a string of digits. +If the grid has multiple rows of cells, the rows should be separated in the output with a `","`. + +- The grid is made of one of more lines of cells. +- Each line of the grid is made of one or more cells. +- Each cell is three columns wide and four rows high (3x4) and represents one digit. +- Digits are drawn using pipes (`"|"`), underscores (`"_"`), and spaces (`" "`). + +## Edge cases + +- If the input is not a valid size, your program should indicate there is an error. +- If the input is the correct size, but a cell is not recognizable, your program should output a `"?"` for that character. + +## Examples + +The following input (without the comments) is converted to `"1234567890"`. + +```text + _ _ _ _ _ _ _ _ # + | _| _||_||_ |_ ||_||_|| | # Decimal numbers. + ||_ _| | _||_| ||_| _||_| # + # The fourth line is always blank, +``` + +The following input is converted to `"123,456,789"`. + + + +```text + _ _ + | _| _| + ||_ _| + + _ _ +|_||_ |_ + | _||_| + + _ _ _ + ||_||_| + ||_| _| + +``` + + diff --git a/exercises/practice/ocr-numbers/.docs/introduction.md b/exercises/practice/ocr-numbers/.docs/introduction.md new file mode 100644 index 0000000..366d760 --- /dev/null +++ b/exercises/practice/ocr-numbers/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +Your best friend Marta recently landed their dream job working with a local history museum's collections. +Knowing of your interests in programming, they confide in you about an issue at work for an upcoming exhibit on computing history. +A local university's math department had donated several boxes of historical printouts, but given the poor condition of the documents, the decision has been made to digitize the text. +However, the university's old printer had some quirks in how text was represented, and your friend could use your help to extract the data successfully. diff --git a/exercises/practice/ocr-numbers/.meta/config.json b/exercises/practice/ocr-numbers/.meta/config.json new file mode 100644 index 0000000..2f044cf --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "glennj" + ], + "files": { + "solution": [ + "ocr_numbers.moon" + ], + "test": [ + "ocr_numbers_spec.moon" + ], + "example": [ + ".meta/example.moon" + ] + }, + "blurb": "Given a 3 x 4 grid of pipes, underscores, and spaces, determine which number is represented, or whether it is garbled.", + "source": "Inspired by the Bank OCR kata", + "source_url": "https://codingdojo.org/kata/BankOCR/" +} diff --git a/exercises/practice/ocr-numbers/.meta/example.moon b/exercises/practice/ocr-numbers/.meta/example.moon new file mode 100644 index 0000000..ff9ca14 --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/example.moon @@ -0,0 +1,31 @@ +List = require 'pl.List' + +digits = { + ' _ | ||_| ': "0" + ' | | ': "1" + ' _ _||_ ': "2" + ' _ _| _| ': "3" + ' |_| | ': "4" + ' _ |_ _| ': "5" + ' _ |_ |_| ': "6" + ' _ | | ': "7" + ' _ |_||_| ': "8" + ' _ |_| _| ': "9" +} + + +toNumber = (row) -> + chunks = [table.concat [line\sub(i, i+2) for line in *row] for i = 1, #row[1], 3] + table.concat [digits[chunk] or "?" for chunk in *chunks] + + +convert = (rows) -> + rows = List rows + assert #rows % 4 == 0, "Number of input lines is not a multiple of four" + assert #rows[1] % 3 == 0, "Number of input columns is not a multiple of three" + + numbers = [toNumber rows\slice(i, i+3) for i = 1, #rows, 4] + table.concat numbers, ',' + + +{ :convert } diff --git a/exercises/practice/ocr-numbers/.meta/generator.tmpl b/exercises/practice/ocr-numbers/.meta/generator.tmpl new file mode 100644 index 0000000..f248f91 --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/generator.tmpl @@ -0,0 +1,14 @@ +import convert from require <%- h.quote(slug.snake) %> + +describe <%- h.quote(slug.kebab .. ':') %>, -> +<% for i, case in ipairs(data.cases) do -%> + <%= test_cmd(i == 1) %> <%- h.quote(case.description) %>, -> + rows = <%- h.string_list(case.input.rows, 2, {inline=0}) %> +<% if not case.expected.error then -%> + assert.are.equal <%- h.quote(case.expected) %>, convert rows +<% else -%> + f = -> <%= case.property %> rows + assert.has.errors f, <%- h.quote(case.expected.error) %> +<% end -%> + +<% end -%> diff --git a/exercises/practice/ocr-numbers/.meta/tests.toml b/exercises/practice/ocr-numbers/.meta/tests.toml new file mode 100644 index 0000000..0d7a5b7 --- /dev/null +++ b/exercises/practice/ocr-numbers/.meta/tests.toml @@ -0,0 +1,61 @@ +# 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. + +[5ee54e1a-b554-4bf3-a056-9a7976c3f7e8] +description = "Recognizes 0" + +[027ada25-17fd-4d78-aee6-35a19623639d] +description = "Recognizes 1" + +[3cce2dbd-01d9-4f94-8fae-419a822e89bb] +description = "Unreadable but correctly sized inputs return ?" + +[cb19b733-4e36-4cf9-a4a1-6e6aac808b9a] +description = "Input with a number of lines that is not a multiple of four raises an error" + +[235f7bd1-991b-4587-98d4-84206eec4cc6] +description = "Input with a number of columns that is not a multiple of three raises an error" + +[4a841794-73c9-4da9-a779-1f9837faff66] +description = "Recognizes 110101100" + +[70c338f9-85b1-4296-a3a8-122901cdfde8] +description = "Garbled numbers in a string are replaced with ?" + +[ea494ff4-3610-44d7-ab7e-72fdef0e0802] +description = "Recognizes 2" + +[1acd2c00-412b-4268-93c2-bd7ff8e05a2c] +description = "Recognizes 3" + +[eaec6a15-be17-4b6d-b895-596fae5d1329] +description = "Recognizes 4" + +[440f397a-f046-4243-a6ca-81ab5406c56e] +description = "Recognizes 5" + +[f4c9cf6a-f1e2-4878-bfc3-9b85b657caa0] +description = "Recognizes 6" + +[e24ebf80-c611-41bb-a25a-ac2c0f232df5] +description = "Recognizes 7" + +[b79cad4f-e264-4818-9d9e-77766792e233] +description = "Recognizes 8" + +[5efc9cfc-9227-4688-b77d-845049299e66] +description = "Recognizes 9" + +[f60cb04a-42be-494e-a535-3451c8e097a4] +description = "Recognizes string of decimal numbers" + +[b73ecf8b-4423-4b36-860d-3710bdb8a491] +description = "Numbers separated by empty lines are recognized. Lines are joined by commas." diff --git a/exercises/practice/ocr-numbers/ocr_numbers.moon b/exercises/practice/ocr-numbers/ocr_numbers.moon new file mode 100644 index 0000000..67190af --- /dev/null +++ b/exercises/practice/ocr-numbers/ocr_numbers.moon @@ -0,0 +1,4 @@ +{ + convert: (rows) -> + error 'Implement me' +} diff --git a/exercises/practice/ocr-numbers/ocr_numbers_spec.moon b/exercises/practice/ocr-numbers/ocr_numbers_spec.moon new file mode 100644 index 0000000..7543137 --- /dev/null +++ b/exercises/practice/ocr-numbers/ocr_numbers_spec.moon @@ -0,0 +1,165 @@ +import convert from require 'ocr_numbers' + +describe 'ocr-numbers:', -> + it 'Recognizes 0', -> + rows = { + ' _ ', + '| |', + '|_|', + ' ', + } + assert.are.equal '0', convert rows + + pending 'Recognizes 1', -> + rows = { + ' ', + ' |', + ' |', + ' ', + } + assert.are.equal '1', convert rows + + pending 'Unreadable but correctly sized inputs return ?', -> + rows = { + ' ', + ' _', + ' |', + ' ', + } + assert.are.equal '?', convert rows + + pending 'Input with a number of lines that is not a multiple of four raises an error', -> + rows = { + ' _ ', + '| |', + ' ', + } + f = -> convert rows + assert.has.errors f, 'Number of input lines is not a multiple of four' + + pending 'Input with a number of columns that is not a multiple of three raises an error', -> + rows = { + ' ', + ' |', + ' |', + ' ', + } + f = -> convert rows + assert.has.errors f, 'Number of input columns is not a multiple of three' + + pending 'Recognizes 110101100', -> + rows = { + ' _ _ _ _ ', + ' | || | || | | || || |', + ' | ||_| ||_| | ||_||_|', + ' ', + } + assert.are.equal '110101100', convert rows + + pending 'Garbled numbers in a string are replaced with ?', -> + rows = { + ' _ _ _ ', + ' | || | || | || || |', + ' | | _| ||_| | ||_||_|', + ' ', + } + assert.are.equal '11?10?1?0', convert rows + + pending 'Recognizes 2', -> + rows = { + ' _ ', + ' _|', + '|_ ', + ' ', + } + assert.are.equal '2', convert rows + + pending 'Recognizes 3', -> + rows = { + ' _ ', + ' _|', + ' _|', + ' ', + } + assert.are.equal '3', convert rows + + pending 'Recognizes 4', -> + rows = { + ' ', + '|_|', + ' |', + ' ', + } + assert.are.equal '4', convert rows + + pending 'Recognizes 5', -> + rows = { + ' _ ', + '|_ ', + ' _|', + ' ', + } + assert.are.equal '5', convert rows + + pending 'Recognizes 6', -> + rows = { + ' _ ', + '|_ ', + '|_|', + ' ', + } + assert.are.equal '6', convert rows + + pending 'Recognizes 7', -> + rows = { + ' _ ', + ' |', + ' |', + ' ', + } + assert.are.equal '7', convert rows + + pending 'Recognizes 8', -> + rows = { + ' _ ', + '|_|', + '|_|', + ' ', + } + assert.are.equal '8', convert rows + + pending 'Recognizes 9', -> + rows = { + ' _ ', + '|_|', + ' _|', + ' ', + } + assert.are.equal '9', convert rows + + pending 'Recognizes string of decimal numbers', -> + rows = { + ' _ _ _ _ _ _ _ _ ', + ' | _| _||_||_ |_ ||_||_|| |', + ' ||_ _| | _||_| ||_| _||_|', + ' ', + } + assert.are.equal '1234567890', convert rows + + pending 'Numbers separated by empty lines are recognized. Lines are joined by commas.', -> + rows = { + ' _ _ ', + ' | _| _|', + ' ||_ _|', + ' ', + ' _ _ ', + '|_||_ |_ ', + ' | _||_|', + ' ', + ' _ _ _ ', + ' ||_||_|', + ' ||_| _|', + ' ', + } + assert.are.equal '123,456,789', convert rows +