@civ-clone/base-city-lifecycle 0.0.0-stage → 0.1.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.
Files changed (61) hide show
  1. package/.gitattributes +2 -0
  2. package/.github/workflows/build-on-push.yml +39 -0
  3. package/.prettierrc +4 -0
  4. package/LICENSE +21 -0
  5. package/README.md +42 -2
  6. package/lib/assignWorkers.d.ts +114 -0
  7. package/lib/assignWorkers.js +184 -0
  8. package/lib/assignWorkers.js.map +1 -0
  9. package/lib/assignWorkers.ts +372 -0
  10. package/lib/canBeWorked.d.ts +28 -0
  11. package/lib/canBeWorked.js +27 -0
  12. package/lib/canBeWorked.js.map +1 -0
  13. package/lib/canBeWorked.ts +43 -0
  14. package/lib/captured.d.ts +12 -0
  15. package/lib/captured.js +30 -0
  16. package/lib/captured.js.map +1 -0
  17. package/lib/captured.ts +31 -0
  18. package/lib/claimName.d.ts +12 -0
  19. package/lib/claimName.js +9 -0
  20. package/lib/claimName.js.map +1 -0
  21. package/lib/claimName.ts +13 -0
  22. package/lib/created.d.ts +41 -0
  23. package/lib/created.js +52 -0
  24. package/lib/created.js.map +1 -0
  25. package/lib/created.ts +110 -0
  26. package/lib/destroyed.d.ts +23 -0
  27. package/lib/destroyed.js +29 -0
  28. package/lib/destroyed.js.map +1 -0
  29. package/lib/destroyed.ts +39 -0
  30. package/lib/events.d.ts +39 -0
  31. package/lib/events.js +33 -0
  32. package/lib/events.js.map +1 -0
  33. package/lib/events.ts +51 -0
  34. package/lib/famine.d.ts +41 -0
  35. package/lib/famine.js +44 -0
  36. package/lib/famine.js.map +1 -0
  37. package/lib/famine.ts +68 -0
  38. package/lib/foodStorage.d.ts +21 -0
  39. package/lib/foodStorage.js +27 -0
  40. package/lib/foodStorage.js.map +1 -0
  41. package/lib/foodStorage.ts +30 -0
  42. package/lib/occupiedTiles.d.ts +39 -0
  43. package/lib/occupiedTiles.js +50 -0
  44. package/lib/occupiedTiles.js.map +1 -0
  45. package/lib/occupiedTiles.ts +84 -0
  46. package/lib/playerActions.d.ts +72 -0
  47. package/lib/playerActions.js +68 -0
  48. package/lib/playerActions.js.map +1 -0
  49. package/lib/playerActions.ts +114 -0
  50. package/lib/processYield.d.ts +30 -0
  51. package/lib/processYield.js +54 -0
  52. package/lib/processYield.js.map +1 -0
  53. package/lib/processYield.ts +82 -0
  54. package/lib/unsupported.d.ts +6 -0
  55. package/lib/unsupported.js +9 -0
  56. package/lib/unsupported.js.map +1 -0
  57. package/lib/unsupported.ts +8 -0
  58. package/package.json +45 -4
  59. package/tests/base-city-lifecycle.test.ts +536 -0
  60. package/tests/r3b.test.ts +341 -0
  61. package/tsconfig.json +16 -0
