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.
@@ -183,4 +183,111 @@ exports.map = {
183
183
  { nodes: [Cities.Seongjin, Cities.Hamheung], cost: 17 },
184
184
  { nodes: [Cities.Hamheung, Cities.Hyesan], cost: 23 },
185
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],
200
+ [2, 3, 2],
201
+ [3, 3, 2],
202
+ [3, 4, 3],
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],
277
+ startingResourcesNorth: [12, 6, 2],
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: 'Korea has two separate resource markets: North and South.\n' +
290
+ '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' +
291
+ 'The North market has no uranium track; uranium is only available from the South.\n' +
292
+ 'Both markets share a single supply pool. During bureaucracy, North restocks first, then South from whatever remains.',
186
293
  };
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.map = exports.Cities = exports.Regions = void 0;
4
+ // by John and Cici
5
+ const gamestate_1 = require("./../gamestate");
4
6
  var Regions;
5
7
  (function (Regions) {
6
8
  Regions["Brown"] = "brown";
@@ -101,6 +103,79 @@ exports.map = {
101
103
  { name: Cities.Bergen, region: Regions.Red, x: 68, y: 586 },
102
104
  { name: Cities.Stavanger, region: Regions.Red, x: 72, y: 647 },
103
105
  ],
106
+ layout: 'Portrait',
107
+ adjustRatio: [1.2, 1.0],
108
+ mapPosition: [80, 30],
109
+ startingResources: [18, 18, 12, 6],
110
+ resupply: [
111
+ // Coal
112
+ [
113
+ [2, 4, 3],
114
+ [3, 4, 4],
115
+ [4, 5, 5],
116
+ [4, 5, 6],
117
+ [6, 8, 5], // 6P
118
+ ],
119
+ // Oil
120
+ [
121
+ [1, 2, 3],
122
+ [2, 2, 3],
123
+ [2, 3, 4],
124
+ [3, 4, 5],
125
+ [4, 5, 6],
126
+ ],
127
+ // Garbage
128
+ [
129
+ [1, 2, 3],
130
+ [1, 2, 3],
131
+ [2, 3, 4],
132
+ [3, 3, 5],
133
+ [3, 5, 6],
134
+ ],
135
+ // Uranium
136
+ [
137
+ [1, 2, 2],
138
+ [1, 2, 2],
139
+ [2, 3, 2],
140
+ [2, 4, 3],
141
+ [2, 4, 4],
142
+ ],
143
+ ],
144
+ regionalPowerPlants: {
145
+ [Regions.Orange]: [
146
+ // Denmark
147
+ { number: 19, type: gamestate_1.PowerPlantType.Oil, cost: 2, citiesPowered: 4 },
148
+ { number: 26, type: gamestate_1.PowerPlantType.Coal, cost: 3, citiesPowered: 5 },
149
+ ],
150
+ [Regions.Red]: [
151
+ // Norway
152
+ { number: 7, type: gamestate_1.PowerPlantType.Wind, cost: 0, citiesPowered: 1 },
153
+ { number: 46, type: gamestate_1.PowerPlantType.Wind, cost: 0, citiesPowered: 6 },
154
+ ],
155
+ [Regions.Purple]: [
156
+ // Sweden South
157
+ { number: 10, type: gamestate_1.PowerPlantType.Uranium, cost: 2, citiesPowered: 3 },
158
+ { number: 39, type: gamestate_1.PowerPlantType.Wind, cost: 0, citiesPowered: 5 },
159
+ ],
160
+ [Regions.Yellow]: [
161
+ // Sweden North
162
+ { number: 21, type: gamestate_1.PowerPlantType.Wind, cost: 0, citiesPowered: 2 },
163
+ { number: 25, type: gamestate_1.PowerPlantType.Uranium, cost: 1, citiesPowered: 4 },
164
+ ],
165
+ [Regions.Brown]: [
166
+ // Finland
167
+ { number: 32, type: gamestate_1.PowerPlantType.Garbage, cost: 2, citiesPowered: 6 },
168
+ { number: 44, type: gamestate_1.PowerPlantType.Uranium, cost: 1, citiesPowered: 7 },
169
+ ],
170
+ [Regions.Pink]: [
171
+ // Baltic States
172
+ { number: 18, type: gamestate_1.PowerPlantType.Coal, cost: 1, citiesPowered: 3 },
173
+ { number: 22, type: gamestate_1.PowerPlantType.Wind, cost: 0, citiesPowered: 3 },
174
+ ],
175
+ },
176
+ mapSpecificRules: 'Nuclear plants may only be purchased by players with at least one city in Sweden, Finland, or the Baltic States. ' +
177
+ 'Players with cities only in Norway or Denmark may not bid on or buy nuclear plants. ' +
178
+ 'Resources start at: coal 3 Elektro, oil 3 Elektro, garbage 5 Elektro, uranium 7 Elektro.',
104
179
  connections: [
105
180
  { nodes: [Cities.Bood, Cities.Tromso], cost: 17 },
106
181
  { nodes: [Cities.Tromso, Cities.Oulu], cost: 25 },
@@ -35,6 +35,11 @@ export interface GameMap {
35
35
  resupply?: number[][][];
36
36
  startingResources?: number[];
37
37
  startingSupply?: number[];
38
+ resupplyNorth?: number[][][];
39
+ startingResourcesNorth?: number[];
40
+ coalPricesNorth?: number[];
41
+ oilPricesNorth?: number[];
42
+ garbagePricesNorth?: number[];
38
43
  maxPriceAvailable?: number[];
39
44
  coalPrices?: number[];
40
45
  oilPrices?: number[];
@@ -45,6 +50,7 @@ export interface GameMap {
45
50
  futureMarket: PowerPlant[];
46
51
  powerPlantsDeck: PowerPlant[];
47
52
  };
53
+ regionalPowerPlants?: Record<string, PowerPlant[]>;
48
54
  mapSpecificRules?: string;
49
55
  }
50
56
  export declare const maps: GameMap[];
package/dist/src/maps.js CHANGED
@@ -12,7 +12,10 @@ const france_1 = require("./maps/france");
12
12
  const germany_1 = require("./maps/germany");
13
13
  const indian_1 = require("./maps/indian");
14
14
  const italy_1 = require("./maps/italy");
15
+ // import { map as japan } from './maps/japan';
16
+ const korea_1 = require("./maps/korea");
15
17
  const middleeast_1 = require("./maps/middleeast");
18
+ const northerneurope_1 = require("./maps/northerneurope");
16
19
  const quebec_1 = require("./maps/quebec");
17
20
  const russia_1 = require("./maps/russia");
18
21
  const spainportugal_1 = require("./maps/spainportugal");
@@ -31,10 +34,10 @@ exports.maps = [
31
34
  russia_1.map,
32
35
  centraleurope_1.map,
33
36
  badenwurttemberg_1.map,
37
+ northerneurope_1.map,
38
+ korea_1.map,
34
39
  // australia,
35
40
  // japan,
36
- // korea,
37
- // northerneurope,
38
41
  // southafrica,
39
42
  // ukireland,
40
43
  ];
@@ -53,11 +56,11 @@ exports.mapsRecharged = [
53
56
  russia_1.map,
54
57
  centraleurope_1.map,
55
58
  badenwurttemberg_1.map,
59
+ northerneurope_1.map,
60
+ korea_1.map,
56
61
  // australia,
57
62
  // china,
58
63
  // japan,
59
- // korea,
60
- // northerneurope,
61
64
  // southafrica,
62
65
  // ukireland,
63
66
  ];
@@ -22,6 +22,7 @@ export declare namespace Moves {
22
22
  name: MoveName.BuyResource;
23
23
  data: {
24
24
  resource: ResourceType;
25
+ side?: 'north' | 'south';
25
26
  };
26
27
  fromSupply?: boolean;
27
28
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powergrid-engine",
3
- "version": "1.9.9",
3
+ "version": "1.11.0",
4
4
  "description": "An engine for Power Grid",
5
5
  "main": "dist/index.js",
6
6
  "types": "index.ts",
@@ -11,6 +11,9 @@ export interface AvailableMoves {
11
11
  [MoveName.BuyResource]?: {
12
12
  resource: ResourceType;
13
13
  price: number;
14
+ // Korea: which side's market this buy option draws from.
15
+ // Omitted on all other maps.
16
+ side?: 'north' | 'south';
14
17
  }[];
15
18
  [MoveName.Build]?: { name: string; price: number }[];
16
19
  [MoveName.UsePowerPlant]?: {
@@ -157,7 +160,7 @@ export function availableMoves(G: GameState, player: Player): AvailableMoves {
157
160
  break;
158
161
  }
159
162
 
160
- const toBuy: { resource: ResourceType }[] = [];
163
+ const toBuy: { resource: ResourceType; side?: 'north' | 'south' }[] = [];
161
164
  let maxPriceAvailable: number;
162
165
  if (G.map.maxPriceAvailable) {
163
166
  maxPriceAvailable = G.map.maxPriceAvailable[G.step - 1];
@@ -165,7 +168,13 @@ export function availableMoves(G: GameState, player: Player): AvailableMoves {
165
168
  maxPriceAvailable = 16;
166
169
  }
167
170
 
168
- if (G.coalMarket > 0) {
171
+ // Korea: each side is offered separately, tagged with the side. The
172
+ // player's chosenSide locks them to one side once they make a buy.
173
+ const isKorea = G.coalResupplyNorth !== undefined;
174
+ const allowSouth = !isKorea || G.chosenSide !== 'north';
175
+ const allowNorth = isKorea && G.chosenSide !== 'south';
176
+
177
+ if (allowSouth && G.coalMarket > 0) {
169
178
  const hybridCapacityUsed =
170
179
  player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
171
180
  const coalPrices = G.coalPrices ?? prices[ResourceType.Coal];
@@ -176,9 +185,11 @@ export function availableMoves(G: GameState, player: Player): AvailableMoves {
176
185
  player.coalCapacity + player.hybridCapacity > hybridCapacityUsed + player.coalLeft &&
177
186
  price <= maxPriceAvailable
178
187
  ) {
179
- toBuy.push({ resource: ResourceType.Coal });
188
+ toBuy.push(
189
+ isKorea ? { resource: ResourceType.Coal, side: 'south' } : { resource: ResourceType.Coal }
190
+ );
180
191
  }
181
- } else {
192
+ } else if (allowSouth) {
182
193
  if (G.options.variant == 'recharged' && G.map.name == 'USA' && G.coalSupply > 0) {
183
194
  const hybridCapacityUsed =
184
195
  player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
@@ -191,7 +202,22 @@ export function availableMoves(G: GameState, player: Player): AvailableMoves {
191
202
  }
192
203
  }
193
204
 
194
- if (G.oilMarket > 0) {
205
+ if (allowNorth && G.coalMarketNorth! > 0) {
206
+ const hybridCapacityUsed =
207
+ player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
208
+ const coalPrices = G.coalPricesNorth!;
209
+ const price = coalPrices[coalPrices.length - G.coalMarketNorth!];
210
+
211
+ if (
212
+ player.money >= price &&
213
+ player.coalCapacity + player.hybridCapacity > hybridCapacityUsed + player.coalLeft &&
214
+ price <= maxPriceAvailable
215
+ ) {
216
+ toBuy.push({ resource: ResourceType.Coal, side: 'north' });
217
+ }
218
+ }
219
+
220
+ if (allowSouth && G.oilMarket > 0) {
195
221
  const hybridCapacityUsed =
196
222
  player.hybridCapacity > 0 ? Math.max(0, player.coalLeft - player.coalCapacity) : 0;
197
223
  const oilPrices = G.oilPrices ?? prices[ResourceType.Oil];
@@ -202,11 +228,28 @@ export function availableMoves(G: GameState, player: Player): AvailableMoves {
202
228
  player.oilCapacity + player.hybridCapacity > hybridCapacityUsed + player.oilLeft &&
203
229
  price <= maxPriceAvailable
204
230
  ) {
205
- toBuy.push({ resource: ResourceType.Oil });
231
+ toBuy.push(
232
+ isKorea ? { resource: ResourceType.Oil, side: 'south' } : { resource: ResourceType.Oil }
233
+ );
206
234
  }
207
235
  }
208
236
 
209
- if (G.garbageMarket > 0) {
237
+ if (allowNorth && G.oilMarketNorth! > 0) {
238
+ const hybridCapacityUsed =
239
+ player.hybridCapacity > 0 ? Math.max(0, player.coalLeft - player.coalCapacity) : 0;
240
+ const oilPrices = G.oilPricesNorth!;
241
+ const price = oilPrices[oilPrices.length - G.oilMarketNorth!];
242
+
243
+ if (
244
+ player.money >= price &&
245
+ player.oilCapacity + player.hybridCapacity > hybridCapacityUsed + player.oilLeft &&
246
+ price <= maxPriceAvailable
247
+ ) {
248
+ toBuy.push({ resource: ResourceType.Oil, side: 'north' });
249
+ }
250
+ }
251
+
252
+ if (allowSouth && G.garbageMarket > 0) {
210
253
  const garbagePrices = G.garbagePrices ?? prices[ResourceType.Garbage];
211
254
  let price = garbagePrices[garbagePrices.length - G.garbageMarket];
212
255
 
@@ -223,11 +266,27 @@ export function availableMoves(G: GameState, player: Player): AvailableMoves {
223
266
  player.garbageCapacity > player.garbageLeft &&
224
267
  price <= maxPriceAvailable
225
268
  ) {
226
- toBuy.push({ resource: ResourceType.Garbage });
269
+ toBuy.push(
270
+ isKorea ? { resource: ResourceType.Garbage, side: 'south' } : { resource: ResourceType.Garbage }
271
+ );
272
+ }
273
+ }
274
+
275
+ if (allowNorth && G.garbageMarketNorth! > 0) {
276
+ const garbagePrices = G.garbagePricesNorth!;
277
+ const price = garbagePrices[garbagePrices.length - G.garbageMarketNorth!];
278
+
279
+ if (
280
+ player.money >= price &&
281
+ player.garbageCapacity > player.garbageLeft &&
282
+ price <= maxPriceAvailable
283
+ ) {
284
+ toBuy.push({ resource: ResourceType.Garbage, side: 'north' });
227
285
  }
228
286
  }
229
287
 
230
- if (G.uraniumMarket > 0) {
288
+ // Uranium is South only (or non-Korea maps).
289
+ if (allowSouth && G.uraniumMarket > 0) {
231
290
  const uraniumPrices = G.uraniumPrices ?? prices[ResourceType.Uranium];
232
291
  const price = uraniumPrices[uraniumPrices.length - G.uraniumMarket];
233
292
  if (
@@ -235,7 +294,9 @@ export function availableMoves(G: GameState, player: Player): AvailableMoves {
235
294
  player.uraniumCapacity > player.uraniumLeft &&
236
295
  price <= maxPriceAvailable
237
296
  ) {
238
- toBuy.push({ resource: ResourceType.Uranium });
297
+ toBuy.push(
298
+ isKorea ? { resource: ResourceType.Uranium, side: 'south' } : { resource: ResourceType.Uranium }
299
+ );
239
300
  }
240
301
  }
241
302