powergrid-engine 1.9.9 → 1.11.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/dist/src/available-moves.d.ts +1 -0
- package/dist/src/available-moves.js +44 -9
- package/dist/src/engine.js +219 -49
- package/dist/src/gamestate.d.ts +12 -1
- package/dist/src/maps/korea.js +107 -0
- package/dist/src/maps/northerneurope.js +75 -0
- package/dist/src/maps.d.ts +6 -0
- package/dist/src/maps.js +7 -4
- package/dist/src/move.d.ts +1 -0
- package/package.json +1 -1
- package/src/available-moves.ts +71 -10
- package/src/engine.ts +237 -42
- package/src/gamestate.ts +24 -4
- package/src/maps/korea.ts +109 -0
- package/src/maps/northerneurope.ts +76 -0
- package/src/maps.ts +16 -7
- package/src/move.ts +3 -0
package/src/engine.ts
CHANGED
|
@@ -161,6 +161,15 @@ export function setup(
|
|
|
161
161
|
let oilResupply: number[][];
|
|
162
162
|
let garbageResupply: number[][];
|
|
163
163
|
let uraniumResupply: number[][];
|
|
164
|
+
// Korea: parallel North-side resupply tables (no uranium row).
|
|
165
|
+
let coalResupplyNorth: number[][] | undefined;
|
|
166
|
+
let oilResupplyNorth: number[][] | undefined;
|
|
167
|
+
let garbageResupplyNorth: number[][] | undefined;
|
|
168
|
+
if (chosenMap.resupplyNorth) {
|
|
169
|
+
coalResupplyNorth = chosenMap.resupplyNorth[0];
|
|
170
|
+
oilResupplyNorth = chosenMap.resupplyNorth[1];
|
|
171
|
+
garbageResupplyNorth = chosenMap.resupplyNorth[2];
|
|
172
|
+
}
|
|
164
173
|
if (chosenMap.resupply) {
|
|
165
174
|
coalResupply = chosenMap.resupply[0];
|
|
166
175
|
oilResupply = chosenMap.resupply[1];
|
|
@@ -273,6 +282,23 @@ export function setup(
|
|
|
273
282
|
);
|
|
274
283
|
|
|
275
284
|
finalMap = filteredMap;
|
|
285
|
+
|
|
286
|
+
if (chosenMap.regionalPowerPlants) {
|
|
287
|
+
for (const region of playRegions) {
|
|
288
|
+
const replacements = chosenMap.regionalPowerPlants[region];
|
|
289
|
+
if (replacements) {
|
|
290
|
+
for (const newPlant of replacements) {
|
|
291
|
+
const swapIn = (arr: PowerPlant[]) => {
|
|
292
|
+
const idx = arr.findIndex((p) => p.number === newPlant.number);
|
|
293
|
+
if (idx !== -1) arr[idx] = { ...newPlant };
|
|
294
|
+
};
|
|
295
|
+
swapIn(actualMarket);
|
|
296
|
+
swapIn(futureMarket);
|
|
297
|
+
swapIn(powerPlantsDeck);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
276
302
|
}
|
|
277
303
|
|
|
278
304
|
const coalMarket = chosenMap.startingResources ? chosenMap.startingResources[0] : 24;
|
|
@@ -285,9 +311,17 @@ export function setup(
|
|
|
285
311
|
const totalGarbage = chosenMap.startingSupply ? chosenMap.startingSupply[2] : 24;
|
|
286
312
|
const totalUranium = chosenMap.startingSupply ? chosenMap.startingSupply[3] : 12;
|
|
287
313
|
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
const
|
|
314
|
+
// Korea: parallel North-side market and prices. Supply is shared — see below.
|
|
315
|
+
const coalMarketNorth = chosenMap.startingResourcesNorth?.[0];
|
|
316
|
+
const oilMarketNorth = chosenMap.startingResourcesNorth?.[1];
|
|
317
|
+
const garbageMarketNorth = chosenMap.startingResourcesNorth?.[2];
|
|
318
|
+
|
|
319
|
+
// Supply pools are shared between both sides for Korea. `startingSupply`
|
|
320
|
+
// represents the TOTAL cubes in the game; the supply pool is whatever is
|
|
321
|
+
// left after both markets are filled.
|
|
322
|
+
const coalSupply = totalCoal - coalMarket - (coalMarketNorth ?? 0);
|
|
323
|
+
const oilSupply = totalOil - oilMarket - (oilMarketNorth ?? 0);
|
|
324
|
+
const garbageSupply = totalGarbage - garbageMarket - (garbageMarketNorth ?? 0);
|
|
291
325
|
const uraniumSupply = totalUranium - uraniumMarket;
|
|
292
326
|
|
|
293
327
|
const coalPrices = cloneDeep(chosenMap.coalPrices ?? prices.coal);
|
|
@@ -295,6 +329,10 @@ export function setup(
|
|
|
295
329
|
const garbagePrices = cloneDeep(chosenMap.garbagePrices ?? prices.garbage);
|
|
296
330
|
const uraniumPrices = cloneDeep(chosenMap.uraniumPrices ?? prices.uranium);
|
|
297
331
|
|
|
332
|
+
const coalPricesNorth = chosenMap.coalPricesNorth ? cloneDeep(chosenMap.coalPricesNorth) : undefined;
|
|
333
|
+
const oilPricesNorth = chosenMap.oilPricesNorth ? cloneDeep(chosenMap.oilPricesNorth) : undefined;
|
|
334
|
+
const garbagePricesNorth = chosenMap.garbagePricesNorth ? cloneDeep(chosenMap.garbagePricesNorth) : undefined;
|
|
335
|
+
|
|
298
336
|
const G: GameState = {
|
|
299
337
|
map: forceMap || finalMap,
|
|
300
338
|
players,
|
|
@@ -317,6 +355,17 @@ export function setup(
|
|
|
317
355
|
oilPrices,
|
|
318
356
|
garbagePrices,
|
|
319
357
|
uraniumPrices,
|
|
358
|
+
// Korea: parallel North-side fields. Undefined for non-Korea maps.
|
|
359
|
+
// Supply is shared with the primary `*Supply` fields above.
|
|
360
|
+
coalMarketNorth,
|
|
361
|
+
oilMarketNorth,
|
|
362
|
+
garbageMarketNorth,
|
|
363
|
+
coalResupplyNorth,
|
|
364
|
+
oilResupplyNorth,
|
|
365
|
+
garbageResupplyNorth,
|
|
366
|
+
coalPricesNorth,
|
|
367
|
+
oilPricesNorth,
|
|
368
|
+
garbagePricesNorth,
|
|
320
369
|
actualMarket,
|
|
321
370
|
futureMarket,
|
|
322
371
|
chosenPowerPlant: undefined,
|
|
@@ -341,6 +390,14 @@ export function setup(
|
|
|
341
390
|
`[${coalResupply[p][1]}, ${oilResupply[p][1]}, ${garbageResupply[p][1]}, ${uraniumResupply[p][1]}]`,
|
|
342
391
|
`[${coalResupply[p][2]}, ${oilResupply[p][2]}, ${garbageResupply[p][2]}, ${uraniumResupply[p][2]}]`,
|
|
343
392
|
],
|
|
393
|
+
resourceResupplyNorth:
|
|
394
|
+
coalResupplyNorth && oilResupplyNorth && garbageResupplyNorth
|
|
395
|
+
? [
|
|
396
|
+
`[${coalResupplyNorth[p][0]}, ${oilResupplyNorth[p][0]}, ${garbageResupplyNorth[p][0]}]`,
|
|
397
|
+
`[${coalResupplyNorth[p][1]}, ${oilResupplyNorth[p][1]}, ${garbageResupplyNorth[p][1]}]`,
|
|
398
|
+
`[${coalResupplyNorth[p][2]}, ${oilResupplyNorth[p][2]}, ${garbageResupplyNorth[p][2]}]`,
|
|
399
|
+
]
|
|
400
|
+
: undefined,
|
|
344
401
|
paymentTable: cityIncome,
|
|
345
402
|
variant,
|
|
346
403
|
minimunBid: 0,
|
|
@@ -640,6 +697,12 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
640
697
|
player.passed = true;
|
|
641
698
|
}
|
|
642
699
|
|
|
700
|
+
// Korea: end of this player's buying turn — clear the side lock
|
|
701
|
+
// so the next player can pick freely.
|
|
702
|
+
if (G.map.name == 'Korea') {
|
|
703
|
+
G.chosenSide = undefined;
|
|
704
|
+
}
|
|
705
|
+
|
|
643
706
|
if (G.players.filter((p) => !p.passed && !p.isDropped).length == 0) {
|
|
644
707
|
G.players.forEach((p) => {
|
|
645
708
|
p.passed = p.isDropped;
|
|
@@ -774,9 +837,56 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
774
837
|
}
|
|
775
838
|
|
|
776
839
|
if (G.players.filter((p) => !p.passed && !p.isDropped).length == 0) {
|
|
840
|
+
// Resupply is also capped by remaining market capacity (the
|
|
841
|
+
// prices array length minus current market size). Without
|
|
842
|
+
// this, smaller markets like Korea's can overflow past the
|
|
843
|
+
// number of slots and break price lookups.
|
|
844
|
+
const coalCapSouth = (G.coalPrices?.length ?? prices[ResourceType.Coal].length) - G.coalMarket;
|
|
845
|
+
const oilCapSouth = (G.oilPrices?.length ?? prices[ResourceType.Oil].length) - G.oilMarket;
|
|
846
|
+
const garbageCapSouth =
|
|
847
|
+
(G.garbagePrices?.length ?? prices[ResourceType.Garbage].length) - G.garbageMarket;
|
|
848
|
+
const uraniumCapSouth =
|
|
849
|
+
(G.uraniumPrices?.length ?? prices[ResourceType.Uranium].length) - G.uraniumMarket;
|
|
850
|
+
|
|
851
|
+
// Korea: North restocks FIRST from the shared supply pool, then
|
|
852
|
+
// South takes whatever remains. If supply runs short, South gets less.
|
|
853
|
+
let coalResupplyNorthValue = 0;
|
|
854
|
+
let oilResupplyNorthValue = 0;
|
|
855
|
+
let garbageResupplyNorthValue = 0;
|
|
856
|
+
if (G.coalResupplyNorth) {
|
|
857
|
+
const coalCapNorth = G.coalPricesNorth!.length - G.coalMarketNorth!;
|
|
858
|
+
const oilCapNorth = G.oilPricesNorth!.length - G.oilMarketNorth!;
|
|
859
|
+
const garbageCapNorth = G.garbagePricesNorth!.length - G.garbageMarketNorth!;
|
|
860
|
+
|
|
861
|
+
coalResupplyNorthValue = Math.min(
|
|
862
|
+
G.coalSupply,
|
|
863
|
+
G.coalResupplyNorth[G.players.length - 2][G.step - 1],
|
|
864
|
+
coalCapNorth
|
|
865
|
+
);
|
|
866
|
+
G.coalMarketNorth! += coalResupplyNorthValue;
|
|
867
|
+
G.coalSupply -= coalResupplyNorthValue;
|
|
868
|
+
|
|
869
|
+
oilResupplyNorthValue = Math.min(
|
|
870
|
+
G.oilSupply,
|
|
871
|
+
G.oilResupplyNorth![G.players.length - 2][G.step - 1],
|
|
872
|
+
oilCapNorth
|
|
873
|
+
);
|
|
874
|
+
G.oilMarketNorth! += oilResupplyNorthValue;
|
|
875
|
+
G.oilSupply -= oilResupplyNorthValue;
|
|
876
|
+
|
|
877
|
+
garbageResupplyNorthValue = Math.min(
|
|
878
|
+
G.garbageSupply,
|
|
879
|
+
G.garbageResupplyNorth![G.players.length - 2][G.step - 1],
|
|
880
|
+
garbageCapNorth
|
|
881
|
+
);
|
|
882
|
+
G.garbageMarketNorth! += garbageResupplyNorthValue;
|
|
883
|
+
G.garbageSupply -= garbageResupplyNorthValue;
|
|
884
|
+
}
|
|
885
|
+
|
|
777
886
|
const coalResupplyValue = Math.min(
|
|
778
887
|
G.coalSupply,
|
|
779
|
-
G.coalResupply![G.players.length - 2][G.step - 1]
|
|
888
|
+
G.coalResupply![G.players.length - 2][G.step - 1],
|
|
889
|
+
coalCapSouth
|
|
780
890
|
);
|
|
781
891
|
G.coalMarket += coalResupplyValue;
|
|
782
892
|
G.coalSupply -= coalResupplyValue;
|
|
@@ -799,14 +909,19 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
799
909
|
}
|
|
800
910
|
}
|
|
801
911
|
} else {
|
|
802
|
-
oilResupplyValue = Math.min(
|
|
912
|
+
oilResupplyValue = Math.min(
|
|
913
|
+
G.oilSupply,
|
|
914
|
+
G.oilResupply![G.players.length - 2][G.step - 1],
|
|
915
|
+
oilCapSouth
|
|
916
|
+
);
|
|
803
917
|
G.oilMarket += oilResupplyValue;
|
|
804
918
|
G.oilSupply -= oilResupplyValue;
|
|
805
919
|
}
|
|
806
920
|
|
|
807
921
|
const garbageResupplyValue = Math.min(
|
|
808
922
|
G.garbageSupply,
|
|
809
|
-
G.garbageResupply![G.players.length - 2][G.step - 1]
|
|
923
|
+
G.garbageResupply![G.players.length - 2][G.step - 1],
|
|
924
|
+
garbageCapSouth
|
|
810
925
|
);
|
|
811
926
|
G.garbageMarket += garbageResupplyValue;
|
|
812
927
|
G.garbageSupply -= garbageResupplyValue;
|
|
@@ -819,16 +934,24 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
819
934
|
) {
|
|
820
935
|
uraniumResupplyValue = Math.min(
|
|
821
936
|
G.uraniumSupply,
|
|
822
|
-
G.uraniumResupply![G.players.length - 2][G.step - 1]
|
|
937
|
+
G.uraniumResupply![G.players.length - 2][G.step - 1],
|
|
938
|
+
uraniumCapSouth
|
|
823
939
|
);
|
|
824
940
|
G.uraniumMarket += uraniumResupplyValue;
|
|
825
941
|
G.uraniumSupply -= uraniumResupplyValue;
|
|
826
942
|
}
|
|
827
943
|
|
|
828
|
-
G.
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
944
|
+
if (G.coalResupplyNorth) {
|
|
945
|
+
G.log.push({
|
|
946
|
+
type: 'event',
|
|
947
|
+
event: `Resupplying resources — North: [${coalResupplyNorthValue}, ${oilResupplyNorthValue}, ${garbageResupplyNorthValue}], South: [${coalResupplyValue}, ${oilResupplyValue}, ${garbageResupplyValue}, ${uraniumResupplyValue}].`,
|
|
948
|
+
});
|
|
949
|
+
} else {
|
|
950
|
+
G.log.push({
|
|
951
|
+
type: 'event',
|
|
952
|
+
event: `Resupplying resources: [${coalResupplyValue}, ${oilResupplyValue}, ${garbageResupplyValue}, ${uraniumResupplyValue}].`,
|
|
953
|
+
});
|
|
954
|
+
}
|
|
832
955
|
|
|
833
956
|
if (G.map.name == 'Middle East' && G.step == 2 && G.futureMarket.length > 0) {
|
|
834
957
|
// If we aren't about to enter step 3, discard top two plants instead of one.
|
|
@@ -1145,10 +1268,23 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
1145
1268
|
asserts<Moves.MoveBuyResource>(move);
|
|
1146
1269
|
G.chosenResource = move.data.resource;
|
|
1147
1270
|
|
|
1271
|
+
// Korea: lock the player to the side of their first buy this turn.
|
|
1272
|
+
// Subsequent buys must come from the same side until they pass.
|
|
1273
|
+
if (move.data.side) {
|
|
1274
|
+
G.chosenSide = move.data.side;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
const isNorth = move.data.side === 'north';
|
|
1278
|
+
|
|
1148
1279
|
let price: number;
|
|
1149
1280
|
switch (move.data.resource) {
|
|
1150
1281
|
case ResourceType.Coal: {
|
|
1151
|
-
if (
|
|
1282
|
+
if (isNorth) {
|
|
1283
|
+
const coalPrices = G.coalPricesNorth!;
|
|
1284
|
+
price = coalPrices[coalPrices.length - G.coalMarketNorth!];
|
|
1285
|
+
player.coalLeft++;
|
|
1286
|
+
G.coalMarketNorth!--;
|
|
1287
|
+
} else if (G.coalMarket == 0) {
|
|
1152
1288
|
price = 8;
|
|
1153
1289
|
player.coalLeft++;
|
|
1154
1290
|
G.coalSupply--;
|
|
@@ -1164,31 +1300,46 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
1164
1300
|
}
|
|
1165
1301
|
|
|
1166
1302
|
case ResourceType.Oil: {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1303
|
+
if (isNorth) {
|
|
1304
|
+
const oilPrices = G.oilPricesNorth!;
|
|
1305
|
+
price = oilPrices[oilPrices.length - G.oilMarketNorth!];
|
|
1306
|
+
player.oilLeft++;
|
|
1307
|
+
G.oilMarketNorth!--;
|
|
1308
|
+
} else {
|
|
1309
|
+
const oilPrices = G.oilPrices ?? prices[ResourceType.Oil];
|
|
1310
|
+
price = oilPrices[oilPrices.length - G.oilMarket];
|
|
1311
|
+
player.oilLeft++;
|
|
1312
|
+
G.oilMarket--;
|
|
1313
|
+
}
|
|
1171
1314
|
break;
|
|
1172
1315
|
}
|
|
1173
1316
|
|
|
1174
1317
|
case ResourceType.Garbage: {
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1318
|
+
if (isNorth) {
|
|
1319
|
+
const garbagePrices = G.garbagePricesNorth!;
|
|
1320
|
+
price = garbagePrices[garbagePrices.length - G.garbageMarketNorth!];
|
|
1321
|
+
player.garbageLeft++;
|
|
1322
|
+
G.garbageMarketNorth!--;
|
|
1323
|
+
} else {
|
|
1324
|
+
const garbagePrices = G.garbagePrices ?? prices[ResourceType.Garbage];
|
|
1325
|
+
price = garbagePrices[garbagePrices.length - G.garbageMarket];
|
|
1326
|
+
|
|
1327
|
+
// $1 cheaper for players in Wien in Central Europe
|
|
1328
|
+
if (G.map.name == 'Central Europe') {
|
|
1329
|
+
const wienCity = player.cities.filter((c) => c.name == 'Wien');
|
|
1330
|
+
if (wienCity?.length > 0) {
|
|
1331
|
+
price--;
|
|
1332
|
+
}
|
|
1183
1333
|
}
|
|
1184
|
-
}
|
|
1185
1334
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1335
|
+
player.garbageLeft++;
|
|
1336
|
+
G.garbageMarket--;
|
|
1337
|
+
}
|
|
1188
1338
|
break;
|
|
1189
1339
|
}
|
|
1190
1340
|
|
|
1191
1341
|
case ResourceType.Uranium: {
|
|
1342
|
+
// Uranium is only available from the South market (or non-Korea maps).
|
|
1192
1343
|
const uraniumPrices = G.uraniumPrices ?? prices[ResourceType.Uranium];
|
|
1193
1344
|
price = uraniumPrices[uraniumPrices.length - G.uraniumMarket];
|
|
1194
1345
|
player.uraniumLeft++;
|
|
@@ -1324,10 +1475,16 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
1324
1475
|
}
|
|
1325
1476
|
|
|
1326
1477
|
case MoveName.BuyResource: {
|
|
1478
|
+
const undoIsNorth = lastMove.data.side === 'north';
|
|
1327
1479
|
let price: number;
|
|
1328
1480
|
switch (lastMove.data.resource) {
|
|
1329
1481
|
case ResourceType.Coal:
|
|
1330
|
-
if (
|
|
1482
|
+
if (undoIsNorth) {
|
|
1483
|
+
player.coalLeft--;
|
|
1484
|
+
G.coalMarketNorth!++;
|
|
1485
|
+
const coalPrices = G.coalPricesNorth!;
|
|
1486
|
+
price = coalPrices[coalPrices.length - G.coalMarketNorth!];
|
|
1487
|
+
} else if (lastMove.fromSupply) {
|
|
1331
1488
|
price = 8;
|
|
1332
1489
|
player.coalLeft--;
|
|
1333
1490
|
G.coalSupply++;
|
|
@@ -1341,24 +1498,38 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
1341
1498
|
break;
|
|
1342
1499
|
|
|
1343
1500
|
case ResourceType.Oil: {
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1501
|
+
if (undoIsNorth) {
|
|
1502
|
+
player.oilLeft--;
|
|
1503
|
+
G.oilMarketNorth!++;
|
|
1504
|
+
const oilPrices = G.oilPricesNorth!;
|
|
1505
|
+
price = oilPrices[oilPrices.length - G.oilMarketNorth!];
|
|
1506
|
+
} else {
|
|
1507
|
+
player.oilLeft--;
|
|
1508
|
+
G.oilMarket++;
|
|
1509
|
+
const oilPrices = G.oilPrices ?? prices[ResourceType.Oil];
|
|
1510
|
+
price = oilPrices[oilPrices.length - G.oilMarket];
|
|
1511
|
+
}
|
|
1348
1512
|
break;
|
|
1349
1513
|
}
|
|
1350
1514
|
|
|
1351
1515
|
case ResourceType.Garbage: {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1516
|
+
if (undoIsNorth) {
|
|
1517
|
+
player.garbageLeft--;
|
|
1518
|
+
G.garbageMarketNorth!++;
|
|
1519
|
+
const garbagePrices = G.garbagePricesNorth!;
|
|
1520
|
+
price = garbagePrices[garbagePrices.length - G.garbageMarketNorth!];
|
|
1521
|
+
} else {
|
|
1522
|
+
player.garbageLeft--;
|
|
1523
|
+
G.garbageMarket++;
|
|
1524
|
+
const garbagePrices = G.garbagePrices ?? prices[ResourceType.Garbage];
|
|
1525
|
+
price = garbagePrices[garbagePrices.length - G.garbageMarket];
|
|
1526
|
+
|
|
1527
|
+
// $1 cheaper for players in Wien in Central Europe
|
|
1528
|
+
if (G.map.name == 'Central Europe') {
|
|
1529
|
+
const wienCity = player.cities.filter((c) => c.name == 'Wien');
|
|
1530
|
+
if (wienCity?.length > 0) {
|
|
1531
|
+
price--;
|
|
1532
|
+
}
|
|
1362
1533
|
}
|
|
1363
1534
|
}
|
|
1364
1535
|
|
|
@@ -1366,6 +1537,7 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
1366
1537
|
}
|
|
1367
1538
|
|
|
1368
1539
|
case ResourceType.Uranium: {
|
|
1540
|
+
// Uranium is only available from the South market (or non-Korea maps).
|
|
1369
1541
|
player.uraniumLeft--;
|
|
1370
1542
|
G.uraniumMarket++;
|
|
1371
1543
|
const uraniumPrices = G.uraniumPrices ?? prices[ResourceType.Uranium];
|
|
@@ -1385,6 +1557,22 @@ export function move(G: GameState, move: Move, playerNumber: number, isUndo = fa
|
|
|
1385
1557
|
}
|
|
1386
1558
|
|
|
1387
1559
|
G.log.pop();
|
|
1560
|
+
|
|
1561
|
+
// Korea: keep chosenSide locked while the player still has
|
|
1562
|
+
// outstanding BuyResource moves this phase, but clear it once
|
|
1563
|
+
// the last one is undone so they can switch sides again.
|
|
1564
|
+
if (G.map.name == 'Korea' && G.chosenSide) {
|
|
1565
|
+
let stillCommitted = false;
|
|
1566
|
+
for (let i = G.log.length - 1; i >= 0; i--) {
|
|
1567
|
+
const entry = G.log[i];
|
|
1568
|
+
if (entry.type !== 'move') continue;
|
|
1569
|
+
stillCommitted = entry.player === playerNumber && entry.move.name === MoveName.BuyResource;
|
|
1570
|
+
break;
|
|
1571
|
+
}
|
|
1572
|
+
if (!stillCommitted) {
|
|
1573
|
+
G.chosenSide = undefined;
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1388
1576
|
break;
|
|
1389
1577
|
}
|
|
1390
1578
|
|
|
@@ -2229,6 +2417,13 @@ function updateGameState(G: GameState) {
|
|
|
2229
2417
|
`[${G.coalResupply[p][1]}, ${G.oilResupply[p][1]}, ${G.garbageResupply[p][1]}, ${G.uraniumResupply[p][1]}]`,
|
|
2230
2418
|
`[${G.coalResupply[p][2]}, ${G.oilResupply[p][2]}, ${G.garbageResupply[p][2]}, ${G.uraniumResupply[p][2]}]`,
|
|
2231
2419
|
];
|
|
2420
|
+
if (G.coalResupplyNorth && G.oilResupplyNorth && G.garbageResupplyNorth) {
|
|
2421
|
+
G.resourceResupplyNorth = [
|
|
2422
|
+
`[${G.coalResupplyNorth[p][0]}, ${G.oilResupplyNorth[p][0]}, ${G.garbageResupplyNorth[p][0]}]`,
|
|
2423
|
+
`[${G.coalResupplyNorth[p][1]}, ${G.oilResupplyNorth[p][1]}, ${G.garbageResupplyNorth[p][1]}]`,
|
|
2424
|
+
`[${G.coalResupplyNorth[p][2]}, ${G.oilResupplyNorth[p][2]}, ${G.garbageResupplyNorth[p][2]}]`,
|
|
2425
|
+
];
|
|
2426
|
+
}
|
|
2232
2427
|
}
|
|
2233
2428
|
}
|
|
2234
2429
|
|
package/src/gamestate.ts
CHANGED
|
@@ -16,12 +16,12 @@ export type MapName =
|
|
|
16
16
|
| 'China'
|
|
17
17
|
| 'Benelux'
|
|
18
18
|
| 'Russia'
|
|
19
|
-
| 'Central Europe'
|
|
19
|
+
| 'Central Europe'
|
|
20
|
+
| 'Baden-Württemberg'
|
|
21
|
+
| 'Northern Europe'
|
|
22
|
+
| 'Korea';
|
|
20
23
|
// | 'Australia'
|
|
21
|
-
// | 'Baden-Württemberg'
|
|
22
24
|
// | 'Japan'
|
|
23
|
-
// | 'Korea'
|
|
24
|
-
// | 'Northern Europe'
|
|
25
25
|
// | 'South Africa'
|
|
26
26
|
// | 'UK & Ireland'
|
|
27
27
|
export type Variant = 'original' | 'recharged';
|
|
@@ -129,6 +129,22 @@ export interface GameState {
|
|
|
129
129
|
oilResupply?: number[][];
|
|
130
130
|
garbageResupply?: number[][];
|
|
131
131
|
uraniumResupply?: number[][];
|
|
132
|
+
// Korea: parallel North-side markets and resupply tables. The primary
|
|
133
|
+
// `*Market`/`*Resupply` fields above act as the South side. The supply pools
|
|
134
|
+
// (`coalSupply` etc.) are SHARED — used cubes return there and both sides
|
|
135
|
+
// restock from the same pool, with North restocking first.
|
|
136
|
+
// North has no uranium track (nuclear plants are banned on the North side).
|
|
137
|
+
coalMarketNorth?: number;
|
|
138
|
+
oilMarketNorth?: number;
|
|
139
|
+
garbageMarketNorth?: number;
|
|
140
|
+
coalResupplyNorth?: number[][];
|
|
141
|
+
oilResupplyNorth?: number[][];
|
|
142
|
+
garbageResupplyNorth?: number[][];
|
|
143
|
+
// Korea: per-side price arrays. Each side's market has different slot counts
|
|
144
|
+
// per price space, so the price arrays differ. (No uranium on the North side.)
|
|
145
|
+
coalPricesNorth?: number[];
|
|
146
|
+
oilPricesNorth?: number[];
|
|
147
|
+
garbagePricesNorth?: number[];
|
|
132
148
|
coalPrices?: number[];
|
|
133
149
|
oilPrices?: number[];
|
|
134
150
|
garbagePrices?: number[];
|
|
@@ -137,6 +153,9 @@ export interface GameState {
|
|
|
137
153
|
futureMarket: PowerPlant[];
|
|
138
154
|
chosenPowerPlant: PowerPlant | undefined;
|
|
139
155
|
chosenResource?: ResourceType | undefined; // Used for India map, where only one resource can be bought at a time.
|
|
156
|
+
// Korea: locked to the side a player bought their first resource from this turn.
|
|
157
|
+
// All subsequent buys this turn must be from the same side. Cleared when the player passes.
|
|
158
|
+
chosenSide?: 'north' | 'south';
|
|
140
159
|
currentBid: number | undefined;
|
|
141
160
|
auctioningPlayer: number | undefined;
|
|
142
161
|
step: number;
|
|
@@ -151,6 +170,7 @@ export interface GameState {
|
|
|
151
170
|
citiesToEndGame: number;
|
|
152
171
|
citiesBuiltInCurrentRound?: number; // In India, if the players build too many cities in a single round, a power outage will occur.
|
|
153
172
|
resourceResupply: string[];
|
|
173
|
+
resourceResupplyNorth?: string[]; // Korea: per-step North resupply, parallel to resourceResupply (no uranium).
|
|
154
174
|
paymentTable: number[];
|
|
155
175
|
minimunBid: number;
|
|
156
176
|
plantDiscountActive: boolean;
|
package/src/maps/korea.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// by John and Cici
|
|
1
2
|
import { GameMap } from './../maps';
|
|
2
3
|
|
|
3
4
|
export enum Regions {
|
|
@@ -182,4 +183,112 @@ export const map: GameMap = {
|
|
|
182
183
|
{ nodes: [Cities.Seongjin, Cities.Hamheung], cost: 17 },
|
|
183
184
|
{ nodes: [Cities.Hamheung, Cities.Hyesan], cost: 23 },
|
|
184
185
|
],
|
|
186
|
+
layout: 'Portrait',
|
|
187
|
+
adjustRatio: [0.22, 0.22],
|
|
188
|
+
mapPosition: [80, 30],
|
|
189
|
+
// Taller than the default Portrait viewBox to make room for Korea's stacked
|
|
190
|
+
// dual resource markets (North above South).
|
|
191
|
+
viewBox: [1480, 1180],
|
|
192
|
+
supplyPosition: [675, 1040],
|
|
193
|
+
// South-side resource market.
|
|
194
|
+
// Indexed [resource][playerCount-2][step-1].
|
|
195
|
+
// Verified from the Korea Recharged rulebook resource table (S rows).
|
|
196
|
+
resupply: [
|
|
197
|
+
// Coal (S)
|
|
198
|
+
[
|
|
199
|
+
[2, 2, 2], // 2P
|
|
200
|
+
[2, 3, 2], // 3P
|
|
201
|
+
[3, 3, 2], // 4P
|
|
202
|
+
[3, 4, 3], // 5P
|
|
203
|
+
[4, 5, 3], // 6P
|
|
204
|
+
],
|
|
205
|
+
// Oil (S)
|
|
206
|
+
[
|
|
207
|
+
[1, 1, 3],
|
|
208
|
+
[1, 2, 3],
|
|
209
|
+
[2, 3, 3],
|
|
210
|
+
[3, 3, 4],
|
|
211
|
+
[3, 4, 4],
|
|
212
|
+
],
|
|
213
|
+
// Garbage (S)
|
|
214
|
+
[
|
|
215
|
+
[1, 1, 2],
|
|
216
|
+
[1, 1, 2],
|
|
217
|
+
[1, 2, 2],
|
|
218
|
+
[2, 2, 3],
|
|
219
|
+
[2, 3, 3],
|
|
220
|
+
],
|
|
221
|
+
// Uranium (S only — North bans nuclear resupply)
|
|
222
|
+
[
|
|
223
|
+
[1, 1, 1],
|
|
224
|
+
[1, 1, 1],
|
|
225
|
+
[1, 2, 2],
|
|
226
|
+
[2, 3, 2],
|
|
227
|
+
[2, 3, 3],
|
|
228
|
+
],
|
|
229
|
+
],
|
|
230
|
+
// North-side resource market. Three resources only (no uranium row).
|
|
231
|
+
// Verified from the Korea Recharged rulebook resource table (N rows).
|
|
232
|
+
resupplyNorth: [
|
|
233
|
+
// Coal (N)
|
|
234
|
+
[
|
|
235
|
+
[1, 2, 1],
|
|
236
|
+
[2, 2, 1],
|
|
237
|
+
[2, 3, 2],
|
|
238
|
+
[2, 3, 2],
|
|
239
|
+
[3, 4, 3],
|
|
240
|
+
],
|
|
241
|
+
// Oil (N)
|
|
242
|
+
[
|
|
243
|
+
[1, 1, 1],
|
|
244
|
+
[1, 1, 1],
|
|
245
|
+
[1, 1, 2],
|
|
246
|
+
[1, 2, 2],
|
|
247
|
+
[2, 2, 3],
|
|
248
|
+
],
|
|
249
|
+
// Garbage (N)
|
|
250
|
+
[
|
|
251
|
+
[0, 1, 1],
|
|
252
|
+
[0, 1, 1],
|
|
253
|
+
[1, 1, 2],
|
|
254
|
+
[1, 1, 2],
|
|
255
|
+
[1, 2, 3],
|
|
256
|
+
],
|
|
257
|
+
],
|
|
258
|
+
// Korea has custom slot counts per price space, different per side.
|
|
259
|
+
// Total resources (across both markets + supply) match standard Power Grid:
|
|
260
|
+
// 24 coal, 24 oil, 24 garbage, 12 uranium.
|
|
261
|
+
//
|
|
262
|
+
// North slots per price [1..8]:
|
|
263
|
+
// coal [2,2,2,2,1,1,1,1] = 12 total
|
|
264
|
+
// oil [1,1,1,1,1,1,1,1] = 8 total
|
|
265
|
+
// garbage [1,1,1,1,1,1,1,1] = 8 total
|
|
266
|
+
//
|
|
267
|
+
// South slots per price [1..8] (uranium prices [1..8,10,12,14,16]):
|
|
268
|
+
// coal [1,1,1,1,2,2,2,2] = 12 total
|
|
269
|
+
// oil [2,2,2,2,2,2,2,2] = 16 total
|
|
270
|
+
// garbage [2,2,2,2,2,2,2,2] = 16 total
|
|
271
|
+
// uranium [1,1,1,1,1,1,1,1,1,1,1,1] = 12 total
|
|
272
|
+
//
|
|
273
|
+
// Initial market fill per rulebook:
|
|
274
|
+
// North: coal 1–8 (12), oil 3–8 (6), garbage 7–8 (2).
|
|
275
|
+
// South: coal 1–8 (12), oil 3–8 (12), garbage 7–8 (4), uranium 14–16 (2).
|
|
276
|
+
startingResources: [12, 12, 4, 2], // South initial market: coal, oil, garbage, uranium
|
|
277
|
+
startingResourcesNorth: [12, 6, 2], // North initial market: coal, oil, garbage
|
|
278
|
+
// Total cubes in the game across BOTH markets + shared supply.
|
|
279
|
+
// Used cubes return to the shared supply; on bureaucracy, North restocks
|
|
280
|
+
// first, then South from whatever remains.
|
|
281
|
+
startingSupply: [24, 24, 24, 12],
|
|
282
|
+
coalPrices: [1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8],
|
|
283
|
+
oilPrices: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8],
|
|
284
|
+
garbagePrices: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8],
|
|
285
|
+
uraniumPrices: [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16],
|
|
286
|
+
coalPricesNorth: [1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8],
|
|
287
|
+
oilPricesNorth: [1, 2, 3, 4, 5, 6, 7, 8],
|
|
288
|
+
garbagePricesNorth: [1, 2, 3, 4, 5, 6, 7, 8],
|
|
289
|
+
mapSpecificRules:
|
|
290
|
+
'Korea has two separate resource markets: North and South.\n' +
|
|
291
|
+
'On your buying turn, choose one side — all resources you buy that turn must come from that side. The next turn you may choose the other side.\n' +
|
|
292
|
+
'The North market has no uranium track; uranium is only available from the South.\n' +
|
|
293
|
+
'Both markets share a single supply pool. During bureaucracy, North restocks first, then South from whatever remains.',
|
|
185
294
|
};
|