gwent-classic-v4.0/gametracker.js
Sylver 9e5f83df37 Large initial push
7 factions already implemented:

- Norther Realms
- Nilfgaard Empire
- Scoia'Tael
- Monsters
- Skellige
- Redenia
- Toussaint

In progress:
- Velen

To do:
- Witchers
- Wild Hunt
2023-11-02 02:02:29 +01:00

326 lines
11 KiB
JavaScript

"use strict"
class GameTracker {
constructor(player1,player2) {
this.player1 = player1;
this.player2 = player2;
this.rounds = [];
this.winner = null;
}
startRound() {
this.currentRound = new Round(this.rounds.length + 1);
this.currentRound.setBeforeStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
}
endRound(winpl) {
this.currentRound.setAfterStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
this.currentRound.endRound(winpl);
this.rounds.push(this.currentRound);
}
startTurn(pl) {
this.currentRound.startTurn(pl);
}
endTurn() {
this.currentRound.endTurn();
}
getCurrentTurn() {
return this.currentRound.currentTurn;
}
endGame(winpl) {
this.winner = winpl;
}
getScores() {
let scores = [];
this.rounds.forEach(r => {
scores.push([r.finalScores[0], r.finalScores[1]]);
});
return scores;
}
getCardImpacts() {
let data = {};
this.rounds.forEach(r => {
r.turns.forEach(t => {
if (t.action instanceof UnitCardAction) {
let ckey = t.action.card.key;
if (!data[ckey])
data[ckey] = [];
data[ckey].push(t.action.impact);
}
});
});
return data;
}
getAbilitiesImpact() {
let cimp = this.getCardImpacts();
let data = {"bond":[],"muster":[],"medic":[],"witcher":[],"whorshiped":[],"inspire":[],"scorch":[],"scorch_c":[],"morale":[],"horn":[], "witch_hunt":[]};
Object.keys(cimp).forEach(key => {
let abi = card_dict[key]["ability"].split(" ");
if (abi.includes("bond")) {
data["bond"] = data["bond"].concat(cimp[key].map(v => v / card_dict[key]["strength"]));
} else if (abi.includes("muster")) {
data["muster"] = data["muster"].concat(cimp[key].map(v => v / card_dict[key]["strength"]));
} else if (abi.includes("medic")) {
data["medic"] = data["medic"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.at(-1) && abi.at(-1).startsWith("witcher_")) {
data["witcher"] = data["witcher"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("whorshiped")) {
data["whorshiped"] = data["whorshiped"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("inspire")) {
data["inspire"] = data["inspire"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("scorch")) {
data["scorch"] = data["scorch"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("scorch_c")) {
data["scorch_c"] = data["scorch_c"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("morale")) {
data["morale"] = data["morale"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("horn")) {
data["horn"] = data["horn"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
} else if (abi.includes("witch_hunt")) {
data["witch_hunt"] = data["witch_hunt"].concat(cimp[key].map(v => v - card_dict[key]["strength"]));
}
});
return data;
}
}
class Round {
constructor(n) {
this.number = n;
this.turns = [];
this.winner = null;
this.finalScores = [0, 0];
this.start_hand_size = [0, 0];
this.end_hand_size = [0, 0];
}
startTurn(pl) {
this.currentTurn = new Turn(this.turns.length + 1, this, pl);
this.currentTurn.setBeforeStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
}
endTurn() {
this.currentTurn.setAfterStats([player_me.total, player_op.total], board.row, [player_me.hand.cards.length, player_op.hand.cards.length]);
this.turns.push(this.currentTurn);
}
setBeforeStats(scores, rows, handsize) {
this.startScores = scores;
this.startRows = [];
rows.forEach(r => {
this.startRows.push(Object.assign({}, r));
});
this.start_hand_size = handsize;
}
setAfterStats(scores, rows, handsize) {
this.endScores = scores;
this.endRows = [];
rows.forEach(r => {
this.endRows.push(Object.assign({}, r));
});
this.end_hand_size = handsize;
this.turns.forEach(t => {
if (t.action instanceof UnitCardAction && t.action.card.currentLocation instanceof Row) {
let pl = t.action.card.holder;
if (!t.action.card.isLocked() &&
(t.action.card.abilities.includes("bond")
|| t.action.card.abilities.includes("whorshipped")
|| t.action.card.abilities.includes("inspire")
|| (t.action.card.abilities.at(-1) && t.action.card.abilities.at(-1).startsWith("witcher_"))
)) {
t.action.impact = t.action.card.power;
} else if (!t.action.card.isLocked() &&
(t.action.card.abilities.includes("scorch")
|| t.action.card.abilities.includes("scorch_c")
|| t.action.card.abilities.includes("medic")
|| t.action.card.abilities.includes("witch_hunt")
)) {
t.action.impact = (t.endScores[getPlayerIndex(pl)] - t.startScores[getPlayerIndex(pl)]) + (t.startScores[getPlayerIndex(pl.opponent())] - t.endScores[getPlayerIndex(pl.opponent())]);
} else if (!t.action.card.isLocked() && t.action.card.abilities.includes("morale")) {
t.action.impact = t.action.card.power + t.action.card.currentLocation.cards.filter(c => c.isUnit()).length;
} else if (!t.action.card.isLocked() && t.action.card.abilities.includes("horn")) {
t.action.impact = t.action.card.power + t.action.card.currentLocation.cards.filter(c => c.isUnit()).reduce((a,c) => a + (c.power / 2),0);
}
}
});
}
endRound(winpl) {
this.winner = winpl;
this.finalScores = [player_me.total, player_op.total];
}
}
class Turn {
constructor(n,rnd,pl) {
this.number = n;
this.round = rnd;
this.player = pl;
this.action = null;
this.start_hand_size = [0, 0];
this.end_hand_size = [0, 0];
}
setBeforeStats(scores, rows, handsize) {
this.startScores = scores;
this.startRows = [];
rows.forEach(r => {
this.startRows.push(Object.assign({}, r));
});
this.start_hand_size = handsize;
}
setAfterStats(scores, rows, handsize) {
this.endScores = scores;
this.endRows = [];
rows.forEach(r => {
this.endRows.push(Object.assign({}, r));
});
this.end_hand_size = handsize;
if (this.action instanceof UnitCardAction) {
var pl = this.action.card.holder;
if (this.action.card.abilities.length === 0) {
this.action.impact = this.action.card.power;
} else if (this.action.card.abilities.includes("muster")) {
// We devide the impact by the number of cards from hand it took
let handdiff = Math.max(1, (this.start_hand_size[getPlayerIndex(pl)] - this.end_hand_size[getPlayerIndex(pl)]));
this.action.impact = (this.endScores[getPlayerIndex(pl)] - this.startScores[getPlayerIndex(pl)]) / handdiff;
}
}
}
playUnitCard(card, row) {
this.action = new UnitCardAction(this, card, row.getRowType());
}
playWeatherCard(card) {
this.action = new WeatherCardAction(this, card.key, card.abilities.at(-1));
}
playSpecialCard(card, row) {
this.action = new SpecialCardAction(this, card.key, row.getRowType());
}
playSpecialCardBoard(card) {
this.action = new SpecialCardAction(this, card.key, "board");
}
passAction() {
this.action = new PassAction(this);
}
useLeader(card) {
this.action = new LeaderAction(this, card.key);
}
useLeaderOnRow(card,r) {
this.action = new LeaderAction(this, card.key, r.getRowType());
}
useLeaderOnCard(card,t) {
this.action = new LeaderAction(this, card.key, false, t.key);
}
summary() {
let action_string = "No action";
if (this.action)
action_string = this.action.summary();
return `[Round:${this.round.number}] [Turn:${this.number}] [ScoresBefore:${this.startScores[0]}/${this.startScores[1]}]\nPlayer ${this.player.tag}: ${action_string}]\n[ScoresAfter:${this.endScores[0]}/${this.endScores[1]}]`;
}
}
class Action {
constructor(turn) {
this.turn = turn;
this.targetRow = false;
this.targetCard = false;
this.impact = 0;
}
summary() {
return "No output";
}
}
class UnitCardAction extends Action {
constructor(turn, card, r) {
super(turn);
this.card = card;
this.key = card.key;
this.targetRow = r;
}
summary() {
return `Card '${this.key}' played in ${this.targetRow} row.`;
}
}
class SpecialCardAction extends Action {
constructor(turn, ckey, r) {
super(turn);
this.key = ckey;
this.targetRow = r;
}
summary() {
return `Special Card '${this.key}' played on ${this.targetRow}.`;
}
}
class WeatherCardAction extends Action {
constructor(turn, ckey, type) {
super(turn);
this.key = ckey;
this.type = type;
}
summary() {
return `Weather Card '${this.key}' played with ${this.type} effect.`;
}
}
class LeaderAction extends Action {
constructor(turn, ckey, targetRow=false, targetCard=false) {
super(turn);
this.key = ckey;
this.targetRow = targetRow;
this.targetCard = targetCard;
}
summary() {
return `Leader '${this.key}' ability used`;
}
}
class FactionAction extends Action {
constructor(turn, ckey) {
super(turn);
this.key = ckey;
}
}
class PassAction extends Action {
constructor(turn) {
super(turn);
}
summary() {
return "Passed";
}
}
// UTIL FUNCTIONS
function getPlayerIndex(pl) {
return pl.tag === "me" ? 0 : 1;
}