--- /dev/null
+const nChoice = 5,; //fixed for this game\r
+\r
+// Rewards matrix. Order: rock, lizard, Spock, scissors, paper\r
+const rewards = Array.from(Array(nChoice)).map( i => { //lines\r
+ return Array.from(Array(nChoice)).map( j => { //columns\r
+ // i against j: gain from i viewpoint\r
+ if (j == (i+1) % nChoice || j == (i+3) % nChoice)\r
+ return 1; //I win :)\r
+ else if ((i+1) % nChoice == j || j == (i+2) % nChoice)\r
+ return -1; //I lose :(\r
+ else\r
+ return 0;\r
+ });\r
+});\r
+\r
+const symbols = [ "Rock", "Lizard", "Spock", "Scissors", "Paper" ];\r
+\r
+new Vue({\r
+ el: "#rpsls",\r
+ data: {\r
+ humanMove: -1, //integer in 0...nChoice-1\r
+ nInput: 5, //default\r
+ humanHistory: [ ], //your nInput last moves, stored (oldest first)\r
+ gameState: 0, //total points of the computer\r
+ drawIsLost: false, //normal (or true: draw is considered loss)\r
+ rewards: [ ], //initialized at first human move with new nInput\r
+ weights: [ ], //same as above\r
+ },\r
+ methods: {\r
+ // Called on nInput change\r
+ reinitialize: function() {\r
+ // weights[i][j][k]: output i -- input j/choice k\r
+ this.weights = Array.from(Array(nChoice)).map( i => {\r
+ return Array.from(Array(this.nInput)).map( j => {\r
+ return Array.from(Array(nChoice)).map( k => {\r
+ return 0;\r
+ });\r
+ })\r
+ });\r
+ if (this.humanHistory.length > this.nInput)\r
+ this.humanHistory.splice(this.nInput);\r
+ },\r
+ // Play a move given current data (*after* human move: trigger onChange)\r
+ play: function() {\r
+ let candidates = [ ];\r
+ Array.from(Array(nChoice)).forEach( i => {\r
+ // Sum all weights from an activated input to this output\r
+ let sumWeights = this.weights[i].reduce( (acc,input,j) => {\r
+ if (this.humanHistory.length <= j)\r
+ return 0;\r
+ return input[ this.humanHistory[j] ];\r
+ }, 0 );\r
+ let currentValue = {\r
+ val: sumWeights,\r
+ index: i\r
+ };\r
+ if (candidates.length == 0 || sumWeights > candidates[0].val)\r
+ candidates = [ currentValue ];\r
+ else if (sumWeights == candidates[0].val)\r
+ candidates.push(currentValue);\r
+ });\r
+ // Pick a choice at random in maxValue (total random for first move)\r
+ let randIdx = Math.floor((Math.random() * candidates.length) + 1);\r
+ this.updateGameState(candidates[randIdx]);\r
+ },\r
+ updateGameState: function(move) {\r
+ let reward = rewards[move.val][this.humanMove]; //viewpoint of computer\r
+ this.gameState += reward;\r
+ this.updateWeights(reward, move.index);\r
+ },\r
+ updateWeights: function(reward, index) {\r
+ let delta = Math.sign(reward);\r
+ if (this.drawIsLost && reward == 0)\r
+ delta = -1;\r
+ this.weights[index].forEach( (input,i) => {\r
+ if (i < this.humanHistory.length)\r
+ input[ this.humanHistory[i] ] += delta;\r
+ });\r
+ this.postUpdate();\r
+ },\r
+ // Finalize weights update\r
+ postUpdate: function() {\r
+ // Re-center the weights\r
+ let sumAllWeights = this.weights.reduce( (a,output) => {\r
+ return a + output.reduce( (b,input) => {\r
+ return b + input.reduce( (c,choiceWeight) => {\r
+ return c + choiceWeight;\r
+ }, 0);\r
+ }, 0);\r
+ }, 0);\r
+ let meanWeight = sumAllWeights / (this.nInput * nChoice * nChoice);\r
+ this.weights.forEach( output => {\r
+ output.forEach( input => {\r
+ for (let i=0; i<input.length; i++)\r
+ input[i] -= meanWeight;\r
+ });\r
+ });\r
+ // Update human moves history\r
+ this.humanHistory.push(coup_adversaire);\r
+ this.humanHistory.shift();\r
+ },\r
+ },\r
+};\r