Skip to content

Commit d51fc05

Browse files
committed
added deep copying for abilities
1 parent 9fac52c commit d51fc05

File tree

5 files changed

+39
-95
lines changed

5 files changed

+39
-95
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ bundle.js
77

88
node_modules
99

10+
coverage
11+
test/*.js
1012
#Ignore notes
1113
TODO.txt
1214
.DS_Store
13-

games/classic/classic.ts

+6-15
Original file line numberDiff line numberDiff line change
@@ -481,25 +481,16 @@ export class Classic extends Game {
481481
this.tallyInterval = setInterval(this.tallyVotes.bind(this), 1000);
482482
}
483483
private tallyVotes() {
484-
let count = 0;
485484
let defendant = 0;
486485
let aliveCount = this.players.filter(player => player.alive).length;
487-
let beginTrial: boolean = false;
488-
for (let i = 0; i < this.players.length; i++) {
489-
count = 0;
490-
if (beginTrial) {
486+
let beginTrial = false;
487+
for (let possibleDefendant of this.players) {
488+
let count = this.players.map(elem => (elem.vote == possibleDefendant.user.id ? 1 : 0)).reduce<number>((acc, val) => acc + val, 0);
489+
if (count >= Math.floor(aliveCount / 2) + 1) {
490+
beginTrial = true;
491+
defendant = this.players.indexOf(possibleDefendant);
491492
break;
492493
}
493-
for (let j = 0; j < this.players.length; j++) {
494-
if (this.players[j].vote == this.players[i].user.id) {
495-
count++;
496-
}
497-
if (count >= Math.floor(aliveCount / 2) + 1) {
498-
beginTrial = true;
499-
defendant = i;
500-
break;
501-
}
502-
}
503494
}
504495
if (beginTrial) {
505496
this.trialClock.stop();

games/classic/classicPlayer.ts

+22-13
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,35 @@ export class ClassicPlayer extends Player {
3333
//the username of the player that this player is nominating during the trial
3434
private _vote: string = "";
3535
private _finalVote: string = FinalVote.abstain;
36+
//! Important: Roles are immutable, so to keep track of uses of abilities etc, a deep copy of abilities is made.
37+
//! This deep copy is accessible by .abilities. Whenever role is updated, _abilities must be updated too.
38+
//! The setter for roles will do this automatically.
39+
//! TLDR: don't use this._role, use this.role instead.
3640
private _role: Role;
41+
private _abilities: Array<{ ability: Ability; uses?: number }> = [];
3742
private _winLynchTarget: ClassicPlayer | undefined = undefined;
43+
//the abilities of this player
3844
constructor(user: User, role: Role) {
3945
super(user);
4046
this._role = role;
47+
this._abilities = Array.from(role.abilities) as Array<{ ability: Ability; uses?: number }>;
4148
}
4249
public get alive() {
4350
return this._alive;
4451
}
4552
public get role() {
4653
return this._role;
4754
}
55+
public set role(role: Role) {
56+
this._role = role;
57+
//give the player their abilities
58+
this._abilities = Array.from(role.abilities) as Array<{ ability: Ability; uses?: number }>;
59+
}
60+
public get abilities() {
61+
return this._abilities;
62+
}
4863
public upgradeToGodfather() {
49-
this._role = Roles.godfather;
64+
this.role = Roles.godfather;
5065
this.user.send("You have been promoted to godfather - now you get final say on who to kill.");
5166
}
5267
get winLynchTarget() {
@@ -62,13 +77,13 @@ export class ClassicPlayer extends Player {
6277
this._hanged = true;
6378
}
6479
public get alignment(): Alignment {
65-
return this._role.alignment;
80+
return this.role.alignment;
6681
}
6782
public get roleName(): string {
68-
return this._role.roleName;
83+
return this.role.roleName;
6984
}
7085
public isRole(role: Role): boolean {
71-
return this._role.roleName == role.roleName;
86+
return this.role.roleName == role.roleName;
7287
}
7388
public set target(target: string) {
7489
this._target = target;
@@ -80,13 +95,13 @@ export class ClassicPlayer extends Player {
8095
this._target = "";
8196
}
8297
public get winCondition() {
83-
return this._role.winCondition;
98+
return this.role.winCondition;
8499
}
85100
public resetAfterNight() {
86101
this.clearTarget();
87102
this._healed = false;
88103
this._mafiaVotes = 0;
89-
this._roleBlocked = false;
104+
this.roleBlocked = false;
90105
}
91106
public resetAfterTrial() {
92107
this._vote = "";
@@ -99,7 +114,7 @@ export class ClassicPlayer extends Player {
99114
return this._healed;
100115
}
101116
public roleBlock() {
102-
this._roleBlocked = true;
117+
this.roleBlocked = true;
103118
}
104119
get roleBlocked() {
105120
return this._roleBlocked;
@@ -146,10 +161,4 @@ export class ClassicPlayer extends Player {
146161
public get finalVote() {
147162
return this._finalVote;
148163
}
149-
public get abilities(): Array<{
150-
ability: Ability;
151-
uses?: number | undefined;
152-
}> {
153-
return this._role.abilities;
154-
}
155164
}

games/classic/roles.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,26 @@ type WinCondition = (player: ClassicPlayer, game: Classic) => boolean;
3434
type GameEndCondition = (game: Classic) => boolean;
3535

3636
export type Ability = {
37-
condition: (
37+
readonly condition: (
3838
targetPlayer: ClassicPlayer,
3939
game: Classic,
4040
player?: Player,
4141
) => boolean | undefined;
42-
action: (
42+
readonly action: (
4343
targetPlayer: ClassicPlayer,
4444
game: Classic,
4545
player?: ClassicPlayer,
4646
) => void;
4747
};
4848

4949
export interface Role {
50-
roleName: string;
51-
alignment: Alignment;
52-
winCondition: WinCondition;
53-
abilities: Array<{ ability: Ability; uses?: number }>;
54-
passives: Array<Passives>;
55-
color?: Colors;
56-
backgroundColor?: Colors;
50+
readonly roleName: string;
51+
readonly alignment: Alignment;
52+
readonly winCondition: WinCondition;
53+
abilities: Readonly<Array<Readonly<{ ability: Ability; uses?: number }>>>;
54+
readonly passives: Array<Passives>;
55+
readonly color?: Colors;
56+
readonly backgroundColor?: Colors;
5757
};
5858

5959
export namespace GameEndConditions {

test/classic.test.js

-57
This file was deleted.

0 commit comments

Comments
 (0)