powergrid-engine 1.11.0 → 1.12.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 +66 -0
- package/dist/src/engine.js +72 -9
- 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 +70 -12
- 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];
|
|
@@ -220,7 +221,14 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
220
221
|
while (playRegions.size != Math.min(regionsInPlay[p], regions.length)) {
|
|
221
222
|
const region = regions[Math.floor(rng() * regions.length)];
|
|
222
223
|
if (playRegions.size == 0 ||
|
|
223
|
-
regionConnections[regions.indexOf(region)].some((con) => playRegions.has(con))
|
|
224
|
+
regionConnections[regions.indexOf(region)].some((con) => playRegions.has(con)) ||
|
|
225
|
+
// UK & Ireland: regions on the two islands have no edges between
|
|
226
|
+
// them (no sea connection). Skipping the connectivity check lets
|
|
227
|
+
// the random selection span both islands; the cross-island
|
|
228
|
+
// surcharge handles the disconnect at build time. Without this,
|
|
229
|
+
// requiring 5-of-6 regions for 5p would loop forever (GB has 4
|
|
230
|
+
// regions, IE has 2).
|
|
231
|
+
chosenMap.name === 'UK & Ireland') {
|
|
224
232
|
playRegions.add(region);
|
|
225
233
|
// Avoid italy Red Green Blue
|
|
226
234
|
if (chosenMap.name === 'Italy') {
|
|
@@ -280,6 +288,7 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
280
288
|
const coalPricesNorth = chosenMap.coalPricesNorth ? lodash_1.cloneDeep(chosenMap.coalPricesNorth) : undefined;
|
|
281
289
|
const oilPricesNorth = chosenMap.oilPricesNorth ? lodash_1.cloneDeep(chosenMap.oilPricesNorth) : undefined;
|
|
282
290
|
const garbagePricesNorth = chosenMap.garbagePricesNorth ? lodash_1.cloneDeep(chosenMap.garbagePricesNorth) : undefined;
|
|
291
|
+
const isSouthAfrica = (forceMap || finalMap).name == 'South Africa';
|
|
283
292
|
const G = {
|
|
284
293
|
map: forceMap || finalMap,
|
|
285
294
|
players,
|
|
@@ -290,6 +299,9 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
290
299
|
oilSupply,
|
|
291
300
|
garbageSupply,
|
|
292
301
|
uraniumSupply,
|
|
302
|
+
// South Africa: separate coal pool below the market. Starts empty;
|
|
303
|
+
// used coal returns here; refill draws from here first.
|
|
304
|
+
coalStorage: isSouthAfrica ? 0 : undefined,
|
|
293
305
|
coalResupply,
|
|
294
306
|
oilResupply,
|
|
295
307
|
garbageResupply,
|
|
@@ -329,7 +341,9 @@ function setup(numPlayers, { fastBid = false, map = 'USA', variant = 'original',
|
|
|
329
341
|
auctionSkips: 0,
|
|
330
342
|
citiesToStep2: (forceMap || finalMap).name == 'Baden-Württemberg'
|
|
331
343
|
? citiesToStep2BadenWurttemberg[numPlayers - 2]
|
|
332
|
-
:
|
|
344
|
+
: (forceMap || finalMap).name == 'UK & Ireland'
|
|
345
|
+
? citiesToStep2UKIreland[numPlayers - 2]
|
|
346
|
+
: citiesToStep2[numPlayers - 2],
|
|
333
347
|
citiesToEndGame: citiesToEndGame[numPlayers - 2],
|
|
334
348
|
resourceResupply: [
|
|
335
349
|
`[${coalResupply[p][0]}, ${oilResupply[p][0]}, ${garbageResupply[p][0]}, ${uraniumResupply[p][0]}]`,
|
|
@@ -648,7 +662,18 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
648
662
|
G.powerPlantsDeck.unshift(getPowerPlant(18));
|
|
649
663
|
}
|
|
650
664
|
}
|
|
651
|
-
|
|
665
|
+
if (G.map.name == 'Europe') {
|
|
666
|
+
// Europe: do NOT draw a replacement from the deck.
|
|
667
|
+
// The future market shrinks from 5 to 4; reorganize
|
|
668
|
+
// the remaining 8 plants so actual stays at 4.
|
|
669
|
+
const market = [...G.actualMarket, ...G.futureMarket];
|
|
670
|
+
market.sort((a, b) => a.number - b.number);
|
|
671
|
+
G.actualMarket = market.slice(0, 4);
|
|
672
|
+
G.futureMarket = market.slice(4);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
addPowerPlant(G);
|
|
676
|
+
}
|
|
652
677
|
}
|
|
653
678
|
}
|
|
654
679
|
if (maxCities >= G.citiesToEndGame) {
|
|
@@ -742,9 +767,23 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
742
767
|
G.garbageMarketNorth += garbageResupplyNorthValue;
|
|
743
768
|
G.garbageSupply -= garbageResupplyNorthValue;
|
|
744
769
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
770
|
+
// South Africa pulls from coalStorage first, then coalSupply.
|
|
771
|
+
// Other maps just pull from coalSupply.
|
|
772
|
+
let coalResupplyValue;
|
|
773
|
+
if (G.coalStorage !== undefined) {
|
|
774
|
+
const wantCoal = Math.min(G.coalResupply[G.players.length - 2][G.step - 1], coalCapSouth);
|
|
775
|
+
const fromStorage = Math.min(G.coalStorage, wantCoal);
|
|
776
|
+
const fromSupply = Math.min(G.coalSupply, wantCoal - fromStorage);
|
|
777
|
+
coalResupplyValue = fromStorage + fromSupply;
|
|
778
|
+
G.coalMarket += coalResupplyValue;
|
|
779
|
+
G.coalStorage -= fromStorage;
|
|
780
|
+
G.coalSupply -= fromSupply;
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
coalResupplyValue = Math.min(G.coalSupply, G.coalResupply[G.players.length - 2][G.step - 1], coalCapSouth);
|
|
784
|
+
G.coalMarket += coalResupplyValue;
|
|
785
|
+
G.coalSupply -= coalResupplyValue;
|
|
786
|
+
}
|
|
748
787
|
let oilResupplyValue;
|
|
749
788
|
if (G.map.name == 'Middle East') {
|
|
750
789
|
if (G.oilMarket == 0) {
|
|
@@ -1077,6 +1116,12 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1077
1116
|
player.coalLeft++;
|
|
1078
1117
|
G.coalMarketNorth--;
|
|
1079
1118
|
}
|
|
1119
|
+
else if (move.data.fromStorage) {
|
|
1120
|
+
// South Africa: $8 flat from the storage pool below the market.
|
|
1121
|
+
price = 8;
|
|
1122
|
+
player.coalLeft++;
|
|
1123
|
+
G.coalStorage--;
|
|
1124
|
+
}
|
|
1080
1125
|
else if (G.coalMarket == 0) {
|
|
1081
1126
|
price = 8;
|
|
1082
1127
|
player.coalLeft++;
|
|
@@ -1156,8 +1201,15 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1156
1201
|
player.cities.push({ name: move.data.name, position });
|
|
1157
1202
|
player.money -= move.data.price;
|
|
1158
1203
|
if (G.options.trackTotalSpent) {
|
|
1159
|
-
|
|
1160
|
-
|
|
1204
|
+
const cityData = G.map.cities.find((c) => c.name == move.data.name);
|
|
1205
|
+
if (cityData.singleOccupancy) {
|
|
1206
|
+
// SA cross-border: no house base — full price is "connection".
|
|
1207
|
+
player.totalSpentConnections += move.data.price;
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
player.totalSpentCities += 10 + position * 5;
|
|
1211
|
+
player.totalSpentConnections += move.data.price - (10 + position * 5);
|
|
1212
|
+
}
|
|
1161
1213
|
}
|
|
1162
1214
|
G.log.push({
|
|
1163
1215
|
type: 'move',
|
|
@@ -1188,7 +1240,13 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1188
1240
|
switch (resourceType) {
|
|
1189
1241
|
case gamestate_1.ResourceType.Coal:
|
|
1190
1242
|
player.coalLeft--;
|
|
1191
|
-
|
|
1243
|
+
// SA: used coal returns to the separate storage pool below the market.
|
|
1244
|
+
if (G.coalStorage !== undefined) {
|
|
1245
|
+
G.coalStorage++;
|
|
1246
|
+
}
|
|
1247
|
+
else {
|
|
1248
|
+
G.coalSupply++;
|
|
1249
|
+
}
|
|
1192
1250
|
break;
|
|
1193
1251
|
case gamestate_1.ResourceType.Oil:
|
|
1194
1252
|
player.oilLeft--;
|
|
@@ -1242,6 +1300,11 @@ function move(G, move, playerNumber, isUndo = false) {
|
|
|
1242
1300
|
const coalPrices = G.coalPricesNorth;
|
|
1243
1301
|
price = coalPrices[coalPrices.length - G.coalMarketNorth];
|
|
1244
1302
|
}
|
|
1303
|
+
else if (lastMove.data.fromStorage) {
|
|
1304
|
+
price = 8;
|
|
1305
|
+
player.coalLeft--;
|
|
1306
|
+
G.coalStorage++;
|
|
1307
|
+
}
|
|
1245
1308
|
else if (lastMove.fromSupply) {
|
|
1246
1309
|
price = 8;
|
|
1247
1310
|
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;
|