package/.gitattributes ADDED
@@ -0,0 +1,2 @@
1
+ **/*.js linguist-generated=true
2
+
@@ -0,0 +1,39 @@
1
+ name: Build and test application on push to remote
2
+
3
+ on: [push]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v4
10
+ - uses: pnpm/action-setup@v4
11
+ with:
12
+ version: 11
13
+ - uses: actions/setup-node@v4
14
+ with:
15
+ node-version: 22
16
+ # No lock file, so this tests what a consumer resolves. The settings match
17
+ # web-renderer's pnpm-workspace.yaml: several @civ-clone packages depend on
18
+ # each other as `github:` URLs, this project's own releases are often only
19
+ # minutes old, and nothing here needs a dependency's build script.
20
+ - run: >-
21
+ pnpm install --no-frozen-lockfile
22
+ --config.block-exotic-subdeps=false
23
+ --config.minimum-release-age=0
24
+ --config.strict-dep-builds=false
25
+ - run: npm run --if-present prettier:check
26
+ # The tests load the committed `.js`, so compile to prove the source
27
+ # type-checks, and fail if the committed output is stale. `--force`
28
+ # because a fresh checkout's timestamps can make `tsc --build` treat the
29
+ # committed output as up to date and skip the compile entirely.
30
+ - run: npm run --if-present ts:compile -- --force
31
+ - run: npm run --if-present prettier:format
32
+ # Stale *or never committed* output fails: `git diff` alone misses a new
33
+ # module whose `.js` was never added, which the published package would
34
+ # then omit. Only the compiled extensions, so the lock file the install
35
+ # writes cannot fail it; `node_modules` is ignored, so it stays out.
36
+ - run: |
37
+ git status --short --untracked-files=all -- '*.js' '*.d.ts' '*.js.map'
38
+ test -z "$(git status --porcelain --untracked-files=all -- '*.js' '*.d.ts' '*.js.map')"
39
+ - run: npm run --if-present test
package/.prettierrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true
4
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Dom Hastings
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,43 @@
1
- # Temporary Holding Version
1
+ # base-city-lifecycle
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A city's turn and what goes with it, as a library for a ruleset's city rules:
4
+
5
+ - `lib/processYield`: `storeFood(food, city, cityGrowthRegistry)` puts a city's food for the turn into its store and
6
+ checks it. `buildWithProduction(production, city, cityYields, ProductionSupportType, cityBuildRegistry, ruleRegistry)`
7
+ puts its production into its build and checks it: while production net of unit support is below 0, the units it
8
+ can't support go to the `Unsupported` rules, furthest from the city first, and nothing is added while a
9
+ `CivilDisorder` rule holds.
10
+ - `lib/foodStorage`: `hasFoodToGrow(cityGrowth)` and `grow(cityGrowth)`; `isFoodExhausted(cityGrowth)` (the store is
11
+ below 0) and `exhaustFood(cityGrowth, ruleRegistry)`, which runs the `FoodExhausted` rules.
12
+ - `lib/famine`: `foodSupportedUnit(cityGrowth, FoodSupportType)`, the first unit a city feeds;
13
+ `emitFoodStorageExhausted(cityGrowth, unit, engine)`; `loseFoodSupportedUnit(cityGrowth, FoodSupportType)`,
14
+ `shrinkFromFamine(cityGrowth)` and `emptyFoodStore(cityGrowth)`.
15
+ - `lib/canBeWorked`: `isNotWorked(tile, workedTileRegistry)`, `isNotOccupiedByEnemy(tile, city, unitRegistry)` and
16
+ `isNotAnotherCity(tile, city, cityRegistry)`.
17
+ - `lib/occupiedTiles`: `enemyWorkedTile(unit, tile, workedTileRegistry)`, a tile another player's city works (not its
18
+ centre), and `releaseEnemyWorkedTile(unit, tile, workedTileRegistry, ruleRegistry)`, which takes it from that city
19
+ and runs its `TileReassigned` rules; `releaseOccupiedWorkedTiles(player, cityRegistry, unitRegistry,
20
+ workedTileRegistry, ruleRegistry)` does the same at the start of a player's turn for every tile its cities work that
21
+ another player's unit stands on.
22
+ - `lib/playerActions`: the city actions a player is offered, each as a check and a list: `hasCityToChooseProduction`
23
+ and `chooseProductionActions`, `hasCityToChangeProduction` and `changeProductionActions`, `hasCity` and
24
+ `changeWorkedTileActions`, `hasSpecialist` and `changeSpecialistActions`.
25
+ - `lib/unsupported`: `disbandUnsupported(city, unit)`.
26
+ - `lib/claimName`: `claimName(city, cityNames)` and the `CityNames` type.
27
+ - `lib/events`: `emitTileWorked(city, tile, engine)`, `emitTileReleased(city, tile, engine)`,
28
+ `emitBuildingHeld(cityBuild, item, held, engine)` and `emitBuildingComplete(cityBuild, built, engine)`, for clients.
29
+ - `lib/assignWorkers`: placing a city's citizens on its best tiles or as specialists (`assignWorkers`, `assignWorker`,
30
+ `reassignWorkers`, `releaseCitizens`, `changeWorkedTile`, `addSpecialist`, `changeSpecialist`, `citizenCount` and
31
+ the rest), lifted unchanged from library-city.
32
+ - `lib/created`: `registerCityBuild`, `registerCityGrowth` and `workCityTile(city, ...)`, which takes the new city's
33
+ centre from any city that worked it (running that city's `TileReassigned` rules) and puts its citizens to work.
34
+ - `lib/captured`: `clearCapturedBuild(cityBuild)` (the new owner chooses) and `keepCapturedBuild(cityBuild)` (the item
35
+ stays, repriced, without the queue).
36
+ - `lib/destroyed`: `destroyUnits(units)` (skipping any already destroyed), `releaseWorkedTiles(city, ...)` and
37
+ `releaseSpecialists(city, ...)`.
38
+
39
+ The yield types a ruleset pays unit support with are passed in (`FoodSupportType`, `ProductionSupportType`: a yield
40
+ that knows its `unit()`).
41
+
42
+ A library: it registers no rules. The ruleset decides the rules, their ids and order, and when each runs. See
43
+ `civ1-city` for Civilization's.
@@ -0,0 +1,114 @@
1
+ import { AvailableSpecialistRegistry } from '@civ-clone/core-city/AvailableSpecialistRegistry';
2
+ import { CityGrowthRegistry } from '@civ-clone/core-city-growth/CityGrowthRegistry';
3
+ import { PlayerWorldRegistry } from '@civ-clone/core-player-world/PlayerWorldRegistry';
4
+ import { SpecialistRegistry } from '@civ-clone/core-city/SpecialistRegistry';
5
+ import { WorkedTileRegistry } from '@civ-clone/core-city/WorkedTileRegistry';
6
+ import City from '@civ-clone/core-city/City';
7
+ import Tile from '@civ-clone/core-world/Tile';
8
+ import Yield from '@civ-clone/core-yield/Yield';
9
+ import CityGrowth from '@civ-clone/core-city-growth/CityGrowth';
10
+ import Specialist from '@civ-clone/core-city/Specialist';
11
+ /**
12
+ * How many citizens `city` has placed: one per worked tile, the city centre included, and one per `Specialist`. A city
13
+ * of size `n` has `n + 1`.
14
+ */
15
+ export declare const citizenCount: (
16
+ city: City,
17
+ workedTileRegistry?: WorkedTileRegistry,
18
+ specialistRegistry?: SpecialistRegistry
19
+ ) => number;
20
+ /**
21
+ * Makes a citizen of `city` a `Specialist` of the first available kind (an Entertainer, in Civ1). Returns `null` when
22
+ * the ruleset offers no specialists.
23
+ */
24
+ export declare const addSpecialist: (
25
+ city: City,
26
+ specialistRegistry?: SpecialistRegistry,
27
+ availableSpecialistRegistry?: AvailableSpecialistRegistry
28
+ ) => Specialist | null;
29
+ /**
30
+ * Turns `specialist` into the next available kind, wrapping back to the first. Returns the `Specialist` that replaces
31
+ * it, or `specialist` itself when there is nothing to change it to.
32
+ */
33
+ export declare const changeSpecialist: (
34
+ specialist: Specialist,
35
+ specialistRegistry?: SpecialistRegistry,
36
+ availableSpecialistRegistry?: AvailableSpecialistRegistry
37
+ ) => Specialist;
38
+ export declare const getHighestValueCityTiles: (city: City) => Tile[];
39
+ export declare const sortTiles: (
40
+ tiles: Tile[],
41
+ city: City,
42
+ weights?: [typeof Yield, number][]
43
+ ) => Tile[];
44
+ export declare const getHighestValueAvailableCityTiles: (
45
+ city: City,
46
+ playerWorldRegistry?: PlayerWorldRegistry,
47
+ workedTileRegistry?: WorkedTileRegistry
48
+ ) => Tile[];
49
+ /**
50
+ * Rearranges the tiles `city` works from scratch. Its specialists stay as they are.
51
+ */
52
+ export declare const reassignWorkers: (
53
+ city: City,
54
+ playerWorldRegistry?: PlayerWorldRegistry,
55
+ cityGrowthRegistry?: CityGrowthRegistry,
56
+ workedTileRegistry?: WorkedTileRegistry,
57
+ specialistRegistry?: SpecialistRegistry,
58
+ availableSpecialistRegistry?: AvailableSpecialistRegistry
59
+ ) => void;
60
+ /**
61
+ * Places each of `city`'s citizens that has no job on the best tile available, or makes them a specialist if there is
62
+ * no tile left to work.
63
+ */
64
+ export declare const assignWorkers: (
65
+ city: City,
66
+ playerWorldRegistry?: PlayerWorldRegistry,
67
+ cityGrowthRegistry?: CityGrowthRegistry,
68
+ workedTileRegistry?: WorkedTileRegistry,
69
+ specialistRegistry?: SpecialistRegistry,
70
+ availableSpecialistRegistry?: AvailableSpecialistRegistry
71
+ ) => void;
72
+ /**
73
+ * Places one new citizen of `city` on the best tile available, or makes them a specialist if there is no tile left.
74
+ */
75
+ export declare const assignWorker: (
76
+ city: City,
77
+ playerWorldRegistry?: PlayerWorldRegistry,
78
+ cityGrowthRegistry?: CityGrowthRegistry,
79
+ workedTileRegistry?: WorkedTileRegistry,
80
+ specialistRegistry?: SpecialistRegistry,
81
+ availableSpecialistRegistry?: AvailableSpecialistRegistry
82
+ ) => void;
83
+ export type WorkedTileChange = 'removed' | 'added' | 'reassigned' | 'none';
84
+ /**
85
+ * Changes whether `tile` is worked by `city`, as a player clicking it on the city map would.
86
+ *
87
+ * A tile the city works stops being worked, and its worker becomes a specialist. Any other tile is worked by a
88
+ * specialist, or by a citizen with no job, if the city has one and is allowed to work it. With every citizen already
89
+ * working a tile, the city's workers are reassigned instead. The city centre is always worked.
90
+ */
91
+ export declare const changeWorkedTile: (
92
+ city: City,
93
+ tile: Tile,
94
+ playerWorldRegistry?: PlayerWorldRegistry,
95
+ cityGrowthRegistry?: CityGrowthRegistry,
96
+ workedTileRegistry?: WorkedTileRegistry,
97
+ specialistRegistry?: SpecialistRegistry,
98
+ availableSpecialistRegistry?: AvailableSpecialistRegistry
99
+ ) => WorkedTileChange;
100
+ /**
101
+ * Takes jobs away from `city`'s citizens until it has no more than its size allows: specialists first, then the
102
+ * workers on the least valuable tiles.
103
+ */
104
+ export declare const releaseCitizens: (
105
+ city: City,
106
+ cityGrowth: CityGrowth,
107
+ workedTileRegistry?: WorkedTileRegistry,
108
+ specialistRegistry?: SpecialistRegistry
109
+ ) => void;
110
+ export declare const reduceWorkers: (
111
+ city: City,
112
+ cityGrowth: CityGrowth
113
+ ) => Tile[];
114
+ export default assignWorkers;
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reduceWorkers = exports.releaseCitizens = exports.changeWorkedTile = exports.assignWorker = exports.assignWorkers = exports.reassignWorkers = exports.getHighestValueAvailableCityTiles = exports.sortTiles = exports.getHighestValueCityTiles = exports.changeSpecialist = exports.addSpecialist = exports.citizenCount = void 0;
4
+ // Lifted from library-city 0.1.4 (`lib/assignWorkers.ts`), unchanged but for where the yields come from, so a game's
5
+ // rules can place a city's citizens without depending on library-city (civ-clone/web-renderer Civ2 M2a, R3b).
6
+ // library-city re-exports this in R3b.
7
+ const AvailableSpecialistRegistry_1 = require("@civ-clone/core-city/AvailableSpecialistRegistry");
8
+ const CityGrowthRegistry_1 = require("@civ-clone/core-city-growth/CityGrowthRegistry");
9
+ const Food_1 = require("@civ-clone/base-terrain-yield-food/Food");
10
+ const Production_1 = require("@civ-clone/base-terrain-yield-production/Production");
11
+ const Trade_1 = require("@civ-clone/base-terrain-yield-trade/Trade");
12
+ const PlayerWorldRegistry_1 = require("@civ-clone/core-player-world/PlayerWorldRegistry");
13
+ const SpecialistRegistry_1 = require("@civ-clone/core-city/SpecialistRegistry");
14
+ const WorkedTileRegistry_1 = require("@civ-clone/core-city/WorkedTileRegistry");
15
+ const reduceYields_1 = require("@civ-clone/core-yield/lib/reduceYields");
16
+ const WorkedTile_1 = require("@civ-clone/core-city/WorkedTile");
17
+ /**
18
+ * How many citizens `city` has placed: one per worked tile, the city centre included, and one per `Specialist`. A city
19
+ * of size `n` has `n + 1`.
20
+ */
21
+ const citizenCount = (city, workedTileRegistry = WorkedTileRegistry_1.instance, specialistRegistry = SpecialistRegistry_1.instance) => workedTileRegistry.getByCity(city).length +
22
+ specialistRegistry.getByCity(city).length;
23
+ exports.citizenCount = citizenCount;
24
+ /**
25
+ * Makes a citizen of `city` a `Specialist` of the first available kind (an Entertainer, in Civ1). Returns `null` when
26
+ * the ruleset offers no specialists.
27
+ */
28
+ const addSpecialist = (city, specialistRegistry = SpecialistRegistry_1.instance, availableSpecialistRegistry = AvailableSpecialistRegistry_1.instance) => {
29
+ const [SpecialistType] = availableSpecialistRegistry.entries();
30
+ if (!SpecialistType) {
31
+ return null;
32
+ }
33
+ const specialist = new SpecialistType(city);
34
+ specialistRegistry.register(specialist);
35
+ return specialist;
36
+ };
37
+ exports.addSpecialist = addSpecialist;
38
+ /**
39
+ * Turns `specialist` into the next available kind, wrapping back to the first. Returns the `Specialist` that replaces
40
+ * it, or `specialist` itself when there is nothing to change it to.
41
+ */
42
+ const changeSpecialist = (specialist, specialistRegistry = SpecialistRegistry_1.instance, availableSpecialistRegistry = AvailableSpecialistRegistry_1.instance) => {
43
+ const city = specialist.city(), types = availableSpecialistRegistry.entries(), NextType = types[(types.indexOf(specialist.constructor) + 1) %
44
+ types.length];
45
+ if (!NextType || specialist instanceof NextType) {
46
+ return specialist;
47
+ }
48
+ const replacement = new NextType(city);
49
+ specialistRegistry.unregister(specialist);
50
+ specialistRegistry.register(replacement);
51
+ return replacement;
52
+ };
53
+ exports.changeSpecialist = changeSpecialist;
54
+ /**
55
+ * Puts one of `city`'s specialists back to work, preferring the first available kind (Entertainers), and returns
56
+ * whether there was one to remove.
57
+ */
58
+ const removeSpecialist = (city, specialistRegistry, availableSpecialistRegistry) => {
59
+ var _a;
60
+ const specialists = specialistRegistry.getByCity(city), [DefaultType] = availableSpecialistRegistry.entries(), specialist = (_a = specialists.find((specialist) => DefaultType && specialist instanceof DefaultType)) !== null && _a !== void 0 ? _a : specialists[specialists.length - 1];
61
+ if (!specialist) {
62
+ return false;
63
+ }
64
+ specialistRegistry.unregister(specialist);
65
+ return true;
66
+ };
67
+ const getHighestValueCityTiles = (city) => (0, exports.sortTiles)(city.tiles().entries(), city);
68
+ exports.getHighestValueCityTiles = getHighestValueCityTiles;
69
+ const sortTiles = (tiles, city, weights = [
70
+ [Food_1.default, 8],
71
+ [
72
+ Production_1.default,
73
+ 3 *
74
+ ((0, reduceYields_1.reduceYield)(city.tilesWorked().yields(city.player()), Production_1.default) === 0
75
+ ? 3
76
+ : 1),
77
+ ],
78
+ [
79
+ Trade_1.default,
80
+ 1 *
81
+ ((0, reduceYields_1.reduceYield)(city.tilesWorked().yields(city.player()), Trade_1.default) === 0
82
+ ? 2
83
+ : 1),
84
+ ],
85
+ ]) => tiles.sort((a, b) => b.score(city.player(), weights) - a.score(city.player(), weights));
86
+ exports.sortTiles = sortTiles;
87
+ const getHighestValueAvailableCityTiles = (city, playerWorldRegistry = PlayerWorldRegistry_1.instance, workedTileRegistry = WorkedTileRegistry_1.instance) => (0, exports.getHighestValueCityTiles)(city).filter((tile) => workedTileRegistry.tileCanBeWorkedBy(tile, city) &&
88
+ playerWorldRegistry.getByPlayer(city.player()).includes(tile));
89
+ exports.getHighestValueAvailableCityTiles = getHighestValueAvailableCityTiles;
90
+ /**
91
+ * Rearranges the tiles `city` works from scratch. Its specialists stay as they are.
92
+ */
93
+ const reassignWorkers = (city, playerWorldRegistry = PlayerWorldRegistry_1.instance, cityGrowthRegistry = CityGrowthRegistry_1.instance, workedTileRegistry = WorkedTileRegistry_1.instance, specialistRegistry = SpecialistRegistry_1.instance, availableSpecialistRegistry = AvailableSpecialistRegistry_1.instance) => {
94
+ workedTileRegistry
95
+ .getBy('city', city)
96
+ .forEach((workedTile) => workedTileRegistry.unregister(workedTile));
97
+ workedTileRegistry.register(new WorkedTile_1.default(city.tile(), city));
98
+ (0, exports.assignWorkers)(city, playerWorldRegistry, cityGrowthRegistry, workedTileRegistry, specialistRegistry, availableSpecialistRegistry);
99
+ };
100
+ exports.reassignWorkers = reassignWorkers;
101
+ /**
102
+ * Places each of `city`'s citizens that has no job on the best tile available, or makes them a specialist if there is
103
+ * no tile left to work.
104
+ */
105
+ const assignWorkers = (city, playerWorldRegistry = PlayerWorldRegistry_1.instance, cityGrowthRegistry = CityGrowthRegistry_1.instance, workedTileRegistry = WorkedTileRegistry_1.instance, specialistRegistry = SpecialistRegistry_1.instance, availableSpecialistRegistry = AvailableSpecialistRegistry_1.instance) => {
106
+ const cityGrowth = cityGrowthRegistry.getByCity(city), placed = () => (0, exports.citizenCount)(city, workedTileRegistry, specialistRegistry) >=
107
+ cityGrowth.size() + 1;
108
+ (0, exports.getHighestValueAvailableCityTiles)(city, playerWorldRegistry, workedTileRegistry).some((tile) => {
109
+ if (placed()) {
110
+ return true;
111
+ }
112
+ workedTileRegistry.register(new WorkedTile_1.default(tile, city));
113
+ return false;
114
+ });
115
+ while (!placed() &&
116
+ (0, exports.addSpecialist)(city, specialistRegistry, availableSpecialistRegistry)) { }
117
+ };
118
+ exports.assignWorkers = assignWorkers;
119
+ /**
120
+ * Places one new citizen of `city` on the best tile available, or makes them a specialist if there is no tile left.
121
+ */
122
+ const assignWorker = (city, playerWorldRegistry = PlayerWorldRegistry_1.instance, cityGrowthRegistry = CityGrowthRegistry_1.instance, workedTileRegistry = WorkedTileRegistry_1.instance, specialistRegistry = SpecialistRegistry_1.instance, availableSpecialistRegistry = AvailableSpecialistRegistry_1.instance) => {
123
+ const [newTile] = (0, exports.getHighestValueAvailableCityTiles)(city, playerWorldRegistry, workedTileRegistry);
124
+ if (!newTile) {
125
+ (0, exports.addSpecialist)(city, specialistRegistry, availableSpecialistRegistry);
126
+ return;
127
+ }
128
+ workedTileRegistry.register(new WorkedTile_1.default(newTile, city));
129
+ };
130
+ exports.assignWorker = assignWorker;
131
+ /**
132
+ * Changes whether `tile` is worked by `city`, as a player clicking it on the city map would.
133
+ *
134
+ * A tile the city works stops being worked, and its worker becomes a specialist. Any other tile is worked by a
135
+ * specialist, or by a citizen with no job, if the city has one and is allowed to work it. With every citizen already
136
+ * working a tile, the city's workers are reassigned instead. The city centre is always worked.
137
+ */
138
+ const changeWorkedTile = (city, tile, playerWorldRegistry = PlayerWorldRegistry_1.instance, cityGrowthRegistry = CityGrowthRegistry_1.instance, workedTileRegistry = WorkedTileRegistry_1.instance, specialistRegistry = SpecialistRegistry_1.instance, availableSpecialistRegistry = AvailableSpecialistRegistry_1.instance) => {
139
+ var _a;
140
+ if (tile === city.tile() || !city.tiles().includes(tile)) {
141
+ return 'none';
142
+ }
143
+ if (((_a = workedTileRegistry.getByTile(tile)) === null || _a === void 0 ? void 0 : _a.city()) === city) {
144
+ workedTileRegistry.unregisterByTile(tile);
145
+ (0, exports.addSpecialist)(city, specialistRegistry, availableSpecialistRegistry);
146
+ return 'removed';
147
+ }
148
+ const size = cityGrowthRegistry.getByCity(city).size();
149
+ if (workedTileRegistry.getByCity(city).length >= size + 1) {
150
+ (0, exports.reassignWorkers)(city, playerWorldRegistry, cityGrowthRegistry, workedTileRegistry, specialistRegistry, availableSpecialistRegistry);
151
+ return 'reassigned';
152
+ }
153
+ if (!workedTileRegistry.tileCanBeWorkedBy(tile, city) ||
154
+ !playerWorldRegistry.getByPlayer(city.player()).includes(tile)) {
155
+ return 'none';
156
+ }
157
+ if ((0, exports.citizenCount)(city, workedTileRegistry, specialistRegistry) >= size + 1) {
158
+ removeSpecialist(city, specialistRegistry, availableSpecialistRegistry);
159
+ }
160
+ workedTileRegistry.register(new WorkedTile_1.default(tile, city));
161
+ return 'added';
162
+ };
163
+ exports.changeWorkedTile = changeWorkedTile;
164
+ /**
165
+ * Takes jobs away from `city`'s citizens until it has no more than its size allows: specialists first, then the
166
+ * workers on the least valuable tiles.
167
+ */
168
+ const releaseCitizens = (city, cityGrowth, workedTileRegistry = WorkedTileRegistry_1.instance, specialistRegistry = SpecialistRegistry_1.instance) => {
169
+ const specialists = specialistRegistry.getByCity(city);
170
+ while (specialists.length > 0 &&
171
+ (0, exports.citizenCount)(city, workedTileRegistry, specialistRegistry) >
172
+ cityGrowth.size() + 1) {
173
+ specialistRegistry.unregister(specialists.pop());
174
+ }
175
+ (0, exports.reduceWorkers)(city, cityGrowth).forEach((tile) => workedTileRegistry.unregisterByTile(tile));
176
+ };
177
+ exports.releaseCitizens = releaseCitizens;
178
+ const reduceWorkers = (city, cityGrowth) => (0, exports.sortTiles)(city
179
+ .tilesWorked()
180
+ .entries()
181
+ .filter((tile) => tile !== city.tile()), city).slice(cityGrowth.size());
182
+ exports.reduceWorkers = reduceWorkers;
183
+ exports.default = exports.assignWorkers;
184
+ //# sourceMappingURL=assignWorkers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assignWorkers.js","sourceRoot":"","sources":["assignWorkers.ts"],"names":[],"mappings":"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"}