Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"socket.io-client": "^4.8.3",
"stackmat-signal-processor": "^0.1.0",
"uifx": "^2.0.7",
"letscube-scrambles": "0.1.0",
"workbox-window": "^7.4.1"
},
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion client/src/lib/events.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Events from './events.json';
import Events from 'letscube-scrambles/events';

export { Events };

Expand Down
134 changes: 0 additions & 134 deletions client/src/lib/events.json

This file was deleted.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"packageManager": "yarn@1.22.22",
"workspaces": [
"client",
"server"
"server",
"packages/*"
],
"resolutions": {
"@hono/node-server": "1.19.13",
Expand Down
16 changes: 16 additions & 0 deletions packages/scrambles/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"env": {
"es6": true,
"node": true,
"jest": true
},
"extends": [
"airbnb-base"
],
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {
"no-console": "error"
}
}
24 changes: 24 additions & 0 deletions packages/scrambles/events.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{ "id": "333", "name": "3x3", "group": "WCA" },
{ "id": "222", "name": "2x2", "group": "WCA" },
{ "id": "444", "name": "4x4", "group": "WCA" },
{ "id": "555", "name": "5x5", "group": "WCA" },
{ "id": "666", "name": "6x6", "group": "WCA" },
{ "id": "777", "name": "7x7", "group": "WCA" },
{ "id": "333bf", "name": "3x3 Blindfolded", "group": "WCA" },
{ "id": "333oh", "name": "3x3 One-Handed", "group": "WCA" },
{ "id": "333ft", "name": "3x3 With Feet", "group": "WCA" },
{ "id": "minx", "name": "Megaminx", "group": "WCA" },
{ "id": "pyram", "name": "Pyraminx", "group": "WCA" },
{ "id": "clock", "name": "Clock", "group": "WCA" },
{ "id": "clock-optimal", "name": "Clock (Optimal)", "group": "Other" },
{ "id": "skewb", "name": "Skewb", "group": "WCA" },
{ "id": "sq1", "name": "Square-1", "group": "WCA" },
{ "id": "444bf", "name": "4x4 Blindfolded", "group": "WCA" },
{ "id": "555bf", "name": "5x5 Blindfolded", "group": "WCA" },
{ "id": "fto", "name": "FTO", "group": "Other" },
{ "id": "pll", "name": "PLL", "group": "Other" },
{ "id": "zbll", "name": "ZBLL", "group": "Other" },
{ "id": "lse", "name": "Last Six Edges", "group": "Other" },
{ "id": "ru", "name": "RU 2gen", "group": "Other" }
]
64 changes: 64 additions & 0 deletions packages/scrambles/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const events = require('./events.json');
const { generateCubingScramble } = require('./providers/cubing');
const { generateScrambowScramble } = require('./providers/scrambow');

const cubingEventIds = new Set([
'222',
'333',
'333bf',
'333oh',
'333ft',
'444',
'444bf',
'555',
'555bf',
'666',
'777',
'minx',
'pyram',
'clock',
'skewb',
'sq1',
'fto',
]);

const scrambowTypesByEventId = {
333: '333',
222: '222',
444: '444',
555: '555',
666: '666',
777: '777',
'333bf': '333',
'333oh': '333',
'333ft': '333',
minx: 'minx',
pyram: 'pyram',
clock: 'clock',
'clock-optimal': 'clock-optimal',
skewb: 'skewb',
sq1: 'sq1',
'444bf': '444',
'555bf': '555',
fto: 'fto',
pll: 'pll',
zbll: 'zbll',
lse: 'lse',
ru: 'ru',
};

const knownEventIds = new Set(events.map(({ id }) => id));

async function generateScramble(eventId) {
if (!knownEventIds.has(eventId)) {
throw new Error(`Unsupported scramble event: ${eventId}`);
}

if (cubingEventIds.has(eventId)) {
return generateCubingScramble(eventId);
}

return generateScrambowScramble(scrambowTypesByEventId[eventId]);
}

