diff --git a/config.json b/config.json index e36be78be3..25fa27416b 100644 --- a/config.json +++ b/config.json @@ -2382,6 +2382,19 @@ "parsing" ] }, + { + "slug": "flower-field", + "name": "Flower Field", + "uuid": "cb4da136-db03-44fa-a5c8-5235f273320c", + "practices": [], + "prerequisites": [], + "difficulty": 7, + "topics": [ + "algorithms", + "arrays", + "games" + ] + }, { "slug": "minesweeper", "name": "Minesweeper", @@ -2389,6 +2402,7 @@ "practices": [], "prerequisites": [], "difficulty": 7, + "status": "deprecated", "topics": [ "algorithms", "arrays", diff --git a/exercises/practice/flower-field/.docs/instructions.md b/exercises/practice/flower-field/.docs/instructions.md new file mode 100644 index 0000000000..bbdae0c2cb --- /dev/null +++ b/exercises/practice/flower-field/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to add flower counts to empty squares in a completed Flower Field garden. +The garden itself is a rectangle board composed of squares that are either empty (`' '`) or a flower (`'*'`). + +For each empty square, count the number of flowers adjacent to it (horizontally, vertically, diagonally). +If the empty square has no adjacent flowers, leave it empty. +Otherwise replace it with the count of adjacent flowers. + +For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen): + +```text +·*·*· +··*·· +··*·· +····· +``` + +Which your code should transform into this: + +```text +1*3*1 +13*31 +·2*2· +·111· +``` diff --git a/exercises/practice/flower-field/.docs/introduction.md b/exercises/practice/flower-field/.docs/introduction.md new file mode 100644 index 0000000000..af9b615361 --- /dev/null +++ b/exercises/practice/flower-field/.docs/introduction.md @@ -0,0 +1,7 @@ +# Introduction + +[Flower Field][history] is a compassionate reimagining of the popular game Minesweeper. +The object of the game is to find all the flowers in the garden using numeric hints that indicate how many flowers are directly adjacent (horizontally, vertically, diagonally) to a square. +"Flower Field" shipped in regional versions of Microsoft Windows in Italy, Germany, South Korea, Japan and Taiwan. + +[history]: https://web.archive.org/web/20020409051321fw_/http://rcm.usr.dsi.unimi.it/rcmweb/fnm/ diff --git a/exercises/practice/flower-field/.gitignore b/exercises/practice/flower-field/.gitignore new file mode 100644 index 0000000000..0c88ff6ec3 --- /dev/null +++ b/exercises/practice/flower-field/.gitignore @@ -0,0 +1,5 @@ +/node_modules +/bin/configlet +/bin/configlet.exe +/package-lock.json +/yarn.lock diff --git a/exercises/practice/flower-field/.meta/config.json b/exercises/practice/flower-field/.meta/config.json new file mode 100644 index 0000000000..0deab7edd9 --- /dev/null +++ b/exercises/practice/flower-field/.meta/config.json @@ -0,0 +1,32 @@ +{ + "authors": [ + "matthewmorgan" + ], + "contributors": [ + "BNAndras", + "brendanmckeown", + "cr0t", + "rchavarria", + "serixscorpio", + "SleeplessByte", + "xarxziux" + ], + "files": { + "solution": [ + "flower-field.js" + ], + "test": [ + "flower-field.spec.js" + ], + "example": [ + ".meta/proof.ci.js" + ] + }, + "blurb": "Mark all the flowers in a garden.", + "custom": { + "version.tests.compatibility": "jest-27", + "flag.tests.task-per-describe": false, + "flag.tests.may-run-long": false, + "flag.tests.includes-optional": false + } +} diff --git a/exercises/practice/flower-field/.meta/proof.ci.js b/exercises/practice/flower-field/.meta/proof.ci.js new file mode 100644 index 0000000000..ca8756b1fc --- /dev/null +++ b/exercises/practice/flower-field/.meta/proof.ci.js @@ -0,0 +1,56 @@ +const FLOWER = '*'; + +const DELTAS = [ + [-1, -1], + [-1, 0], + [-1, 1], + [1, 1], + [1, 0], + [1, -1], + [0, 1], + [0, -1], +]; + +function adjacentSquareIsOnBoard(board, x, d) { + return board[x + d[0]]; +} + +function adjacentSquareHasFlower(board, x, y, d) { + return board[x + d[0]][y + d[1]] === FLOWER; +} + +function countAdjacentFlowers(board, x, y) { + return DELTAS.filter((d) => adjacentSquareIsOnBoard(board, x, d)).filter( + (d) => adjacentSquareHasFlower(board, x, y, d), + ).length; +} + +function cellToFlowerOrCount(cell, inputBoard, x, y) { + if (cell === FLOWER) { + return FLOWER; + } + + return countAdjacentFlowers(inputBoard, x, y) || ' '; +} + +function stringify(board) { + return board.map((row) => row.join('')); +} + +function noDataPresent(rows) { + return rows.length === 0 || rows[0].length === 0; +} + +export function annotate(rows) { + if (noDataPresent(rows)) { + return rows; + } + + const inputBoard = rows.map((row) => [...row]); + + return stringify( + inputBoard.map((row, x) => + [...row].map((cell, y) => cellToFlowerOrCount(cell, inputBoard, x, y)), + ), + ); +} diff --git a/exercises/practice/flower-field/.meta/tests.toml b/exercises/practice/flower-field/.meta/tests.toml new file mode 100644 index 0000000000..c2b24fdaf5 --- /dev/null +++ b/exercises/practice/flower-field/.meta/tests.toml @@ -0,0 +1,46 @@ +# 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. + +[237ff487-467a-47e1-9b01-8a891844f86c] +description = "no rows" + +[4b4134ec-e20f-439c-a295-664c38950ba1] +description = "no columns" + +[d774d054-bbad-4867-88ae-069cbd1c4f92] +description = "no flowers" + +[225176a0-725e-43cd-aa13-9dced501f16e] +description = "garden full of flowers" + +[3f345495-f1a5-4132-8411-74bd7ca08c49] +description = "flower surrounded by spaces" + +[6cb04070-4199-4ef7-a6fa-92f68c660fca] +description = "space surrounded by flowers" + +[272d2306-9f62-44fe-8ab5-6b0f43a26338] +description = "horizontal line" + +[c6f0a4b2-58d0-4bf6-ad8d-ccf4144f1f8e] +description = "horizontal line, flowers at edges" + +[a54e84b7-3b25-44a8-b8cf-1753c8bb4cf5] +description = "vertical line" + +[b40f42f5-dec5-4abc-b167-3f08195189c1] +description = "vertical line, flowers at edges" + +[58674965-7b42-4818-b930-0215062d543c] +description = "cross" + +[dd9d4ca8-9e68-4f78-a677-a2a70fd7a7b8] +description = "large garden" diff --git a/exercises/practice/flower-field/.npmrc b/exercises/practice/flower-field/.npmrc new file mode 100644 index 0000000000..d26df800bb --- /dev/null +++ b/exercises/practice/flower-field/.npmrc @@ -0,0 +1 @@ +audit=false diff --git a/exercises/practice/flower-field/LICENSE b/exercises/practice/flower-field/LICENSE new file mode 100644 index 0000000000..90e73be03b --- /dev/null +++ b/exercises/practice/flower-field/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Exercism + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/exercises/practice/flower-field/babel.config.js b/exercises/practice/flower-field/babel.config.js new file mode 100644 index 0000000000..a638497df1 --- /dev/null +++ b/exercises/practice/flower-field/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: [['@exercism/babel-preset-javascript', { corejs: '3.40' }]], + plugins: [], +}; diff --git a/exercises/practice/flower-field/eslint.config.mjs b/exercises/practice/flower-field/eslint.config.mjs new file mode 100644 index 0000000000..ca517111ed --- /dev/null +++ b/exercises/practice/flower-field/eslint.config.mjs @@ -0,0 +1,45 @@ +// @ts-check + +import config from '@exercism/eslint-config-javascript'; +import maintainersConfig from '@exercism/eslint-config-javascript/maintainers.mjs'; + +import globals from 'globals'; + +export default [ + ...config, + ...maintainersConfig, + { + files: maintainersConfig[1].files, + rules: { + 'jest/expect-expect': ['warn', { assertFunctionNames: ['expect*'] }], + }, + }, + { + files: ['scripts/**/*.mjs'], + languageOptions: { + globals: { + ...globals.node, + }, + }, + }, + // <> + { + ignores: [ + // # Protected or generated + '/.appends/**/*', + '/.github/**/*', + '/.vscode/**/*', + + // # Binaries + '/bin/*', + + // # Configuration + '/config', + '/babel.config.js', + + // # Typings + '/exercises/**/global.d.ts', + '/exercises/**/env.d.ts', + ], + }, +]; diff --git a/exercises/practice/flower-field/flower-field.js b/exercises/practice/flower-field/flower-field.js new file mode 100644 index 0000000000..b133690abd --- /dev/null +++ b/exercises/practice/flower-field/flower-field.js @@ -0,0 +1,8 @@ +// +// This is only a SKELETON file for the 'Flower Field' exercise. It's been provided as a +// convenience to get you started writing code faster. +// + +export const annotate = (input) => { + throw new Error('Remove this statement and implement this function'); +}; diff --git a/exercises/practice/flower-field/flower-field.spec.js b/exercises/practice/flower-field/flower-field.spec.js new file mode 100644 index 0000000000..115693b513 --- /dev/null +++ b/exercises/practice/flower-field/flower-field.spec.js @@ -0,0 +1,79 @@ +import { describe, expect, test, xtest } from '@jest/globals'; +import { annotate } from './flower-field'; + +describe('Flower Field', () => { + test('handles no rows', () => { + expect(annotate([])).toEqual([]); + }); + + xtest('handles no columns', () => { + expect(annotate([''])).toEqual(['']); + }); + + xtest('handles no flowers', () => { + const input = [' ', ' ', ' ']; + const expected = [' ', ' ', ' ']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles garden full of flowers', () => { + const input = ['***', '***', '***']; + const expected = ['***', '***', '***']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles flower surrounded by spaces', () => { + const input = [' ', ' * ', ' ']; + const expected = ['111', '1*1', '111']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles space surrounded by flowers', () => { + const input = ['***', '* *', '***']; + const expected = ['***', '*8*', '***']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles horizontal line', () => { + const input = [' * * ']; + const expected = ['1*2*1']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles horizontal line, flowers at edges', () => { + const input = ['* *']; + const expected = ['*1 1*']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles vertical line', () => { + const input = [' ', '*', ' ', '*', ' ']; + const expected = ['1', '*', '2', '*', '1']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles vertical line, flowers at edges', () => { + const input = ['*', ' ', ' ', ' ', '*']; + const expected = ['*', '1', ' ', '1', '*']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles cross', () => { + const input = [' * ', ' * ', '*****', ' * ', ' * ']; + const expected = [' 2*2 ', '25*52', '*****', '25*52', ' 2*2 ']; + expect(annotate(input)).toEqual(expected); + }); + + xtest('handles large garden', () => { + const input = [' * * ', ' * ', ' * ', ' * *', ' * * ', ' ']; + const expected = [ + '1*22*1', + '12*322', + ' 123*2', + '112*4*', + '1*22*2', + '111111', + ]; + expect(annotate(input)).toEqual(expected); + }); +}); diff --git a/exercises/practice/flower-field/jest.config.js b/exercises/practice/flower-field/jest.config.js new file mode 100644 index 0000000000..ec8e908127 --- /dev/null +++ b/exercises/practice/flower-field/jest.config.js @@ -0,0 +1,22 @@ +module.exports = { + verbose: true, + projects: [''], + testMatch: [ + '**/__tests__/**/*.[jt]s?(x)', + '**/test/**/*.[jt]s?(x)', + '**/?(*.)+(spec|test).[jt]s?(x)', + ], + testPathIgnorePatterns: [ + '/(?:production_)?node_modules/', + '.d.ts$', + '/test/fixtures', + '/test/helpers', + '__mocks__', + ], + transform: { + '^.+\\.[jt]sx?$': 'babel-jest', + }, + moduleNameMapper: { + '^(\\.\\/.+)\\.js$': '$1', + }, +}; diff --git a/exercises/practice/flower-field/package.json b/exercises/practice/flower-field/package.json new file mode 100644 index 0000000000..079f2b9aa9 --- /dev/null +++ b/exercises/practice/flower-field/package.json @@ -0,0 +1,34 @@ +{ + "name": "@exercism/javascript-flower-field", + "description": "Exercism exercises in Javascript.", + "author": "Katrina Owen", + "private": true, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/exercism/javascript", + "directory": "exercises/practice/flower-field" + }, + "devDependencies": { + "@exercism/babel-preset-javascript": "^0.5.1", + "@exercism/eslint-config-javascript": "^0.8.1", + "@jest/globals": "^29.7.0", + "@types/node": "^22.15.29", + "@types/shelljs": "^0.8.16", + "babel-jest": "^29.7.0", + "core-js": "~3.42.0", + "diff": "^8.0.2", + "eslint": "^9.28.0", + "expect": "^29.7.0", + "globals": "^16.2.0", + "jest": "^29.7.0" + }, + "dependencies": {}, + "scripts": { + "lint": "corepack pnpm eslint .", + "test": "corepack pnpm jest", + "watch": "corepack pnpm jest --watch", + "format": "corepack pnpm prettier -w ." + }, + "packageManager": "pnpm@9.15.2" +}