diff --git a/config.json b/config.json index 2d0bd0f..32e6db3 100644 --- a/config.json +++ b/config.json @@ -119,6 +119,14 @@ "prerequisites": [], "difficulty": 1 }, + { + "slug": "kindergarten-garden", + "name": "Kindergarten Garden", + "uuid": "a4df662d-57a0-42be-991e-3d086e643484", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, { "slug": "perfect-numbers", "name": "Perfect Numbers", diff --git a/exercises/practice/kindergarten-garden/.docs/instructions.append.md b/exercises/practice/kindergarten-garden/.docs/instructions.append.md new file mode 100644 index 0000000..ff90648 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/instructions.append.md @@ -0,0 +1,12 @@ +# Instruction append + +## Output format + +Output the student's plants as a null-terminated string, using a comma and a space as separators. + +## Reserved Memory + +The buffer for the diagram string uses bytes 1024-2047 of linear memory. +The buffer for the student string uses bytes 2048-3071 of linear memory. + +The input strings can be modified in place if desired. diff --git a/exercises/practice/kindergarten-garden/.docs/instructions.md b/exercises/practice/kindergarten-garden/.docs/instructions.md new file mode 100644 index 0000000..6fe11a5 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/instructions.md @@ -0,0 +1,56 @@ +# Instructions + +Your task is to, given a diagram, determine which plants each child in the kindergarten class is responsible for. + +There are 12 children in the class: + +- Alice, Bob, Charlie, David, Eve, Fred, Ginny, Harriet, Ileana, Joseph, Kincaid, and Larry. + +Four different types of seeds are planted: + +| Plant | Diagram encoding | +| ------ | ---------------- | +| Grass | G | +| Clover | C | +| Radish | R | +| Violet | V | + +Each child gets four cups, two on each row: + +```text +[window][window][window] +........................ # each dot represents a cup +........................ +``` + +Their teacher assigns cups to the children alphabetically by their names, which means that Alice comes first and Larry comes last. + +Here is an example diagram representing Alice's plants: + +```text +[window][window][window] +VR...................... +RG...................... +``` + +In the first row, nearest the windows, she has a violet and a radish. +In the second row she has a radish and some grass. + +Your program will be given the plants from left-to-right starting with the row nearest the windows. +From this, it should be able to determine which plants belong to each student. + +For example, if it's told that the garden looks like so: + +```text +[window][window][window] +VRCGVVRVCGGCCGVRGCVCGCGV +VRCCCGCRRGVCGCRVVCVGCGCV +``` + +Then if asked for Alice's plants, it should provide: + +- Violets, radishes, violets, radishes + +While asking for Bob's plants would yield: + +- Clover, grass, clover, clover diff --git a/exercises/practice/kindergarten-garden/.docs/introduction.md b/exercises/practice/kindergarten-garden/.docs/introduction.md new file mode 100644 index 0000000..5ad97d2 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.docs/introduction.md @@ -0,0 +1,6 @@ +# Introduction + +The kindergarten class is learning about growing plants. +The teacher thought it would be a good idea to give the class seeds to plant and grow in the dirt. +To this end, the children have put little cups along the window sills and planted one type of plant in each cup. +The children got to pick their favorites from four available types of seeds: grass, clover, radishes, and violets. diff --git a/exercises/practice/kindergarten-garden/.eslintrc b/exercises/practice/kindergarten-garden/.eslintrc new file mode 100644 index 0000000..1dbeac2 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.eslintrc @@ -0,0 +1,18 @@ +{ + "root": true, + "extends": "@exercism/eslint-config-javascript", + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.spec.js" + ], + "excludedFiles": [ + "custom.spec.js" + ], + "extends": "@exercism/eslint-config-javascript/maintainers" + } + ] +} diff --git a/exercises/practice/kindergarten-garden/.meta/config.json b/exercises/practice/kindergarten-garden/.meta/config.json new file mode 100644 index 0000000..2750e03 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/config.json @@ -0,0 +1,28 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "kindergarten-garden.wat" + ], + "test": [ + "kindergarten-garden.spec.js" + ], + "example": [ + ".meta/proof.ci.wat" + ], + "invalidator": [ + "package.json" + ] + }, + "blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.", + "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://turing.edu", + "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/kindergarten-garden/.meta/proof.ci.wat b/exercises/practice/kindergarten-garden/.meta/proof.ci.wat new file mode 100644 index 0000000..26b0e2c --- /dev/null +++ b/exercises/practice/kindergarten-garden/.meta/proof.ci.wat @@ -0,0 +1,171 @@ +(module + (memory (export "mem") 1) + + (data $names (i32.const 195) " 6clover, 5grass, 8radishes, 7violets, ") + + (global $cloverLength i32 (i32.const 8)) + (global $grassLength i32 (i32.const 7)) + (global $radishesLength i32 (i32.const 10)) + (global $violetsLength i32 (i32.const 9)) + (global $commaLength i32 (i32.const 2)) + + (global $A i32 (i32.const 65)) + (global $C i32 (i32.const 67)) + (global $G i32 (i32.const 71)) + (global $R i32 (i32.const 82)) + (global $V i32 (i32.const 86)) + + (func $appendPlant (param $destOffset i32) (param $plant i32) (result i32) + (local $plantOffset i32) + (local $plantLength i32) + + (local.set + $plantOffset + (i32.mul + (i32.const 3) + (local.get $plant) + ) + ) + + (local.set + $plantLength + (i32.sub + (i32.load8_u + (i32.sub + (local.get $plantOffset) + (i32.const 1) + ) + ) + ;; We convert a digit like '6' to a number like 8, to allow for the comma and space. + (i32.const 46) + ) + ) + + (memory.copy + (local.get $destOffset) + (local.get $plantOffset) + (local.get $plantLength) + ) + + (return + (i32.add + (local.get $destOffset) + (local.get $plantLength) + ) + ) + ) + + ;; + ;; Determine which plants a child in the kindergarten class is responsible for. + ;; + ;; @param {i32} diagramOffset - The offset of the diagram string in linear memory. + ;; @param {i32} diagramLength - The length of the diagram string in linear memory. + ;; @param {i32} studentOffset - The offset of the student string in linear memory. + ;; @param {i32} studentLength - The length of the student string in linear memory. + ;; + ;; @returns {(i32,i32)} - Offset and length of plants string + ;; in linear memory. + ;; + (func (export "plants") + (param $diagramOffset i32) (param $diagramLength i32) (param $studentOffset i32) (param $studentLength i32) (result i32 i32) + (local $dest i32) + (local $diagramHalfLength i32) + (local $first i32) + (local $second i32) + (local $third i32) + (local $fourth i32) + + (local.set $diagramHalfLength + (i32.shr_u + (i32.add + (local.get $diagramLength) + (i32.const 1) + ) + (i32.const 1) + ) + ) + + (local.set $first + (i32.add + (local.get $diagramOffset) + (i32.shl + (i32.sub + (i32.load8_u (local.get $studentOffset)) + (global.get $A) + ) + (i32.const 1) + ) + ) + ) + + (local.set $second + (i32.add + (local.get $first) + (i32.const 1) + ) + ) + + (local.set $third + (i32.add + (local.get $diagramHalfLength) + (local.get $first) + ) + ) + + (local.set $fourth + (i32.add + (local.get $third) + (i32.const 1) + ) + ) + + ;; We overwrite the student string + (local.set $dest + (local.get $studentOffset) + ) + + (local.set $dest + (call $appendPlant + (local.get $dest) + (i32.load8_u (local.get $first)) + ) + ) + + (local.set $dest + (call $appendPlant + (local.get $dest) + (i32.load8_u (local.get $second)) + ) + ) + + (local.set $dest + (call $appendPlant + (local.get $dest) + (i32.load8_u (local.get $third)) + ) + ) + + (local.set $dest + (call $appendPlant + (local.get $dest) + (i32.load8_u (local.get $fourth)) + ) + ) + + (local.set $dest + (i32.sub + (local.get $dest) + (global.get $commaLength) + ) + ) + + (return + (local.get $studentOffset) + + (i32.sub + (local.get $dest) + (local.get $studentOffset) + ) + ) + ) +) diff --git a/exercises/practice/kindergarten-garden/.meta/tests.toml b/exercises/practice/kindergarten-garden/.meta/tests.toml new file mode 100644 index 0000000..0cdd9ad --- /dev/null +++ b/exercises/practice/kindergarten-garden/.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. + +[1fc316ed-17ab-4fba-88ef-3ae78296b692] +description = "partial garden -> garden with single student" + +[acd19dc1-2200-4317-bc2a-08f021276b40] +description = "partial garden -> different garden with single student" + +[c376fcc8-349c-446c-94b0-903947315757] +description = "partial garden -> garden with two students" + +[2d620f45-9617-4924-9d27-751c80d17db9] +description = "partial garden -> multiple students for the same garden with three students -> second student's garden" + +[57712331-4896-4364-89f8-576421d69c44] +description = "partial garden -> multiple students for the same garden with three students -> third student's garden" + +[149b4290-58e1-40f2-8ae4-8b87c46e765b] +description = "full garden -> for Alice, first student's garden" + +[ba25dbbc-10bd-4a37-b18e-f89ecd098a5e] +description = "full garden -> for Bob, second student's garden" + +[566b621b-f18e-4c5f-873e-be30544b838c] +description = "full garden -> for Charlie" + +[3ad3df57-dd98-46fc-9269-1877abf612aa] +description = "full garden -> for David" + +[0f0a55d1-9710-46ed-a0eb-399ba8c72db2] +description = "full garden -> for Eve" + +[a7e80c90-b140-4ea1-aee3-f4625365c9a4] +description = "full garden -> for Fred" + +[9d94b273-2933-471b-86e8-dba68694c615] +description = "full garden -> for Ginny" + +[f55bc6c2-ade8-4844-87c4-87196f1b7258] +description = "full garden -> for Harriet" + +[759070a3-1bb1-4dd4-be2c-7cce1d7679ae] +description = "full garden -> for Ileana" + +[78578123-2755-4d4a-9c7d-e985b8dda1c6] +description = "full garden -> for Joseph" + +[6bb66df7-f433-41ab-aec2-3ead6e99f65b] +description = "full garden -> for Kincaid, second to last student's garden" + +[d7edec11-6488-418a-94e6-ed509e0fa7eb] +description = "full garden -> for Larry, last student's garden" diff --git a/exercises/practice/kindergarten-garden/.npmrc b/exercises/practice/kindergarten-garden/.npmrc new file mode 100644 index 0000000..d26df80 --- /dev/null +++ b/exercises/practice/kindergarten-garden/.npmrc @@ -0,0 +1 @@ +audit=false diff --git a/exercises/practice/kindergarten-garden/LICENSE b/exercises/practice/kindergarten-garden/LICENSE new file mode 100644 index 0000000..90e73be --- /dev/null +++ b/exercises/practice/kindergarten-garden/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/kindergarten-garden/babel.config.js b/exercises/practice/kindergarten-garden/babel.config.js new file mode 100644 index 0000000..9c17ba5 --- /dev/null +++ b/exercises/practice/kindergarten-garden/babel.config.js @@ -0,0 +1,4 @@ +export default { + presets: ["@exercism/babel-preset-javascript"], + plugins: [], +}; diff --git a/exercises/practice/kindergarten-garden/kindergarten-garden.spec.js b/exercises/practice/kindergarten-garden/kindergarten-garden.spec.js new file mode 100644 index 0000000..a9f4778 --- /dev/null +++ b/exercises/practice/kindergarten-garden/kindergarten-garden.spec.js @@ -0,0 +1,176 @@ +import { compileWat, WasmRunner } from "@exercism/wasm-lib"; + +let wasmModule; +let currentInstance; + +function plants(diagram, student) { + const diagramBufferOffset = 1024; + const diagramBufferCapacity = 1024; + + const diagramLengthEncoded = new TextEncoder().encode(diagram).length; + if (diagramLengthEncoded > diagramBufferCapacity) { + throw new Error( + `String is too large for buffer of size ${diagramBufferCapacity} bytes` + ); + } + + currentInstance.set_mem_as_utf8( + diagramBufferOffset, + diagramLengthEncoded, + diagram); + + const studentBufferOffset = 2048; + const studentBufferCapacity = 1024; + + const studentLengthEncoded = new TextEncoder().encode(student).length; + if (studentLengthEncoded > studentBufferCapacity) { + throw new Error( + `String is too large for buffer of size ${studentBufferCapacity} bytes` + ); + } + + currentInstance.set_mem_as_utf8( + studentBufferOffset, + studentLengthEncoded, + student + ); + + const [outputOffset, outputLength] = currentInstance.exports.plants( + diagramBufferOffset, + diagramLengthEncoded, + studentBufferOffset, + studentLengthEncoded + ); + + // Decode JS string from returned offset and length + return currentInstance.get_mem_as_utf8(outputOffset, outputLength); +} + +beforeAll(async () => { + try { + const watPath = new URL("./kindergarten-garden.wat", import.meta.url); + const { buffer } = await compileWat(watPath); + wasmModule = await WebAssembly.compile(buffer); + } catch (err) { + console.log(`Error compiling *.wat: \n${err}`); + process.exit(1); + } +}); + +describe("Kindergarten Garden", () => { + beforeEach(async () => { + currentInstance = null; + if (!wasmModule) { + return Promise.reject(); + } + try { + currentInstance = await new WasmRunner(wasmModule); + return Promise.resolve(); + } catch (err) { + console.log(`Error instantiating WebAssembly module: ${err}`); + return Promise.reject(); + } + }); + + test("garden with single student", () => { + const expected = "radishes, clover, grass, grass"; + const actual = plants("RC\nGG", "Alice"); + expect(actual).toEqual(expected); + }); + + xtest("different garden with single student", () => { + const expected = "violets, clover, radishes, clover"; + const actual = plants("VC\nRC", "Alice"); + expect(actual).toEqual(expected); + }); + + xtest("garden with two students", () => { + const expected = "clover, grass, radishes, clover"; + const actual = plants("VVCG\nVVRC", "Bob"); + expect(actual).toEqual(expected); + }); + + xtest("second student's garden", () => { + const expected = "clover, clover, clover, clover"; + const actual = plants("VVCCGG\nVVCCGG", "Bob"); + expect(actual).toEqual(expected); + }); + + xtest("third student's garden", () => { + const expected = "grass, grass, grass, grass"; + const actual = plants("VVCCGG\nVVCCGG", "Charlie"); + expect(actual).toEqual(expected); + }); + + xtest("for Alice, first student's garden", () => { + const expected = "violets, radishes, violets, radishes"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Alice"); + expect(actual).toEqual(expected); + }); + + xtest("for Bob, second student's garden", () => { + const expected = "clover, grass, clover, clover"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Bob"); + expect(actual).toEqual(expected); + }); + + xtest("for Charlie", () => { + const expected = "violets, violets, clover, grass"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Charlie"); + expect(actual).toEqual(expected); + }); + + xtest("for David", () => { + const expected = "radishes, violets, clover, radishes"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "David"); + expect(actual).toEqual(expected); + }); + + xtest("for Eve", () => { + const expected = "clover, grass, radishes, grass"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Eve"); + expect(actual).toEqual(expected); + }); + + xtest("for Fred", () => { + const expected = "grass, clover, violets, clover"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Fred"); + expect(actual).toEqual(expected); + }); + + xtest("for Ginny", () => { + const expected = "clover, grass, grass, clover"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Ginny"); + expect(actual).toEqual(expected); + }); + + xtest("for Harriet", () => { + const expected = "violets, radishes, radishes, violets"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Harriet"); + expect(actual).toEqual(expected); + }); + + xtest("for Ileana", () => { + const expected = "grass, clover, violets, clover"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Ileana"); + expect(actual).toEqual(expected); + }); + + xtest("for Joseph", () => { + const expected = "violets, clover, violets, grass"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Joseph"); + expect(actual).toEqual(expected); + }); + + xtest("for Kincaid, second to last student's garden", () => { + const expected = "grass, clover, clover, grass"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Kincaid"); + expect(actual).toEqual(expected); + }); + + xtest("for Larry, last student's garden", () => { + const expected = "grass, violets, clover, violets"; + const actual = plants("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV", "Larry"); + expect(actual).toEqual(expected); + }); +}); diff --git a/exercises/practice/kindergarten-garden/kindergarten-garden.wat b/exercises/practice/kindergarten-garden/kindergarten-garden.wat new file mode 100644 index 0000000..e8faa94 --- /dev/null +++ b/exercises/practice/kindergarten-garden/kindergarten-garden.wat @@ -0,0 +1,19 @@ +(module + (memory (export "mem") 1) + + ;; + ;; Determine which plants a child in the kindergarten class is responsible for. + ;; + ;; @param {i32} diagramOffset - The offset of the diagram string in linear memory. + ;; @param {i32} diagramLength - The length of the diagram string in linear memory. + ;; @param {i32} studentOffset - The offset of the student string in linear memory. + ;; @param {i32} studentLength - The length of the student string in linear memory. + ;; + ;; @returns {(i32,i32)} - Offset and length of plants string + ;; in linear memory. + ;; + (func (export "plants") + (param $diagramOffset i32) (param $diagramLength i32) (param $studentOffset i32) (param $studentLength i32) (result i32 i32) + (return (i32.const 0) (i32.const 0)) + ) +) diff --git a/exercises/practice/kindergarten-garden/package.json b/exercises/practice/kindergarten-garden/package.json new file mode 100644 index 0000000..c4086f5 --- /dev/null +++ b/exercises/practice/kindergarten-garden/package.json @@ -0,0 +1,34 @@ +{ + "name": "@exercism/wasm-kindergarten-garden", + "description": "Exercism exercises in WebAssembly.", + "type": "module", + "private": true, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/exercism/wasm", + "directory": "exercises/practice/kindergarten-garden" + }, + "jest": { + "maxWorkers": 1 + }, + "devDependencies": { + "@babel/core": "^7.23.3", + "@exercism/babel-preset-javascript": "^0.4.0", + "@exercism/eslint-config-javascript": "^0.6.0", + "@types/jest": "^29.5.8", + "@types/node": "^20.9.1", + "babel-jest": "^29.7.0", + "core-js": "^3.33.2", + "eslint": "^8.54.0", + "jest": "^29.7.0" + }, + "dependencies": { + "@exercism/wasm-lib": "^0.2.0" + }, + "scripts": { + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js ./*", + "watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch ./*", + "lint": "eslint ." + } +}