Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: boardgamers/gaia-project
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1ea7f8cdd2932accf65f2028d9d35183ebe9bf43
Choose a base ref
..
head repository: boardgamers/gaia-project
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d849889d83be3049898d8f89cb383c8672785c94
Choose a head ref
Showing with 3,011 additions and 1,120 deletions.
  1. +2 −1 .prettierignore
  2. +1 −0 batch/package.json
  3. +38 −15 batch/src/stats.ts
  4. +5 −1 engine/index.ts
  5. +1 −1 engine/package.json
  6. +3 −1 engine/src/available-command.ts
  7. +46 −20 engine/src/engine.ts
  8. +1 −0 engine/src/enums.ts
  9. +27 −1 engine/src/federation.ts
  10. +8 −7 engine/src/map.ts
  11. +9 −0 engine/src/player-data.ts
  12. +29 −31 engine/src/player.ts
  13. +259 −0 engine/src/setup.ts
  14. +5 −1 engine/wrapper.ts
  15. +9 −11 old-ui/src/components/AdvancedLog.vue
  16. +26 −24 old-ui/src/components/BoardAction.vue
  17. +15 −15 old-ui/src/components/Booster.vue
  18. +5 −5 old-ui/src/components/Commands.vue
  19. +12 −12 old-ui/src/components/FederationTile.vue
  20. +4 −4 old-ui/src/components/FinalScoringTile.vue
  21. +14 −16 old-ui/src/components/Game.vue
  22. +25 −28 old-ui/src/components/MoveButton.vue
  23. +3 −3 old-ui/src/components/PlayerInfo.vue
  24. +13 −18 old-ui/src/components/Pool.vue
  25. +17 −17 old-ui/src/components/ResearchBoard.vue
  26. +62 −40 old-ui/src/components/ResearchTile.vue
  27. +8 −8 old-ui/src/components/ResearchTrack.vue
  28. +8 −12 old-ui/src/components/ScoringBoard.vue
  29. +14 −14 old-ui/src/components/ScoringTile.vue
  30. +15 −15 old-ui/src/components/Sector.vue
  31. +5 −5 old-ui/src/components/SpaceHex.vue
  32. +14 −14 old-ui/src/components/SpaceMap.vue
  33. +12 −12 old-ui/src/components/SpecialAction.vue
  34. +20 −20 old-ui/src/components/TechTile.vue
  35. +1 −1 old-ui/src/components/TurnOrder.vue
  36. +5 −5 old-ui/src/components/Wrapper.vue
  37. +12 −12 old-ui/src/launcher.ts
  38. +1 −1 old-ui/src/self-contained.ts
  39. +1 −6 old-ui/src/store.ts
  40. +1 −1 viewer/package.json
  41. +5 −10 viewer/src/components/AdvancedLog.vue
  42. +6 −6 viewer/src/components/BoardAction.spec.ts
  43. +5 −5 viewer/src/components/BoardAction.vue
  44. +12 −12 viewer/src/components/Charts.vue
  45. +25 −15 viewer/src/components/Commands.vue
  46. +1 −1 viewer/src/components/Condition.vue
  47. +41 −26 viewer/src/components/FactionWheel.vue
  48. +4 −4 viewer/src/components/FinalScoringTile.vue
  49. +18 −20 viewer/src/components/Game.vue
  50. +23 −19 viewer/src/components/MoveButton.vue
  51. +14 −14 viewer/src/components/PlayerBoard/BuildingGroup.vue
  52. +5 −7 viewer/src/components/PlayerBoard/Info.vue
  53. +4 −4 viewer/src/components/PlayerBoard/PowerBowl.vue
  54. +3 −5 viewer/src/components/PlayerBoard/PowerBowls.vue
  55. +1 −1 viewer/src/components/PlayerCircle.vue
  56. +5 −5 viewer/src/components/PlayerInfo.vue
  57. +3 −6 viewer/src/components/Pool.vue
  58. +2 −2 viewer/src/components/ResearchBoard.vue
  59. +4 −4 viewer/src/components/ResearchTile.vue
  60. +1 −1 viewer/src/components/ResearchTrack.vue
  61. +1 −1 viewer/src/components/Resource.vue
  62. +3 −3 viewer/src/components/Resources/Undo.vue
  63. +12 −1 viewer/src/components/Rules.vue
  64. +17 −18 viewer/src/components/ScoringBoard.vue
  65. +3 −5 viewer/src/components/ScoringTile.vue
  66. +2 −2 viewer/src/components/Sector.vue
  67. +52 −28 viewer/src/components/SpaceHex.vue
  68. +17 −5 viewer/src/components/SpaceMap.vue
  69. +4 −4 viewer/src/components/SpecialAction.vue
  70. +1 −1 viewer/src/components/TechContent.vue
  71. +17 −17 viewer/src/components/TechTile.vue
  72. +1 −1 viewer/src/components/TurnOrder.vue
  73. +33 −8 viewer/src/components/Wrapper.vue
  74. +1 −1 viewer/src/components/definitions/Buildings.vue
  75. +5 −1 viewer/src/data/index.ts
  76. +2 −1 viewer/src/data/log.spec.ts
  77. +1 −1 viewer/src/data/log.ts
  78. +14 −0 viewer/src/data/round-scorings.ts
  79. +12 −12 viewer/src/launcher.ts
  80. +99 −0 viewer/src/logic/buttons/setup.ts
  81. +1 −1 viewer/src/logic/charts/buildings.ts
  82. +203 −20 viewer/src/logic/charts/charge.ts
  83. +37 −21 viewer/src/logic/charts/chart-factory.ts
  84. +6 −3 viewer/src/logic/charts/chart.spec.ts
  85. +13 −8 viewer/src/logic/charts/charts.ts
  86. +223 −0 viewer/src/logic/charts/factions.ts
  87. +108 −2 viewer/src/logic/charts/federations.ts
  88. +11 −18 viewer/src/logic/charts/final-scoring.ts
  89. +50 −0 viewer/src/logic/charts/power-leverage.ts
  90. +168 −0 viewer/src/logic/charts/resource-counter.spec.ts
  91. +242 −0 viewer/src/logic/charts/resource-counter.ts
  92. +0 −49 viewer/src/logic/charts/resources.spec.ts
  93. +22 −197 viewer/src/logic/charts/resources.ts
  94. +63 −34 viewer/src/logic/charts/simple-charts.ts
  95. +16 −13 viewer/src/logic/charts/simple-sources.ts
  96. +3 −3 viewer/src/logic/charts/tech.ts
  97. +65 −0 viewer/src/logic/charts/testdata/all-families/faction-specials.json
  98. +8 −1 viewer/src/logic/charts/testdata/all-families/final-scoring-conditions.json
  99. +14 −11 viewer/src/logic/charts/testdata/all-families/power-charges.json
  100. +81 −0 viewer/src/logic/charts/testdata/all-families/power-leech.json
  101. +2 −2 viewer/src/logic/charts/testdata/all-families/resources.json
  102. +81 −0 viewer/src/logic/charts/testdata/federation-types/power-leech.json
  103. +1 −1 viewer/src/logic/charts/testdata/federation-types/test-case.json
  104. +73 −0 viewer/src/logic/charts/testdata/final-scoring/faction-specials.json
  105. +8 −1 viewer/src/logic/charts/testdata/final-scoring/final-scoring-conditions.json
  106. +1 −1 viewer/src/logic/charts/testdata/final-scoring/test-case.json
  107. +2 −2 viewer/src/logic/charts/testdata/nevla-leverage/resources.json
  108. +69 −0 viewer/src/logic/charts/testdata/q-range-2/faction-specials.json
  109. +72 −0 viewer/src/logic/charts/testdata/q-range-2/power-charges.json
  110. +1 −1 viewer/src/logic/charts/testdata/q-range-2/test-case.json
  111. +0 −1 viewer/src/logic/charts/victory-point-charts.ts
  112. +14 −13 viewer/src/logic/commands.ts
  113. +41 −0 viewer/src/logic/test-utils.ts
  114. +12 −29 viewer/src/logic/utils.ts
  115. +43 −3 viewer/src/self-contained.ts
  116. +22 −9 viewer/src/store.ts
  117. +0 −1 viewer/src/stylesheets/planets.css
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pnpm-lock.yaml
dist
dist
replay
1 change: 1 addition & 0 deletions batch/package.json
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
"scripts": {
"build": "tsc -p .",
"stats": "ts-node src/stats.ts",
"stats-replay-errors": "ts-node src/stats.ts replay-errors",
"replay": "ts-node src/replay.ts"
},
"dependencies": {
53 changes: 38 additions & 15 deletions batch/src/stats.ts
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@ import { Game, GameDocument, PlayerInfo } from "./game";
import { connectMongo, shouldReplay } from "./util";
import { ChartSetup } from "../../viewer/src/logic/charts/chart-factory";

const errorDir = "error/";

function getDetailStats(commonProps: any, data: Engine, pl: Player) {
const newDetailRow = (round: any) => {
const d = {
@@ -54,12 +56,18 @@ function getGameStats(pl: Player, outerPlayer: PlayerInfo<any>, data: Engine, ga
const rank = sortBy(data.players, (p: Player) => -p.data.victoryPoints);
const rankWithoutBid = sortBy(data.players, (p: Player) => -(p.data.victoryPoints + (p.data.bid ?? 0))); // bid is positive

const date = game.createdAt;

function toIso(date: any) {
return typeof date == "string" ? date : date?.toISOString();
}

const row = {
initialTurnOrder: pl.player + 1,
version: data.version ?? "1.0.0",
players: data.players.length,
started: game.createdAt?.toISOString(),
ended: game.lastMove?.toISOString(),
started: toIso(game.createdAt),
ended: toIso(game.lastMove),
variant: data.options.factionVariant,
auction: data.options.auction,
layout: data.options.layout,
@@ -143,7 +151,13 @@ function getStats(game: GameDocument, data: Engine): { game: any[]; detail: any[
});
}

async function main() {
function readJson(file: string) {
return JSON.parse(fs.readFileSync(file, { encoding: "utf8" }));
}

async function main(args: string[]) {
const replayErrors = args.length > 0 && args[0] == "replay-errors";

let success = 0;
let errors = 0;
let skipReplay = 0;
@@ -190,7 +204,7 @@ async function main() {
if (shouldReplay(game)) {
const file = `replay/${game._id}.json`;
if (fs.existsSync(file)) {
data = Engine.fromData(JSON.parse(fs.readFileSync(file, { encoding: "utf8" })));
data = Engine.fromData(readJson(file));
} else {
skipReplay++;
return;
@@ -202,13 +216,16 @@ async function main() {
const stats = getStats(game, data);

if (gameWriter == null) {
const append = replayErrors
gameWriter = createObjectCsvWriter({
path: "gaia-stats-game.csv",
header: Object.keys(stats.game[0]).map((k) => ({ id: k, title: k })),
append,
});
detailWriter = createObjectCsvWriter({
path: "gaia-stats-turns.csv",
header: Object.keys(stats.detail[0]).map((k) => ({ id: k, title: k })),
append,
});
}

@@ -217,24 +234,30 @@ async function main() {
success++;
}

connectMongo();

for await (const game of Game.find().where("game.name").equals("gaia-project")) {
try {
await process(game);
} catch (e) {
console.log(game._id);
console.log(e);
fs.writeFileSync("error/" + game._id, JSON.stringify(game.toJSON()), { encoding: "utf8" });
errors++;
if (replayErrors) {
for (const file of fs.readdirSync(errorDir)) {
console.log(file);
await process(readJson(errorDir + file));
}
} else {
connectMongo();
for await (const game of Game.find().where("game.name").equals("gaia-project")) {
try {
await process(game);
} catch (e) {
console.log(game._id);
console.log(e);
fs.writeFileSync(errorDir + game._id + ".json", JSON.stringify(game.toJSON()), { encoding: "utf8" });
errors++;
}
}
}

console.log(outcomes());
}

const start = new Date();
main().then(() => {
main(process.argv.slice(2)).then(() => {
console.log("done");
console.log(new Date().getTime() - start.getTime());
process.exit(0);
6 changes: 5 additions & 1 deletion engine/index.ts
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import Engine from "./src/engine";
import Event, { EventSource } from "./src/events";
import SpaceMap, { parseLocation } from "./src/map";
import Player, { BuildWarning, MAX_SATELLITES } from "./src/player";
import PlayerData, { Power } from "./src/player-data";
import PlayerData, { BrainstoneDest, MaxLeech, Power } from "./src/player-data";
import researchTracks from "./src/research-tracks";
import Reward from "./src/reward";
import tiles from "./src/tiles";
@@ -65,11 +65,15 @@ export {
} from "./src/enums";
export { FactionBoard, factionBoard, factionVariantBoard } from "./src/faction-boards";
export { factionPlanet } from "./src/factions";
export { federationCost, parseFederationLocation } from "./src/federation";
export { GaiaHex, GaiaHexData } from "./src/gaia-hex";
export { applyChargePowers } from "./src/income";
export { planetNames, terraformingStepsRequired } from "./src/planets";
export { AvailableSetupOption, SetupType } from "./src/setup";
export { finalScorings, roundScorings } from "./src/tiles/scoring";
export {
BrainstoneDest,
MaxLeech,
BuildWarning,
Player,
PlayerData,
2 changes: 1 addition & 1 deletion engine/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gaia-project/engine",
"version": "4.8.18",
"version": "4.8.21",
"description": "Javascript engine for project gaia",
"main": "dist/index.js",
"types": "index.ts",
4 changes: 3 additions & 1 deletion engine/src/available-command.ts
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@ import PlayerObject, { BuildCheck, BuildWarning } from "./player";
import PlayerData, { BrainstoneDest, resourceLimits } from "./player-data";
import * as researchTracks from "./research-tracks";
import Reward from "./reward";
import { AvailableSetupOption, possibleSetupBoardActions } from "./setup";
import { isAdvanced } from "./tiles/techs";

const ISOLATED_DISTANCE = 3;
@@ -141,6 +142,7 @@ interface CommandData {
[Command.PISwap]: AvailableBuildCommandData;
[Command.PlaceLostPlanet]: { spaces: AvailableHex[] };
[Command.RotateSectors]: never;
[Command.Setup]: AvailableSetupOption;
[Command.Special]: { specialacts: { income: string; spec: string }[] };
[Command.Spend]: AvailableFreeActionData;
[Command.UpgradeResearch]: AvailableResearchData;
@@ -223,7 +225,7 @@ export function generate(engine: Engine, subPhase: SubPhase = null, data?: any):
case Phase.SetupInit:
return [{ name: Command.Init } as AvailableCommand]; //doesn't have player
case Phase.SetupBoard:
return [{ name: Command.RotateSectors, player }];
return possibleSetupBoardActions(engine, player);
case Phase.SetupFaction:
return chooseFactionOrBid(engine, player);
case Phase.SetupAuction:
66 changes: 46 additions & 20 deletions engine/src/engine.ts
Original file line number Diff line number Diff line change
@@ -40,11 +40,20 @@ import {
import Event, { EventSource } from "./events";
import { factionVariantBoard, latestVariantVersion } from "./faction-boards";
import { remainingFactions } from "./factions";
import { GaiaHex } from "./gaia-hex";
import SpaceMap, { MapConfiguration } from "./map";
import Player from "./player";
import PlayerData, { BrainstoneDest, MoveTokens, powerLogString } from "./player-data";
import * as researchTracks from "./research-tracks";
import Reward from "./reward";
import {
applySetupOption,
initCustomSetup,
possibleSetupBoardActions,
SetupOption,
SetupPosition,
SetupType,
} from "./setup";
import federations from "./tiles/federations";
import { roundScorings } from "./tiles/scoring";
import { isAdvanced } from "./tiles/techs";
@@ -76,6 +85,8 @@ export type Layout = "standard" | "balanced" | "xshape";
export interface EngineOptions {
/** Allow last player to rotate sector BEFORE faction selection */
advancedRules?: boolean;
/** Allow last player the entire board */
customBoardSetup?: boolean;
/** disable Federation check for available commands */
noFedCheck?: boolean;
/** Custom map given */
@@ -395,7 +406,7 @@ export default class Engine {
}
} else {
// Add new entry
this.advancedLog.push({
this.addAdvancedLog({
player,
move,
changes: amount
@@ -407,6 +418,10 @@ export default class Engine {
}
}

protected addAdvancedLog(entry: LogEntry) {
this.advancedLog.push(entry);
}

generateAvailableCommandsIfNeeded(subphase: SubPhase = null, data?: any): AvailableCommand[] {
return this.availableCommands || this.generateAvailableCommands(subphase, data);
}
@@ -1091,13 +1106,17 @@ export default class Engine {

beginSetupBoardPhase() {
this.changePhase(Phase.SetupBoard);
// The last player is the one to rotate the sectors
this.currentPlayer = this.players.slice(-1).pop().player;

if (!this.options.advancedRules) {
// No sector rotation
if (this.options.customBoardSetup) {
initCustomSetup(this);

// The first player (host) does board setup and rotation
this.currentPlayer = this.players[0].player;
} else if (this.options.advancedRules) {
// The last player is the one to rotate the sectors
this.currentPlayer = this.players.slice(-1).pop().player;
} else {
this.beginSetupFactionPhase();
return;
}
}

@@ -1162,7 +1181,7 @@ export default class Engine {

beginRoundStartPhase() {
this.round += 1;
this.advancedLog.push({ round: this.round });
this.addAdvancedLog({ round: this.round });
this.turnOrder = this.passedPlayers || this.turnOrderAfterSetupAuction;
this.passedPlayers = [];
this.currentPlayer = this.turnOrder[0];
@@ -1176,7 +1195,7 @@ export default class Engine {

beginIncomePhase() {
this.changePhase(Phase.RoundIncome);
this.advancedLog.push({ phase: Phase.RoundIncome });
this.addAdvancedLog({ phase: Phase.RoundIncome });
this.tempTurnOrder = [...this.turnOrder];

this.moveToNextPlayer(this.tempTurnOrder, { loop: false });
@@ -1194,7 +1213,7 @@ export default class Engine {

beginGaiaPhase() {
this.changePhase(Phase.RoundGaia);
this.advancedLog.push({ phase: Phase.RoundGaia });
this.addAdvancedLog({ phase: Phase.RoundGaia });
this.tempTurnOrder = [...this.turnOrder];

// transform Transdim planets into Gaia if gaiaformed
@@ -1258,7 +1277,7 @@ export default class Engine {

finalScoringPhase() {
this.changePhase(Phase.EndGame);
this.advancedLog.push({ phase: Phase.EndGame });
this.addAdvancedLog({ phase: Phase.EndGame });
this.currentPlayer = this.tempCurrentPlayer = undefined;
const allRankings = finalRankings(this.tiles.scorings.final, this.players);

@@ -1298,15 +1317,8 @@ export default class Engine {
pl.data.leechPossible = 0;
continue;
}

// Exclude the one who made the building from the leech
let leech = 0;
for (const hex of this.map.withinDistance(sourceHex, LEECHING_DISTANCE)) {
leech = Math.max(leech, pl.buildingValue(this.map.grid.get(hex)));
}

// Do not use maxLeech() here, cuz taklons
pl.data.leechPossible = leech;
// Do not use PlayerData.maxLeech() here, cuz taklons
pl.data.leechPossible = this.leechPossible(sourceHex, (hex) => pl.buildingValue(this.map.grid.get(hex)));
if (pl.canLeech()) {
canLeechPlayers.push(pl);
}
@@ -1321,6 +1333,14 @@ export default class Engine {
}
}

leechPossible(sourceHex: GaiaHex, buildingValue: (GaiaHex) => number) {
let leech = 0;
for (const hex of this.map.withinDistance(sourceHex, LEECHING_DISTANCE)) {
leech = Math.max(leech, buildingValue(hex));
}
return leech;
}

advanceResearchAreaPhase(player: PlayerEnum, cost: string, field: ResearchField) {
const pl = this.player(player);

@@ -1371,7 +1391,9 @@ export default class Engine {
[Phase.SetupBoard](move: string) {
this.loadTurnMoves(move, { split: false, processFirst: true });

this.beginSetupFactionPhase();
if (possibleSetupBoardActions(this, this.currentPlayer).length === 0 || move.includes(Command.RotateSectors)) {
this.beginSetupFactionPhase();
}
}

[Phase.SetupFaction](move: string) {
@@ -1569,6 +1591,10 @@ export default class Engine {
}
}

[Command.Setup](player: PlayerEnum, type: SetupType, position: SetupPosition, _to: "to", option: SetupOption) {
applySetupOption(this, type, position, option);
}

[Command.RotateSectors](player: PlayerEnum, ...params: string[]) {
assert(params.length % 2 === 0, "The rotate command needs an even number of parameters");

1 change: 1 addition & 0 deletions engine/src/enums.ts
Original file line number Diff line number Diff line change
@@ -188,6 +188,7 @@ export enum Command {
PlaceLostPlanet = "lostPlanet",
RotateSectors = "rotate",
Special = "special",
Setup = "set",
Spend = "spend",
UpgradeResearch = "up",
}
28 changes: 27 additions & 1 deletion engine/src/federation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { difference } from "lodash";
import assert from "assert";
import { difference, uniq } from "lodash";
import { Faction } from "./enums";
import { GaiaHex } from "./gaia-hex";
import SpaceMap from "./map";

export interface FederationInfo {
hexes: GaiaHex[];
@@ -56,3 +59,26 @@ export function isOutclassedBy(fed: FederationInfo, comparison: FederationInfo)
// Not outclassed
return false;
}

export function federationCost(faction: Faction, hasPlanetaryInstitute: boolean, federationCount: number) {
if (faction === Faction.Xenos && hasPlanetaryInstitute) {
return 6;
}
if (faction === Faction.Ivits) {
return 7 * (1 + federationCount);
}
return 7;
}

export function parseFederationLocation(location: string, map: SpaceMap) {
const coords = location.split(",").map((loc) => map.parse(loc));

for (const coord of coords) {
assert(map.grid.get(coord), `Coord ${coord.q}x${coord.r} is not part of the map`);
}

const hexes: GaiaHex[] = uniq(coords.map((coord) => map.grid.get(coord)));

assert(hexes.length === coords.length, "There are repeating coordinates in the given federation");
return hexes;
}
Loading