module.exports = { generateScramble };
101 changes: 101 additions & 0 deletions packages/scrambles/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/** @jest-environment node */

jest.mock('./providers/cubing', () => ({
generateCubingScramble: jest.fn(),
}));
jest.mock('./providers/scrambow', () => ({
generateScrambowScramble: jest.fn(),
}));

const events = require('./events.json');
const { generateScramble } = require('./index');
const { generateCubingScramble } = require('./providers/cubing');
const { generateScrambowScramble } = require('./providers/scrambow');

const cubingEventIds = [
'222',
'333',
'333bf',
'333oh',
'333ft',
'444',
'444bf',
'555',
'555bf',
'666',
'777',
'minx',
'pyram',
'clock',
'skewb',
'sq1',
'fto',
];

describe('generateScramble', () => {
beforeEach(() => {
jest.clearAllMocks();
generateCubingScramble.mockResolvedValue('cubing scramble');
generateScrambowScramble.mockReturnValue('scrambow scramble');
});

it.each(cubingEventIds)('uses cubing.js for %s', async (eventId) => {
await expect(generateScramble(eventId)).resolves.toBe('cubing scramble');

expect(generateCubingScramble).toHaveBeenCalledWith(eventId);
expect(generateScrambowScramble).not.toHaveBeenCalled();
});

it('uses Scrambow for the custom practice events', async () => {
await expect(generateScramble('clock-optimal')).resolves.toBe('scrambow scramble');
await expect(generateScramble('pll')).resolves.toBe('scrambow scramble');
await expect(generateScramble('zbll')).resolves.toBe('scrambow scramble');
await expect(generateScramble('lse')).resolves.toBe('scrambow scramble');
await expect(generateScramble('ru')).resolves.toBe('scrambow scramble');

expect(generateScrambowScramble).toHaveBeenNthCalledWith(1, 'clock-optimal');
expect(generateScrambowScramble).toHaveBeenNthCalledWith(2, 'pll');
expect(generateScrambowScramble).toHaveBeenNthCalledWith(3, 'zbll');
expect(generateScrambowScramble).toHaveBeenNthCalledWith(4, 'lse');
expect(generateScrambowScramble).toHaveBeenNthCalledWith(5, 'ru');
});

it('rejects events that are not in the shared catalog', async () => {
await expect(generateScramble('unknown')).rejects.toThrow('Unsupported scramble event: unknown');

expect(generateCubingScramble).not.toHaveBeenCalled();
expect(generateScrambowScramble).not.toHaveBeenCalled();
});

it('surfaces cubing.js failures without using Scrambow', async () => {
const error = new Error('cubing worker failed');
generateCubingScramble.mockRejectedValue(error);

await expect(generateScramble('333')).rejects.toBe(error);
expect(generateScrambowScramble).not.toHaveBeenCalled();
});
});

describe('events', () => {
it('contains every event supported by the generator', () => {
expect(events.map(({ id }) => id)).toEqual(expect.arrayContaining([
...cubingEventIds,
'clock-optimal',
'pll',
'zbll',
'lse',
'ru',
]));
});

it('does not expose provider implementation details', () => {
events.forEach((event) => {
expect(event).toEqual(expect.objectContaining({
id: expect.any(String),
name: expect.any(String),
group: expect.any(String),
}));
expect(event.scrambler).toBeUndefined();
});
});
});
30 changes: 30 additions & 0 deletions packages/scrambles/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "letscube-scrambles",
"version": "0.1.0",
"private": true,
"main": "index.js",
"exports": {
".": "./index.js",
"./events": "./events.json"
},
"files": [
"index.js",
"events.json",
"providers"
],
"dependencies": {
"cubing": "0.63.3",
"scrambow": "^1.8.1"
},
"scripts": {
"lint": "eslint ./",
"test": "jest --passWithNoTests",
"test:ci": "yarn test"
},
"devDependencies": {
"eslint": "6.8.0",
"eslint-config-airbnb-base": "14.1.0",
"eslint-plugin-import": "2.20.2",
"jest": "^26.6.0"
}
}
Loading
Loading