From 8412e39209b4581bb917282bf03efc5e15b31370 Mon Sep 17 00:00:00 2001
From: Courtney Pitcher <cr.pitcher@gmail.com>
Date: Sat, 3 Aug 2019 13:41:36 +0200
Subject: [PATCH] Added unit tests

---
 .gitignore   | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 package.json | 22 +++++++++++++
 solver.js    | 20 +++++++++---
 test.js      | 16 +++++++++
 4 files changed, 144 insertions(+), 5 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 package.json
 create mode 100644 test.js

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);
+    });
+});