b22e5af3eb217306128dfb7e8f589b5492da9bf5
1 const nChoice
= 5; //fixed for this game
3 const symbols
= [ "Rock", "Lizard", "Spock", "Scissors", "Paper" ];
5 // Same order as in symbols
7 [ "can't win vs.", "crushes", "is vaporized by", "cruches", "is covered by" ],
8 [ "is crushed by", "can't win vs.", "poisons", "is decapitated by", "eats" ],
9 [ "vaporizes", "is poisoned by", "can't win vs.", "smashes", "is disproved by" ],
10 [ "is crushed by", "decapitates", "is smashed by", "can't win vs.", "cuts" ],
11 [ "covers", "is eaten by", "disproves", "is cut by", "can't win vs." ],
14 // Rewards matrix, order as in symbols
15 const rewards
= Array
.from(Array(nChoice
)).map( (e
,i
) => { //lines
16 return Array
.from(Array(nChoice
)).map( (f
,j
) => { //columns
17 // i against j: gain from i viewpoint
18 if (j
== (i
+1) % nChoice
|| j
== (i
+3) % nChoice
)
21 return -1; //I lose :(
30 humanHistory: [ ], //your nInput last moves, stored (oldest first)
32 gameState: 0, //total points of the human
33 compMove: -1, //last move played by computer
34 drawIsLost: false, //normal (or true: draw is considered loss)
35 rewards: [ ], //initialized at first human move with new nInput
36 weights: [ ], //same as above
44 // Called on nInput change
45 reinitialize: function() {
46 // weights[i][j][k]: output i -- input j/choice k
47 this.weights
= Array
.from(Array(nChoice
)).map( i
=> {
48 return Array
.from(Array(this.nInput
)).map( j
=> {
49 return Array
.from(Array(nChoice
)).map( k
=> {
54 if (this.humanHistory
.length
> this.nInput
)
55 this.humanHistory
.splice(this.nInput
);
57 // Play a move given current data (*after* human move)
58 play: function(humanMove
) {
59 this.humanMove
= humanMove
;
61 Array
.from(Array(nChoice
)).forEach( (e
,i
) => {
62 // Sum all weights from an activated input to this output
63 let sumWeights
= this.weights
[i
].reduce( (acc
,input
,j
) => {
64 if (this.humanHistory
.length
<= j
)
66 return input
[ this.humanHistory
[j
] ];
72 if (candidates
.length
== 0 || sumWeights
> candidates
[0].val
)
74 else if (sumWeights
== candidates
[0].val
)
75 candidates
.push(move);
77 // Pick a choice at random in maxValue (total random for first move)
78 let randIdx
= Math
.floor(Math
.random() * candidates
.length
);
79 this.compMove
= candidates
[randIdx
].index
;
81 let reward
= rewards
[this.compMove
][humanMove
]; //viewpoint of computer
82 this.gameState
-= reward
;
83 this.updateWeights(reward
);
84 // Update human moves history
85 this.humanHistory
.push(humanMove
);
86 if (this.humanHistory
.length
> this.nInput
)
87 this.humanHistory
.shift();
89 updateWeights: function(reward
) {
90 let delta
= Math
.sign(reward
);
91 if (this.drawIsLost
&& reward
== 0)
93 this.weights
[this.compMove
].forEach( (input
,i
) => {
94 if (i
< this.humanHistory
.length
)
95 input
[ this.humanHistory
[i
] ] += delta
;
97 // Re-center the weights
98 let sumAllWeights
= this.weights
.reduce( (a
,output
) => {
99 return a
+ output
.reduce( (b
,input
) => {
100 return b
+ input
.reduce( (c
,choiceWeight
) => {
101 return c
+ choiceWeight
;
105 let meanWeight
= sumAllWeights
/ (this.nInput
* nChoice
* nChoice
);
106 this.weights
.forEach( output
=> {
107 output
.forEach( input
=> {
108 for (let i
=0; i
<input
.length
; i
++)
109 input
[i
] -= meanWeight
;
113 imgSource: function(symbol
) {
114 return "img/" + symbol
+ ".png";