In the game code below: even though the javascript new
keyword was used to make a new object to hold the next-state object, methods applied to the next-state object still refer to first-state object for "naked" properties passed to the constructor when making the next-state object.
When a transformation is applied to a property before using it as a parameter for the next-object constructor, preserving the first-state property is easy (e.g. with timeOfDay it is easy to avoid overwriting the first state).
I've resorted to a "map" workaround to avoid referencing the first-state object when modifying the list of mafia, but it is probably not the cleanest or easiest to understand.
A few other previously answered questions suggest using something like Object.assign({},this.mafia)
but this does not allow usage of the "pop method" in the code below.
Please suggest an alternative to avoid overwriting the first-state object as shown below.
class mafiaVillageState{
constructor(timeOfDay, villagers, mafia){
this.timeOfDay = timeOfDay;
this.villagers = villagers;
this.mafia = mafia;
}
killPlayer(){
let gameStep = new mafiaVillageState(1-this.timeOfDay,
this.villagers.map(v => {return v}),
// this.mafia
this.mafia.map(v => {return v})
// Object.assign({},this.mafia)
);
if (this.timeOfDay == 1){
gameStep.mafia.pop();
} else {
gameStep.villagers.pop();
}
return gameStep
}
}
let first = new mafiaVillageState(
1,
["Sana","Dahyun"], //the villagers
["Chaeyoung","Tzuyu"] //the mafia
)
"Playing the game" is done by getting the next game state:
let next = first.killPlayer();
console.log(first.timeOfDay);
console.log(first.mafia);
console.log(next.timeOfDay);
console.log(next.mafia);
1
Array(1) ["Chaeyoung"]
0
Array(1) ["Chaeyoung"]
1
Array(2) ["Chaeyoung", "Tzuyu"]
0
Array(1) ["Chaeyoung"]
-using Object.assign({},this.mafia)
results in error below
' TypeError: gameStep.mafia.pop is not a function '
You need to create a new object for the new state if you want to preserve the old one for debugging reasons, there is no way around that. I don't think that using .map is a bad solution but if you are looking for something cleaner you can use the es6 array spread operator or Array.prototype.splice
.
killPlayer(){
let gameStep = new mafiaVillageState(
1 - this.timeOfDay,
[...this.villagers], // alternatively: this.villagers.splice(),
[...this.mafia],
);
...
}
The spread operator dumps the items in the array being spread into the new one (you can also do things like [...arr1, ...arr2] to combine arrays). You should note that the items are not copied, so if you mutate something in either array the mutation will affect all states that contain that object.