web page fully working as desired in dev
This commit is contained in:
parent
242243b246
commit
1ae0c4237c
10 changed files with 263 additions and 63 deletions
13
.eslintrc.js
13
.eslintrc.js
|
@ -1,13 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: "module",
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
es2022: true,
|
|
||||||
mocha: true,
|
|
||||||
node: true,
|
|
||||||
},
|
|
||||||
plugins: ["html"],
|
|
||||||
extends: ["eslint:recommended", "prettier"],
|
|
||||||
};
|
|
13
.eslintrc.json
Normal file
13
.eslintrc.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": ["eslint:recommended", "prettier"],
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": ["html"],
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"es2022": true,
|
||||||
|
"mocha": true,
|
||||||
|
"node": true
|
||||||
|
}
|
||||||
|
}
|
55
index.html
55
index.html
|
@ -50,26 +50,37 @@
|
||||||
<form
|
<form
|
||||||
id="cardsInputForm"
|
id="cardsInputForm"
|
||||||
x-data="cardsInputForm"
|
x-data="cardsInputForm"
|
||||||
@submit.prevent="submit"
|
@submit.prevent="$dispatch('solve-game')"
|
||||||
class="needs-validation"
|
class="needs-validation"
|
||||||
:class="{'was-validated': isFormValid && inputValue.length > 2 }"
|
:class="{'was-validated': isFormValid && inputValue.length > 2 }"
|
||||||
novalidate
|
novalidate
|
||||||
>
|
>
|
||||||
<div class="mb-3">
|
<h3 id="cardsInputLabel" for="cardsInput" class="form-label">
|
||||||
<label id="cardsInputLabel" for="cardsInput" class="form-label"
|
Enter Your Cards
|
||||||
>Enter Your Cards</label
|
</h3>
|
||||||
>
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
id="cardsInput"
|
id="cardsInput"
|
||||||
aria-describedby="cardsInputLabel"
|
aria-describedby="cardsInputLabel"
|
||||||
|
type="text"
|
||||||
x-model="inputValue"
|
x-model="inputValue"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:class="{ 'is-valid': isFormValid && inputValue.length > 2, 'is-invalid': !isFormValid && inputValue.length > 2 }"
|
:class="{ 'is-valid': isFormValid && inputValue.length > 2, 'is-invalid': !isFormValid && inputValue.length > 2 }"
|
||||||
maxlength="206"
|
maxlength="206"
|
||||||
@keyup.debounce="validateCardsInput()"
|
@keyup.debounce="validateCardsInput()"
|
||||||
placeholder="e.g., 2S TC 4S QD KH 5S 9S ..."
|
placeholder="e.g., 2S TC 4S QD KH 5S 9S ..."
|
||||||
|
:disabled="$store.global.solvingInProgress"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
id="cardsInputButton"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-outline-primary"
|
||||||
|
:disabled="!isFormValid || $store.global.solvingInProgress"
|
||||||
|
>
|
||||||
|
Solve
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
<template x-for="msg in validMessages">
|
<template x-for="msg in validMessages">
|
||||||
<div
|
<div
|
||||||
class="valid-feedback"
|
class="valid-feedback"
|
||||||
|
@ -88,7 +99,11 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Friendly display of game cards -->
|
<!-- Friendly display of game cards -->
|
||||||
<div id="playingCardsPreview" x-data="playingCardsPreview">
|
<div
|
||||||
|
id="playingCardsPreview"
|
||||||
|
x-data="playingCardsPreview"
|
||||||
|
class="mb-4"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
id="gamePyramid"
|
id="gamePyramid"
|
||||||
class="game-pyramid-container p-2 p-md-3 p-lg-4"
|
class="game-pyramid-container p-2 p-md-3 p-lg-4"
|
||||||
|
@ -139,6 +154,32 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Display of solvingprogress or solution -->
|
||||||
|
<div id="gameSolving" x-data="gameSolving" x-init="onInit()">
|
||||||
|
<div class="d-flex justify-content-start">
|
||||||
|
<div
|
||||||
|
class="spinner-border me-2"
|
||||||
|
role="status"
|
||||||
|
x-show="$store.global.solvingInProgress"
|
||||||
|
></div>
|
||||||
|
<h3 @solve-game.window="startSolver()" x-text="headerText"></h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul x-show="$store.global.solvingInProgress">
|
||||||
|
<template x-for="msg in statusMessages">
|
||||||
|
<li x-text="msg"></li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- <p x-show="" x-text="statusMessage"></p> -->
|
||||||
|
|
||||||
|
<ol x-show="solutionMoves.length > 0">
|
||||||
|
<template x-for="move in solutionMoves">
|
||||||
|
<li x-text="move"></li>
|
||||||
|
</template>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
104
main.js
104
main.js
|
@ -1,6 +1,8 @@
|
||||||
import "./style.scss";
|
import "./style.scss";
|
||||||
|
//import 'bootstrap';
|
||||||
import Alpine from "alpinejs";
|
import Alpine from "alpinejs";
|
||||||
import cardSvgs from "./cardSvgs";
|
import cardSvgs from "./cardSvgs";
|
||||||
|
import SolverWorker from "./solverWorker?worker";
|
||||||
|
|
||||||
// Some helpful constants
|
// Some helpful constants
|
||||||
const suits = {
|
const suits = {
|
||||||
|
@ -61,9 +63,10 @@ Object.keys(cardSvgs).forEach((ckey) => {
|
||||||
Alpine.store("global", {
|
Alpine.store("global", {
|
||||||
deck,
|
deck,
|
||||||
cardsToSolve: Array(deck.length).fill(0),
|
cardsToSolve: Array(deck.length).fill(0),
|
||||||
|
solvingInProgress: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// card preview component data
|
// card preview component logic
|
||||||
Alpine.data("playingCardsPreview", () => ({
|
Alpine.data("playingCardsPreview", () => ({
|
||||||
cardSvgs,
|
cardSvgs,
|
||||||
cardsBySlice(start, length) {
|
cardsBySlice(start, length) {
|
||||||
|
@ -74,7 +77,7 @@ Alpine.data("playingCardsPreview", () => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// input component data
|
// input component logic
|
||||||
Alpine.data("cardsInputForm", () => ({
|
Alpine.data("cardsInputForm", () => ({
|
||||||
// "constants" for validation etc
|
// "constants" for validation etc
|
||||||
nonAlphaNumRegEx: /[\W_]+/g,
|
nonAlphaNumRegEx: /[\W_]+/g,
|
||||||
|
@ -184,6 +187,103 @@ Alpine.data("cardsInputForm", () => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// long-running solve messages
|
||||||
|
const encouragements = [
|
||||||
|
"Hang in there!",
|
||||||
|
"Let go like a bird flies, not fighting the wind but gliding on it",
|
||||||
|
"Stay patient and trust the journey.",
|
||||||
|
"Everything is coming together…",
|
||||||
|
"Solitaire is a journey, not a destination.",
|
||||||
|
"For things to reveal themselves to us, we need to be ready to abandon our views about them.",
|
||||||
|
"Patience is bitter, but its fruit is sweet.",
|
||||||
|
"Strive for progress, not perfection.",
|
||||||
|
"I wish only to be alive and to experience this living to the fullest.",
|
||||||
|
"This too shall pass.",
|
||||||
|
"Patience is the companion of wisdom.",
|
||||||
|
"The mountains are calling and I must go.",
|
||||||
|
"Give time time.",
|
||||||
|
"If you find a path with no obstacles, it probably doesn't lead anywhere",
|
||||||
|
"A smooth sea never made a good sailor.",
|
||||||
|
"Stick with the winners.",
|
||||||
|
"I immerse myself in the experience of living without having to evaluate or understand it.",
|
||||||
|
"Why fit in when you were born to stand out?",
|
||||||
|
"Don't let yesterday take up too much of today.",
|
||||||
|
"The least I owe the mountains is a body.",
|
||||||
|
"Getting so close!",
|
||||||
|
"Many people think excitement is happiness. But when you are excited you are not peaceful.",
|
||||||
|
"Misery is optional.",
|
||||||
|
"Mistakes are proof that you're trying.",
|
||||||
|
"Life would be so much easier if we only had the source code.",
|
||||||
|
"A computer once beat me at chess, but it was no match for me at kickboxing.",
|
||||||
|
"Patience is not simply the ability to wait, it's how we behave while we're waiting.",
|
||||||
|
"We must let go of the life we have planned so as to accept the one that is waiting for us.",
|
||||||
|
"Somewhere, something incredible is waiting to be known.",
|
||||||
|
"If you spend your whole life waiting for the storm, you'll never enjoy the sunshine.",
|
||||||
|
];
|
||||||
|
|
||||||
|
// game solving component logic
|
||||||
|
Alpine.data("gameSolving", () => ({
|
||||||
|
encouragements,
|
||||||
|
solverWorker: null,
|
||||||
|
headerText: "",
|
||||||
|
moveCount: 23,
|
||||||
|
statusMessages: [],
|
||||||
|
solutionMoves: [],
|
||||||
|
nodesTried: 0,
|
||||||
|
nodesTriedFloor: 0,
|
||||||
|
reset() {
|
||||||
|
this.$store.global.solvingInProgress = false;
|
||||||
|
this.moveCount = 0;
|
||||||
|
this.statusMessages = [];
|
||||||
|
this.nodesTried = 0;
|
||||||
|
this.nodesTriedFloor = 0;
|
||||||
|
},
|
||||||
|
onInit() {
|
||||||
|
this.solverWorker = new SolverWorker();
|
||||||
|
this.solverWorker.addEventListener("message", async (e) => {
|
||||||
|
if (e.data.msg === "solve-progress") {
|
||||||
|
this.nodesTried++;
|
||||||
|
this.moveCount = e.data.moveCount;
|
||||||
|
this.statusMessages[0] = `Most moves found so far: ${this.moveCount}`;
|
||||||
|
|
||||||
|
let newFloor = Math.floor(this.nodesTried / 10000) * 10000;
|
||||||
|
if (newFloor > this.nodesTriedFloor) {
|
||||||
|
this.nodesTriedFloor = newFloor;
|
||||||
|
if (this.nodesTriedFloor > 50000) {
|
||||||
|
this.statusMessages[1] = `Over ${this.nodesTriedFloor.toLocaleString(
|
||||||
|
"en"
|
||||||
|
)} possibilities tried. Still working…`;
|
||||||
|
}
|
||||||
|
if (this.nodesTriedFloor % 250000 === 0) {
|
||||||
|
let randInRange = Math.floor(
|
||||||
|
Math.random() * this.encouragements.length
|
||||||
|
);
|
||||||
|
this.statusMessages.push(this.encouragements[randInRange]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (e.data.msg === "solve-result") {
|
||||||
|
if (e.data.result[0]) {
|
||||||
|
this.headerText = "Solution found:";
|
||||||
|
this.solutionMoves = e.data.result[1];
|
||||||
|
this.reset();
|
||||||
|
} else {
|
||||||
|
this.headerText = "Could not solve. Best moves found:";
|
||||||
|
this.solutionMoves = e.data.result[2];
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async startSolver() {
|
||||||
|
this.headerText = "Solving…";
|
||||||
|
this.solutionMoves = [];
|
||||||
|
this.$store.global.solvingInProgress = true;
|
||||||
|
await this.$nextTick();
|
||||||
|
let game = JSON.parse(JSON.stringify(this.$store.global.cardsToSolve));
|
||||||
|
this.solverWorker.postMessage({ msg: "try-to-solve", game: game });
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
window.Alpine = Alpine;
|
window.Alpine = Alpine;
|
||||||
|
|
||||||
Alpine.start();
|
Alpine.start();
|
||||||
|
|
56
package-lock.json
generated
56
package-lock.json
generated
|
@ -37,9 +37,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint/eslintrc": {
|
"node_modules/@eslint/eslintrc": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
|
||||||
"integrity": "sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==",
|
"integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
|
@ -1031,12 +1031,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eslint": {
|
"node_modules/eslint": {
|
||||||
"version": "8.23.0",
|
"version": "8.23.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz",
|
||||||
"integrity": "sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==",
|
"integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint/eslintrc": "^1.3.1",
|
"@eslint/eslintrc": "^1.3.2",
|
||||||
"@humanwhocodes/config-array": "^0.10.4",
|
"@humanwhocodes/config-array": "^0.10.4",
|
||||||
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
|
@ -1055,7 +1055,6 @@
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"file-entry-cache": "^6.0.1",
|
"file-entry-cache": "^6.0.1",
|
||||||
"find-up": "^5.0.0",
|
"find-up": "^5.0.0",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
|
||||||
"glob-parent": "^6.0.1",
|
"glob-parent": "^6.0.1",
|
||||||
"globals": "^13.15.0",
|
"globals": "^13.15.0",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
|
@ -1064,6 +1063,7 @@
|
||||||
"import-fresh": "^3.0.0",
|
"import-fresh": "^3.0.0",
|
||||||
"imurmurhash": "^0.1.4",
|
"imurmurhash": "^0.1.4",
|
||||||
"is-glob": "^4.0.0",
|
"is-glob": "^4.0.0",
|
||||||
|
"js-sdsl": "^4.1.4",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"levn": "^0.4.1",
|
"levn": "^0.4.1",
|
||||||
|
@ -1386,12 +1386,6 @@
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/functional-red-black-tree": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/get-caller-file": {
|
"node_modules/get-caller-file": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
@ -1691,6 +1685,12 @@
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/js-sdsl": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/js-yaml": {
|
"node_modules/js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
|
@ -2589,9 +2589,9 @@
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"@eslint/eslintrc": {
|
"@eslint/eslintrc": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
|
||||||
"integrity": "sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==",
|
"integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "^6.12.4",
|
"ajv": "^6.12.4",
|
||||||
|
@ -3226,12 +3226,12 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"eslint": {
|
"eslint": {
|
||||||
"version": "8.23.0",
|
"version": "8.23.1",
|
||||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz",
|
||||||
"integrity": "sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==",
|
"integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@eslint/eslintrc": "^1.3.1",
|
"@eslint/eslintrc": "^1.3.2",
|
||||||
"@humanwhocodes/config-array": "^0.10.4",
|
"@humanwhocodes/config-array": "^0.10.4",
|
||||||
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
||||||
"@humanwhocodes/module-importer": "^1.0.1",
|
"@humanwhocodes/module-importer": "^1.0.1",
|
||||||
|
@ -3250,7 +3250,6 @@
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"file-entry-cache": "^6.0.1",
|
"file-entry-cache": "^6.0.1",
|
||||||
"find-up": "^5.0.0",
|
"find-up": "^5.0.0",
|
||||||
"functional-red-black-tree": "^1.0.1",
|
|
||||||
"glob-parent": "^6.0.1",
|
"glob-parent": "^6.0.1",
|
||||||
"globals": "^13.15.0",
|
"globals": "^13.15.0",
|
||||||
"globby": "^11.1.0",
|
"globby": "^11.1.0",
|
||||||
|
@ -3259,6 +3258,7 @@
|
||||||
"import-fresh": "^3.0.0",
|
"import-fresh": "^3.0.0",
|
||||||
"imurmurhash": "^0.1.4",
|
"imurmurhash": "^0.1.4",
|
||||||
"is-glob": "^4.0.0",
|
"is-glob": "^4.0.0",
|
||||||
|
"js-sdsl": "^4.1.4",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||||
"levn": "^0.4.1",
|
"levn": "^0.4.1",
|
||||||
|
@ -3501,12 +3501,6 @@
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"functional-red-black-tree": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"get-caller-file": {
|
"get-caller-file": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
@ -3726,6 +3720,12 @@
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"js-sdsl": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"js-yaml": {
|
"js-yaml": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"solver",
|
"solver",
|
||||||
"cards"
|
"cards"
|
||||||
],
|
],
|
||||||
"main": "solver.js",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
|
39
run.js
Normal file
39
run.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
const solver = require("./solver");
|
||||||
|
|
||||||
|
// // Easy MS Tripeaks level
|
||||||
|
// let userCardsInput = " js as ts ad tc qd 9s 4s 2h 9h 8s jh 6c 3d ks 5s 5c 6h 9c 2c ac 8c 6d 5d th 8d kc kd 9d 4c 5h 8h qh 6s "
|
||||||
|
// userCardsInput = " 2d 7c 7d 3s kh qs jc 2s 7s " + userCardsInput
|
||||||
|
// userCardsInput = " qc 3h 3c 7h td 4h " + userCardsInput
|
||||||
|
// userCardsInput = " jd 4d ah " + userCardsInput
|
||||||
|
// Unsolvable board
|
||||||
|
let userCardsInput =
|
||||||
|
"2D 6D AD 9S 4C 7C 7S 7D 9C 2S AC 8D 6S 6H 3C 5H QS JS 4S JH 5C AS 3H 3S AH TD 4D 5S TH 7H KS QH 6C KD 8S 2C TC JC 5D 3D 2H TS 4H JD KC KH 8H QC 8C QD 9D 9H";
|
||||||
|
|
||||||
|
// continue!
|
||||||
|
userCardsInput = userCardsInput.toUpperCase();
|
||||||
|
let userCards = userCardsInput.split(" ").filter((s) => s);
|
||||||
|
userCards = Array(52 - userCards.length)
|
||||||
|
.fill("0")
|
||||||
|
.concat(userCards)
|
||||||
|
.map((c) => (c === "0" ? 0 : c));
|
||||||
|
|
||||||
|
let start_time = process.hrtime.bigint();
|
||||||
|
let result = solver.solve(
|
||||||
|
userCards.slice(0, 28),
|
||||||
|
userCards.slice(28, 52),
|
||||||
|
0,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
let end_time = process.hrtime.bigint();
|
||||||
|
|
||||||
|
let resultStr = JSON.stringify(result, null, 2);
|
||||||
|
process.stdout.write(resultStr + "\n");
|
||||||
|
process.stdout.write(`moves array length: ${result[1].length}\n`);
|
||||||
|
process.stdout.write(`best moves array length: ${result[2].length}\n`);
|
||||||
|
|
||||||
|
const MS_PER_NS = 1e-6;
|
||||||
|
const NS_PER_SEC = 1e9;
|
||||||
|
let elapsedMs = Number(end_time - start_time) * MS_PER_NS;
|
||||||
|
let elapsedS = Number(end_time - start_time) / NS_PER_SEC;
|
||||||
|
process.stdout.write(`solving took: ${elapsedMs} milliseconds\n`);
|
||||||
|
process.stdout.write(`solving took: ${elapsedS} seconds\n`);
|
19
solver.js
19
solver.js
|
@ -103,11 +103,12 @@ function getBestMoveArray(bestMoveArray, newMoveArray) {
|
||||||
* @param stockArray The cards in the stock.
|
* @param stockArray The cards in the stock.
|
||||||
* @param stockIndex The index of the top stock card.
|
* @param stockIndex The index of the top stock card.
|
||||||
* @param moveArray The list of moves that have been made to get a deck in this configuration.
|
* @param moveArray The list of moves that have been made to get a deck in this configuration.
|
||||||
* @returns {*[]|([*, *, *]|[*, *, *]|[*, *, *])}
|
* @returns {Promise<{*[]|([*, *, *]|[*, *, *]|[*, *, *])}>}
|
||||||
*/
|
*/
|
||||||
function solve(
|
async function solve(
|
||||||
pyramidArray,
|
pyramidArray,
|
||||||
stockArray,
|
stockArray,
|
||||||
|
worker = null,
|
||||||
stockIndex = 0,
|
stockIndex = 0,
|
||||||
moveArray = [],
|
moveArray = [],
|
||||||
bestMoveArray = []
|
bestMoveArray = []
|
||||||
|
@ -121,6 +122,12 @@ function solve(
|
||||||
// If yes, replace the known best move array
|
// If yes, replace the known best move array
|
||||||
newBestMoveArray = getBestMoveArray(newBestMoveArray, newMoveArray);
|
newBestMoveArray = getBestMoveArray(newBestMoveArray, newMoveArray);
|
||||||
newBestMoveArray = JSON.parse(JSON.stringify(newBestMoveArray));
|
newBestMoveArray = JSON.parse(JSON.stringify(newBestMoveArray));
|
||||||
|
if (worker) {
|
||||||
|
worker.postMessage({
|
||||||
|
msg: "solve-progress",
|
||||||
|
moveCount: newBestMoveArray.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// We cleared the pyramid
|
// We cleared the pyramid
|
||||||
if (pyramid.isCleared) {
|
if (pyramid.isCleared) {
|
||||||
|
@ -150,9 +157,10 @@ function solve(
|
||||||
let newPyramidArray = JSON.parse(JSON.stringify(pyramidArray));
|
let newPyramidArray = JSON.parse(JSON.stringify(pyramidArray));
|
||||||
newPyramidArray[freeCardsIndices[i]] = 0;
|
newPyramidArray[freeCardsIndices[i]] = 0;
|
||||||
|
|
||||||
let result = solve(
|
let result = await solve(
|
||||||
newPyramidArray,
|
newPyramidArray,
|
||||||
newStock,
|
newStock,
|
||||||
|
worker,
|
||||||
stockIndex,
|
stockIndex,
|
||||||
newMoveArray,
|
newMoveArray,
|
||||||
newBestMoveArray
|
newBestMoveArray
|
||||||
|
@ -167,9 +175,10 @@ function solve(
|
||||||
// Flip over a new card
|
// Flip over a new card
|
||||||
newMoveArray = JSON.parse(JSON.stringify(moveArray));
|
newMoveArray = JSON.parse(JSON.stringify(moveArray));
|
||||||
newMoveArray.push(MoveString.flipStock());
|
newMoveArray.push(MoveString.flipStock());
|
||||||
let result = solve(
|
let result = await solve(
|
||||||
pyramidArray,
|
pyramidArray,
|
||||||
stockArray,
|
stockArray,
|
||||||
|
worker,
|
||||||
++stockIndex,
|
++stockIndex,
|
||||||
newMoveArray,
|
newMoveArray,
|
||||||
newBestMoveArray
|
newBestMoveArray
|
||||||
|
@ -184,4 +193,4 @@ function solve(
|
||||||
return [GameStates.lost, moveArray, newBestMoveArray];
|
return [GameStates.lost, moveArray, newBestMoveArray];
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { Card, solve };
|
export { Card, solve };
|
||||||
|
|
12
solverWorker.js
Normal file
12
solverWorker.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { solve } from "./solver";
|
||||||
|
|
||||||
|
const runSolve = async (game) => {
|
||||||
|
let result = await solve(game.slice(0, 28), game.slice(28, 52), self);
|
||||||
|
postMessage({ msg: "solve-result", result: result });
|
||||||
|
};
|
||||||
|
|
||||||
|
addEventListener("message", async (e) => {
|
||||||
|
if (e.data.msg === "try-to-solve") {
|
||||||
|
runSolve(e.data.game);
|
||||||
|
}
|
||||||
|
});
|
13
test.js
13
test.js
|
@ -1,6 +1,5 @@
|
||||||
const assert = require("assert");
|
import { Card, solve } from "./solver.js";
|
||||||
const solver = require("./solver");
|
import assert from "assert";
|
||||||
const Card = solver.Card;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tests for classes
|
* Tests for classes
|
||||||
|
@ -56,17 +55,17 @@ const partial_games = [
|
||||||
];
|
];
|
||||||
|
|
||||||
it("solve should solve known games", () => {
|
it("solve should solve known games", () => {
|
||||||
solvable_games.forEach((i) => {
|
solvable_games.forEach(async (i) => {
|
||||||
const array = i.split(" ");
|
const array = i.split(" ");
|
||||||
const result = solver.solve(array.slice(0, 28), array.slice(28, 52), 0, []);
|
const result = await solve(array.slice(0, 28), array.slice(28, 52));
|
||||||
assert.equal(result[0], true);
|
assert.equal(result[0], true);
|
||||||
});
|
});
|
||||||
}).timeout(200000);
|
}).timeout(200000);
|
||||||
|
|
||||||
it("solve should solve partial games", () => {
|
it("solve should solve partial games", () => {
|
||||||
partial_games.forEach((i) => {
|
partial_games.forEach(async (i) => {
|
||||||
const array = i.split(" ").map((x) => (x === "0" ? 0 : x));
|
const array = i.split(" ").map((x) => (x === "0" ? 0 : x));
|
||||||
const result = solver.solve(array.slice(0, 28), array.slice(28, 52), 0, []);
|
const result = await solve(array.slice(0, 28), array.slice(28, 52));
|
||||||
assert.equal(result[0], true);
|
assert.equal(result[0], true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue