-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
usage: `yarn cli dumpsolo [--start <YYYY-MM-DD>] [--end <YYYY-MM-DD>]` now tracking initialSystemHealth and initialPoints values per solo game round. instead of relying on being able to accurately infer this in the future, a (large) migration computes these values for all existing rounds and future games will simply keep track of them at runtime * migration also does some further cleanup of existing rounds by deleting all extra duplicate records in order for the initial values inference to take place resolves virtualcommons/planning#60
- Loading branch information
Showing
6 changed files
with
297 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
104 changes: 104 additions & 0 deletions
104
server/src/migration/1701805620516-ComputeSoloRoundInitialValues.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; | ||
import { SoloGame, SoloGameRound } from "@port-of-mars/server/entity"; | ||
import { SoloGameState } from "@port-of-mars/server/rooms/sologame/state"; | ||
|
||
const WEAR_AND_TEAR = SoloGameState.DEFAULTS.systemHealthWear; | ||
const MAX_SYSTEM_HEALTH = SoloGameState.DEFAULTS.systemHealthMax; | ||
const STARTING_POINTS = SoloGameState.DEFAULTS.points; | ||
|
||
export class ComputeSoloRoundInitialValues1701805620516 implements MigrationInterface { | ||
name = "ComputeSoloRoundInitialValues1701805620516"; | ||
|
||
public async up(queryRunner: QueryRunner): Promise<void> { | ||
// add (temporarily nullable) columns to SoloGameRound | ||
await queryRunner.addColumns("solo_game_round", [ | ||
new TableColumn({ | ||
name: "initialSystemHealth", | ||
type: "int", | ||
isNullable: true, | ||
}), | ||
new TableColumn({ | ||
name: "initialPoints", | ||
type: "int", | ||
isNullable: true, | ||
}), | ||
]); | ||
|
||
await this.deleteSuperfluousRounds(queryRunner); | ||
await this.computeMissingInitialValues(queryRunner); | ||
|
||
// remove nullable constraint from columns | ||
await queryRunner.changeColumn( | ||
"solo_game_round", | ||
"initialSystemHealth", | ||
new TableColumn({ | ||
name: "initialSystemHealth", | ||
type: "int", | ||
isNullable: false, | ||
}) | ||
); | ||
await queryRunner.changeColumn( | ||
"solo_game_round", | ||
"initialPoints", | ||
new TableColumn({ | ||
name: "initialPoints", | ||
type: "int", | ||
isNullable: false, | ||
}) | ||
); | ||
} | ||
|
||
private async computeMissingInitialValues(queryRunner: QueryRunner): Promise<void> { | ||
// fetch all existing games with rounds, cards, decisions | ||
const games = await queryRunner.manager.find(SoloGame, { | ||
relations: ["rounds", "rounds.cards", "rounds.decision"], | ||
}); | ||
for (const game of games) { | ||
let initialSystemHealth = MAX_SYSTEM_HEALTH; | ||
let initialPoints = STARTING_POINTS; | ||
game.rounds.sort((a, b) => a.roundNumber - b.roundNumber); | ||
for (const round of game.rounds) { | ||
// simulate a round | ||
// apply wear/tear -> save initial values -> apply cards -> apply decision -> repeat | ||
initialSystemHealth = Math.max(0, initialSystemHealth - WEAR_AND_TEAR); | ||
await queryRunner.manager.update(SoloGameRound, round.id, { | ||
initialSystemHealth, | ||
initialPoints, | ||
}); | ||
for (const card of round.cards) { | ||
initialSystemHealth = Math.min( | ||
MAX_SYSTEM_HEALTH, | ||
initialSystemHealth + card.systemHealthEffect | ||
); | ||
initialPoints = Math.max(0, initialPoints + card.pointsEffect); | ||
} | ||
if (round.decision) { | ||
initialSystemHealth = Math.min( | ||
MAX_SYSTEM_HEALTH, | ||
initialSystemHealth + round.decision.systemHealthInvestment | ||
); | ||
initialPoints = Math.max(0, initialPoints + round.decision.pointsInvestment); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private async deleteSuperfluousRounds(queryRunner: QueryRunner): Promise<void> { | ||
// remove duplicate round records that were created by a bug in the game logic | ||
// we keep the last created round since this is the one that is referenced by the | ||
// deck cards | ||
await queryRunner.query(` | ||
DELETE FROM "solo_game_round" | ||
WHERE "id" NOT IN ( | ||
SELECT MAX("id") | ||
FROM "solo_game_round" | ||
GROUP BY "gameId", "roundNumber" | ||
) | ||
`); | ||
} | ||
|
||
public async down(queryRunner: QueryRunner): Promise<void> { | ||
await queryRunner.query(`ALTER TABLE "solo_game_round" DROP COLUMN "initialPoints"`); | ||
await queryRunner.query(`ALTER TABLE "solo_game_round" DROP COLUMN "initialSystemHealth"`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters