Robot name

This commit is contained in:
2022-11-06 14:58:29 +01:00
parent 4e9d5e310e
commit 1e9da939c0
13 changed files with 16414 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
!.meta
# Protected or generated
.git
.vscode
# When using npm
node_modules/*
# Configuration files
.eslintrc.cjs
babel.config.cjs
jest.config.cjs

View File

@@ -0,0 +1,41 @@
module.exports = {
root: true,
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
},
overrides: [
// Student provided files
{
files: ['*.ts'],
excludedFiles: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
extends: '@exercism/eslint-config-typescript',
},
// Exercism given tests
{
files: ['*.test.ts'],
excludedFiles: ['custom.test.ts'],
env: {
jest: true,
},
extends: '@exercism/eslint-config-typescript/maintainers',
},
// Student provided tests
{
files: ['custom.test.ts'],
env: {
jest: true,
},
extends: '@exercism/eslint-config-typescript',
},
// Exercism provided files
{
files: ['.meta/proof.ci.ts', '.meta/exemplar.ts', '*.test.ts'],
excludedFiles: ['custom.test.ts'],
extends: '@exercism/eslint-config-typescript/maintainers',
rules: {
"@typescript-eslint/semi": 0,
},
},
],
}

View File

@@ -0,0 +1,11 @@
{
"blurb": "Manage robot factory settings.",
"authors": ["masters3d"],
"contributors": ["joshgoebel", "lukaszklis", "SleeplessByte"],
"files": {
"solution": ["robot-name.ts"],
"test": ["robot-name.test.ts"],
"example": [".meta/proof.ci.ts"]
},
"source": "A debugging session with Paul Blackwell at gSchool."
}

View File

@@ -0,0 +1 @@
{"track":"typescript","exercise":"robot-name","id":"d853a3b12979413dadbf0f3e331eec3a","url":"https://exercism.org/tracks/typescript/exercises/robot-name","handle":"briemens","is_requester":true,"auto_approve":false}

View File

@@ -0,0 +1,44 @@
# Help
## Running the tests
Execute the tests with:
```bash
$ 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`.
## Submitting your solution
You can submit your solution using the `exercism submit robot-name.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)
- [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.

View File

@@ -0,0 +1,37 @@
# Robot Name
Welcome to Robot Name on Exercism's TypeScript Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Manage robot factory settings.
When a robot comes off the factory floor, it has no name.
The first time you turn on a robot, a random name is generated in the format
of two uppercase letters followed by three digits, such as RX837 or BC811.
Every once in a while we need to reset a robot to its factory settings,
which means that its name gets wiped. The next time you ask, that robot will
respond with a new random name.
The names must be random: they should not follow a predictable sequence.
Using random names means a risk of collisions. Your solution must ensure that
every existing robot has a unique name.
## Source
### Created by
- @masters3d
### Contributed to by
- @joshgoebel
- @lukaszklis
- @SleeplessByte
### Based on
A debugging session with Paul Blackwell at gSchool.

View File

@@ -0,0 +1,4 @@
module.exports = {
presets: ['@exercism/babel-preset-typescript'],
plugins: [],
}

View File

@@ -0,0 +1,19 @@
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',
},
}

15996
typescript/robot-name/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
{
"name": "@exercism/typescript-robot-name",
"version": "1.0.0",
"description": "Exercism exercises in Typescript.",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/exercism/typescript"
},
"type": "module",
"engines": {
"node": "^14.13.1 || >=16.0.0"
},
"devDependencies": {
"@exercism/babel-preset-typescript": "^0.1.0",
"@exercism/eslint-config-typescript": "^0.4.1",
"@types/jest": "^27.4.0",
"@types/node": "^16.11.24",
"babel-jest": "^27.5.1",
"core-js": "^3.21.0",
"eslint": "^8.9.0",
"jest": "^27.5.1",
"typescript": "^4.5.4"
},
"scripts": {
"test": "yarn lint:types && jest --no-cache",
"lint": "yarn lint:types && yarn lint:ci",
"lint:types": "yarn tsc --noEmit -p .",
"lint:ci": "eslint . --ext .tsx,.ts"
}
}

View File

@@ -0,0 +1,142 @@
import { Robot } from "./robot-name";
const areSequential = (name1: string, name2: string): boolean => {
const alpha1 = name1.substr(0, 2);
const alpha2 = name2.substr(0, 2);
const num1 = Number(name1.substr(2, 3));
const num2 = Number(name2.substr(2, 3));
const numDiff = num2 - num1;
const alphaDiff =
(alpha2.charCodeAt(0) - alpha1.charCodeAt(0)) * 26 +
(alpha2.charCodeAt(1) - alpha1.charCodeAt(1));
const totalDiff = alphaDiff * 1000 + numDiff;
return Math.abs(totalDiff) <= 1;
};
const NAME_RE = /^[A-Z]{2}\d{3}$/;
const TOTAL_NUMBER_OF_NAMES =
26 * // A-Z
26 * // A-Z
10 * // 0-9
10 * // 0-9
10; // 0-9
describe("Robot", () => {
let robot: Robot;
beforeEach(() => {
robot = new Robot();
});
afterEach(() => {
Robot.releaseNames();
});
it("has a name", () => {
expect(robot.name).toMatch(NAME_RE);
});
it("name is the same each time", () => {
expect(robot.name).toEqual(robot.name);
});
it("different robots have different names", () => {
const differentRobot = new Robot();
expect(differentRobot.name).not.toEqual(robot.name);
});
it("is able to reset the name", () => {
const originalName = robot.name;
robot.resetName();
const newName = robot.name;
expect(newName).toMatch(NAME_RE);
expect(originalName).not.toEqual(newName);
});
it("should set a unique name after reset", () => {
const NUMBER_OF_ROBOTS = 10000;
const usedNames = new Set();
usedNames.add(robot.name);
for (let i = 0; i < NUMBER_OF_ROBOTS; i++) {
robot.resetName();
usedNames.add(robot.name);
}
expect(usedNames.size).toEqual(NUMBER_OF_ROBOTS + 1);
});
it("new names should not be sequential", () => {
const name1 = robot.name;
const name2 = new Robot().name;
const name3 = new Robot().name;
expect(areSequential(name1, name1)).toBe(true);
expect(areSequential(name1, name2)).toBe(false);
expect(areSequential(name2, name3)).toBe(false);
});
it("names from reset should not be sequential", () => {
const name1 = robot.name;
robot.resetName();
const name2 = robot.name;
robot.resetName();
const name3 = robot.name;
expect(areSequential(name1, name2)).toBe(false);
expect(areSequential(name2, name3)).toBe(false);
expect(areSequential(name3, name3)).toBe(true);
});
it("uses all letters", () => {
let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (let i = 0; i < 1000 - 1; i += 1) {
const newRobot = new Robot();
const lettersInName = newRobot.name.slice(0, 2);
letters = letters.replace(lettersInName[0], "");
letters = letters.replace(lettersInName[1], "");
if (letters.length === 0) {
break;
}
}
expect(letters).toEqual("");
});
it("uses all numbers", () => {
let numbers = "0123456789";
for (let i = 0; i < 1000 - 1; i += 1) {
const newRobot = new Robot();
const digitsInName = newRobot.name.slice(2, 5);
numbers = numbers.replace(digitsInName[0], "");
numbers = numbers.replace(digitsInName[1], "");
numbers = numbers.replace(digitsInName[2], "");
if (numbers.length === 0) {
break;
}
}
expect(numbers).toEqual("");
});
// This test is optional.
//
// This test doesn't run on our online test runner because it will time-out
// with most implementations. It's up to you to test your solution locally.
it.skip("all the names can be generated", () => {
const usedNames = new Set();
usedNames.add(robot.name);
for (let i = 0; i < TOTAL_NUMBER_OF_NAMES - 1; i += 1) {
const newRobot = new Robot();
usedNames.add(newRobot.name);
}
expect(usedNames.size).toEqual(TOTAL_NUMBER_OF_NAMES);
});
});

View File

@@ -0,0 +1,47 @@
const allNames: string[] = [];
for (let c1 = 0; c1 < 26; c1++) {
const letter1 = String.fromCharCode(c1 + 65);
for (let c2 = 0; c2 < 26; c2++) {
const letter2 = String.fromCharCode(c2 + 65);
for (let n1 = 0; n1 < 10; n1++) {
const number1 = String(n1);
for (let n2 = 0; n2 < 10; n2++) {
const number2 = String(n2);
for (let n3 = 0; n3 < 10; n3++) {
const number3 = String(n3);
allNames.push(`${letter2}${letter1}${number1}${number2}${number3}`);
}
}
}
}
}
let possibleNames = [...allNames];
const generateName = (): string => {
const index = Math.floor(Math.random() * possibleNames.length);
const name = possibleNames[index];
possibleNames.splice(index, 1);
return name;
};
export class Robot {
private _name = "";
constructor() {
this.resetName();
}
public get name(): string {
return this._name;
}
public resetName(): void {
this._name = generateName();
}
public static releaseNames(): void {
possibleNames = [...allNames];
}
}

View File

@@ -0,0 +1,28 @@
{
"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": ["ESNEXT", "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": "ES2020",
// 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": "ESNext", // ESLint doesn't support this yet: "es2022",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": 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"],
"exclude": ["node_modules"]
}