diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1117462 --- /dev/null +++ b/.gitignore @@ -0,0 +1,91 @@ +# WebStorm +.idea/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ diff --git a/package.json b/package.json new file mode 100644 index 0000000..53e6a63 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "javascript-tri-peaks-solitaire-solver", + "version": "1.0.0", + "description": "A brute force solver for tri peaks solitaire written in javascript", + "main": "solver.js", + "scripts": { + "test": "mocha" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/IgniparousTempest/javascript-tri-peaks-solitaire-solver.git" + }, + "author": "Courtney Pitcher", + "license": "ISC", + "bugs": { + "url": "https://github.com/IgniparousTempest/javascript-tri-peaks-solitaire-solver/issues" + }, + "homepage": "https://github.com/IgniparousTempest/javascript-tri-peaks-solitaire-solver#readme", + "devDependencies": { + "mocha": "^6.2.0" + } +} diff --git a/solver.js b/solver.js index be1e941..29eba07 100644 --- a/solver.js +++ b/solver.js @@ -73,9 +73,17 @@ class MoveString { static flipStock() {return "Draw a new stock card.";} } -const GameStates = Object.freeze({"won": 1, "lost": 2, "inProgress": 3}); +const GameStates = Object.freeze({"won": true, "lost": false}); -function solve(pyramidArray, stockArray, stockIndex, remainingStocks, moveArray) { +/** + * Solves a Tri Peaks solitaire game. + * @param pyramidArray The cards in the pyramids, starting in the top-left peak. The cards are in left-to-right, then top-to-bottom order. + * @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 {*[]|([*, *]|[*, *]|[*, *])} + */ +function solve(pyramidArray, stockArray, stockIndex = 0, moveArray = []) { let newMoveArray = JSON.parse(JSON.stringify(moveArray)); let pyramid = new Pyramid(pyramidArray); @@ -109,7 +117,7 @@ function solve(pyramidArray, stockArray, stockIndex, remainingStocks, moveArray) let newPyramidArray = JSON.parse(JSON.stringify(pyramidArray)); newPyramidArray[freeCardsIndices[i]] = 0; - let result = solve(newPyramidArray, newStock, stockIndex, remainingStocks, newMoveArray); + let result = solve(newPyramidArray, newStock, stockIndex, newMoveArray); if (result[0] === GameStates.won) return result; } @@ -117,10 +125,12 @@ function solve(pyramidArray, stockArray, stockIndex, remainingStocks, moveArray) // Flip over a new card newMoveArray = JSON.parse(JSON.stringify(moveArray)); newMoveArray.push(MoveString.flipStock()); - let result = solve(pyramidArray, stockArray, stockIndex + 1, remainingStocks, newMoveArray); + let result = solve(pyramidArray, stockArray, stockIndex + 1, newMoveArray); if (result[0] === GameStates.won) return result; // This node was useless - return [GameStates.inProgress, moveArray]; + return [GameStates.lost, moveArray]; } + +exports.solve = solve; diff --git a/test.js b/test.js new file mode 100644 index 0000000..9463f2e --- /dev/null +++ b/test.js @@ -0,0 +1,16 @@ +const assert = require('assert'); +const solver = require('./solver'); + +const solvable_games = [ + "8S TS 4D 7S 5D 7C 2D JH AC 3S 2H 3H 9H KC QC TD 8D 9C 7H 9D JS QS 4H 5C 5S 4C 2C QD 8C KD 3D KS JD 2S 7D KH AH 5H 9S 4S QH 6S 6D 3C JC TC 8H 6C TH AS AD 6H", + "5D 9C 5S QS 8S 9D AS 5C 2S QD KC 9H 4H QC 2H 8D 4C 4D JC TS 6D 7H QH 3S 5H JH 6H 2D AC 7S 7C 3D KD 9S 3C TH 6C AH 8H TC 4S 8C AD 3H KS 6S JS 7D JD TD 2C KH" +]; + +it('should solve known games', () => { + assert.equal(true, true); + solvable_games.forEach(function (i) { + const array = i.split(' '); + const result = solver.solve(array.slice(0, 28), array.slice(28, 52), 0, []); + assert.equal(result[0], true); + }); +});