1 const nChoice
= 5,; //fixed for this game
3 // Rewards matrix. Order: rock, lizard, Spock, scissors, paper
4 const rewards
= Array
.from(Array(nChoice
)).map( i
=> { //lines
5 return Array
.from(Array(nChoice
)).map( j
=> { //columns
6 // i against j: gain from i viewpoint
7 if (j
== (i
+1) % nChoice
|| j
== (i
+3) % nChoice
)
9 else if ((i
+1) % nChoice
== j
|| j
== (i
+2) % nChoice
)
10 return -1; //I lose :(
16 const symbols
= [ "Rock", "Lizard", "Spock", "Scissors", "Paper" ];
21 humanMove: -1, //integer in 0...nChoice-1
23 humanHistory: [ ], //your nInput last moves, stored (oldest first)
24 gameState: 0, //total points of the computer
25 drawIsLost: false, //normal (or true: draw is considered loss)
26 rewards: [ ], //initialized at first human move with new nInput
27 weights: [ ], //same as above
30 // Called on nInput change
31 reinitialize: function() {
32 // weights[i][j][k]: output i -- input j/choice k
33 this.weights
= Array
.from(Array(nChoice
)).map( i
=> {
34 return Array
.from(Array(this.nInput
)).map( j
=> {
35 return Array
.from(Array(nChoice
)).map( k
=> {
40 if (this.humanHistory
.length
> this.nInput
)
41 this.humanHistory
.splice(this.nInput
);
43 // Play a move given current data (*after* human move: trigger onChange)
46 Array
.from(Array(nChoice
)).forEach( i
=> {
47 // Sum all weights from an activated input to this output
48 let sumWeights
= this.weights
[i
].reduce( (acc
,input
,j
) => {
49 if (this.humanHistory
.length
<= j
)
51 return input
[ this.humanHistory
[j
] ];
57 if (candidates
.length
== 0 || sumWeights
> candidates
[0].val
)
58 candidates
= [ currentValue
];
59 else if (sumWeights
== candidates
[0].val
)
60 candidates
.push(currentValue
);
62 // Pick a choice at random in maxValue (total random for first move)
63 let randIdx
= Math
.floor((Math
.random() * candidates
.length
) + 1);
64 this.updateGameState(candidates
[randIdx
]);
66 updateGameState: function(move) {
67 let reward
= rewards
[move.val
][this.humanMove
]; //viewpoint of computer
68 this.gameState
+= reward
;
69 this.updateWeights(reward
, move.index
);
71 updateWeights: function(reward
, index
) {
72 let delta
= Math
.sign(reward
);
73 if (this.drawIsLost
&& reward
== 0)
75 this.weights
[index
].forEach( (input
,i
) => {
76 if (i
< this.humanHistory
.length
)
77 input
[ this.humanHistory
[i
] ] += delta
;
81 // Finalize weights update
82 postUpdate: function() {
83 // Re-center the weights
84 let sumAllWeights
= this.weights
.reduce( (a
,output
) => {
85 return a
+ output
.reduce( (b
,input
) => {
86 return b
+ input
.reduce( (c
,choiceWeight
) => {
87 return c
+ choiceWeight
;
91 let meanWeight
= sumAllWeights
/ (this.nInput
* nChoice
* nChoice
);
92 this.weights
.forEach( output
=> {
93 output
.forEach( input
=> {
94 for (let i
=0; i
<input
.length
; i
++)
95 input
[i
] -= meanWeight
;
98 // Update human moves history
99 this.humanHistory
.push(coup_adversaire
);
100 this.humanHistory
.shift();