powergrid-engine 1.11.0 → 1.12.1
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 +66 -0
- package/dist/src/engine.js +77 -10
- package/dist/src/gamestate.d.ts +2 -1
- package/dist/src/maps/europe.d.ts +62 -0
- package/dist/src/maps/europe.js +356 -0
- package/dist/src/maps/northamerica.d.ts +62 -0
- package/dist/src/maps/northamerica.js +330 -0
- package/dist/src/maps/southafrica.d.ts +26 -26
- package/dist/src/maps/southafrica.js +239 -116
- package/dist/src/maps/ukireland.js +163 -40
- package/dist/src/maps.d.ts +10 -0
- package/dist/src/maps.js +12 -4
- package/dist/src/move.d.ts +1 -0
- package/package.json +1 -1
- package/src/available-moves.ts +88 -1
- package/src/engine.spec.ts +17 -0
- package/src/engine.ts +76 -15
- package/src/gamestate.ts +10 -3
- package/src/maps/europe.ts +391 -0
- package/src/maps/northamerica.ts +360 -0
- package/src/maps/southafrica.ts +247 -116
- package/src/maps/ukireland.ts +170 -40
- package/src/maps.ts +37 -6
- package/src/move.ts +4 -0
|
@@ -66,6 +66,15 @@ function availableMoves(G, player) {
|
|
|
66
66
|
canBid = canBid.filter((p) => p.type != gamestate_1.PowerPlantType.Uranium);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
// No nuclear plants for Republic of Ireland (Green region) on UK&I.
|
|
70
|
+
// The restriction lifts as soon as the player has any non-Green city
|
|
71
|
+
// (Scotland/Wales/England/Northern Ireland = Brown/Yellow/Red/Pink/Orange).
|
|
72
|
+
if (G.map.name == 'UK & Ireland') {
|
|
73
|
+
const playerCities = player.cities.map((c) => G.map.cities.find((c_) => c_.name == c.name));
|
|
74
|
+
if (playerCities.every((c) => c.region == 'green')) {
|
|
75
|
+
canBid = canBid.filter((p) => p.type != gamestate_1.PowerPlantType.Uranium);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
69
78
|
if (canBid.length > 0) {
|
|
70
79
|
moves[move_1.MoveName.ChoosePowerPlant] = canBid.map((p) => p.number);
|
|
71
80
|
}
|
|
@@ -107,6 +116,14 @@ function availableMoves(G, player) {
|
|
|
107
116
|
moves[move_1.MoveName.Bid] = undefined;
|
|
108
117
|
}
|
|
109
118
|
}
|
|
119
|
+
// No nuclear plants for Republic of Ireland (Green region) on UK&I.
|
|
120
|
+
if (G.map.name == 'UK & Ireland') {
|
|
121
|
+
const playerCities = player.cities.map((c) => G.map.cities.find((c_) => c_.name == c.name));
|
|
122
|
+
if (playerCities.every((c) => c.region == 'green') &&
|
|
123
|
+
G.chosenPowerPlant.type == gamestate_1.PowerPlantType.Uranium) {
|
|
124
|
+
moves[move_1.MoveName.Bid] = undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
110
127
|
if (G.options.fastBid) {
|
|
111
128
|
if (player.id != G.auctioningPlayer) {
|
|
112
129
|
moves[move_1.MoveName.Pass] = [true];
|
|
@@ -159,6 +176,17 @@ function availableMoves(G, player) {
|
|
|
159
176
|
}
|
|
160
177
|
}
|
|
161
178
|
}
|
|
179
|
+
// South Africa: $8 flat coal from the storage pool below the market.
|
|
180
|
+
// Always available alongside the regular market option (not gated on
|
|
181
|
+
// market being empty), as long as there are cubes in storage.
|
|
182
|
+
if (allowSouth && G.coalStorage !== undefined && G.coalStorage > 0) {
|
|
183
|
+
const hybridCapacityUsed = player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
|
|
184
|
+
if (player.money >= 8 &&
|
|
185
|
+
player.coalCapacity + player.hybridCapacity > hybridCapacityUsed + player.coalLeft &&
|
|
186
|
+
8 <= maxPriceAvailable) {
|
|
187
|
+
toBuy.push({ resource: gamestate_1.ResourceType.Coal, fromStorage: true });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
162
190
|
if (allowNorth && G.coalMarketNorth > 0) {
|
|
163
191
|
const hybridCapacityUsed = player.hybridCapacity > 0 ? Math.max(0, player.oilLeft - player.oilCapacity) : 0;
|
|
164
192
|
const coalPrices = G.coalPricesNorth;
|
|
@@ -256,6 +284,44 @@ function availableMoves(G, player) {
|
|
|
256
284
|
}
|
|
257
285
|
return;
|
|
258
286
|
}
|
|
287
|
+
// UK & Ireland: starting a network on an island where the player has
|
|
288
|
+
// no city yet pays the first-house base + crossIslandSurcharge. There
|
|
289
|
+
// is no sea edge so dijkstra reports the target city as unreachable
|
|
290
|
+
// (price=9999); we override here. The first build ever (player.cities
|
|
291
|
+
// empty) goes through the normal first-build path and pays no surcharge.
|
|
292
|
+
if (cityData.island && G.map.crossIslandSurcharge !== undefined && player.cities.length > 0) {
|
|
293
|
+
const playerIslands = new Set(player.cities
|
|
294
|
+
.map((c) => { var _a; return (_a = G.map.cities.find((mc) => mc.name == c.name)) === null || _a === void 0 ? void 0 : _a.island; })
|
|
295
|
+
.filter((i) => !!i));
|
|
296
|
+
if (!playerIslands.has(cityData.island)) {
|
|
297
|
+
city.price = 10 + othersCount * 5 + G.map.crossIslandSurcharge;
|
|
298
|
+
if (othersCount == G.step) {
|
|
299
|
+
city.price = 9999;
|
|
300
|
+
}
|
|
301
|
+
if (player.cities.find((c) => c.name == city.name)) {
|
|
302
|
+
city.price = 9999;
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// South Africa's cross-border foreign-country spaces: cap at 1 occupant
|
|
308
|
+
// ever, and the dijkstra path cost (30 via the cross-border edge) is the
|
|
309
|
+
// complete cost — no 10+position*5 house base is added. Players cannot
|
|
310
|
+
// start in one of these (you have to build INTO South Africa first).
|
|
311
|
+
if (cityData.singleOccupancy) {
|
|
312
|
+
if (player.cities.length == 0) {
|
|
313
|
+
city.price = 9999;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (othersCount >= 1) {
|
|
317
|
+
city.price = 9999;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (player.cities.find((c) => c.name == city.name)) {
|
|
321
|
+
city.price = 9999;
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
259
325
|
city.price += 10 + othersCount * 5;
|
|
260
326
|
if (othersCount == G.step) {
|
|
261
327
|
city.price = 9999;
|
package/dist/src/engine.js
CHANGED
|
@@ -18,6 +18,7 @@ const utils_1 = require("./utils");
|
|
|
18
18
|
exports.playerColors = ['limegreen', 'mediumorchid', 'red', 'dodgerblue', 'yellow', 'brown'];
|
|
19
19
|
const citiesToStep2 = [10, 7, 7, 7, 6];
|
|
20
20
|
const citiesToStep2BadenWurttemberg = [9, 6, 6, 6, 5];
|
|
21
|
+
const citiesToStep2UKIreland = [7, 7, 7, 7, 6];
|
|
21
22
|
const citiesToEndGame = [21, 17, 17, 15, 14];
|
|
22
23
|
const cityIncome = [10, 22, 33, 44, 54, 64, 73, 82, 90, 98, 105, 112, 118, 124, 129, 134, 138, 142, 145, 148, 150, 150];
|
|
23
24
|
const regionsInPlay = [3, 3, 4, 5, 5];
|
|
@@ -73,7 +74,11 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
73
74
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
74
75
|
seed = seed !== null && seed !== void 0 ? seed : Math.random().toString();
|
|
75
76
|
const rng = seedrandom_1.default(seed);
|
|
76
|
-
const
|
|
77
|
+
const chosenMapRaw = variant == 'original' ? maps_1.maps.find((m) => m.name == map) : maps_1.mapsRecharged.find((m) => m.name == map);
|
|
78
|
+
if (!chosenMapRaw) {
|
|
79
|
+
throw new Error(`Map "${map}" not found for variant "${variant}"`);
|
|
80
|
+
}
|
|
81
|
+
const chosenMap = lodash_1.cloneDeep(chosenMapRaw);
|
|
77
82
|
let actualMarket;
|
|
78
83
|
let futureMarket;
|
|
79
84
|
let powerPlantsDeck;
|
|
@@ -220,7 +225,14 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
220
225
|
while (playRegions.size != Math.min(regionsInPlay[p], regions.length)) {
|
|
221
226
|
const region = regions[Math.floor(rng() * regions.length)];
|
|
222
227
|
if (playRegions.size == 0 ||
|
|
223
|
-
regionConnections[regions.indexOf(region)].some((con) => playRegions.has(con))
|
|
228
|
+
regionConnections[regions.indexOf(region)].some((con) => playRegions.has(con)) ||
|
|
229
|
+
// UK & Ireland: regions on the two islands have no edges between
|
|
230
|
+
// them (no sea connection). Skipping the connectivity check lets
|
|
231
|
+
// the random selection span both islands; the cross-island
|
|
232
|
+
// surcharge handles the disconnect at build time. Without this,
|
|
233
|
+
// requiring 5-of-6 regions for 5p would loop forever (GB has 4
|
|
234
|
+
// regions, IE has 2).
|
|
235
|
+
chosenMap.name === 'UK & Ireland') {
|
|
224
236
|
playRegions.add(region);
|
|
225
237
|
// Avoid italy Red Green Blue
|
|
226
238
|
if (chosenMap.name === 'Italy') {
|
|
@@ -280,6 +292,7 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
280
292
|
const coalPricesNorth = chosenMap.coalPricesNorth ? lodash_1.cloneDeep(chosenMap.coalPricesNorth) : undefined;
|
|
281
293
|
const oilPricesNorth = chosenMap.oilPricesNorth ? lodash_1.cloneDeep(chosenMap.oilPricesNorth) : undefined;
|
|
282
294
|
const garbagePricesNorth = chosenMap.garbagePricesNorth ? lodash_1.cloneDeep(chosenMap.garbagePricesNorth) : undefined;
|
|
295
|
+
const isSouthAfrica = (forceMap || finalMap).name == 'South Africa';
|
|
283
296
|
const G = {
|
|
284
297
|
map: forceMap || finalMap,
|
|
285
298
|
players,
|
|
@@ -290,6 +303,9 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
290
303
|
oilSupply,
|
|
291
304
|
garbageSupply,
|
|
292
305
|
uraniumSupply,
|
|
306
|
+
// South Africa: separate coal pool below the market. Starts empty;
|
|
307
|
+
// used coal returns here; refill draws from here first.
|
|
308
|
+
coalStorage: isSouthAfrica ? 0 : undefined,
|
|
293
309
|
coalResupply,
|
|
294
310
|
oilResupply,
|
|
295
311
|
garbageResupply,
|
|
@@ -329,7 +345,9 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
329
345
|
auctionSkips: 0,
|
|
330
346
|
citiesToStep2: (forceMap || finalMap).name == 'Baden-Württemberg'
|
|
331
347
|
? citiesToStep2BadenWurttemberg[numPlayers - 2]
|
|
332
|
-
:
|
|
348
|
+
: (forceMap || finalMap).name == 'UK & Ireland'
|
|
349
|
+
? citiesToStep2UKIreland[numPlayers - 2]
|
|
350
|
+
: citiesToStep2[numPlayers - 2],
|
|
333
351
|
citiesToEndGame: citiesToEndGame[numPlayers - 2],
|
|
334
352
|
resourceResupply: [
|
|
335
353
|
`[${coalResupply[p][0]}, ${oilResupply[p][0]}, ${garbageResupply[p][0]}, ${uraniumResupply[p][0]}]`,
|
|
@@ -648,7 +666,18 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
648
666
|
G.powerPlantsDeck.unshift(getPowerPlant(18));
|
|
649
667
|
}
|
|
650
668
|
}
|
|
651
|
-
|
|
669
|
+
if (G.map.name == 'Europe') {
|
|
670
|
+
// Europe: do NOT draw a replacement from the deck.
|
|
671
|
+
// The future market shrinks from 5 to 4; reorganize
|
|
672
|
+
// the remaining 8 plants so actual stays at 4.
|
|
673
|
+
const market = [...G.actualMarket, ...G.futureMarket];
|
|
674
|
+
market.sort((a, b) => a.number - b.number);
|
|
675
|
+
G.actualMarket = market.slice(0, 4);
|
|
676
|
+
G.futureMarket = market.slice(4);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
addPowerPlant(G);
|
|
680
|
+
}
|
|
652
681
|
}
|
|
653
682
|
}
|
|
654
683
|
if (maxCities >= G.citiesToEndGame) {
|
|
@@ -742,9 +771,23 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
742
771
|
G.garbageMarketNorth += garbageResupplyNorthValue;
|
|
743
772
|
G.garbageSupply -= garbageResupplyNorthValue;
|
|
744
773
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
774
|
+
// South Africa pulls from coalStorage first, then coalSupply.
|
|
775
|
+
// Other maps just pull from coalSupply.
|
|
776
|
+
let coalResupplyValue;
|
|
777
|
+
if (G.coalStorage !== undefined) {
|
|
778
|
+
const wantCoal = Math.min(G.coalResupply[G.players.length - 2][G.step - 1], coalCapSouth);
|
|
779
|
+
const fromStorage = Math.min(G.coalStorage, wantCoal);
|
|
780
|
+
const fromSupply = Math.min(G.coalSupply, wantCoal - fromStorage);
|
|
781
|
+
coalResupplyValue = fromStorage + fromSupply;
|
|
782
|
+
G.coalMarket += coalResupplyValue;
|
|
783
|
+
G.coalStorage -= fromStorage;
|
|
784
|
+
G.coalSupply -= fromSupply;
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
coalResupplyValue = Math.min(G.coalSupply, G.coalResupply[G.players.length - 2][G.step - 1], coalCapSouth);
|
|
788
|
+
G.coalMarket += coalResupplyValue;
|
|
789
|
+
G.coalSupply -= coalResupplyValue;
|
|
790
|
+
}
|
|
748
791
|
let oilResupplyValue;
|
|
749
792
|
if (G.map.name == 'Middle East') {
|
|
750
793
|
if (G.oilMarket == 0) {
|
|
@@ -1077,6 +1120,12 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1077
1120
|
player.coalLeft++;
|
|
1078
1121
|
G.coalMarketNorth--;
|
|
1079
1122
|
}
|
|
1123
|
+
else if (move.data.fromStorage) {
|
|
1124
|
+
// South Africa: $8 flat from the storage pool below the market.
|
|
1125
|
+
price = 8;
|
|
1126
|
+
player.coalLeft++;
|
|
1127
|
+
G.coalStorage--;
|
|
1128
|
+
}
|
|
1080
1129
|
else if (G.coalMarket == 0) {
|
|
1081
1130
|
price = 8;
|
|
1082
1131
|
player.coalLeft++;
|
|
@@ -1156,8 +1205,15 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1156
1205
|
player.cities.push({ name: move.data.name, position });
|
|
1157
1206
|
player.money -= move.data.price;
|
|
1158
1207
|
if (G.options.trackTotalSpent) {
|
|
1159
|
-
|
|
1160
|
-
|
|
1208
|
+
const cityData = G.map.cities.find((c) => c.name == move.data.name);
|
|
1209
|
+
if (cityData.singleOccupancy) {
|
|
1210
|
+
// SA cross-border: no house base — full price is "connection".
|
|
1211
|
+
player.totalSpentConnections += move.data.price;
|
|
1212
|
+
}
|
|
1213
|
+
else {
|
|
1214
|
+
player.totalSpentCities += 10 + position * 5;
|
|
1215
|
+
player.totalSpentConnections += move.data.price - (10 + position * 5);
|
|
1216
|
+
}
|
|
1161
1217
|
}
|
|
1162
1218
|
G.log.push({
|
|
1163
1219
|
type: 'move',
|
|
@@ -1188,7 +1244,13 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1188
1244
|
switch (resourceType) {
|
|
1189
1245
|
case gamestate_1.ResourceType.Coal:
|
|
1190
1246
|
player.coalLeft--;
|
|
1191
|
-
|
|
1247
|
+
// SA: used coal returns to the separate storage pool below the market.
|
|
1248
|
+
if (G.coalStorage !== undefined) {
|
|
1249
|
+
G.coalStorage++;
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
G.coalSupply++;
|
|
1253
|
+
}
|
|
1192
1254
|
break;
|
|
1193
1255
|
case gamestate_1.ResourceType.Oil:
|
|
1194
1256
|
player.oilLeft--;
|
|
@@ -1242,6 +1304,11 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1242
1304
|
const coalPrices = G.coalPricesNorth;
|
|
1243
1305
|
price = coalPrices[coalPrices.length - G.coalMarketNorth];
|
|
1244
1306
|
}
|
|
1307
|
+
else if (lastMove.data.fromStorage) {
|
|
1308
|
+
price = 8;
|
|
1309
|
+
player.coalLeft--;
|
|
1310
|
+
G.coalStorage++;
|
|
1311
|
+
}
|
|
1245
1312
|
else if (lastMove.fromSupply) {
|
|
1246
1313
|
price = 8;
|
|
1247
1314
|
player.coalLeft--;
|
package/dist/src/gamestate.d.ts
CHANGED
|
@@ -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' | 'Baden-Württemberg' | 'Northern Europe' | 'Korea';
|
|
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' | 'Europe' | 'North America' | 'South Africa' | 'UK & Ireland';
|
|
6
6
|
export declare type Variant = 'original' | 'recharged';
|
|
7
7
|
export interface GameOptions {
|
|
8
8
|
fastBid?: boolean;
|
|
@@ -92,6 +92,7 @@ export interface GameState {
|
|
|
92
92
|
oilSupply: number;
|
|
93
93
|
garbageSupply: number;
|
|
94
94
|
uraniumSupply: number;
|
|
95
|
+
coalStorage?: number;
|
|
95
96
|
coalMarket: number;
|
|
96
97
|
oilMarket: number;
|
|
97
98
|
garbageMarket: number;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { GameMap } from './../maps';
|
|
2
|
+
export declare enum Regions {
|
|
3
|
+
Pink = "pink",
|
|
4
|
+
Red = "red",
|
|
5
|
+
Brown = "brown",
|
|
6
|
+
Yellow = "yellow",
|
|
7
|
+
Orange = "orange",
|
|
8
|
+
Blue = "blue",
|
|
9
|
+
Green = "green"
|
|
10
|
+
}
|
|
11
|
+
export declare enum Cities {
|
|
12
|
+
Glasgow = "Glasgow",
|
|
13
|
+
Dublin = "Dublin",
|
|
14
|
+
Birmingham = "Birmingham",
|
|
15
|
+
London = "London",
|
|
16
|
+
Randstad = "Randstad",
|
|
17
|
+
Vlaanderen = "Vlaanderen",
|
|
18
|
+
RheinRuhr = "Rhein-Ruhr",
|
|
19
|
+
Paris = "Paris",
|
|
20
|
+
Bordeaux = "Bordeaux",
|
|
21
|
+
Lyon = "Lyon",
|
|
22
|
+
Marseille = "Marseille",
|
|
23
|
+
Lisboa = "Lisboa",
|
|
24
|
+
Madrid = "Madrid",
|
|
25
|
+
Barcelona = "Barcelona",
|
|
26
|
+
Bremen = "Bremen",
|
|
27
|
+
Berlin = "Berlin",
|
|
28
|
+
RheinMain = "Rhein-Main",
|
|
29
|
+
Stuttgart = "Stuttgart",
|
|
30
|
+
Praha = "Praha",
|
|
31
|
+
Katowice = "Katowice",
|
|
32
|
+
München = "M\u00FCnchen",
|
|
33
|
+
Zürich = "Z\u00FCrich",
|
|
34
|
+
Milano = "Milano",
|
|
35
|
+
Roma = "Roma",
|
|
36
|
+
Napoli = "Napoli",
|
|
37
|
+
Wien = "Wien",
|
|
38
|
+
Budapest = "Budapest",
|
|
39
|
+
Zagreb = "Zagreb",
|
|
40
|
+
Oslo = "Oslo",
|
|
41
|
+
Stockholm = "Stockholm",
|
|
42
|
+
Helsinki = "Helsinki",
|
|
43
|
+
Tallinn = "Tallinn",
|
|
44
|
+
Kobenhavn = "K\u00F8benhavn",
|
|
45
|
+
Riga = "Riga",
|
|
46
|
+
StPetersburg = "St. Petersburg",
|
|
47
|
+
Minsk = "Minsk",
|
|
48
|
+
Moskwa = "Moskwa",
|
|
49
|
+
Warszawa = "Warszawa",
|
|
50
|
+
Kyjiv = "Kyjiv",
|
|
51
|
+
Kharkiv = "Kharkiv",
|
|
52
|
+
Odessa = "Odessa",
|
|
53
|
+
Bucuresti = "Bucure\u015Fti",
|
|
54
|
+
Beograd = "Beograd",
|
|
55
|
+
Sofia = "Sofia",
|
|
56
|
+
Tirana = "Tirana",
|
|
57
|
+
Athina = "Athina",
|
|
58
|
+
Istanbul = "Istanbul",
|
|
59
|
+
Izmir = "Izmir",
|
|
60
|
+
Ankara = "Ankara"
|
|
61
|
+
}
|
|
62
|
+
export declare const map: GameMap;
|