@pompidup/kingdomino-engine 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -17
- package/dist/index.d.ts +81 -4
- package/dist/index.js +346 -145
- package/dist/index.js.map +1 -1
- package/dist/index.universal.js +346 -145
- package/dist/wasm-bridge.js +325 -135
- package/package.json +1 -1
package/dist/wasm-bridge.js
CHANGED
|
@@ -161,6 +161,12 @@ var wrapWithDebug = (engine2, options) => {
|
|
|
161
161
|
logger,
|
|
162
162
|
level
|
|
163
163
|
),
|
|
164
|
+
getValidDominoPicks: wrapMethod(
|
|
165
|
+
"getValidDominoPicks",
|
|
166
|
+
engine2.getValidDominoPicks,
|
|
167
|
+
logger,
|
|
168
|
+
level
|
|
169
|
+
),
|
|
164
170
|
serialize: wrapMethod("serialize", engine2.serialize, logger, level),
|
|
165
171
|
deserialize: wrapMethod("deserialize", engine2.deserialize, logger, level),
|
|
166
172
|
getDynastyResults: wrapMethod(
|
|
@@ -221,6 +227,12 @@ var wrapWithDebug = (engine2, options) => {
|
|
|
221
227
|
logger,
|
|
222
228
|
level
|
|
223
229
|
),
|
|
230
|
+
getValidDragonTargets: wrapMethod(
|
|
231
|
+
"getValidDragonTargets",
|
|
232
|
+
engine2.getValidDragonTargets,
|
|
233
|
+
logger,
|
|
234
|
+
level
|
|
235
|
+
),
|
|
224
236
|
getValidCavemanPlacements: wrapMethod(
|
|
225
237
|
"getValidCavemanPlacements",
|
|
226
238
|
engine2.getValidCavemanPlacements,
|
|
@@ -266,6 +278,23 @@ var wrapWithEvents = (engine2, callbacks) => {
|
|
|
266
278
|
callbacks.onGameEnd?.({ game: after });
|
|
267
279
|
}
|
|
268
280
|
};
|
|
281
|
+
const emitNeolithicPair = (before, after, lordId) => {
|
|
282
|
+
if (!callbacks.onNeolithicPairChosen) return;
|
|
283
|
+
if (before.currentDominoes.some((d) => d.reservedFor !== void 0)) return;
|
|
284
|
+
const playerId = before.lords.find((l) => l.id === lordId)?.playerId;
|
|
285
|
+
const reserved = after.currentDominoes.filter(
|
|
286
|
+
(d) => d.reservedFor !== void 0
|
|
287
|
+
);
|
|
288
|
+
if (!playerId || reserved.length === 0) return;
|
|
289
|
+
const numbersFor = (own) => reserved.filter((d) => d.reservedFor === playerId === own).map((d) => d.domino.number);
|
|
290
|
+
callbacks.onNeolithicPairChosen({
|
|
291
|
+
game: after,
|
|
292
|
+
lordId,
|
|
293
|
+
playerId,
|
|
294
|
+
dominoNumbers: numbersFor(true),
|
|
295
|
+
opponentDominoNumbers: numbersFor(false)
|
|
296
|
+
});
|
|
297
|
+
};
|
|
269
298
|
const gameplay = (run, emit) => (cmd) => {
|
|
270
299
|
const game = run(cmd);
|
|
271
300
|
emit(game, cmd);
|
|
@@ -279,6 +308,7 @@ var wrapWithEvents = (engine2, callbacks) => {
|
|
|
279
308
|
calculateScore: (cmd) => engine2.calculateScore(cmd),
|
|
280
309
|
getValidPlacements: (cmd) => engine2.getValidPlacements(cmd),
|
|
281
310
|
canPlaceDomino: (cmd) => engine2.canPlaceDomino(cmd),
|
|
311
|
+
getValidDominoPicks: (cmd) => engine2.getValidDominoPicks(cmd),
|
|
282
312
|
serialize: (cmd) => engine2.serialize(cmd),
|
|
283
313
|
deserialize: (cmd) => engine2.deserialize(cmd),
|
|
284
314
|
getResults: (cmd) => engine2.getResults(cmd),
|
|
@@ -287,6 +317,7 @@ var wrapWithEvents = (engine2, callbacks) => {
|
|
|
287
317
|
getValidKnightPositions: (cmd) => engine2.getValidKnightPositions(cmd),
|
|
288
318
|
getValidBuildingPositions: (cmd) => engine2.getValidBuildingPositions(cmd),
|
|
289
319
|
getAffordableBuildings: (cmd) => engine2.getAffordableBuildings(cmd),
|
|
320
|
+
getValidDragonTargets: (cmd) => engine2.getValidDragonTargets(cmd),
|
|
290
321
|
getValidCavemanPlacements: (cmd) => engine2.getValidCavemanPlacements(cmd),
|
|
291
322
|
getValidGiantPositions: (cmd) => engine2.getValidGiantPositions(cmd),
|
|
292
323
|
getValidGiantTargets: (cmd) => engine2.getValidGiantTargets(cmd),
|
|
@@ -317,11 +348,14 @@ var wrapWithEvents = (engine2, callbacks) => {
|
|
|
317
348
|
// Gameplay methods — specific event, then derived turn / game-end events
|
|
318
349
|
chooseDomino: gameplay(
|
|
319
350
|
(cmd) => engine2.chooseDomino(cmd),
|
|
320
|
-
(game, cmd) =>
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
351
|
+
(game, cmd) => {
|
|
352
|
+
callbacks.onDominoPicked?.({
|
|
353
|
+
game,
|
|
354
|
+
lordId: cmd.lordId,
|
|
355
|
+
dominoNumber: cmd.dominoPick
|
|
356
|
+
});
|
|
357
|
+
emitNeolithicPair(cmd.game, game, cmd.lordId);
|
|
358
|
+
}
|
|
325
359
|
),
|
|
326
360
|
placeDomino: gameplay(
|
|
327
361
|
(cmd) => engine2.placeDomino(cmd),
|
|
@@ -3854,6 +3888,29 @@ var rules_default = {
|
|
|
3854
3888
|
dominoesDiscardedPerTurn: 0
|
|
3855
3889
|
}
|
|
3856
3890
|
],
|
|
3891
|
+
modes: {
|
|
3892
|
+
QueenDomino: [
|
|
3893
|
+
{
|
|
3894
|
+
playersLimit: 2,
|
|
3895
|
+
lordsPerPlayer: 2,
|
|
3896
|
+
maxDominoes: 48,
|
|
3897
|
+
dominoesPerTurn: 4,
|
|
3898
|
+
maxTurns: 12,
|
|
3899
|
+
maxKingdomSize: 7
|
|
3900
|
+
}
|
|
3901
|
+
],
|
|
3902
|
+
"AgeOfGiants-QueenDomino": [
|
|
3903
|
+
{
|
|
3904
|
+
playersLimit: 2,
|
|
3905
|
+
lordsPerPlayer: 2,
|
|
3906
|
+
maxDominoes: 60,
|
|
3907
|
+
dominoesPerTurn: 5,
|
|
3908
|
+
maxTurns: 12,
|
|
3909
|
+
maxKingdomSize: 7,
|
|
3910
|
+
dominoesDiscardedPerTurn: 1
|
|
3911
|
+
}
|
|
3912
|
+
]
|
|
3913
|
+
},
|
|
3857
3914
|
extra: [
|
|
3858
3915
|
{
|
|
3859
3916
|
name: "The middle Kingdom",
|
|
@@ -3967,6 +4024,24 @@ var jsonRules = () => {
|
|
|
3967
4024
|
};
|
|
3968
4025
|
});
|
|
3969
4026
|
}
|
|
4027
|
+
const modeRules = rules_default.modes ?? {};
|
|
4028
|
+
for (const [modeName, modeEntries] of Object.entries(modeRules)) {
|
|
4029
|
+
rules.modeBasic ??= {};
|
|
4030
|
+
const byPlayers = {};
|
|
4031
|
+
modeEntries.forEach((rule) => {
|
|
4032
|
+
byPlayers[rule.playersLimit] = {
|
|
4033
|
+
lords: rule.lordsPerPlayer,
|
|
4034
|
+
maxDominoes: rule.maxDominoes,
|
|
4035
|
+
dominoesPerTurn: rule.dominoesPerTurn,
|
|
4036
|
+
maxTurns: rule.maxTurns,
|
|
4037
|
+
maxKingdomSize: rule.maxKingdomSize,
|
|
4038
|
+
...rule.dominoesDiscardedPerTurn !== void 0 && {
|
|
4039
|
+
dominoesDiscardedPerTurn: rule.dominoesDiscardedPerTurn
|
|
4040
|
+
}
|
|
4041
|
+
};
|
|
4042
|
+
});
|
|
4043
|
+
rules.modeBasic[modeName] = byPlayers;
|
|
4044
|
+
}
|
|
3970
4045
|
extraRules.forEach((rule) => {
|
|
3971
4046
|
rules.extraRules.push({
|
|
3972
4047
|
name: rule.name,
|
|
@@ -4026,6 +4101,7 @@ var ErrorCode = {
|
|
|
4026
4101
|
CANNOT_PICK: "CANNOT_PICK",
|
|
4027
4102
|
CANNOT_PLACE: "CANNOT_PLACE",
|
|
4028
4103
|
DOMINO_ALREADY_PICKED: "DOMINO_ALREADY_PICKED",
|
|
4104
|
+
DOMINO_RESERVED: "DOMINO_RESERVED",
|
|
4029
4105
|
// History errors
|
|
4030
4106
|
UNDO_UNAVAILABLE: "UNDO_UNAVAILABLE",
|
|
4031
4107
|
REDO_UNAVAILABLE: "REDO_UNAVAILABLE",
|
|
@@ -4127,6 +4203,7 @@ var TranslationKey = {
|
|
|
4127
4203
|
ERROR_CANNOT_PICK: "error.cannotPick",
|
|
4128
4204
|
ERROR_CANNOT_PLACE: "error.cannotPlace",
|
|
4129
4205
|
ERROR_DOMINO_ALREADY_PICKED: "error.dominoAlreadyPicked",
|
|
4206
|
+
ERROR_DOMINO_RESERVED: "error.dominoReserved",
|
|
4130
4207
|
ERROR_INVALID_PLAYER_COUNT: "error.invalidPlayerCount",
|
|
4131
4208
|
ERROR_INVALID_PLAYER_NAME: "error.invalidPlayerName",
|
|
4132
4209
|
ERROR_EXTRA_RULE_NOT_AVAILABLE: "error.extraRuleNotAvailable",
|
|
@@ -4227,6 +4304,7 @@ var TranslationKey = {
|
|
|
4227
4304
|
// hunter-gatherers reuse the CAVEMAN_* keys)
|
|
4228
4305
|
SCORE_QUEEN: "score.queen",
|
|
4229
4306
|
SCORE_BUILDING: "score.building",
|
|
4307
|
+
SCORE_WEALTH: "score.wealth",
|
|
4230
4308
|
SCORE_TOTEM_MAMMOTH: "score.totemMammoth",
|
|
4231
4309
|
SCORE_TOTEM_FISH: "score.totemFish",
|
|
4232
4310
|
SCORE_TOTEM_MUSHROOM: "score.totemMushroom",
|
|
@@ -4261,6 +4339,7 @@ var defaultTranslations = {
|
|
|
4261
4339
|
[TranslationKey.ERROR_CANNOT_PICK]: "Cannot pick a domino now",
|
|
4262
4340
|
[TranslationKey.ERROR_CANNOT_PLACE]: "Cannot place a domino now",
|
|
4263
4341
|
[TranslationKey.ERROR_DOMINO_ALREADY_PICKED]: "Domino has already been picked",
|
|
4342
|
+
[TranslationKey.ERROR_DOMINO_RESERVED]: "This domino is reserved for the other player (Neolithic: pairs 1 & 4 and 2 & 3)",
|
|
4264
4343
|
[TranslationKey.ERROR_INVALID_PLAYER_COUNT]: "Invalid number of players (1-4 required)",
|
|
4265
4344
|
[TranslationKey.ERROR_INVALID_PLAYER_NAME]: "Player name must be at least 3 characters",
|
|
4266
4345
|
[TranslationKey.ERROR_EXTRA_RULE_NOT_AVAILABLE]: "Extra rule is not available for this game mode or number of players",
|
|
@@ -4359,6 +4438,7 @@ var defaultTranslations = {
|
|
|
4359
4438
|
// Score breakdown labels
|
|
4360
4439
|
[TranslationKey.SCORE_QUEEN]: "Queen",
|
|
4361
4440
|
[TranslationKey.SCORE_BUILDING]: "Building: {{name}}",
|
|
4441
|
+
[TranslationKey.SCORE_WEALTH]: "Wealth",
|
|
4362
4442
|
[TranslationKey.SCORE_TOTEM_MAMMOTH]: "Mammoth totem",
|
|
4363
4443
|
[TranslationKey.SCORE_TOTEM_FISH]: "Fish totem",
|
|
4364
4444
|
[TranslationKey.SCORE_TOTEM_MUSHROOM]: "Mushroom totem",
|
|
@@ -4393,6 +4473,7 @@ var frenchTranslations = {
|
|
|
4393
4473
|
[TranslationKey.ERROR_CANNOT_PICK]: "Impossible de choisir un domino maintenant",
|
|
4394
4474
|
[TranslationKey.ERROR_CANNOT_PLACE]: "Impossible de placer un domino maintenant",
|
|
4395
4475
|
[TranslationKey.ERROR_DOMINO_ALREADY_PICKED]: "Ce domino a d\xE9j\xE0 \xE9t\xE9 choisi",
|
|
4476
|
+
[TranslationKey.ERROR_DOMINO_RESERVED]: "Ce domino est r\xE9serv\xE9 \xE0 l'autre joueur (N\xE9olithique : paires 1 et 4, 2 et 3)",
|
|
4396
4477
|
[TranslationKey.ERROR_INVALID_PLAYER_COUNT]: "Nombre de joueurs invalide (1 \xE0 4 requis)",
|
|
4397
4478
|
[TranslationKey.ERROR_INVALID_PLAYER_NAME]: "Le nom du joueur doit contenir au moins 3 caract\xE8res",
|
|
4398
4479
|
[TranslationKey.ERROR_EXTRA_RULE_NOT_AVAILABLE]: "Cette r\xE8gle optionnelle n'est pas disponible pour ce mode ou ce nombre de joueurs",
|
|
@@ -4491,6 +4572,7 @@ var frenchTranslations = {
|
|
|
4491
4572
|
// Score breakdown labels
|
|
4492
4573
|
[TranslationKey.SCORE_QUEEN]: "Reine",
|
|
4493
4574
|
[TranslationKey.SCORE_BUILDING]: "B\xE2timent : {{name}}",
|
|
4575
|
+
[TranslationKey.SCORE_WEALTH]: "Richesse",
|
|
4494
4576
|
[TranslationKey.SCORE_TOTEM_MAMMOTH]: "Totem mammouth",
|
|
4495
4577
|
[TranslationKey.SCORE_TOTEM_FISH]: "Totem poisson",
|
|
4496
4578
|
[TranslationKey.SCORE_TOTEM_MUSHROOM]: "Totem champignon",
|
|
@@ -4597,6 +4679,7 @@ var errorCodeToTranslationKey = {
|
|
|
4597
4679
|
[ErrorCode.CANNOT_PICK]: TranslationKey.ERROR_CANNOT_PICK,
|
|
4598
4680
|
[ErrorCode.CANNOT_PLACE]: TranslationKey.ERROR_CANNOT_PLACE,
|
|
4599
4681
|
[ErrorCode.DOMINO_ALREADY_PICKED]: TranslationKey.ERROR_DOMINO_ALREADY_PICKED,
|
|
4682
|
+
[ErrorCode.DOMINO_RESERVED]: TranslationKey.ERROR_DOMINO_RESERVED,
|
|
4600
4683
|
[ErrorCode.UNDO_UNAVAILABLE]: TranslationKey.ERROR_UNDO_UNAVAILABLE,
|
|
4601
4684
|
[ErrorCode.REDO_UNAVAILABLE]: TranslationKey.ERROR_REDO_UNAVAILABLE,
|
|
4602
4685
|
[ErrorCode.CANNOT_PLACE_KNIGHT]: TranslationKey.ERROR_CANNOT_PLACE_KNIGHT,
|
|
@@ -5609,7 +5692,7 @@ var addExtraRulesUseCase = (deps) => (game, rules) => {
|
|
|
5609
5692
|
}
|
|
5610
5693
|
let updatedBasic = game.rules.basic;
|
|
5611
5694
|
let updatedDominoes = game.dominoes;
|
|
5612
|
-
const hasLargeKingdomRule = newExtraRules.some(isLargeKingdomExtraRule);
|
|
5695
|
+
const hasLargeKingdomRule = newExtraRules.some(isLargeKingdomExtraRule) && game.rules.basic.maxKingdomSize < 7;
|
|
5613
5696
|
if (hasLargeKingdomRule) {
|
|
5614
5697
|
updatedBasic = {
|
|
5615
5698
|
...updatedBasic,
|
|
@@ -5660,7 +5743,10 @@ var isFootprintDomino = (domino) => (domino.left.hasFootprint ?? false) || (domi
|
|
|
5660
5743
|
|
|
5661
5744
|
// src/core/domain/entities/economy.ts
|
|
5662
5745
|
var STARTING_COINS = 7;
|
|
5663
|
-
var
|
|
5746
|
+
var STARTING_KNIGHTS = 1;
|
|
5747
|
+
var DRAGON_COST = 1;
|
|
5748
|
+
var QUEEN_DISCOUNT = 1;
|
|
5749
|
+
var COINS_PER_WEALTH_POINT = 3;
|
|
5664
5750
|
var addCoins = (player, amount) => ({
|
|
5665
5751
|
...player,
|
|
5666
5752
|
coins: (player.coins ?? 0) + amount
|
|
@@ -5679,7 +5765,8 @@ var addBonusKnights = (player, amount) => ({
|
|
|
5679
5765
|
...player,
|
|
5680
5766
|
bonusKnights: (player.bonusKnights ?? 0) + amount
|
|
5681
5767
|
});
|
|
5682
|
-
var getMaxKnights = (player) =>
|
|
5768
|
+
var getMaxKnights = (player) => STARTING_KNIGHTS + (player.bonusKnights ?? 0);
|
|
5769
|
+
var getWealthPoints = (player) => Math.floor(Math.max(0, player.coins ?? 0) / COINS_PER_WEALTH_POINT);
|
|
5683
5770
|
|
|
5684
5771
|
// src/core/domain/entities/domino.ts
|
|
5685
5772
|
var isGround = (ground) => {
|
|
@@ -6034,7 +6121,7 @@ var addPlayersUseCase = (deps) => (game, players) => {
|
|
|
6034
6121
|
newPlayers.push(newPlayer.value);
|
|
6035
6122
|
}
|
|
6036
6123
|
const rules = ruleRepository.getAll();
|
|
6037
|
-
const basicRules = isAoG && rules.aogBasic ? rules.aogBasic[newPlayers.length] : rules.basic[newPlayers.length];
|
|
6124
|
+
const basicRules = rules.modeBasic?.[game.mode.name]?.[newPlayers.length] ?? (isAoG && rules.aogBasic ? rules.aogBasic[newPlayers.length] : rules.basic[newPlayers.length]);
|
|
6038
6125
|
if (!basicRules) {
|
|
6039
6126
|
return err(ErrorCode.STEP_EXECUTION_FAILED);
|
|
6040
6127
|
}
|
|
@@ -6293,7 +6380,15 @@ var calculateOriginsFireBonus = (kingdom, placedFireTokens) => {
|
|
|
6293
6380
|
};
|
|
6294
6381
|
|
|
6295
6382
|
// src/core/domain/entities/queen.ts
|
|
6296
|
-
var determineQueenHolder = (players, currentHolderId) => {
|
|
6383
|
+
var determineQueenHolder = (players, currentHolderId, actingPlayerId) => {
|
|
6384
|
+
if (actingPlayerId !== void 0) {
|
|
6385
|
+
const acting = players.find((p) => p.id === actingPlayerId);
|
|
6386
|
+
if (!acting) return currentHolderId;
|
|
6387
|
+
const towers = getTowerCount(acting);
|
|
6388
|
+
const others = players.filter((p) => p.id !== actingPlayerId);
|
|
6389
|
+
const reachesMost = towers > 0 && others.every((p) => getTowerCount(p) <= towers);
|
|
6390
|
+
return reachesMost ? actingPlayerId : currentHolderId;
|
|
6391
|
+
}
|
|
6297
6392
|
let maxTowers = 0;
|
|
6298
6393
|
let leaderId = null;
|
|
6299
6394
|
let isTied = false;
|
|
@@ -6805,6 +6900,12 @@ var queendominoBonuses = (game, player, provisional) => {
|
|
|
6805
6900
|
provisional
|
|
6806
6901
|
});
|
|
6807
6902
|
}
|
|
6903
|
+
bonuses.push({
|
|
6904
|
+
type: "wealth",
|
|
6905
|
+
label: TranslationKey.SCORE_WEALTH,
|
|
6906
|
+
points: getWealthPoints(player),
|
|
6907
|
+
provisional
|
|
6908
|
+
});
|
|
6808
6909
|
for (const placed of player.buildings ?? []) {
|
|
6809
6910
|
if (!placed.building.endGameScoring) continue;
|
|
6810
6911
|
bonuses.push({
|
|
@@ -7000,31 +7101,58 @@ var calculateScoreUseCase = (kingdom, giantPositions) => {
|
|
|
7000
7101
|
};
|
|
7001
7102
|
|
|
7002
7103
|
// src/core/domain/entities/building.ts
|
|
7003
|
-
var
|
|
7104
|
+
var BUILDERS_BOARD_SLOT_COSTS = [5, 4, 3, 2, 1, 0];
|
|
7105
|
+
var BOARD_SLOTS = BUILDERS_BOARD_SLOT_COSTS.length;
|
|
7004
7106
|
var createBuildersBoard = (tiles, shuffle) => {
|
|
7005
7107
|
const shuffled = shuffle([...tiles]);
|
|
7006
7108
|
const slots = shuffled.slice(0, BOARD_SLOTS);
|
|
7109
|
+
while (slots.length < BOARD_SLOTS) slots.push(null);
|
|
7007
7110
|
const drawPile = shuffled.slice(BOARD_SLOTS);
|
|
7008
7111
|
return {
|
|
7009
7112
|
slots,
|
|
7010
|
-
drawPile
|
|
7113
|
+
drawPile,
|
|
7114
|
+
slotCosts: [...BUILDERS_BOARD_SLOT_COSTS]
|
|
7011
7115
|
};
|
|
7012
7116
|
};
|
|
7013
7117
|
var refillBuildersBoard = (board) => {
|
|
7014
|
-
const newSlots = [...board.slots];
|
|
7015
7118
|
const newDrawPile = [...board.drawPile];
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7119
|
+
if (!board.slotCosts) {
|
|
7120
|
+
const newSlots2 = [...board.slots];
|
|
7121
|
+
for (let i = 0; i < newSlots2.length; i++) {
|
|
7122
|
+
if (newSlots2[i] === null && newDrawPile.length > 0) {
|
|
7123
|
+
newSlots2[i] = newDrawPile.shift();
|
|
7124
|
+
}
|
|
7019
7125
|
}
|
|
7126
|
+
return { slots: newSlots2, drawPile: newDrawPile };
|
|
7127
|
+
}
|
|
7128
|
+
const remaining = board.slots.filter(
|
|
7129
|
+
(slot) => slot !== null
|
|
7130
|
+
);
|
|
7131
|
+
const empty = board.slots.length - remaining.length;
|
|
7132
|
+
const newSlots = [
|
|
7133
|
+
...Array.from({ length: empty }, () => null),
|
|
7134
|
+
...remaining
|
|
7135
|
+
];
|
|
7136
|
+
for (let i = empty - 1; i >= 0 && newDrawPile.length > 0; i--) {
|
|
7137
|
+
newSlots[i] = newDrawPile.shift();
|
|
7020
7138
|
}
|
|
7021
|
-
return { slots: newSlots, drawPile: newDrawPile };
|
|
7139
|
+
return { ...board, slots: newSlots, drawPile: newDrawPile };
|
|
7022
7140
|
};
|
|
7023
7141
|
var startNewQueenDominoRound = (state) => ({
|
|
7024
7142
|
...state,
|
|
7025
7143
|
buildersBoard: refillBuildersBoard(state.buildersBoard),
|
|
7026
7144
|
dragonUsedThisRound: false
|
|
7027
7145
|
});
|
|
7146
|
+
var getSlotCost = (board, slotIndex) => {
|
|
7147
|
+
const building = board.slots[slotIndex];
|
|
7148
|
+
if (!building) return null;
|
|
7149
|
+
return board.slotCosts?.[slotIndex] ?? building.cost;
|
|
7150
|
+
};
|
|
7151
|
+
var getConstructionCost = (board, slotIndex, isQueenHost) => {
|
|
7152
|
+
const cost = getSlotCost(board, slotIndex);
|
|
7153
|
+
if (cost === null) return null;
|
|
7154
|
+
return Math.max(0, cost - (isQueenHost ? QUEEN_DISCOUNT : 0));
|
|
7155
|
+
};
|
|
7028
7156
|
var findBuildingOnBoard = (board, buildingId) => {
|
|
7029
7157
|
for (let i = 0; i < board.slots.length; i++) {
|
|
7030
7158
|
const slot = board.slots[i];
|
|
@@ -7039,12 +7167,17 @@ var removeBuildingFromBoard = (board, slotIndex) => {
|
|
|
7039
7167
|
newSlots[slotIndex] = null;
|
|
7040
7168
|
return { ...board, slots: newSlots };
|
|
7041
7169
|
};
|
|
7042
|
-
var purchaseBuilding = (board, player, buildingId) => {
|
|
7170
|
+
var purchaseBuilding = (board, player, buildingId, queenHolderId = null) => {
|
|
7043
7171
|
const found = findBuildingOnBoard(board, buildingId);
|
|
7044
7172
|
if (!found) return null;
|
|
7045
7173
|
const { building, slotIndex } = found;
|
|
7046
|
-
|
|
7047
|
-
|
|
7174
|
+
const cost = getConstructionCost(
|
|
7175
|
+
board,
|
|
7176
|
+
slotIndex,
|
|
7177
|
+
queenHolderId !== null && queenHolderId === player.id
|
|
7178
|
+
);
|
|
7179
|
+
if (cost === null || !canAfford(player, cost)) return null;
|
|
7180
|
+
let updatedPlayer = removeCoins(player, cost);
|
|
7048
7181
|
if (building.immediateBonus) {
|
|
7049
7182
|
switch (building.immediateBonus.type) {
|
|
7050
7183
|
case "coins":
|
|
@@ -7068,7 +7201,7 @@ var purchaseBuilding = (board, player, buildingId) => {
|
|
|
7068
7201
|
updatedPlayer = addTowers(updatedPlayer, building.towers);
|
|
7069
7202
|
}
|
|
7070
7203
|
const updatedBoard = removeBuildingFromBoard(board, slotIndex);
|
|
7071
|
-
return { updatedBoard, updatedPlayer, building };
|
|
7204
|
+
return { updatedBoard, updatedPlayer, building, cost };
|
|
7072
7205
|
};
|
|
7073
7206
|
var isValidConstructionPosition = (kingdom, placedBuildings, position) => {
|
|
7074
7207
|
const tile = kingdom[position.y]?.[position.x];
|
|
@@ -7094,9 +7227,9 @@ var placeBuildingOnKingdom = (kingdom, building, position) => {
|
|
|
7094
7227
|
var destroyWithDragon = (board, buildingId, player) => {
|
|
7095
7228
|
const found = findBuildingOnBoard(board, buildingId);
|
|
7096
7229
|
if (!found) return null;
|
|
7097
|
-
const {
|
|
7098
|
-
if (!canAfford(player,
|
|
7099
|
-
const updatedPlayer = removeCoins(player,
|
|
7230
|
+
const { slotIndex } = found;
|
|
7231
|
+
if (!canAfford(player, DRAGON_COST)) return null;
|
|
7232
|
+
const updatedPlayer = removeCoins(player, DRAGON_COST);
|
|
7100
7233
|
const updatedBoard = removeBuildingFromBoard(board, slotIndex);
|
|
7101
7234
|
return { updatedBoard, updatedPlayer };
|
|
7102
7235
|
};
|
|
@@ -7215,6 +7348,30 @@ var nextOriginsAction = (currentAction, isTribe) => {
|
|
|
7215
7348
|
return sequence[index + 1];
|
|
7216
7349
|
};
|
|
7217
7350
|
|
|
7351
|
+
// src/core/domain/entities/neolithic.ts
|
|
7352
|
+
var NEOLITHIC_RULE_NAME = "Neolithic";
|
|
7353
|
+
var NEOLITHIC_PAIRS = [
|
|
7354
|
+
[1, 4],
|
|
7355
|
+
[2, 3]
|
|
7356
|
+
];
|
|
7357
|
+
var NEOLITHIC_LINE_SIZE = 4;
|
|
7358
|
+
var isNeolithicGame = (game) => game.players.length === 2 && game.rules.extra.some((rule) => rule.name === NEOLITHIC_RULE_NAME);
|
|
7359
|
+
var neolithicPairOf = (position) => NEOLITHIC_PAIRS.find((pair) => pair.includes(position));
|
|
7360
|
+
var isNeolithicPairChoice = (game, line) => isNeolithicGame(game) && line.length === NEOLITHIC_LINE_SIZE && line.every((domino) => !domino.picked && domino.reservedFor === void 0);
|
|
7361
|
+
var canLordPickRevealed = (lord, domino) => !domino.picked && (domino.reservedFor === void 0 || domino.reservedFor === lord.playerId);
|
|
7362
|
+
var reserveNeolithicPairs = (game, line, lord, pickedNumber) => {
|
|
7363
|
+
if (!isNeolithicPairChoice(game, line)) return line;
|
|
7364
|
+
const picked = line.find((domino) => domino.domino.number === pickedNumber);
|
|
7365
|
+
const pair = picked && neolithicPairOf(picked.position);
|
|
7366
|
+
const opponent = game.players.find((player) => player.id !== lord.playerId);
|
|
7367
|
+
if (!pair || !opponent) return line;
|
|
7368
|
+
return line.map((domino) => ({
|
|
7369
|
+
...domino,
|
|
7370
|
+
reservedFor: pair.includes(domino.position) ? lord.playerId : opponent.id
|
|
7371
|
+
}));
|
|
7372
|
+
};
|
|
7373
|
+
var pickableDominoNumbers = (line, lord) => line.filter((domino) => canLordPickRevealed(lord, domino)).map((domino) => domino.domino.number);
|
|
7374
|
+
|
|
7218
7375
|
// src/core/useCases/chooseDomino.ts
|
|
7219
7376
|
var chooseDominoUseCase = (game, lordId, dominoPick) => {
|
|
7220
7377
|
const currentLord = game.lords.find(
|
|
@@ -7238,7 +7395,15 @@ var chooseDominoUseCase = (game, lordId, dominoPick) => {
|
|
|
7238
7395
|
if (selectedDomino.picked) {
|
|
7239
7396
|
return err(ErrorCode.DOMINO_ALREADY_PICKED);
|
|
7240
7397
|
}
|
|
7241
|
-
|
|
7398
|
+
if (selectedDomino.reservedFor !== void 0 && selectedDomino.reservedFor !== currentLord.playerId) {
|
|
7399
|
+
return err(ErrorCode.DOMINO_RESERVED);
|
|
7400
|
+
}
|
|
7401
|
+
const updatedCurrentDominoes = reserveNeolithicPairs(
|
|
7402
|
+
game,
|
|
7403
|
+
game.currentDominoes,
|
|
7404
|
+
currentLord,
|
|
7405
|
+
dominoPick
|
|
7406
|
+
).map(
|
|
7242
7407
|
(domino) => domino.domino.number === dominoPick ? { ...domino, picked: true, lordId: currentLord.id } : domino
|
|
7243
7408
|
);
|
|
7244
7409
|
const isTribe = isTribeMode(game.mode.name);
|
|
@@ -7348,7 +7513,8 @@ var constructBuildingUseCase = (game, lordId, buildingId, position) => {
|
|
|
7348
7513
|
const purchaseResult = purchaseBuilding(
|
|
7349
7514
|
queendomino.buildersBoard,
|
|
7350
7515
|
currentPlayer,
|
|
7351
|
-
buildingId
|
|
7516
|
+
buildingId,
|
|
7517
|
+
queendomino.queenHolderId
|
|
7352
7518
|
);
|
|
7353
7519
|
if (!purchaseResult) {
|
|
7354
7520
|
return err(ErrorCode.CANNOT_AFFORD_BUILDING);
|
|
@@ -7370,7 +7536,8 @@ var constructBuildingUseCase = (game, lordId, buildingId, position) => {
|
|
|
7370
7536
|
);
|
|
7371
7537
|
const newQueenHolder = determineQueenHolder(
|
|
7372
7538
|
updatedPlayers,
|
|
7373
|
-
queendomino.queenHolderId
|
|
7539
|
+
queendomino.queenHolderId,
|
|
7540
|
+
currentPlayer.id
|
|
7374
7541
|
);
|
|
7375
7542
|
const nextAction = nextQueenDominoAction(playerActions.constructBuilding);
|
|
7376
7543
|
return ok({
|
|
@@ -7900,55 +8067,18 @@ var getResultUseCase = (game) => {
|
|
|
7900
8067
|
};
|
|
7901
8068
|
|
|
7902
8069
|
// src/core/domain/entities/knight.ts
|
|
7903
|
-
var
|
|
7904
|
-
const rows = kingdom.length;
|
|
7905
|
-
const cols = kingdom[0]?.length ?? 0;
|
|
8070
|
+
var countTerritorySquares = (kingdom, position) => {
|
|
7906
8071
|
const startTile = kingdom[position.y]?.[position.x];
|
|
7907
8072
|
if (!startTile || startTile.type === "empty" || startTile.type === "castle") {
|
|
7908
8073
|
return 0;
|
|
7909
8074
|
}
|
|
7910
|
-
const terrainType = startTile.type;
|
|
7911
|
-
const visited = kingdom.map((row) => row.map(() => false));
|
|
7912
|
-
const queue = [[position.x, position.y]];
|
|
7913
|
-
visited[position.y][position.x] = true;
|
|
7914
|
-
let count = 0;
|
|
7915
|
-
const directions = [
|
|
7916
|
-
[-1, 0],
|
|
7917
|
-
[1, 0],
|
|
7918
|
-
[0, -1],
|
|
7919
|
-
[0, 1]
|
|
7920
|
-
];
|
|
7921
|
-
while (queue.length > 0) {
|
|
7922
|
-
const [cx, cy] = queue.pop();
|
|
7923
|
-
const tile = kingdom[cy][cx];
|
|
7924
|
-
if (tile.type !== "empty" && tile.type !== "castle" && "hasConstructionSquare" in tile && tile.hasConstructionSquare) {
|
|
7925
|
-
count++;
|
|
7926
|
-
}
|
|
7927
|
-
for (const [dx, dy] of directions) {
|
|
7928
|
-
const nx = cx + dx;
|
|
7929
|
-
const ny = cy + dy;
|
|
7930
|
-
if (nx >= 0 && ny >= 0 && nx < cols && ny < rows && !visited[ny][nx] && kingdom[ny][nx].type === terrainType) {
|
|
7931
|
-
visited[ny][nx] = true;
|
|
7932
|
-
queue.push([nx, ny]);
|
|
7933
|
-
}
|
|
7934
|
-
}
|
|
7935
|
-
}
|
|
7936
|
-
return count;
|
|
7937
|
-
};
|
|
7938
|
-
var getTerritoryTerrain = (kingdom, position) => {
|
|
7939
|
-
const tile = kingdom[position.y]?.[position.x];
|
|
7940
|
-
if (!tile || tile.type === "empty" || tile.type === "castle") return null;
|
|
7941
|
-
return tile.type;
|
|
7942
|
-
};
|
|
7943
|
-
var territoryHasKnight = (kingdom, position, knights) => {
|
|
7944
|
-
const terrain = getTerritoryTerrain(kingdom, position);
|
|
7945
|
-
if (!terrain) return false;
|
|
7946
8075
|
const rows = kingdom.length;
|
|
7947
8076
|
const cols = kingdom[0]?.length ?? 0;
|
|
8077
|
+
const terrain = startTile.type;
|
|
7948
8078
|
const visited = kingdom.map((row) => row.map(() => false));
|
|
7949
8079
|
const queue = [[position.x, position.y]];
|
|
7950
8080
|
visited[position.y][position.x] = true;
|
|
7951
|
-
|
|
8081
|
+
let count = 0;
|
|
7952
8082
|
const directions = [
|
|
7953
8083
|
[-1, 0],
|
|
7954
8084
|
[1, 0],
|
|
@@ -7957,7 +8087,7 @@ var territoryHasKnight = (kingdom, position, knights) => {
|
|
|
7957
8087
|
];
|
|
7958
8088
|
while (queue.length > 0) {
|
|
7959
8089
|
const [cx, cy] = queue.pop();
|
|
7960
|
-
|
|
8090
|
+
count++;
|
|
7961
8091
|
for (const [dx, dy] of directions) {
|
|
7962
8092
|
const nx = cx + dx;
|
|
7963
8093
|
const ny = cy + dy;
|
|
@@ -7967,10 +8097,9 @@ var territoryHasKnight = (kingdom, position, knights) => {
|
|
|
7967
8097
|
}
|
|
7968
8098
|
}
|
|
7969
8099
|
}
|
|
7970
|
-
return
|
|
7971
|
-
(k) => territoryPositions.has(`${k.position.x},${k.position.y}`)
|
|
7972
|
-
);
|
|
8100
|
+
return count;
|
|
7973
8101
|
};
|
|
8102
|
+
var calculateKnightTax = (kingdom, position) => countTerritorySquares(kingdom, position);
|
|
7974
8103
|
var canPlaceKnight = (player, kingdom, position, allowedPositions) => {
|
|
7975
8104
|
const knights = player.knights ?? [];
|
|
7976
8105
|
if (knights.length >= getMaxKnights(player)) return false;
|
|
@@ -7979,13 +8108,17 @@ var canPlaceKnight = (player, kingdom, position, allowedPositions) => {
|
|
|
7979
8108
|
}
|
|
7980
8109
|
const tile = kingdom[position.y]?.[position.x];
|
|
7981
8110
|
if (!tile || tile.type === "empty" || tile.type === "castle") return false;
|
|
7982
|
-
if (
|
|
8111
|
+
if (knights.some(
|
|
8112
|
+
(k) => k.position.x === position.x && k.position.y === position.y
|
|
8113
|
+
)) {
|
|
8114
|
+
return false;
|
|
8115
|
+
}
|
|
7983
8116
|
return true;
|
|
7984
8117
|
};
|
|
7985
8118
|
var placeKnight = (player, kingdom, position) => {
|
|
7986
8119
|
const knight = { playerId: player.id, position };
|
|
7987
8120
|
const knights = [...player.knights ?? [], knight];
|
|
7988
|
-
const taxAmount =
|
|
8121
|
+
const taxAmount = calculateKnightTax(kingdom, position);
|
|
7989
8122
|
const updatedPlayer = addCoins({ ...player, knights }, taxAmount);
|
|
7990
8123
|
return updatedPlayer;
|
|
7991
8124
|
};
|
|
@@ -8321,7 +8454,15 @@ var placeKnightUseCase = (game, lordId, position) => {
|
|
|
8321
8454
|
const updatedPlayers = game.players.map(
|
|
8322
8455
|
(p) => p.id === currentPlayer.id ? updatedPlayer : p
|
|
8323
8456
|
);
|
|
8324
|
-
const
|
|
8457
|
+
const canPlaceAnother = allowedPositions.some(
|
|
8458
|
+
(square) => canPlaceKnight(
|
|
8459
|
+
updatedPlayer,
|
|
8460
|
+
updatedPlayer.kingdom,
|
|
8461
|
+
square,
|
|
8462
|
+
allowedPositions
|
|
8463
|
+
)
|
|
8464
|
+
);
|
|
8465
|
+
const nextAction = canPlaceAnother ? playerActions.placeKnight : nextQueenDominoAction(playerActions.placeKnight);
|
|
8325
8466
|
return ok({
|
|
8326
8467
|
...game,
|
|
8327
8468
|
players: updatedPlayers,
|
|
@@ -8529,6 +8670,74 @@ var advanceToNextLordOrResult2 = (game) => {
|
|
|
8529
8670
|
});
|
|
8530
8671
|
};
|
|
8531
8672
|
|
|
8673
|
+
// src/core/useCases/useDragon.ts
|
|
8674
|
+
var resolveDragon = (game, lordId) => {
|
|
8675
|
+
const currentAction = game.nextAction;
|
|
8676
|
+
if (currentAction.nextLord !== lordId) {
|
|
8677
|
+
return err(ErrorCode.NOT_YOUR_TURN);
|
|
8678
|
+
}
|
|
8679
|
+
if (currentAction.nextAction !== playerActions.useDragon) {
|
|
8680
|
+
return err(ErrorCode.INVALID_OPTIONAL_ACTION);
|
|
8681
|
+
}
|
|
8682
|
+
if (!game.queendomino) {
|
|
8683
|
+
return err(ErrorCode.INVALID_OPTIONAL_ACTION);
|
|
8684
|
+
}
|
|
8685
|
+
if (!game.queendomino.dragonAvailable || game.queendomino.dragonUsedThisRound) {
|
|
8686
|
+
return err(ErrorCode.DRAGON_UNAVAILABLE);
|
|
8687
|
+
}
|
|
8688
|
+
const currentLord = game.lords.find((l) => l.id === lordId);
|
|
8689
|
+
if (!currentLord) return err(ErrorCode.LORD_NOT_FOUND);
|
|
8690
|
+
const currentPlayer = game.players.find((p) => p.id === currentLord.playerId);
|
|
8691
|
+
if (!currentPlayer) return err(ErrorCode.PLAYER_NOT_FOUND);
|
|
8692
|
+
if (game.queendomino.queenHolderId === currentPlayer.id) {
|
|
8693
|
+
return err(ErrorCode.QUEEN_HOLDER_CANNOT_USE_DRAGON);
|
|
8694
|
+
}
|
|
8695
|
+
return ok({ player: currentPlayer, queendomino: game.queendomino });
|
|
8696
|
+
};
|
|
8697
|
+
var useDragonUseCase = (game, lordId, buildingId) => {
|
|
8698
|
+
const context = resolveDragon(game, lordId);
|
|
8699
|
+
if (isErr(context)) {
|
|
8700
|
+
return context;
|
|
8701
|
+
}
|
|
8702
|
+
const { player: currentPlayer, queendomino } = context.value;
|
|
8703
|
+
const result = destroyWithDragon(
|
|
8704
|
+
queendomino.buildersBoard,
|
|
8705
|
+
buildingId,
|
|
8706
|
+
currentPlayer
|
|
8707
|
+
);
|
|
8708
|
+
if (!result) {
|
|
8709
|
+
return err(ErrorCode.BUILDING_NOT_FOUND);
|
|
8710
|
+
}
|
|
8711
|
+
const { updatedBoard, updatedPlayer } = result;
|
|
8712
|
+
const updatedPlayers = game.players.map(
|
|
8713
|
+
(p) => p.id === currentPlayer.id ? updatedPlayer : p
|
|
8714
|
+
);
|
|
8715
|
+
const maxTurns = game.rules.basic.maxTurns;
|
|
8716
|
+
const isLastTurn = game.turn === maxTurns;
|
|
8717
|
+
if (isLastTurn && allLordsHavePlayed(game.lords)) {
|
|
8718
|
+
return ok({
|
|
8719
|
+
...game,
|
|
8720
|
+
players: updatedPlayers,
|
|
8721
|
+
queendomino: {
|
|
8722
|
+
...queendomino,
|
|
8723
|
+
buildersBoard: updatedBoard,
|
|
8724
|
+
dragonUsedThisRound: true
|
|
8725
|
+
},
|
|
8726
|
+
nextAction: { type: "step", step: gameSteps.result }
|
|
8727
|
+
});
|
|
8728
|
+
}
|
|
8729
|
+
return ok({
|
|
8730
|
+
...game,
|
|
8731
|
+
players: updatedPlayers,
|
|
8732
|
+
queendomino: {
|
|
8733
|
+
...queendomino,
|
|
8734
|
+
buildersBoard: updatedBoard,
|
|
8735
|
+
dragonUsedThisRound: true
|
|
8736
|
+
},
|
|
8737
|
+
nextAction: nextLordWithAction(game.lords)
|
|
8738
|
+
});
|
|
8739
|
+
};
|
|
8740
|
+
|
|
8532
8741
|
// src/core/useCases/getValidActionTargets.ts
|
|
8533
8742
|
var turnFor = (game, action, lordId) => {
|
|
8534
8743
|
if (!isGameWithNextAction(game) || game.nextAction.nextAction !== action) {
|
|
@@ -8579,13 +8788,24 @@ var getConstructionOptions = (game, lordId) => {
|
|
|
8579
8788
|
)
|
|
8580
8789
|
);
|
|
8581
8790
|
const buildings = board.slots.filter(
|
|
8582
|
-
(slot) => slot !== null && purchaseBuilding(board, player, slot.id) !== null
|
|
8791
|
+
(slot) => slot !== null && purchaseBuilding(board, player, slot.id, queendomino.queenHolderId) !== null
|
|
8583
8792
|
);
|
|
8584
8793
|
if (positions.length === 0 || buildings.length === 0) return noConstruction();
|
|
8585
8794
|
return { positions, buildings };
|
|
8586
8795
|
};
|
|
8587
8796
|
var getValidBuildingPositionsUseCase = (game, lordId) => getConstructionOptions(game, lordId).positions;
|
|
8588
8797
|
var getAffordableBuildingsUseCase = (game, lordId) => getConstructionOptions(game, lordId).buildings;
|
|
8798
|
+
var getValidDragonTargetsUseCase = (game, lordId) => {
|
|
8799
|
+
const turn = turnFor(game, playerActions.useDragon, lordId);
|
|
8800
|
+
if (!turn) return [];
|
|
8801
|
+
const context = resolveDragon(turn.game, turn.lordId);
|
|
8802
|
+
if (isErr(context)) return [];
|
|
8803
|
+
const { player, queendomino } = context.value;
|
|
8804
|
+
const board = queendomino.buildersBoard;
|
|
8805
|
+
return board.slots.filter(
|
|
8806
|
+
(slot) => slot !== null && destroyWithDragon(board, slot.id, player) !== null
|
|
8807
|
+
);
|
|
8808
|
+
};
|
|
8589
8809
|
var noRecruitment = () => ({
|
|
8590
8810
|
options: [],
|
|
8591
8811
|
positions: []
|
|
@@ -8665,6 +8885,18 @@ var getValidGiantTargetsUseCase = (game, lordId) => {
|
|
|
8665
8885
|
};
|
|
8666
8886
|
var canSkipOptionalActionUseCase = (game, lordId) => isGameWithNextAction(game) && isOk(resolveSkip(game, lordId ?? game.nextAction.nextLord));
|
|
8667
8887
|
|
|
8888
|
+
// src/core/useCases/getValidDominoPicks.ts
|
|
8889
|
+
var getValidDominoPicksUseCase = (game, lordId) => {
|
|
8890
|
+
if (!isGameWithNextAction(game) || game.nextAction.nextAction !== playerActions.pickDomino) {
|
|
8891
|
+
return [];
|
|
8892
|
+
}
|
|
8893
|
+
const id = lordId ?? game.nextAction.nextLord;
|
|
8894
|
+
if (id !== game.nextAction.nextLord) return [];
|
|
8895
|
+
const lord = game.lords.find((l) => l.id === id);
|
|
8896
|
+
if (!lord || !canPick(lord)) return [];
|
|
8897
|
+
return pickableDominoNumbers(game.currentDominoes, lord);
|
|
8898
|
+
};
|
|
8899
|
+
|
|
8668
8900
|
// src/core/useCases/placeDomino.ts
|
|
8669
8901
|
var findVolcanoInDomino = (game, position, rotation) => {
|
|
8670
8902
|
const lord = game.lords.find((l) => l.id === game.nextAction.nextLord);
|
|
@@ -8973,66 +9205,6 @@ var startGameUseCase = (deps) => (game) => {
|
|
|
8973
9205
|
return ok(newState);
|
|
8974
9206
|
};
|
|
8975
9207
|
|
|
8976
|
-
// src/core/useCases/useDragon.ts
|
|
8977
|
-
var useDragonUseCase = (game, lordId, buildingId) => {
|
|
8978
|
-
const currentAction = game.nextAction;
|
|
8979
|
-
if (currentAction.nextLord !== lordId) {
|
|
8980
|
-
return err(ErrorCode.NOT_YOUR_TURN);
|
|
8981
|
-
}
|
|
8982
|
-
if (currentAction.nextAction !== playerActions.useDragon) {
|
|
8983
|
-
return err(ErrorCode.INVALID_OPTIONAL_ACTION);
|
|
8984
|
-
}
|
|
8985
|
-
if (!game.queendomino) {
|
|
8986
|
-
return err(ErrorCode.INVALID_OPTIONAL_ACTION);
|
|
8987
|
-
}
|
|
8988
|
-
if (!game.queendomino.dragonAvailable || game.queendomino.dragonUsedThisRound) {
|
|
8989
|
-
return err(ErrorCode.DRAGON_UNAVAILABLE);
|
|
8990
|
-
}
|
|
8991
|
-
const currentLord = game.lords.find((l) => l.id === lordId);
|
|
8992
|
-
if (!currentLord) return err(ErrorCode.LORD_NOT_FOUND);
|
|
8993
|
-
const currentPlayer = game.players.find((p) => p.id === currentLord.playerId);
|
|
8994
|
-
if (!currentPlayer) return err(ErrorCode.PLAYER_NOT_FOUND);
|
|
8995
|
-
if (game.queendomino.queenHolderId === currentPlayer.id) {
|
|
8996
|
-
return err(ErrorCode.QUEEN_HOLDER_CANNOT_USE_DRAGON);
|
|
8997
|
-
}
|
|
8998
|
-
const result = destroyWithDragon(
|
|
8999
|
-
game.queendomino.buildersBoard,
|
|
9000
|
-
buildingId,
|
|
9001
|
-
currentPlayer
|
|
9002
|
-
);
|
|
9003
|
-
if (!result) {
|
|
9004
|
-
return err(ErrorCode.BUILDING_NOT_FOUND);
|
|
9005
|
-
}
|
|
9006
|
-
const { updatedBoard, updatedPlayer } = result;
|
|
9007
|
-
const updatedPlayers = game.players.map(
|
|
9008
|
-
(p) => p.id === currentPlayer.id ? updatedPlayer : p
|
|
9009
|
-
);
|
|
9010
|
-
const maxTurns = game.rules.basic.maxTurns;
|
|
9011
|
-
const isLastTurn = game.turn === maxTurns;
|
|
9012
|
-
if (isLastTurn && allLordsHavePlayed(game.lords)) {
|
|
9013
|
-
return ok({
|
|
9014
|
-
...game,
|
|
9015
|
-
players: updatedPlayers,
|
|
9016
|
-
queendomino: {
|
|
9017
|
-
...game.queendomino,
|
|
9018
|
-
buildersBoard: updatedBoard,
|
|
9019
|
-
dragonUsedThisRound: true
|
|
9020
|
-
},
|
|
9021
|
-
nextAction: { type: "step", step: gameSteps.result }
|
|
9022
|
-
});
|
|
9023
|
-
}
|
|
9024
|
-
return ok({
|
|
9025
|
-
...game,
|
|
9026
|
-
players: updatedPlayers,
|
|
9027
|
-
queendomino: {
|
|
9028
|
-
...game.queendomino,
|
|
9029
|
-
buildersBoard: updatedBoard,
|
|
9030
|
-
dragonUsedThisRound: true
|
|
9031
|
-
},
|
|
9032
|
-
nextAction: nextLordWithAction(game.lords)
|
|
9033
|
-
});
|
|
9034
|
-
};
|
|
9035
|
-
|
|
9036
9208
|
// src/config.ts
|
|
9037
9209
|
var configureEngine = (config) => {
|
|
9038
9210
|
const modeRepository = jsonModes_default();
|
|
@@ -9125,6 +9297,13 @@ var configureEngine = (config) => {
|
|
|
9125
9297
|
translator,
|
|
9126
9298
|
canPlaceDominoUseCase
|
|
9127
9299
|
),
|
|
9300
|
+
getValidDominoPicksHandler: getValidActionTargetsHandler(
|
|
9301
|
+
logger,
|
|
9302
|
+
translator,
|
|
9303
|
+
"getValidDominoPicks",
|
|
9304
|
+
getValidDominoPicksUseCase,
|
|
9305
|
+
(numbers) => `dominoes [${numbers.join(", ")}]`
|
|
9306
|
+
),
|
|
9128
9307
|
serializeGameHandler: serializeGameHandler(logger, translator),
|
|
9129
9308
|
deserializeGameHandler: deserializeGameHandler(logger, translator),
|
|
9130
9309
|
getDynastyResultHandler: getDynastyResultHandler(
|
|
@@ -9188,6 +9367,13 @@ var configureEngine = (config) => {
|
|
|
9188
9367
|
getAffordableBuildingsUseCase,
|
|
9189
9368
|
(buildings) => `${buildings.length} buildings`
|
|
9190
9369
|
),
|
|
9370
|
+
getValidDragonTargetsHandler: getValidActionTargetsHandler(
|
|
9371
|
+
logger,
|
|
9372
|
+
translator,
|
|
9373
|
+
"getValidDragonTargets",
|
|
9374
|
+
getValidDragonTargetsUseCase,
|
|
9375
|
+
(buildings) => `${buildings.length} buildings`
|
|
9376
|
+
),
|
|
9191
9377
|
getValidCavemanPlacementsHandler: getValidActionTargetsHandler(
|
|
9192
9378
|
logger,
|
|
9193
9379
|
translator,
|
|
@@ -9758,6 +9944,7 @@ var createGameEngine = (config) => {
|
|
|
9758
9944
|
calculateScoreHandler: calculateScoreHandler2,
|
|
9759
9945
|
getValidPlacementsHandler: getValidPlacementsHandler2,
|
|
9760
9946
|
canPlaceDominoHandler: canPlaceDominoHandler2,
|
|
9947
|
+
getValidDominoPicksHandler,
|
|
9761
9948
|
serializeGameHandler: serializeGameHandler2,
|
|
9762
9949
|
deserializeGameHandler: deserializeGameHandler2,
|
|
9763
9950
|
getDynastyResultHandler: getDynastyResultHandler2,
|
|
@@ -9773,6 +9960,7 @@ var createGameEngine = (config) => {
|
|
|
9773
9960
|
getValidKnightPositionsHandler,
|
|
9774
9961
|
getValidBuildingPositionsHandler,
|
|
9775
9962
|
getAffordableBuildingsHandler,
|
|
9963
|
+
getValidDragonTargetsHandler,
|
|
9776
9964
|
getValidCavemanPlacementsHandler,
|
|
9777
9965
|
getValidGiantPositionsHandler,
|
|
9778
9966
|
getValidGiantTargetsHandler,
|
|
@@ -9793,6 +9981,7 @@ var createGameEngine = (config) => {
|
|
|
9793
9981
|
calculateScore: (command) => calculateScoreHandler2(command),
|
|
9794
9982
|
getValidPlacements: (command) => getValidPlacementsHandler2(command),
|
|
9795
9983
|
canPlaceDomino: (command) => canPlaceDominoHandler2(command),
|
|
9984
|
+
getValidDominoPicks: (command) => getValidDominoPicksHandler(command),
|
|
9796
9985
|
serialize: (command) => serializeGameHandler2(command),
|
|
9797
9986
|
deserialize: (command) => deserializeGameHandler2(command),
|
|
9798
9987
|
getDynastyResults: (command) => getDynastyResultHandler2(command),
|
|
@@ -9808,6 +9997,7 @@ var createGameEngine = (config) => {
|
|
|
9808
9997
|
getValidKnightPositions: (command) => getValidKnightPositionsHandler(command),
|
|
9809
9998
|
getValidBuildingPositions: (command) => getValidBuildingPositionsHandler(command),
|
|
9810
9999
|
getAffordableBuildings: (command) => getAffordableBuildingsHandler(command),
|
|
10000
|
+
getValidDragonTargets: (command) => getValidDragonTargetsHandler(command),
|
|
9811
10001
|
getValidCavemanPlacements: (command) => getValidCavemanPlacementsHandler(command),
|
|
9812
10002
|
getValidGiantPositions: (command) => getValidGiantPositionsHandler(command),
|
|
9813
10003
|
getValidGiantTargets: (command) => getValidGiantTargetsHandler(command),
|