Kindergarten

This commit is contained in:
2025-05-04 14:57:42 +02:00
parent 73b74b9060
commit 6837ee3f6b
19 changed files with 27118 additions and 0 deletions
@@ -0,0 +1,32 @@
{
"authors": [
"fredrb"
],
"contributors": [
"CRivasGomez",
"masters3d",
"SleeplessByte"
],
"files": {
"solution": [
"kindergarten-garden.ts"
],
"test": [
"kindergarten-garden.test.ts"
],
"example": [
".meta/proof.ci.ts"
]
},
"blurb": "Given a diagram, determine which plants each child in the kindergarten class is responsible for.",
"custom": {
"version.tests.compatibility": "jest-29",
"flag.tests.task-per-describe": false,
"flag.tests.may-run-long": false,
"flag.tests.includes-optional": false,
"flag.tests.jest": true,
"flag.tests.tstyche": false
},
"source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
"source_url": "https://turing.edu"
}
@@ -0,0 +1 @@
{"track":"typescript","exercise":"kindergarten-garden","id":"595fdf036ca447ebab34754a2ed97602","url":"https://exercism.org/tracks/typescript/exercises/kindergarten-garden","handle":"briemens","is_requester":true,"auto_approve":false}
+17226
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
{
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
+7
View File
@@ -0,0 +1,7 @@
{
"cSpell.words": ["exercism"],
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
}
}
@@ -0,0 +1,3 @@
compressionLevel: mixed
enableGlobalCache: true
+50
View File
@@ -0,0 +1,50 @@
# Help
## Running the tests
Before trying to execute the tests, ensure the assignment folder is set-up correctly by following the installation steps, namely `corepack yarn install` and the Editor SDK setup.
Execute the tests with:
```bash
$ corepack yarn test
```
## Skipped tests
In the test suites all tests but the first have been skipped.
Once you get a test passing, you can enable the next one by changing `xit` to `it`.
Additionally tests may be grouped using `xdescribe`.
Enable the group by changing that to `describe`.
Finally, some exercises may have optional tests `it.skip`.
Remove `.skip` to execute the optional test.
## Submitting your solution
You can submit your solution using the `exercism submit kindergarten-garden.ts` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [TypeScript track's documentation](https://exercism.org/docs/tracks/typescript)
- The [TypeScript track's programming category on the forum](https://forum.exercism.org/c/programming/typescript)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
To get help if you're having trouble, you can use one of the following resources:
- [TypeScript QuickStart](https://www.typescriptlang.org/docs/handbook/release-notes/overview.html)
- [ECMAScript 2015 Language Specification](https://www.ecma-international.org/wp-content/uploads/ECMA-262_6th_edition_june_2015.pdf) (pdf)
- [Mozilla JavaScript Reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference)
- [/r/typescript](https://www.reddit.com/r/typescript) is the TypeScript subreddit.
- [StackOverflow](https://stackoverflow.com/questions/tagged/typescript) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
+84
View File
@@ -0,0 +1,84 @@
# Kindergarten Garden
Welcome to Kindergarten Garden on Exercism's TypeScript Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## 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.
## 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
## Source
### Created by
- @fredrb
### Contributed to by
- @CRivasGomez
- @masters3d
- @SleeplessByte
### Based on
Exercise by the JumpstartLab team for students at The Turing School of Software and Design. - https://turing.edu
@@ -0,0 +1,5 @@
module.exports = {
// eslint-disable-next-line @typescript-eslint/no-require-imports
presets: [[require('@exercism/babel-preset-typescript'), { corejs: '3.38' }]],
plugins: [],
}
@@ -0,0 +1,26 @@
// @ts-check
import tsEslint from 'typescript-eslint'
import config from '@exercism/eslint-config-typescript'
import maintainersConfig from '@exercism/eslint-config-typescript/maintainers.mjs'
export default [
...tsEslint.config(...config, {
files: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
extends: maintainersConfig,
}),
{
ignores: [
// # Protected or generated
'.git/**/*',
'.vscode/**/*',
//# When using npm
'node_modules/**/*',
// # Configuration files
'babel.config.cjs',
'jest.config.cjs',
],
},
]
@@ -0,0 +1,22 @@
module.exports = {
verbose: true,
projects: ['<rootDir>'],
testMatch: [
'**/__tests__/**/*.[jt]s?(x)',
'**/test/**/*.[jt]s?(x)',
'**/?(*.)+(spec|test).[jt]s?(x)',
],
testPathIgnorePatterns: [
'/(?:production_)?node_modules/',
'.d.ts$',
'<rootDir>/test/fixtures',
'<rootDir>/test/helpers',
'__mocks__',
],
transform: {
'^.+\\.[jt]sx?$': 'babel-jest',
},
moduleNameMapper: {
'^(\\.\\/.+)\\.js$': '$1',
},
}
@@ -0,0 +1,239 @@
import { describe, expect, it } from '@jest/globals'
import { Garden } from './kindergarten-garden.ts'
describe('partial Garden', () => {
it('garden with single student', () => {
expect(new Garden('RC\nGG').plants('Alice')).toEqual([
'radishes',
'clover',
'grass',
'grass',
])
})
it('different garden with single student', () => {
expect(new Garden('VC\nRC').plants('Alice')).toEqual([
'violets',
'clover',
'radishes',
'clover',
])
})
it('garden with two students', () => {
expect(new Garden('VVCG\nVVRC').plants('Bob')).toEqual([
'clover',
'grass',
'radishes',
'clover',
])
})
describe('multiple students for the same garden with three students', () => {
it("second student's garden", () => {
expect(new Garden('VVCCGG\nVVCCGG').plants('Bob')).toEqual([
'clover',
'clover',
'clover',
'clover',
])
})
it("third student's garden", () => {
expect(new Garden('VVCCGG\nVVCCGG').plants('Charlie')).toEqual([
'grass',
'grass',
'grass',
'grass',
])
})
})
})
describe('full garden', () => {
const diagram = 'VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV'
const garden = new Garden(diagram)
it("for Alice, first student's garden", () => {
expect(garden.plants('Alice')).toEqual([
'violets',
'radishes',
'violets',
'radishes',
])
})
it("for Bob, second student's garden", () => {
expect(garden.plants('Bob')).toEqual([
'clover',
'grass',
'clover',
'clover',
])
})
it('for Charlie', () => {
expect(garden.plants('Charlie')).toEqual([
'violets',
'violets',
'clover',
'grass',
])
})
it('for David', () => {
expect(garden.plants('David')).toEqual([
'radishes',
'violets',
'clover',
'radishes',
])
})
it('for Eve', () => {
expect(garden.plants('Eve')).toEqual([
'clover',
'grass',
'radishes',
'grass',
])
})
it('for Fred', () => {
expect(garden.plants('Fred')).toEqual([
'grass',
'clover',
'violets',
'clover',
])
})
it('for Ginny', () => {
expect(garden.plants('Ginny')).toEqual([
'clover',
'grass',
'grass',
'clover',
])
})
it('for Harriet', () => {
expect(garden.plants('Harriet')).toEqual([
'violets',
'radishes',
'radishes',
'violets',
])
})
it('for Ileana', () => {
expect(garden.plants('Ileana')).toEqual([
'grass',
'clover',
'violets',
'clover',
])
})
it('for Joseph', () => {
expect(garden.plants('Joseph')).toEqual([
'violets',
'clover',
'violets',
'grass',
])
})
it("for Kincaid, second to last student's garden", () => {
expect(garden.plants('Kincaid')).toEqual([
'grass',
'clover',
'clover',
'grass',
])
})
it("for Larry, last student's garden", () => {
expect(garden.plants('Larry')).toEqual([
'grass',
'violets',
'clover',
'violets',
])
})
})
describe('disordered class', () => {
const diagram = 'VCRRGVRG\nRVGCCGCV'
const students = ['Samantha', 'Patricia', 'Xander', 'Roger']
const garden = new Garden(diagram, students)
it('for Patricia', () => {
expect(garden.plants('Patricia')).toEqual([
'violets',
'clover',
'radishes',
'violets',
])
})
it('for Roger', () => {
expect(garden.plants('Roger')).toEqual([
'radishes',
'radishes',
'grass',
'clover',
])
})
it('for Samantha', () => {
expect(garden.plants('Samantha')).toEqual([
'grass',
'violets',
'clover',
'grass',
])
})
it('for Xander', () => {
expect(garden.plants('Xander')).toEqual([
'radishes',
'grass',
'clover',
'violets',
])
})
})
describe('Two gardens, different students', () => {
const diagram = 'VCRRGVRG\nRVGCCGCV'
const garden1 = new Garden(diagram, ['Alice', 'Bob', 'Charlie', 'Dan'])
const garden2 = new Garden(diagram, ['Bob', 'Charlie', 'Dan', 'Erin'])
it('Bob and Charlie for each garden', () => {
expect(garden1.plants('Bob')).toEqual([
'radishes',
'radishes',
'grass',
'clover',
])
expect(garden2.plants('Bob')).toEqual([
'violets',
'clover',
'radishes',
'violets',
])
expect(garden1.plants('Charlie')).toEqual([
'grass',
'violets',
'clover',
'grass',
])
expect(garden2.plants('Charlie')).toEqual([
'radishes',
'radishes',
'grass',
'clover',
])
})
})
@@ -0,0 +1,35 @@
const DEFAULT_STUDENTS = [ 'Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Fred', 'Ginny', 'Harriet', 'Ileana', 'Joseph', 'Kincaid', 'Larry' ]
const PLANT_CODES = { G: 'grass', V: 'violets', R: 'radishes', C: 'clover', } as const
type Student = string
type PlantCode = keyof (typeof PLANT_CODES)
type Plant = (typeof PLANT_CODES)[PlantCode]
type Spot = Plant | null
type SpotsPerStudent = Record<Student, [Spot, Spot, Spot, Spot]>
const isPlantCode = (code: string): code is PlantCode => PLANT_CODES.hasOwnProperty(code)
export class Garden {
private _state: SpotsPerStudent = {}
static parseDiagram(diagram: string, students: Student[] = DEFAULT_STUDENTS): SpotsPerStudent {
const result: SpotsPerStudent = {}
const rows = diagram.split('\n').map(row => row.split('').filter(isPlantCode).map(code => PLANT_CODES[code]))
if (rows.length !== 2) throw new Error('Expected 2 rows')
if (rows[0].length !== rows[1].length) throw new Error('Rows are not of equal length')
for (let i = 0; i < rows[0].length; i+=2) {
result[students[Math.floor(i/2)]] = [rows[0][i], rows[0][i+1], rows[1][i], rows[1][i+1]]
}
return result
}
constructor(_diagram: string, _students: Student[] = DEFAULT_STUDENTS) {
this._state = Garden.parseDiagram(_diagram, [..._students].sort())
}
public plants(student: Student): Plant[] {
return (this._state[student] || []).filter(spot => spot !== null)
}
}
@@ -0,0 +1,38 @@
{
"name": "@exercism/typescript-kindergarten-garden",
"version": "1.0.0",
"description": "Exercism exercises in Typescript.",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/exercism/typescript"
},
"type": "module",
"engines": {
"node": "^18.16.0 || >=20.0.0"
},
"devDependencies": {
"@exercism/babel-preset-typescript": "^0.6.0",
"@exercism/eslint-config-typescript": "^0.8.0",
"@jest/globals": "^29.7.0",
"@types/node": "~22.7.6",
"babel-jest": "^29.7.0",
"core-js": "~3.38.1",
"eslint": "^9.12.0",
"expect": "^29.7.0",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"tstyche": "^2.1.1",
"typescript": "~5.6.3",
"typescript-eslint": "^8.10.0"
},
"scripts": {
"test": "corepack yarn node test-runner.mjs",
"test:types": "corepack yarn tstyche",
"test:implementation": "corepack yarn jest --no-cache --passWithNoTests",
"lint": "corepack yarn lint:types && corepack yarn lint:ci",
"lint:types": "corepack yarn tsc --noEmit -p .",
"lint:ci": "corepack yarn eslint . --ext .tsx,.ts"
},
"packageManager": "yarn@4.5.1"
}
@@ -0,0 +1,111 @@
#!/usr/bin/env node
/**
* 👋🏽 Hello there reader,
*
* It looks like you are working on this solution using the Exercism CLI and
* not the online editor. That's great! The file you are looking at executes
* the various steps the online test-runner also takes.
*
* @see https://github.com/exercism/typescript-test-runner
*
* TypeScript track exercises generally consist of at least two out of three
* types of tests to run.
*
* 1. tsc, the TypeScript compiler. This tests if the TypeScript code is valid
* 2. tstyche, static analysis tests to see if the types used are expected
* 3. jest, runtime implementation tests to see if the solution is correct
*
* If one of these three fails, this script terminates with -1, -2, or -3
* respectively. If it succeeds, it terminates with exit code 0.
*
* @note you need corepack (bundled with node LTS) enabled in order for this
* test runner to work as expected. Follow the installation and test
* instructions if you see errors about corepack or pnp.
*/
import { execSync } from 'node:child_process'
import { existsSync, readFileSync } from 'node:fs'
import { exit } from 'node:process'
import { URL } from 'node:url'
/**
* Before executing any tests, the test runner attempts to find the
* exercise config.json file which has metadata about which types of tests
* to run for this solution.
*/
const metaDirectory = new URL('./.meta/', import.meta.url)
const exercismDirectory = new URL('./.exercism/', import.meta.url)
const configDirectory = existsSync(metaDirectory)
? metaDirectory
: existsSync(exercismDirectory)
? exercismDirectory
: null
if (configDirectory === null) {
throw new Error(
'Expected .meta or .exercism directory to exist, but I cannot find it.'
)
}
const configFile = new URL('./config.json', configDirectory)
if (!existsSync(configFile)) {
throw new Error('Expected config.json to exist at ' + configFile.toString())
}
// Experimental: import config from './config.json' with { type: 'json' }
/** @type {import('./config.json') } */
const config = JSON.parse(readFileSync(configFile))
const jest = !config.custom || config.custom['flag.tests.jest']
const tstyche = config.custom?.['flag.tests.tstyche']
console.log(
`[tests] tsc: ✅, tstyche: ${tstyche ? '✅' : '❌'}, jest: ${jest ? '✅' : '❌'}, `
)
/**
* 1. tsc: the typescript compiler
*/
try {
console.log('[tests] tsc (compile)')
execSync('corepack yarn lint:types', {
stdio: 'inherit',
cwd: process.cwd(),
})
} catch {
exit(-1)
}
/**
* 2. tstyche: type tests
*/
if (tstyche) {
try {
console.log('[tests] tstyche (type tests)')
execSync('corepack yarn test:types', {
stdio: 'inherit',
cwd: process.cwd(),
})
} catch {
exit(-2)
}
}
/**
* 3. jest: implementation tests
*/
if (jest) {
try {
console.log('[tests] tstyche (implementation tests)')
execSync('corepack yarn test:implementation', {
stdio: 'inherit',
cwd: process.cwd(),
})
} catch {
exit(-3)
}
}
/**
* Done! 🥳
*/
@@ -0,0 +1,38 @@
{
"display": "Configuration for Exercism TypeScript Exercises",
"compilerOptions": {
// Allows you to use the newest syntax, and have access to console.log
// https://www.typescriptlang.org/tsconfig#lib
"lib": ["ES2020", "dom"],
// Make sure typescript is configured to output ESM
// https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-make-my-typescript-project-output-esm
"module": "Node16",
// Since this project is using babel, TypeScript may target something very
// high, and babel will make sure it runs on your local Node version.
// https://babeljs.io/docs/en/
"target": "ES2020", // ESLint doesn't support this yet: "es2022",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
// Because jest-resolve isn't like node resolve, the absolute path must be .ts
"allowImportingTsExtensions": true,
"noEmit": true,
// Because we'll be using babel: ensure that Babel can safely transpile
// files in the TypeScript project.
//
// https://babeljs.io/docs/en/babel-plugin-transform-typescript/#caveats
"isolatedModules": true
},
"include": [
"*.ts",
"*.tsx",
".meta/*.ts",
".meta/*.tsx",
"__typetests__/*.tst.ts"
],
"exclude": ["node_modules"]
}
File diff suppressed because it is too large Load Diff