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
|
||||
id="cardsInputForm"
|
||||
x-data="cardsInputForm"
|
||||
@submit.prevent="submit"
|
||||
@submit.prevent="$dispatch('solve-game')"
|
||||
class="needs-validation"
|
||||
:class="{'was-validated': isFormValid && inputValue.length > 2 }"
|
||||
novalidate
|
||||
>
|
||||
<div class="mb-3">
|
||||
<label id="cardsInputLabel" for="cardsInput" class="form-label"
|
||||
>Enter Your Cards</label
|
||||
>
|
||||
<h3 id="cardsInputLabel" for="cardsInput" class="form-label">
|
||||
Enter Your Cards
|
||||
</h3>
|
||||
<div class="input-group">
|
||||
<input
|
||||
id="cardsInput"
|
||||
aria-describedby="cardsInputLabel"
|
||||
type="text"
|
||||
x-model="inputValue"
|
||||
class="form-control"
|
||||
:class="{ 'is-valid': isFormValid && inputValue.length > 2, 'is-invalid': !isFormValid && inputValue.length > 2 }"
|
||||
maxlength="206"
|
||||
@keyup.debounce="validateCardsInput()"
|
||||
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">
|
||||
<div
|
||||
class="valid-feedback"
|
||||
|
@ -88,7 +99,11 @@
|
|||
</form>
|
||||
|
||||
<!-- Friendly display of game cards -->
|
||||
<div id="playingCardsPreview" x-data="playingCardsPreview">
|
||||
<div
|
||||
id="playingCardsPreview"
|
||||
x-data="playingCardsPreview"
|
||||
class="mb-4"
|
||||
>
|
||||
<div
|
||||
id="gamePyramid"
|
||||
class="game-pyramid-container p-2 p-md-3 p-lg-4"
|
||||
|
@ -139,6 +154,32 @@
|
|||
</template>
|
||||
</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>
|
||||
|
|
104
main.js
104
main.js
|
@ -1,6 +1,8 @@
|
|||
import "./style.scss";
|
||||
//import 'bootstrap';
|
||||
import Alpine from "alpinejs";
|
||||
import cardSvgs from "./cardSvgs";
|
||||
import SolverWorker from "./solverWorker?worker";
|
||||
|
||||
// Some helpful constants
|
||||
const suits = {
|
||||
|
@ -61,9 +63,10 @@ Object.keys(cardSvgs).forEach((ckey) => {
|
|||
Alpine.store("global", {
|
||||
deck,
|
||||
cardsToSolve: Array(deck.length).fill(0),
|
||||
solvingInProgress: false,
|
||||
});
|
||||
|
||||
// card preview component data
|
||||
// card preview component logic
|
||||
Alpine.data("playingCardsPreview", () => ({
|
||||
cardSvgs,
|
||||
cardsBySlice(start, length) {
|
||||
|
@ -74,7 +77,7 @@ Alpine.data("playingCardsPreview", () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
// input component data
|
||||
// input component logic
|
||||
Alpine.data("cardsInputForm", () => ({
|
||||
// "constants" for validation etc
|
||||
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;
|
||||
|
||||
Alpine.start();
|
||||
|
|
56
package-lock.json
generated
56
package-lock.json
generated
|
@ -37,9 +37,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.1.tgz",
|
||||
"integrity": "sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
|
||||
"integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.4",
|
||||
|
@ -1031,12 +1031,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.0.tgz",
|
||||
"integrity": "sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==",
|
||||
"version": "8.23.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz",
|
||||
"integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint/eslintrc": "^1.3.1",
|
||||
"@eslint/eslintrc": "^1.3.2",
|
||||
"@humanwhocodes/config-array": "^0.10.4",
|
||||
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
|
@ -1055,7 +1055,6 @@
|
|||
"fast-deep-equal": "^3.1.3",
|
||||
"file-entry-cache": "^6.0.1",
|
||||
"find-up": "^5.0.0",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"glob-parent": "^6.0.1",
|
||||
"globals": "^13.15.0",
|
||||
"globby": "^11.1.0",
|
||||
|
@ -1064,6 +1063,7 @@
|
|||
"import-fresh": "^3.0.0",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"js-sdsl": "^4.1.4",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"levn": "^0.4.1",
|
||||
|
@ -1386,12 +1386,6 @@
|
|||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"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": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
|
@ -1691,6 +1685,12 @@
|
|||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"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": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
|
@ -2589,9 +2589,9 @@
|
|||
"optional": true
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.1.tgz",
|
||||
"integrity": "sha512-OhSY22oQQdw3zgPOOwdoj01l/Dzl1Z+xyUP33tkSN+aqyEhymJCcPHyXt+ylW8FSe0TfRC2VG+ROQOapD0aZSQ==",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz",
|
||||
"integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.12.4",
|
||||
|
@ -3226,12 +3226,12 @@
|
|||
"dev": true
|
||||
},
|
||||
"eslint": {
|
||||
"version": "8.23.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.0.tgz",
|
||||
"integrity": "sha512-pBG/XOn0MsJcKcTRLr27S5HpzQo4kLr+HjLQIyK4EiCsijDl/TB+h5uEuJU6bQ8Edvwz1XWOjpaP2qgnXGpTcA==",
|
||||
"version": "8.23.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.23.1.tgz",
|
||||
"integrity": "sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@eslint/eslintrc": "^1.3.1",
|
||||
"@eslint/eslintrc": "^1.3.2",
|
||||
"@humanwhocodes/config-array": "^0.10.4",
|
||||
"@humanwhocodes/gitignore-to-minimatch": "^1.0.2",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
|
@ -3250,7 +3250,6 @@
|
|||
"fast-deep-equal": "^3.1.3",
|
||||
"file-entry-cache": "^6.0.1",
|
||||
"find-up": "^5.0.0",
|
||||
"functional-red-black-tree": "^1.0.1",
|
||||
"glob-parent": "^6.0.1",
|
||||
"globals": "^13.15.0",
|
||||
"globby": "^11.1.0",
|
||||
|
@ -3259,6 +3258,7 @@
|
|||
"import-fresh": "^3.0.0",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"js-sdsl": "^4.1.4",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"levn": "^0.4.1",
|
||||
|
@ -3501,12 +3501,6 @@
|
|||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"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": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
|
@ -3726,6 +3720,12 @@
|
|||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"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": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"solver",
|
||||
"cards"
|
||||
],
|
||||
"main": "solver.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"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 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.
|
||||
* @returns {*[]|([*, *, *]|[*, *, *]|[*, *, *])}
|
||||
* @returns {Promise<{*[]|([*, *, *]|[*, *, *]|[*, *, *])}>}
|
||||
*/
|
||||
function solve(
|
||||
async function solve(
|
||||
pyramidArray,
|
||||
stockArray,
|
||||
worker = null,
|
||||
stockIndex = 0,
|
||||
moveArray = [],
|
||||
bestMoveArray = []
|
||||
|
@ -121,6 +122,12 @@ function solve(
|
|||
// If yes, replace the known best move array
|
||||
newBestMoveArray = getBestMoveArray(newBestMoveArray, newMoveArray);
|
||||
newBestMoveArray = JSON.parse(JSON.stringify(newBestMoveArray));
|
||||
if (worker) {
|
||||
worker.postMessage({
|
||||
msg: "solve-progress",
|
||||
moveCount: newBestMoveArray.length,
|
||||
});
|
||||
}
|
||||
|
||||
// We cleared the pyramid
|
||||
if (pyramid.isCleared) {
|
||||
|
@ -150,9 +157,10 @@ function solve(
|
|||
let newPyramidArray = JSON.parse(JSON.stringify(pyramidArray));
|
||||
newPyramidArray[freeCardsIndices[i]] = 0;
|
||||
|
||||
let result = solve(
|
||||
let result = await solve(
|
||||
newPyramidArray,
|
||||
newStock,
|
||||
worker,
|
||||
stockIndex,
|
||||
newMoveArray,
|
||||
newBestMoveArray
|
||||
|
@ -167,9 +175,10 @@ function solve(
|
|||
// Flip over a new card
|
||||
newMoveArray = JSON.parse(JSON.stringify(moveArray));
|
||||
newMoveArray.push(MoveString.flipStock());
|
||||
let result = solve(
|
||||
let result = await solve(
|
||||
pyramidArray,
|
||||
stockArray,
|
||||
worker,
|
||||
++stockIndex,
|
||||
newMoveArray,
|
||||
newBestMoveArray
|
||||
|
@ -184,4 +193,4 @@ function solve(
|
|||
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");
|
||||
const solver = require("./solver");
|
||||
const Card = solver.Card;
|
||||
import { Card, solve } from "./solver.js";
|
||||
import assert from "assert";
|
||||
|
||||
/*
|
||||
* Tests for classes
|
||||
|
@ -56,17 +55,17 @@ const partial_games = [
|
|||
];
|
||||
|
||||
it("solve should solve known games", () => {
|
||||
solvable_games.forEach((i) => {
|
||||
solvable_games.forEach(async (i) => {
|
||||
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);
|
||||
});
|
||||
}).timeout(200000);
|
||||
|
||||
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 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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue