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.
@@ -8,6 +8,7 @@ export interface AvailableMoves {
8
8
  [MoveName.BuyResource]?: {
9
9
  resource: ResourceType;
10
10
  price: number;
11
+ side?: 'north' | 'south';
11
12
  }[];
12
13
  [MoveName.Build]?: {
13
14
  name: string;
@@ -135,17 +135,22 @@ function availableMoves(G, player) {
135
135
  else {
136
136
  maxPriceAvailable = 16;
137
137
  }
138
- if (G.coalMarket > 0) {
138
+ // Korea: each side is offered separately, tagged with the side. The
139
+ // player's chosenSide locks them to one side once they make a buy.
140
+ const isKorea = G.coalResupplyNorth !== undefined;
141
+ const allowSouth = !isKorea || G.chosenSide !== 'north';
142
+ const allowNorth = isKorea && G.chosenSide !== 'south';
143
+ if (allowSouth && G.coalMarket > 0) {
139
144
  const hybridCapacityUsed = player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
140
145
  const coalPrices = (_c = G.coalPrices) !== null && _c !== void 0 ? _c : prices_1.default[gamestate_1.ResourceType.Coal];
141
146
  const price = coalPrices[coalPrices.length - G.coalMarket];
142
147
  if (player.money >= price &&
143
148
  player.coalCapacity + player.hybridCapacity > hybridCapacityUsed + player.coalLeft &&
144
149
  price <= maxPriceAvailable) {
145
- toBuy.push({ resource: gamestate_1.ResourceType.Coal });
150
+ toBuy.push(isKorea ? { resource: gamestate_1.ResourceType.Coal, side: 'south' } : { resource: gamestate_1.ResourceType.Coal });
146
151
  }
147
152
  }
148
- else {
153
+ else if (allowSouth) {
149
154
  if (G.options.variant == 'recharged' && G.map.name == 'USA' && G.coalSupply > 0) {
150
155
  const hybridCapacityUsed = player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
151
156
  if (player.money >= 8 &&
@@ -154,17 +159,37 @@ function availableMoves(G, player) {
154
159
  }
155
160
  }
156
161
  }
157
- if (G.oilMarket > 0) {
162
+ if (allowNorth && G.coalMarketNorth > 0) {
163
+ const hybridCapacityUsed = player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
164
+ const coalPrices = G.coalPricesNorth;
165
+ const price = coalPrices[coalPrices.length - G.coalMarketNorth];
166
+ if (player.money >= price &&
167
+ player.coalCapacity + player.hybridCapacity > hybridCapacityUsed + player.coalLeft &&
168
+ price <= maxPriceAvailable) {
169
+ toBuy.push({ resource: gamestate_1.ResourceType.Coal, side: 'north' });
170
+ }
171
+ }
172
+ if (allowSouth && G.oilMarket > 0) {
158
173
  const hybridCapacityUsed = player.hybridCapacity > 0 ? Math.max(0, player.coalLeft - player.coalCapacity) : 0;
159
174
  const oilPrices = (_d = G.oilPrices) !== null && _d !== void 0 ? _d : prices_1.default[gamestate_1.ResourceType.Oil];
160
175
  const price = oilPrices[oilPrices.length - G.oilMarket];
161
176
  if (player.money >= price &&
162
177
  player.oilCapacity + player.hybridCapacity > hybridCapacityUsed + player.oilLeft &&
163
178
  price <= maxPriceAvailable) {
164
- toBuy.push({ resource: gamestate_1.ResourceType.Oil });
179
+ toBuy.push(isKorea ? { resource: gamestate_1.ResourceType.Oil, side: 'south' } : { resource: gamestate_1.ResourceType.Oil });
180
+ }
181
+ }
182
+ if (allowNorth && G.oilMarketNorth > 0) {
183
+ const hybridCapacityUsed = player.hybridCapacity > 0 ? Math.max(0, player.coalLeft - player.coalCapacity) : 0;
184
+ const oilPrices = G.oilPricesNorth;
185
+ const price = oilPrices[oilPrices.length - G.oilMarketNorth];
186
+ if (player.money >= price &&
187
+ player.oilCapacity + player.hybridCapacity > hybridCapacityUsed + player.oilLeft &&
188
+ price <= maxPriceAvailable) {
189
+ toBuy.push({ resource: gamestate_1.ResourceType.Oil, side: 'north' });
165
190
  }
166
191
  }
167
- if (G.garbageMarket > 0) {
192
+ if (allowSouth && G.garbageMarket > 0) {
168
193
  const garbagePrices = (_e = G.garbagePrices) !== null && _e !== void 0 ? _e : prices_1.default[gamestate_1.ResourceType.Garbage];
169
194
  let price = garbagePrices[garbagePrices.length - G.garbageMarket];
170
195
  // $1 cheaper for players in Wien in Central Europe
@@ -177,16 +202,26 @@ function availableMoves(G, player) {
177
202
  if (player.money >= price &&
178
203
  player.garbageCapacity > player.garbageLeft &&
179
204
  price <= maxPriceAvailable) {
180
- toBuy.push({ resource: gamestate_1.ResourceType.Garbage });
205
+ toBuy.push(isKorea ? { resource: gamestate_1.ResourceType.Garbage, side: 'south' } : { resource: gamestate_1.ResourceType.Garbage });
206
+ }
207
+ }
208
+ if (allowNorth && G.garbageMarketNorth > 0) {
209
+ const garbagePrices = G.garbagePricesNorth;
210
+ const price = garbagePrices[garbagePrices.length - G.garbageMarketNorth];
211
+ if (player.money >= price &&
212
+ player.garbageCapacity > player.garbageLeft &&
213
+ price <= maxPriceAvailable) {
214
+ toBuy.push({ resource: gamestate_1.ResourceType.Garbage, side: 'north' });
181
215
  }
182
216
  }
183
- if (G.uraniumMarket > 0) {
217
+ // Uranium is South only (or non-Korea maps).
218
+ if (allowSouth && G.uraniumMarket > 0) {
184
219
  const uraniumPrices = (_f = G.uraniumPrices) !== null && _f !== void 0 ? _f : prices_1.default[gamestate_1.ResourceType.Uranium];
185
220
  const price = uraniumPrices[uraniumPrices.length - G.uraniumMarket];
186
221
  if (player.money >= price &&
187
222
  player.uraniumCapacity > player.uraniumLeft &&
188
223
  price <= maxPriceAvailable) {
189
- toBuy.push({ resource: gamestate_1.ResourceType.Uranium });
224
+ toBuy.push(isKorea ? { resource: gamestate_1.ResourceType.Uranium, side: 'south' } : { resource: gamestate_1.ResourceType.Uranium });
190
225
  }
191
226
  }
192
227
  if (toBuy.length > 0) {
@@ -70,7 +70,7 @@ function defaultSetupDeck(numPlayers, variant, rng, useNewRechargedSetup) {
70
70
  }
71
71
  exports.defaultSetupDeck = defaultSetupDeck;
72
72
  function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original', showMoney = false, useNewRechargedSetup = true, trackTotalSpent = true, randomizeMap = false, }, seed, forceDeck, forceMap) {
73
- var _a, _b, _c, _d;
73
+ var _a, _b, _c, _d, _e, _f, _g;
74
74
  seed = seed !== null && seed !== void 0 ? seed : Math.random().toString();
75
75
  const rng = seedrandom_1.default(seed);
76
76
  const chosenMap = lodash_1.cloneDeep(variant == 'original' ? maps_1.maps.find((m) => m.name == map) : maps_1.mapsRecharged.find((m) => m.name == map));
@@ -129,6 +129,15 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
129
129
  let oilResupply;
130
130
  let garbageResupply;
131
131
  let uraniumResupply;
132
+ // Korea: parallel North-side resupply tables (no uranium row).
133
+ let coalResupplyNorth;
134
+ let oilResupplyNorth;
135
+ let garbageResupplyNorth;
136
+ if (chosenMap.resupplyNorth) {
137
+ coalResupplyNorth = chosenMap.resupplyNorth[0];
138
+ oilResupplyNorth = chosenMap.resupplyNorth[1];
139
+ garbageResupplyNorth = chosenMap.resupplyNorth[2];
140
+ }
132
141
  if (chosenMap.resupply) {
133
142
  coalResupply = chosenMap.resupply[0];
134
143
  oilResupply = chosenMap.resupply[1];
@@ -227,6 +236,23 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
227
236
  .map((n) => chosenMap.cities.find((city) => city.name == n).region)
228
237
  .every((r) => playRegions.has(r)));
229
238
  finalMap = filteredMap;
239
+ if (chosenMap.regionalPowerPlants) {
240
+ for (const region of playRegions) {
241
+ const replacements = chosenMap.regionalPowerPlants[region];
242
+ if (replacements) {
243
+ for (const newPlant of replacements) {
244
+ const swapIn = (arr) => {
245
+ const idx = arr.findIndex((p) => p.number === newPlant.number);
246
+ if (idx !== -1)
247
+ arr[idx] = { ...newPlant };
248
+ };
249
+ swapIn(actualMarket);
250
+ swapIn(futureMarket);
251
+ swapIn(powerPlantsDeck);
252
+ }
253
+ }
254
+ }
255
+ }
230
256
  }
231
257
  const coalMarket = chosenMap.startingResources ? chosenMap.startingResources[0] : 24;
232
258
  const oilMarket = chosenMap.startingResources ? chosenMap.startingResources[1] : 18;
@@ -236,14 +262,24 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
236
262
  const totalOil = chosenMap.startingSupply ? chosenMap.startingSupply[1] : 24;
237
263
  const totalGarbage = chosenMap.startingSupply ? chosenMap.startingSupply[2] : 24;
238
264
  const totalUranium = chosenMap.startingSupply ? chosenMap.startingSupply[3] : 12;
239
- const coalSupply = totalCoal - coalMarket;
240
- const oilSupply = totalOil - oilMarket;
241
- const garbageSupply = totalGarbage - garbageMarket;
265
+ // Korea: parallel North-side market and prices. Supply is shared — see below.
266
+ const coalMarketNorth = (_a = chosenMap.startingResourcesNorth) === null || _a === void 0 ? void 0 : _a[0];
267
+ const oilMarketNorth = (_b = chosenMap.startingResourcesNorth) === null || _b === void 0 ? void 0 : _b[1];
268
+ const garbageMarketNorth = (_c = chosenMap.startingResourcesNorth) === null || _c === void 0 ? void 0 : _c[2];
269
+ // Supply pools are shared between both sides for Korea. `startingSupply`
270
+ // represents the TOTAL cubes in the game; the supply pool is whatever is
271
+ // left after both markets are filled.
272
+ const coalSupply = totalCoal - coalMarket - (coalMarketNorth !== null && coalMarketNorth !== void 0 ? coalMarketNorth : 0);
273
+ const oilSupply = totalOil - oilMarket - (oilMarketNorth !== null && oilMarketNorth !== void 0 ? oilMarketNorth : 0);
274
+ const garbageSupply = totalGarbage - garbageMarket - (garbageMarketNorth !== null && garbageMarketNorth !== void 0 ? garbageMarketNorth : 0);
242
275
  const uraniumSupply = totalUranium - uraniumMarket;
243
- const coalPrices = lodash_1.cloneDeep((_a = chosenMap.coalPrices) !== null && _a !== void 0 ? _a : prices_1.default.coal);
244
- const oilPrices = lodash_1.cloneDeep((_b = chosenMap.oilPrices) !== null && _b !== void 0 ? _b : prices_1.default.oil);
245
- const garbagePrices = lodash_1.cloneDeep((_c = chosenMap.garbagePrices) !== null && _c !== void 0 ? _c : prices_1.default.garbage);
246
- const uraniumPrices = lodash_1.cloneDeep((_d = chosenMap.uraniumPrices) !== null && _d !== void 0 ? _d : prices_1.default.uranium);
276
+ const coalPrices = lodash_1.cloneDeep((_d = chosenMap.coalPrices) !== null && _d !== void 0 ? _d : prices_1.default.coal);
277
+ const oilPrices = lodash_1.cloneDeep((_e = chosenMap.oilPrices) !== null && _e !== void 0 ? _e : prices_1.default.oil);
278
+ const garbagePrices = lodash_1.cloneDeep((_f = chosenMap.garbagePrices) !== null && _f !== void 0 ? _f : prices_1.default.garbage);
279
+ const uraniumPrices = lodash_1.cloneDeep((_g = chosenMap.uraniumPrices) !== null && _g !== void 0 ? _g : prices_1.default.uranium);
280
+ const coalPricesNorth = chosenMap.coalPricesNorth ? lodash_1.cloneDeep(chosenMap.coalPricesNorth) : undefined;
281
+ const oilPricesNorth = chosenMap.oilPricesNorth ? lodash_1.cloneDeep(chosenMap.oilPricesNorth) : undefined;
282
+ const garbagePricesNorth = chosenMap.garbagePricesNorth ? lodash_1.cloneDeep(chosenMap.garbagePricesNorth) : undefined;
247
283
  const G = {
248
284
  map: forceMap || finalMap,
249
285
  players,
@@ -266,6 +302,17 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
266
302
  oilPrices,
267
303
  garbagePrices,
268
304
  uraniumPrices,
305
+ // Korea: parallel North-side fields. Undefined for non-Korea maps.
306
+ // Supply is shared with the primary `*Supply` fields above.
307
+ coalMarketNorth,
308
+ oilMarketNorth,
309
+ garbageMarketNorth,
310
+ coalResupplyNorth,
311
+ oilResupplyNorth,
312
+ garbageResupplyNorth,
313
+ coalPricesNorth,
314
+ oilPricesNorth,
315
+ garbagePricesNorth,
269
316
  actualMarket,
270
317
  futureMarket,
271
318
  chosenPowerPlant: undefined,
@@ -289,6 +336,13 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
289
336
  `[${coalResupply[p][1]}, ${oilResupply[p][1]}, ${garbageResupply[p][1]}, ${uraniumResupply[p][1]}]`,
290
337
  `[${coalResupply[p][2]}, ${oilResupply[p][2]}, ${garbageResupply[p][2]}, ${uraniumResupply[p][2]}]`,
291
338
  ],
339
+ resourceResupplyNorth: coalResupplyNorth && oilResupplyNorth && garbageResupplyNorth
340
+ ? [
341
+ `[${coalResupplyNorth[p][0]}, ${oilResupplyNorth[p][0]}, ${garbageResupplyNorth[p][0]}]`,
342
+ `[${coalResupplyNorth[p][1]}, ${oilResupplyNorth[p][1]}, ${garbageResupplyNorth[p][1]}]`,
343
+ `[${coalResupplyNorth[p][2]}, ${oilResupplyNorth[p][2]}, ${garbageResupplyNorth[p][2]}]`,
344
+ ]
345
+ : undefined,
292
346
  paymentTable: cityIncome,
293
347
  variant,
294
348
  minimunBid: 0,
@@ -340,7 +394,7 @@ function currentPlayers(G) {
340
394
  }
341
395
  exports.currentPlayers = currentPlayers;
342
396
  function move(G, move, playerNumber, isUndo = false) {
343
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
397
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
344
398
  const player = G.players[playerNumber];
345
399
  const available = (_a = player.availableMoves) === null || _a === void 0 ? void 0 : _a[move.name];
346
400
  updateGameState(G);
@@ -545,6 +599,11 @@ function move(G, move, playerNumber, isUndo = false) {
545
599
  else {
546
600
  player.passed = true;
547
601
  }
602
+ // Korea: end of this player's buying turn — clear the side lock
603
+ // so the next player can pick freely.
604
+ if (G.map.name == 'Korea') {
605
+ G.chosenSide = undefined;
606
+ }
548
607
  if (G.players.filter((p) => !p.passed && !p.isDropped).length == 0) {
549
608
  G.players.forEach((p) => {
550
609
  p.passed = p.isDropped;
@@ -656,7 +715,34 @@ function move(G, move, playerNumber, isUndo = false) {
656
715
  player.targetCitiesPowered = 0;
657
716
  }
658
717
  if (G.players.filter((p) => !p.passed && !p.isDropped).length == 0) {
659
- const coalResupplyValue = Math.min(G.coalSupply, G.coalResupply[G.players.length - 2][G.step - 1]);
718
+ // Resupply is also capped by remaining market capacity (the
719
+ // prices array length minus current market size). Without
720
+ // this, smaller markets like Korea's can overflow past the
721
+ // number of slots and break price lookups.
722
+ const coalCapSouth = ((_d = (_c = G.coalPrices) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : prices_1.default[gamestate_1.ResourceType.Coal].length) - G.coalMarket;
723
+ const oilCapSouth = ((_f = (_e = G.oilPrices) === null || _e === void 0 ? void 0 : _e.length) !== null && _f !== void 0 ? _f : prices_1.default[gamestate_1.ResourceType.Oil].length) - G.oilMarket;
724
+ const garbageCapSouth = ((_h = (_g = G.garbagePrices) === null || _g === void 0 ? void 0 : _g.length) !== null && _h !== void 0 ? _h : prices_1.default[gamestate_1.ResourceType.Garbage].length) - G.garbageMarket;
725
+ const uraniumCapSouth = ((_k = (_j = G.uraniumPrices) === null || _j === void 0 ? void 0 : _j.length) !== null && _k !== void 0 ? _k : prices_1.default[gamestate_1.ResourceType.Uranium].length) - G.uraniumMarket;
726
+ // Korea: North restocks FIRST from the shared supply pool, then
727
+ // South takes whatever remains. If supply runs short, South gets less.
728
+ let coalResupplyNorthValue = 0;
729
+ let oilResupplyNorthValue = 0;
730
+ let garbageResupplyNorthValue = 0;
731
+ if (G.coalResupplyNorth) {
732
+ const coalCapNorth = G.coalPricesNorth.length - G.coalMarketNorth;
733
+ const oilCapNorth = G.oilPricesNorth.length - G.oilMarketNorth;
734
+ const garbageCapNorth = G.garbagePricesNorth.length - G.garbageMarketNorth;
735
+ coalResupplyNorthValue = Math.min(G.coalSupply, G.coalResupplyNorth[G.players.length - 2][G.step - 1], coalCapNorth);
736
+ G.coalMarketNorth += coalResupplyNorthValue;
737
+ G.coalSupply -= coalResupplyNorthValue;
738
+ oilResupplyNorthValue = Math.min(G.oilSupply, G.oilResupplyNorth[G.players.length - 2][G.step - 1], oilCapNorth);
739
+ G.oilMarketNorth += oilResupplyNorthValue;
740
+ G.oilSupply -= oilResupplyNorthValue;
741
+ garbageResupplyNorthValue = Math.min(G.garbageSupply, G.garbageResupplyNorth[G.players.length - 2][G.step - 1], garbageCapNorth);
742
+ G.garbageMarketNorth += garbageResupplyNorthValue;
743
+ G.garbageSupply -= garbageResupplyNorthValue;
744
+ }
745
+ const coalResupplyValue = Math.min(G.coalSupply, G.coalResupply[G.players.length - 2][G.step - 1], coalCapSouth);
660
746
  G.coalMarket += coalResupplyValue;
661
747
  G.coalSupply -= coalResupplyValue;
662
748
  let oilResupplyValue;
@@ -678,25 +764,33 @@ function move(G, move, playerNumber, isUndo = false) {
678
764
  }
679
765
  }
680
766
  else {
681
- oilResupplyValue = Math.min(G.oilSupply, G.oilResupply[G.players.length - 2][G.step - 1]);
767
+ oilResupplyValue = Math.min(G.oilSupply, G.oilResupply[G.players.length - 2][G.step - 1], oilCapSouth);
682
768
  G.oilMarket += oilResupplyValue;
683
769
  G.oilSupply -= oilResupplyValue;
684
770
  }
685
- const garbageResupplyValue = Math.min(G.garbageSupply, G.garbageResupply[G.players.length - 2][G.step - 1]);
771
+ const garbageResupplyValue = Math.min(G.garbageSupply, G.garbageResupply[G.players.length - 2][G.step - 1], garbageCapSouth);
686
772
  G.garbageMarket += garbageResupplyValue;
687
773
  G.garbageSupply -= garbageResupplyValue;
688
774
  let uraniumResupplyValue = 0;
689
775
  if (G.options.variant != 'recharged' ||
690
776
  (G.options.map != 'Germany' && G.options.map != 'Italy') ||
691
777
  !G.card39Bought) {
692
- uraniumResupplyValue = Math.min(G.uraniumSupply, G.uraniumResupply[G.players.length - 2][G.step - 1]);
778
+ uraniumResupplyValue = Math.min(G.uraniumSupply, G.uraniumResupply[G.players.length - 2][G.step - 1], uraniumCapSouth);
693
779
  G.uraniumMarket += uraniumResupplyValue;
694
780
  G.uraniumSupply -= uraniumResupplyValue;
695
781
  }
696
- G.log.push({
697
- type: 'event',
698
- event: `Resupplying resources: [${coalResupplyValue}, ${oilResupplyValue}, ${garbageResupplyValue}, ${uraniumResupplyValue}].`,
699
- });
782
+ if (G.coalResupplyNorth) {
783
+ G.log.push({
784
+ type: 'event',
785
+ event: `Resupplying resources — North: [${coalResupplyNorthValue}, ${oilResupplyNorthValue}, ${garbageResupplyNorthValue}], South: [${coalResupplyValue}, ${oilResupplyValue}, ${garbageResupplyValue}, ${uraniumResupplyValue}].`,
786
+ });
787
+ }
788
+ else {
789
+ G.log.push({
790
+ type: 'event',
791
+ event: `Resupplying resources: [${coalResupplyValue}, ${oilResupplyValue}, ${garbageResupplyValue}, ${uraniumResupplyValue}].`,
792
+ });
793
+ }
700
794
  if (G.map.name == 'Middle East' && G.step == 2 && G.futureMarket.length > 0) {
701
795
  // If we aren't about to enter step 3, discard top two plants instead of one.
702
796
  let powerPlantToPush = G.futureMarket.pop();
@@ -968,17 +1062,29 @@ function move(G, move, playerNumber, isUndo = false) {
968
1062
  case move_1.MoveName.BuyResource: {
969
1063
  utils_1.asserts(move);
970
1064
  G.chosenResource = move.data.resource;
1065
+ // Korea: lock the player to the side of their first buy this turn.
1066
+ // Subsequent buys must come from the same side until they pass.
1067
+ if (move.data.side) {
1068
+ G.chosenSide = move.data.side;
1069
+ }
1070
+ const isNorth = move.data.side === 'north';
971
1071
  let price;
972
1072
  switch (move.data.resource) {
973
1073
  case gamestate_1.ResourceType.Coal: {
974
- if (G.coalMarket == 0) {
1074
+ if (isNorth) {
1075
+ const coalPrices = G.coalPricesNorth;
1076
+ price = coalPrices[coalPrices.length - G.coalMarketNorth];
1077
+ player.coalLeft++;
1078
+ G.coalMarketNorth--;
1079
+ }
1080
+ else if (G.coalMarket == 0) {
975
1081
  price = 8;
976
1082
  player.coalLeft++;
977
1083
  G.coalSupply--;
978
1084
  move.fromSupply = true;
979
1085
  }
980
1086
  else {
981
- const coalPrices = (_c = G.coalPrices) !== null && _c !== void 0 ? _c : prices_1.default[gamestate_1.ResourceType.Coal];
1087
+ const coalPrices = (_l = G.coalPrices) !== null && _l !== void 0 ? _l : prices_1.default[gamestate_1.ResourceType.Coal];
982
1088
  price = coalPrices[coalPrices.length - G.coalMarket];
983
1089
  player.coalLeft++;
984
1090
  G.coalMarket--;
@@ -986,28 +1092,45 @@ function move(G, move, playerNumber, isUndo = false) {
986
1092
  break;
987
1093
  }
988
1094
  case gamestate_1.ResourceType.Oil: {
989
- const oilPrices = (_d = G.oilPrices) !== null && _d !== void 0 ? _d : prices_1.default[gamestate_1.ResourceType.Oil];
990
- price = oilPrices[oilPrices.length - G.oilMarket];
991
- player.oilLeft++;
992
- G.oilMarket--;
1095
+ if (isNorth) {
1096
+ const oilPrices = G.oilPricesNorth;
1097
+ price = oilPrices[oilPrices.length - G.oilMarketNorth];
1098
+ player.oilLeft++;
1099
+ G.oilMarketNorth--;
1100
+ }
1101
+ else {
1102
+ const oilPrices = (_m = G.oilPrices) !== null && _m !== void 0 ? _m : prices_1.default[gamestate_1.ResourceType.Oil];
1103
+ price = oilPrices[oilPrices.length - G.oilMarket];
1104
+ player.oilLeft++;
1105
+ G.oilMarket--;
1106
+ }
993
1107
  break;
994
1108
  }
995
1109
  case gamestate_1.ResourceType.Garbage: {
996
- const garbagePrices = (_e = G.garbagePrices) !== null && _e !== void 0 ? _e : prices_1.default[gamestate_1.ResourceType.Garbage];
997
- price = garbagePrices[garbagePrices.length - G.garbageMarket];
998
- // $1 cheaper for players in Wien in Central Europe
999
- if (G.map.name == 'Central Europe') {
1000
- const wienCity = player.cities.filter((c) => c.name == 'Wien');
1001
- if ((wienCity === null || wienCity === void 0 ? void 0 : wienCity.length) > 0) {
1002
- price--;
1110
+ if (isNorth) {
1111
+ const garbagePrices = G.garbagePricesNorth;
1112
+ price = garbagePrices[garbagePrices.length - G.garbageMarketNorth];
1113
+ player.garbageLeft++;
1114
+ G.garbageMarketNorth--;
1115
+ }
1116
+ else {
1117
+ const garbagePrices = (_o = G.garbagePrices) !== null && _o !== void 0 ? _o : prices_1.default[gamestate_1.ResourceType.Garbage];
1118
+ price = garbagePrices[garbagePrices.length - G.garbageMarket];
1119
+ // $1 cheaper for players in Wien in Central Europe
1120
+ if (G.map.name == 'Central Europe') {
1121
+ const wienCity = player.cities.filter((c) => c.name == 'Wien');
1122
+ if ((wienCity === null || wienCity === void 0 ? void 0 : wienCity.length) > 0) {
1123
+ price--;
1124
+ }
1003
1125
  }
1126
+ player.garbageLeft++;
1127
+ G.garbageMarket--;
1004
1128
  }
1005
- player.garbageLeft++;
1006
- G.garbageMarket--;
1007
1129
  break;
1008
1130
  }
1009
1131
  case gamestate_1.ResourceType.Uranium: {
1010
- const uraniumPrices = (_f = G.uraniumPrices) !== null && _f !== void 0 ? _f : prices_1.default[gamestate_1.ResourceType.Uranium];
1132
+ // Uranium is only available from the South market (or non-Korea maps).
1133
+ const uraniumPrices = (_p = G.uraniumPrices) !== null && _p !== void 0 ? _p : prices_1.default[gamestate_1.ResourceType.Uranium];
1011
1134
  price = uraniumPrices[uraniumPrices.length - G.uraniumMarket];
1012
1135
  player.uraniumLeft++;
1013
1136
  G.uraniumMarket--;
@@ -1109,10 +1232,17 @@ function move(G, move, playerNumber, isUndo = false) {
1109
1232
  break;
1110
1233
  }
1111
1234
  case move_1.MoveName.BuyResource: {
1235
+ const undoIsNorth = lastMove.data.side === 'north';
1112
1236
  let price;
1113
1237
  switch (lastMove.data.resource) {
1114
1238
  case gamestate_1.ResourceType.Coal:
1115
- if (lastMove.fromSupply) {
1239
+ if (undoIsNorth) {
1240
+ player.coalLeft--;
1241
+ G.coalMarketNorth++;
1242
+ const coalPrices = G.coalPricesNorth;
1243
+ price = coalPrices[coalPrices.length - G.coalMarketNorth];
1244
+ }
1245
+ else if (lastMove.fromSupply) {
1116
1246
  price = 8;
1117
1247
  player.coalLeft--;
1118
1248
  G.coalSupply++;
@@ -1120,35 +1250,52 @@ function move(G, move, playerNumber, isUndo = false) {
1120
1250
  else {
1121
1251
  player.coalLeft--;
1122
1252
  G.coalMarket++;
1123
- const coalPrices = (_g = G.coalPrices) !== null && _g !== void 0 ? _g : prices_1.default[gamestate_1.ResourceType.Coal];
1253
+ const coalPrices = (_q = G.coalPrices) !== null && _q !== void 0 ? _q : prices_1.default[gamestate_1.ResourceType.Coal];
1124
1254
  price = coalPrices[coalPrices.length - G.coalMarket];
1125
1255
  }
1126
1256
  break;
1127
1257
  case gamestate_1.ResourceType.Oil: {
1128
- player.oilLeft--;
1129
- G.oilMarket++;
1130
- const oilPrices = (_h = G.oilPrices) !== null && _h !== void 0 ? _h : prices_1.default[gamestate_1.ResourceType.Oil];
1131
- price = oilPrices[oilPrices.length - G.oilMarket];
1258
+ if (undoIsNorth) {
1259
+ player.oilLeft--;
1260
+ G.oilMarketNorth++;
1261
+ const oilPrices = G.oilPricesNorth;
1262
+ price = oilPrices[oilPrices.length - G.oilMarketNorth];
1263
+ }
1264
+ else {
1265
+ player.oilLeft--;
1266
+ G.oilMarket++;
1267
+ const oilPrices = (_r = G.oilPrices) !== null && _r !== void 0 ? _r : prices_1.default[gamestate_1.ResourceType.Oil];
1268
+ price = oilPrices[oilPrices.length - G.oilMarket];
1269
+ }
1132
1270
  break;
1133
1271
  }
1134
1272
  case gamestate_1.ResourceType.Garbage: {
1135
- player.garbageLeft--;
1136
- G.garbageMarket++;
1137
- const garbagePrices = (_j = G.garbagePrices) !== null && _j !== void 0 ? _j : prices_1.default[gamestate_1.ResourceType.Garbage];
1138
- price = garbagePrices[garbagePrices.length - G.garbageMarket];
1139
- // $1 cheaper for players in Wien in Central Europe
1140
- if (G.map.name == 'Central Europe') {
1141
- const wienCity = player.cities.filter((c) => c.name == 'Wien');
1142
- if ((wienCity === null || wienCity === void 0 ? void 0 : wienCity.length) > 0) {
1143
- price--;
1273
+ if (undoIsNorth) {
1274
+ player.garbageLeft--;
1275
+ G.garbageMarketNorth++;
1276
+ const garbagePrices = G.garbagePricesNorth;
1277
+ price = garbagePrices[garbagePrices.length - G.garbageMarketNorth];
1278
+ }
1279
+ else {
1280
+ player.garbageLeft--;
1281
+ G.garbageMarket++;
1282
+ const garbagePrices = (_s = G.garbagePrices) !== null && _s !== void 0 ? _s : prices_1.default[gamestate_1.ResourceType.Garbage];
1283
+ price = garbagePrices[garbagePrices.length - G.garbageMarket];
1284
+ // $1 cheaper for players in Wien in Central Europe
1285
+ if (G.map.name == 'Central Europe') {
1286
+ const wienCity = player.cities.filter((c) => c.name == 'Wien');
1287
+ if ((wienCity === null || wienCity === void 0 ? void 0 : wienCity.length) > 0) {
1288
+ price--;
1289
+ }
1144
1290
  }
1145
1291
  }
1146
1292
  break;
1147
1293
  }
1148
1294
  case gamestate_1.ResourceType.Uranium: {
1295
+ // Uranium is only available from the South market (or non-Korea maps).
1149
1296
  player.uraniumLeft--;
1150
1297
  G.uraniumMarket++;
1151
- const uraniumPrices = (_k = G.uraniumPrices) !== null && _k !== void 0 ? _k : prices_1.default[gamestate_1.ResourceType.Uranium];
1298
+ const uraniumPrices = (_t = G.uraniumPrices) !== null && _t !== void 0 ? _t : prices_1.default[gamestate_1.ResourceType.Uranium];
1152
1299
  price = uraniumPrices[uraniumPrices.length - G.uraniumMarket];
1153
1300
  break;
1154
1301
  }
@@ -1161,6 +1308,22 @@ function move(G, move, playerNumber, isUndo = false) {
1161
1308
  G.chosenResource = undefined;
1162
1309
  }
1163
1310
  G.log.pop();
1311
+ // Korea: keep chosenSide locked while the player still has
1312
+ // outstanding BuyResource moves this phase, but clear it once
1313
+ // the last one is undone so they can switch sides again.
1314
+ if (G.map.name == 'Korea' && G.chosenSide) {
1315
+ let stillCommitted = false;
1316
+ for (let i = G.log.length - 1; i >= 0; i--) {
1317
+ const entry = G.log[i];
1318
+ if (entry.type !== 'move')
1319
+ continue;
1320
+ stillCommitted = entry.player === playerNumber && entry.move.name === move_1.MoveName.BuyResource;
1321
+ break;
1322
+ }
1323
+ if (!stillCommitted) {
1324
+ G.chosenSide = undefined;
1325
+ }
1326
+ }
1164
1327
  break;
1165
1328
  }
1166
1329
  case move_1.MoveName.Build: {
@@ -1905,6 +2068,13 @@ function updateGameState(G) {
1905
2068
  `[${G.coalResupply[p][1]}, ${G.oilResupply[p][1]}, ${G.garbageResupply[p][1]}, ${G.uraniumResupply[p][1]}]`,
1906
2069
  `[${G.coalResupply[p][2]}, ${G.oilResupply[p][2]}, ${G.garbageResupply[p][2]}, ${G.uraniumResupply[p][2]}]`,
1907
2070
  ];
2071
+ if (G.coalResupplyNorth && G.oilResupplyNorth && G.garbageResupplyNorth) {
2072
+ G.resourceResupplyNorth = [
2073
+ `[${G.coalResupplyNorth[p][0]}, ${G.oilResupplyNorth[p][0]}, ${G.garbageResupplyNorth[p][0]}]`,
2074
+ `[${G.coalResupplyNorth[p][1]}, ${G.oilResupplyNorth[p][1]}, ${G.garbageResupplyNorth[p][1]}]`,
2075
+ `[${G.coalResupplyNorth[p][2]}, ${G.oilResupplyNorth[p][2]}, ${G.garbageResupplyNorth[p][2]}]`,
2076
+ ];
2077
+ }
1908
2078
  }
1909
2079
  }
1910
2080
  function fastAuction(G, player, bid) {
@@ -2,7 +2,7 @@ import { AvailableMoves } from './available-moves';
2
2
  import { LogItem } from './log';
3
3
  import { GameMap } from './maps';
4
4
  import { Move } from './move';
5
- export declare type MapName = 'USA' | 'Germany' | 'Brazil' | 'Spain & Portugal' | 'France' | 'Italy' | 'Quebec' | 'Middle East' | 'India' | 'China' | 'Benelux' | 'Russia' | 'Central Europe';
5
+ export declare type MapName = 'USA' | 'Germany' | 'Brazil' | 'Spain & Portugal' | 'France' | 'Italy' | 'Quebec' | 'Middle East' | 'India' | 'China' | 'Benelux' | 'Russia' | 'Central Europe' | 'Baden-Württemberg' | 'Northern Europe' | 'Korea';
6
6
  export declare type Variant = 'original' | 'recharged';
7
7
  export interface GameOptions {
8
8
  fastBid?: boolean;
@@ -100,6 +100,15 @@ export interface GameState {
100
100
  oilResupply?: number[][];
101
101
  garbageResupply?: number[][];
102
102
  uraniumResupply?: number[][];
103
+ coalMarketNorth?: number;
104
+ oilMarketNorth?: number;
105
+ garbageMarketNorth?: number;
106
+ coalResupplyNorth?: number[][];
107
+ oilResupplyNorth?: number[][];
108
+ garbageResupplyNorth?: number[][];
109
+ coalPricesNorth?: number[];
110
+ oilPricesNorth?: number[];
111
+ garbagePricesNorth?: number[];
103
112
  coalPrices?: number[];
104
113
  oilPrices?: number[];
105
114
  garbagePrices?: number[];
@@ -108,6 +117,7 @@ export interface GameState {
108
117
  futureMarket: PowerPlant[];
109
118
  chosenPowerPlant: PowerPlant | undefined;
110
119
  chosenResource?: ResourceType | undefined;
120
+ chosenSide?: 'north' | 'south';
111
121
  currentBid: number | undefined;
112
122
  auctioningPlayer: number | undefined;
113
123
  step: number;
@@ -122,6 +132,7 @@ export interface GameState {
122
132
  citiesToEndGame: number;
123
133
  citiesBuiltInCurrentRound?: number;
124
134
  resourceResupply: string[];
135
+ resourceResupplyNorth?: string[];
125
136
  paymentTable: number[];
126
137
  minimunBid: number;
127
138
  plantDiscountActive: boolean;