@v12-engine/engine 0.0.0-stage → 0.1.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.
Files changed (341) hide show
  1. package/LICENSE +21 -0
  2. package/dist/abilities/buffs.d.ts +83 -0
  3. package/dist/abilities/castTold.d.ts +12 -0
  4. package/dist/abilities/charges.d.ts +28 -0
  5. package/dist/abilities/dispel.d.ts +9 -0
  6. package/dist/abilities/effectFields.d.ts +10 -0
  7. package/dist/abilities/effects/buff.d.ts +8 -0
  8. package/dist/abilities/effects/core.d.ts +1 -0
  9. package/dist/abilities/effects/damage.d.ts +1 -0
  10. package/dist/abilities/effects/heal.d.ts +2 -0
  11. package/dist/abilities/effects/missile.d.ts +1 -0
  12. package/dist/abilities/effects/moves.d.ts +11 -0
  13. package/dist/abilities/effects/passives.d.ts +15 -0
  14. package/dist/abilities/effects/sight.d.ts +4 -0
  15. package/dist/abilities/effects/spawn.d.ts +2 -0
  16. package/dist/abilities/effects/timed.d.ts +4 -0
  17. package/dist/abilities/effects/trade.d.ts +1 -0
  18. package/dist/abilities/effects.d.ts +122 -0
  19. package/dist/abilities/land.d.ts +21 -0
  20. package/dist/abilities/scaled.d.ts +21 -0
  21. package/dist/abilities/text.d.ts +113 -0
  22. package/dist/ai/area.d.ts +53 -0
  23. package/dist/ai/brain.d.ts +528 -0
  24. package/dist/ai/budget.d.ts +67 -0
  25. package/dist/ai/checks.d.ts +115 -0
  26. package/dist/ai/computer.d.ts +166 -0
  27. package/dist/ai/defence.d.ts +108 -0
  28. package/dist/ai/define.d.ts +21 -0
  29. package/dist/ai/economy.d.ts +129 -0
  30. package/dist/ai/geometry.d.ts +35 -0
  31. package/dist/ai/goals.d.ts +360 -0
  32. package/dist/ai/index.d.ts +38 -0
  33. package/dist/ai/knowledge.d.ts +418 -0
  34. package/dist/ai/lanes.d.ts +232 -0
  35. package/dist/ai/melee/build.d.ts +61 -0
  36. package/dist/ai/melee/goals.d.ts +299 -0
  37. package/dist/ai/melee/heroes.d.ts +27 -0
  38. package/dist/ai/melee/index.d.ts +14 -0
  39. package/dist/ai/melee/production.d.ts +8 -0
  40. package/dist/ai/melee/surrender.d.ts +24 -0
  41. package/dist/ai/micro.d.ts +278 -0
  42. package/dist/ai/mission.d.ts +195 -0
  43. package/dist/ai/missionRules.d.ts +18 -0
  44. package/dist/ai/module.d.ts +116 -0
  45. package/dist/ai/options.d.ts +61 -0
  46. package/dist/ai/plans/build.d.ts +280 -0
  47. package/dist/ai/plans/placement.d.ts +82 -0
  48. package/dist/ai/plans/plans.d.ts +105 -0
  49. package/dist/ai/plans/production.d.ts +210 -0
  50. package/dist/ai/plans/purse.d.ts +73 -0
  51. package/dist/ai/plans/tech.d.ts +37 -0
  52. package/dist/ai/profiles.d.ts +204 -0
  53. package/dist/ai/reach.d.ts +4 -0
  54. package/dist/ai/report.d.ts +203 -0
  55. package/dist/ai/roles.d.ts +133 -0
  56. package/dist/ai/rules.d.ts +288 -0
  57. package/dist/ai/scouting.d.ts +98 -0
  58. package/dist/ai/settings.d.ts +73 -0
  59. package/dist/ai/squads.d.ts +429 -0
  60. package/dist/ai/strength.d.ts +167 -0
  61. package/dist/ai/task.d.ts +118 -0
  62. package/dist/ai/tasks/attack.d.ts +7 -0
  63. package/dist/ai/tasks/capture.d.ts +21 -0
  64. package/dist/ai/tasks/creep.d.ts +16 -0
  65. package/dist/ai/tasks/defend.d.ts +29 -0
  66. package/dist/ai/tasks/drop.d.ts +34 -0
  67. package/dist/ai/tasks/escort.d.ts +16 -0
  68. package/dist/ai/tasks/gather.d.ts +23 -0
  69. package/dist/ai/tasks/harass.d.ts +7 -0
  70. package/dist/ai/tasks/hold.d.ts +20 -0
  71. package/dist/ai/tasks/index.d.ts +32 -0
  72. package/dist/ai/tasks/move.d.ts +19 -0
  73. package/dist/ai/tasks/patrol.d.ts +8 -0
  74. package/dist/ai/tasks/retreat.d.ts +20 -0
  75. package/dist/ai/tasks/scout.d.ts +29 -0
  76. package/dist/ai/tasks/shared.d.ts +74 -0
  77. package/dist/ai/team.d.ts +155 -0
  78. package/dist/ai/threat.d.ts +91 -0
  79. package/dist/ai/view.d.ts +366 -0
  80. package/dist/ai/waves.d.ts +126 -0
  81. package/dist/ai.js +6239 -0
  82. package/dist/ai.js.map +1 -0
  83. package/dist/aiTesting.d.ts +179 -0
  84. package/dist/areas.d.ts +149 -0
  85. package/dist/attacks.d.ts +66 -0
  86. package/dist/attributes.d.ts +66 -0
  87. package/dist/brain-BvVdUhX8.js +7659 -0
  88. package/dist/brain-BvVdUhX8.js.map +1 -0
  89. package/dist/builds-C6LfgvEU.js +460 -0
  90. package/dist/builds-C6LfgvEU.js.map +1 -0
  91. package/dist/bytes.d.ts +6 -0
  92. package/dist/camps.d.ts +95 -0
  93. package/dist/capture.d.ts +131 -0
  94. package/dist/cellLayers.d.ts +75 -0
  95. package/dist/cinematics.d.ts +194 -0
  96. package/dist/clock.d.ts +32 -0
  97. package/dist/commands.d.ts +235 -0
  98. package/dist/components.d.ts +184 -0
  99. package/dist/computers.d.ts +108 -0
  100. package/dist/concealment.d.ts +61 -0
  101. package/dist/coreComponents-syuh2fPG.js +5149 -0
  102. package/dist/coreComponents-syuh2fPG.js.map +1 -0
  103. package/dist/coreComponents.d.ts +1 -0
  104. package/dist/dashing.d.ts +8 -0
  105. package/dist/debug.d.ts +36 -0
  106. package/dist/debug.js +3 -0
  107. package/dist/debugCommands-CzjrSm66.js +14739 -0
  108. package/dist/debugCommands-CzjrSm66.js.map +1 -0
  109. package/dist/debugCommands.d.ts +213 -0
  110. package/dist/debugging.d.ts +10 -0
  111. package/dist/definitions.d.ts +43 -0
  112. package/dist/defs.d.ts +1162 -0
  113. package/dist/denying.d.ts +81 -0
  114. package/dist/destructibles.d.ts +27 -0
  115. package/dist/determinism-BCymQwiE.js +518 -0
  116. package/dist/determinism-BCymQwiE.js.map +1 -0
  117. package/dist/determinism.d.ts +121 -0
  118. package/dist/dialog.d.ts +94 -0
  119. package/dist/effectRefs.d.ts +16 -0
  120. package/dist/effects.d.ts +80 -0
  121. package/dist/endings.d.ts +22 -0
  122. package/dist/engagement.d.ts +108 -0
  123. package/dist/engineEvents.d.ts +710 -0
  124. package/dist/entity.d.ts +409 -0
  125. package/dist/entityIndex.d.ts +50 -0
  126. package/dist/errors.d.ts +85 -0
  127. package/dist/events.d.ts +97 -0
  128. package/dist/exactMath.d.ts +63 -0
  129. package/dist/features.d.ts +10 -0
  130. package/dist/fields.d.ts +114 -0
  131. package/dist/fixture.d.ts +17 -0
  132. package/dist/fog.d.ts +110 -0
  133. package/dist/formation.d.ts +111 -0
  134. package/dist/friendlyFire.d.ts +26 -0
  135. package/dist/game-DiJEN-Ks.js +5800 -0
  136. package/dist/game-DiJEN-Ks.js.map +1 -0
  137. package/dist/game.d.ts +501 -0
  138. package/dist/gameData.d.ts +325 -0
  139. package/dist/gameFiles.d.ts +26 -0
  140. package/dist/gates.d.ts +28 -0
  141. package/dist/generate.d.ts +403 -0
  142. package/dist/generateWorker.d.ts +39 -0
  143. package/dist/grid.d.ts +53 -0
  144. package/dist/ground.d.ts +127 -0
  145. package/dist/groundSpeed.d.ts +22 -0
  146. package/dist/hero.d.ts +177 -0
  147. package/dist/home.d.ts +14 -0
  148. package/dist/hooks.d.ts +780 -0
  149. package/dist/hotReload.d.ts +76 -0
  150. package/dist/ids.d.ts +26 -0
  151. package/dist/index.d.ts +227 -0
  152. package/dist/index.js +4701 -0
  153. package/dist/index.js.map +1 -0
  154. package/dist/inspect-D9dWHmZ0.js +491 -0
  155. package/dist/inspect-D9dWHmZ0.js.map +1 -0
  156. package/dist/inspect.d.ts +176 -0
  157. package/dist/inventory.d.ts +18 -0
  158. package/dist/items.d.ts +91 -0
  159. package/dist/lanes.d.ts +213 -0
  160. package/dist/life.d.ts +53 -0
  161. package/dist/local-Dkovq42k.js +1379 -0
  162. package/dist/local-Dkovq42k.js.map +1 -0
  163. package/dist/log.d.ts +140 -0
  164. package/dist/manaBurn.d.ts +39 -0
  165. package/dist/map/file.d.ts +114 -0
  166. package/dist/map/images.d.ts +18 -0
  167. package/dist/map/look.d.ts +47 -0
  168. package/dist/map/pathProfile.d.ts +13 -0
  169. package/dist/map/resize.d.ts +54 -0
  170. package/dist/map/schema.d.ts +10 -0
  171. package/dist/map/terrain.d.ts +32 -0
  172. package/dist/map/types.d.ts +613 -0
  173. package/dist/map.d.ts +7 -0
  174. package/dist/mapCheck/cells.d.ts +19 -0
  175. package/dist/mapCheck/rule.d.ts +30 -0
  176. package/dist/mapCheck/rules/names.d.ts +3 -0
  177. package/dist/mapCheck/rules/objects.d.ts +3 -0
  178. package/dist/mapCheck/rules/paths.d.ts +3 -0
  179. package/dist/mapCheck/rules/players.d.ts +3 -0
  180. package/dist/mapCheck/rules/races.d.ts +3 -0
  181. package/dist/mapCheck/rules/random.d.ts +3 -0
  182. package/dist/mapCheck/rules/regions.d.ts +3 -0
  183. package/dist/mapCheck/rules/targets.d.ts +3 -0
  184. package/dist/mapCheck/rules/walking.d.ts +3 -0
  185. package/dist/mapCheck.d.ts +23 -0
  186. package/dist/mapFields.d.ts +364 -0
  187. package/dist/mapFile.d.ts +23 -0
  188. package/dist/mapMeasure.d.ts +95 -0
  189. package/dist/mapMerge.d.ts +19 -0
  190. package/dist/mapObjectives.d.ts +82 -0
  191. package/dist/mapObjects.d.ts +1 -0
  192. package/dist/math.d.ts +44 -0
  193. package/dist/messages.d.ts +150 -0
  194. package/dist/missionRules-C-bGxm0Q.js +523 -0
  195. package/dist/missionRules-C-bGxm0Q.js.map +1 -0
  196. package/dist/missionRules.d.ts +179 -0
  197. package/dist/mods.d.ts +146 -0
  198. package/dist/names.d.ts +93 -0
  199. package/dist/navigation/agentParams.d.ts +4 -0
  200. package/dist/navigation/air.d.ts +89 -0
  201. package/dist/navigation/builds.d.ts +140 -0
  202. package/dist/navigation/groundAreas.d.ts +23 -0
  203. package/dist/navigation/navigation.d.ts +127 -0
  204. package/dist/navigation/recast.d.ts +214 -0
  205. package/dist/navigation/sidePaths.d.ts +69 -0
  206. package/dist/navigation/sized.d.ts +1 -0
  207. package/dist/navigation-worker.js +11 -0
  208. package/dist/navigation-worker.js.map +1 -0
  209. package/dist/nearest.d.ts +8 -0
  210. package/dist/noise.d.ts +12 -0
  211. package/dist/objectives.d.ts +93 -0
  212. package/dist/orders/basic.d.ts +96 -0
  213. package/dist/orders/build.d.ts +91 -0
  214. package/dist/orders/cast.d.ts +105 -0
  215. package/dist/orders/facts.d.ts +14 -0
  216. package/dist/orders/harvest.d.ts +94 -0
  217. package/dist/orders/idle.d.ts +7 -0
  218. package/dist/orders/items.d.ts +14 -0
  219. package/dist/orders/queue.d.ts +24 -0
  220. package/dist/pack.schema.json +6621 -0
  221. package/dist/packs/director/index.d.ts +81 -0
  222. package/dist/packs/heroes/campaign.d.ts +137 -0
  223. package/dist/packs/heroes/carry.d.ts +59 -0
  224. package/dist/packs/heroes/commands.d.ts +6 -0
  225. package/dist/packs/heroes/hire.d.ts +9 -0
  226. package/dist/packs/heroes/index.d.ts +12 -0
  227. package/dist/packs/heroes/making.d.ts +16 -0
  228. package/dist/packs/heroes/respawn.d.ts +42 -0
  229. package/dist/packs/moba/index.d.ts +34 -0
  230. package/dist/packs/pack.d.ts +84 -0
  231. package/dist/packs-g5thvmGn.js +3199 -0
  232. package/dist/packs-g5thvmGn.js.map +1 -0
  233. package/dist/packs.d.ts +84 -0
  234. package/dist/paths.d.ts +51 -0
  235. package/dist/plain.d.ts +17 -0
  236. package/dist/player.d.ts +117 -0
  237. package/dist/portals.d.ts +66 -0
  238. package/dist/prefabs.d.ts +139 -0
  239. package/dist/presetBuildings.d.ts +52 -0
  240. package/dist/presetGround.d.ts +22 -0
  241. package/dist/profiles-B3NS9jwk.js +335 -0
  242. package/dist/profiles-B3NS9jwk.js.map +1 -0
  243. package/dist/races.d.ts +136 -0
  244. package/dist/randomGroups.d.ts +2 -0
  245. package/dist/registry.d.ts +464 -0
  246. package/dist/remains.d.ts +119 -0
  247. package/dist/replication.d.ts +103 -0
  248. package/dist/rng.d.ts +17 -0
  249. package/dist/rules.d.ts +395 -0
  250. package/dist/rulesCheck.d.ts +37 -0
  251. package/dist/runes.d.ts +41 -0
  252. package/dist/save.d.ts +277 -0
  253. package/dist/scenario.d.ts +56 -0
  254. package/dist/scheduler.d.ts +67 -0
  255. package/dist/score.d.ts +306 -0
  256. package/dist/sequences.d.ts +132 -0
  257. package/dist/server/doorman.d.ts +21 -0
  258. package/dist/server/gameServer.d.ts +346 -0
  259. package/dist/server/index.d.ts +24 -0
  260. package/dist/server/lobby.d.ts +1 -0
  261. package/dist/server/log.d.ts +13 -0
  262. package/dist/server/match.d.ts +24 -0
  263. package/dist/server/mode.d.ts +168 -0
  264. package/dist/server/navigation-worker.d.ts +1 -0
  265. package/dist/server/navigation.d.ts +11 -0
  266. package/dist/server/ops.d.ts +78 -0
  267. package/dist/server/packs.d.ts +15 -0
  268. package/dist/server/ratings.d.ts +62 -0
  269. package/dist/server/relay.d.ts +34 -0
  270. package/dist/server/rooms.d.ts +162 -0
  271. package/dist/server/seat.d.ts +2 -0
  272. package/dist/server/seatSettings.d.ts +15 -0
  273. package/dist/server/secret.d.ts +5 -0
  274. package/dist/server/start.d.ts +54 -0
  275. package/dist/server/websocket.d.ts +74 -0
  276. package/dist/server.js +2861 -0
  277. package/dist/server.js.map +1 -0
  278. package/dist/session/can.d.ts +20 -0
  279. package/dist/session/commandReplay.d.ts +92 -0
  280. package/dist/session/debug.d.ts +65 -0
  281. package/dist/session/delay.d.ts +15 -0
  282. package/dist/session/delta.d.ts +98 -0
  283. package/dist/session/entityState.d.ts +1 -0
  284. package/dist/session/local.d.ts +214 -0
  285. package/dist/session/memory.d.ts +27 -0
  286. package/dist/session/protocol.d.ts +1155 -0
  287. package/dist/session/remote.d.ts +530 -0
  288. package/dist/session/replay.d.ts +114 -0
  289. package/dist/session/seed.d.ts +7 -0
  290. package/dist/session/share.d.ts +78 -0
  291. package/dist/session/socket.d.ts +8 -0
  292. package/dist/session/wire.d.ts +153 -0
  293. package/dist/session/worker.d.ts +227 -0
  294. package/dist/shared.d.ts +171 -0
  295. package/dist/sides.d.ts +75 -0
  296. package/dist/sight.d.ts +27 -0
  297. package/dist/simulate-oXBiw1Ff.js +167 -0
  298. package/dist/simulate-oXBiw1Ff.js.map +1 -0
  299. package/dist/simulate-worker.d.ts +1 -0
  300. package/dist/simulate-worker.js +28 -0
  301. package/dist/simulate-worker.js.map +1 -0
  302. package/dist/simulate.d.ts +154 -0
  303. package/dist/smart.d.ts +9 -0
  304. package/dist/spatial.d.ts +62 -0
  305. package/dist/spawning.d.ts +33 -0
  306. package/dist/stances.d.ts +76 -0
  307. package/dist/start.d.ts +71 -0
  308. package/dist/stats.d.ts +122 -0
  309. package/dist/summons.d.ts +63 -0
  310. package/dist/systems/abilities.d.ts +3 -0
  311. package/dist/systems/combat.d.ts +71 -0
  312. package/dist/systems/combatTold.d.ts +14 -0
  313. package/dist/systems/construction.d.ts +34 -0
  314. package/dist/systems/economy.d.ts +21 -0
  315. package/dist/systems/fog.d.ts +19 -0
  316. package/dist/systems/life.d.ts +3 -0
  317. package/dist/systems/movement.d.ts +105 -0
  318. package/dist/systems/orders.d.ts +7 -0
  319. package/dist/systems/production.d.ts +49 -0
  320. package/dist/systems/productionQueue.d.ts +40 -0
  321. package/dist/systems/victory.d.ts +14 -0
  322. package/dist/talk.d.ts +65 -0
  323. package/dist/tech.d.ts +62 -0
  324. package/dist/techTree.d.ts +38 -0
  325. package/dist/terrain.d.ts +173 -0
  326. package/dist/terrainSave.d.ts +29 -0
  327. package/dist/testing.d.ts +58 -0
  328. package/dist/testing.js +822 -0
  329. package/dist/testing.js.map +1 -0
  330. package/dist/tiles.d.ts +34 -0
  331. package/dist/timers.d.ts +146 -0
  332. package/dist/tomes.d.ts +48 -0
  333. package/dist/trade.d.ts +28 -0
  334. package/dist/transport.d.ts +76 -0
  335. package/dist/tunables.d.ts +45 -0
  336. package/dist/upkeep.d.ts +16 -0
  337. package/dist/veterancy.d.ts +80 -0
  338. package/dist/walls.d.ts +86 -0
  339. package/dist/world.d.ts +1118 -0
  340. package/package.json +80 -5
  341. package/README.md +0 -3
@@ -0,0 +1,75 @@
1
+ import type { Entity, PlayerId } from './entity';
2
+ import type { PlayerRelation, World } from './world';
3
+ export declare const relations: import("./components").Component<{
4
+ pairs: Record<number, PlayerRelation>;
5
+ }, {
6
+ pairs: Record<number, PlayerRelation>;
7
+ }>;
8
+ /** Teams that see what others see: by the team that sees, those it shares with */
9
+ export declare const vision: import("./components").Component<{
10
+ teams: Record<number, number[]>;
11
+ }, {
12
+ teams: Record<number, number[]>;
13
+ }>;
14
+ /** Who may command whose units besides their owner (`shareControl`): by the owner, the players given control. Kept in the world */
15
+ export declare const control: import("./components").Component<{
16
+ players: Record<number, number[]>;
17
+ }, {
18
+ players: Record<number, number[]>;
19
+ }>;
20
+ /**
21
+ * Who is on whose side: kept in the world's state, so a save has it
22
+ *
23
+ * @beta
24
+ */
25
+ export declare class Sides {
26
+ private readonly world;
27
+ constructor(world: World);
28
+ /** Whether everyone on a team is out */
29
+ teamDefeated(team: number): boolean;
30
+ /** Whether two players fight each other: their teams, or what they were set to be (`setRelation`) */
31
+ enemies(a: PlayerId, b: PlayerId): boolean;
32
+ /**
33
+ * What two players are to each other from now on, whatever their teams: `ally`, `enemy`, or `neutral` (neither).
34
+ * Null goes back to what their teams say. Creeps that turn friendly to one player; a ceasefire. Kept in the world:
35
+ * a save has it. Sight stays the teams' (see `shareVision`)
36
+ */
37
+ setRelation(a: PlayerId, b: PlayerId, relation: PlayerRelation | null): void;
38
+ /** What two players were set to be to each other (`setRelation`), if anything */
39
+ relationOf(a: PlayerId, b: PlayerId): PlayerRelation | undefined;
40
+ /**
41
+ * Moves a player to another team, one there is or a new one: whom they fight and see with follows. What their units
42
+ * were doing goes on; what they fought that is now a friend, they stop fighting on their own
43
+ */
44
+ setTeam(player: PlayerId, team: number): void;
45
+ /**
46
+ * Lets one team see what another sees, from now on, or no longer: shared vision without an alliance. Kept in the
47
+ * world. A team sees what its own units see as ever
48
+ */
49
+ shareVision(from: number, to: number, shared?: boolean): void;
50
+ /** The teams that see what this one sees, besides itself */
51
+ visionOf(team: number): readonly number[];
52
+ /**
53
+ * Lets a player command another's units, from now on, or no longer: shared control, as allies in a melee game give
54
+ * each other (a map's force with `sharedControl` gives it to every player of the force). Kept in the world. Who may
55
+ * command what is `mayCommand`, which the rules may widen or narrow (`GameRules.mayCommand`)
56
+ */
57
+ shareControl(owner: PlayerId, withPlayer: PlayerId, shared?: boolean): void;
58
+ /** The players who may command this one's units, besides the owner (`shareControl`) */
59
+ controlOf(owner: PlayerId): readonly PlayerId[];
60
+ /**
61
+ * Shares what a player has with another, from now on, or no longer: their units' control (`shareControl`) and
62
+ * what their team sees (`shareVision`, team to team)
63
+ */
64
+ share(player: PlayerId, withPlayer: PlayerId, what: {
65
+ control?: boolean;
66
+ vision?: boolean;
67
+ }): void;
68
+ /**
69
+ * Whether a player may command an entity: its owner, whoever the owner shares control with, and whatever else the
70
+ * rules say (`GameRules.mayCommand`). What every command of the player's is checked against
71
+ */
72
+ mayCommand(player: PlayerId, entity: Entity): boolean;
73
+ /** Whether two players are on one side: one player, a team, or what they were set to be (`setRelation`) */
74
+ allies(a: PlayerId, b: PlayerId): boolean;
75
+ }
@@ -0,0 +1,27 @@
1
+ import type { Entity } from './entity';
2
+ import type { World } from './world';
3
+ /**
4
+ * What each team sees of a world's entities
5
+ *
6
+ * @beta
7
+ */
8
+ export declare class Sight {
9
+ private readonly world;
10
+ /**
11
+ * What is handed to the `visible` handlers, again and again, so that asking allocates nothing: the world's own, a
12
+ * handler that asks again getting an object of its own
13
+ */
14
+ private readonly asking;
15
+ constructor(world: World);
16
+ /** Whether a team currently sees the entity */
17
+ sees(entity: Entity, team: number): boolean;
18
+ /** Whether an entity is invisible right now: by its type (`invisible`), or a buff that says so */
19
+ isInvisible(entity: Entity): boolean;
20
+ /** Whether a team sees an entity of someone else's, by the fog and what is invisible */
21
+ private inSight;
22
+ /**
23
+ * Whether a team has ever laid eyes on the entity. Kept up to date for structures only, see the fog system.
24
+ * A team that sees the whole map (out of the game, or a debug switch) has seen everything there is
25
+ */
26
+ hasSeen(entity: Entity, team: number): boolean;
27
+ }
@@ -0,0 +1,167 @@
1
+ import "./debugCommands-CzjrSm66.js";
2
+ import { a as createWorldFromMap } from "./game-DiJEN-Ks.js";
3
+ //#region src/simulate.ts
4
+ /** A simulation, as a module that a worker imports gives it: as it is written, for its type *
5
+ * @beta
6
+ */
7
+ var defineSimulation = (simulation) => simulation;
8
+ /** Plays one game of a simulation from a seed, in this thread *
9
+ * @beta
10
+ */
11
+ async function playGame(simulation, seed) {
12
+ const began = performance.now();
13
+ const { game, map } = simulation;
14
+ const world = await createWorldFromMap({
15
+ map: structuredClone(map),
16
+ game,
17
+ seed,
18
+ onError: "end"
19
+ });
20
+ try {
21
+ const starts = map.players.filter((player) => player.start && player.id !== 90);
22
+ const computers = simulation.computers ?? starts.map((player) => player.id);
23
+ simulation.start?.(world);
24
+ for (const player of computers) {
25
+ const enemyBases = starts.filter((other) => other.id !== player && world.areEnemies(player, other.id)).map((other) => other.start);
26
+ const named = simulation.brains?.[player];
27
+ const brain = typeof named === "string" ? world.computers.brain(named) : named;
28
+ if (typeof named === "string" && !brain) throw new Error(`No brain is called "${named}": list it in GameModule.brains, or give the brain itself`);
29
+ const difficulty = simulation.difficulties?.[player];
30
+ const options = {
31
+ enemyBases,
32
+ ...difficulty !== void 0 && { difficulty },
33
+ ...simulation.ai?.(player, world)
34
+ };
35
+ world.computers.seat(player, {
36
+ ...options,
37
+ brain
38
+ });
39
+ }
40
+ const until = simulation.until ?? ((done) => done.winner !== null);
41
+ const ticks = Math.ceil((simulation.seconds ?? 1200) * world.tickRate);
42
+ let tick = 0;
43
+ while (tick < ticks && !world.failure && !until(world)) {
44
+ world.step();
45
+ tick++;
46
+ }
47
+ const seconds = tick / world.tickRate;
48
+ const measures = {};
49
+ for (const player of computers) {
50
+ const computer = world.computers.get(player)?.computer;
51
+ const stats = computer?.stats;
52
+ if (!stats) continue;
53
+ const team = world.players.get(player)?.team ?? player;
54
+ measures[player] = {
55
+ ...computer?.def?.id !== void 0 && { brain: computer.def.id },
56
+ team,
57
+ won: world.winner !== null && world.winner === team,
58
+ commandsAMinute: seconds > 0 ? stats.commands / seconds * 60 : 0,
59
+ idleArmySeconds: stats.idleArmySeconds,
60
+ churn: stats.churn,
61
+ blockedOnMoney: stats.blockedOnMoney,
62
+ refused: stats.refused,
63
+ held: stats.held
64
+ };
65
+ }
66
+ return {
67
+ seed,
68
+ winner: world.winner,
69
+ seconds,
70
+ ticks: tick,
71
+ ...world.failure && { failed: world.failure.message },
72
+ measured: simulation.measure?.(world) ?? {},
73
+ computers: measures,
74
+ took: performance.now() - began
75
+ };
76
+ } finally {
77
+ world.dispose();
78
+ }
79
+ }
80
+ /**
81
+ * The win rate of each brain over games of `simulate`, by brain id (`ComputerMeasures.brain`): what a balance run of two
82
+ * brains reads. A seat that named no brain is left out
83
+ *
84
+ * const games = await simulate({ ...duel, brains: { 1: meleeBrain, 2: myBrain } }, { seeds })
85
+ * winRates(games) // { 'core.melee': { rate: 0.4, … }, 'mygame.brain': { rate: 0.55, … } }
86
+ *
87
+ * @beta
88
+ */
89
+ function winRates(games) {
90
+ const results = {};
91
+ for (const game of games) for (const measures of Object.values(game.computers)) {
92
+ if (measures.brain === void 0) continue;
93
+ const result = results[measures.brain] ??= {
94
+ games: 0,
95
+ wins: 0,
96
+ losses: 0,
97
+ draws: 0,
98
+ rate: 0,
99
+ decided: 0
100
+ };
101
+ result.games++;
102
+ if (measures.won) result.wins++;
103
+ else if (game.winner === null) result.draws++;
104
+ else result.losses++;
105
+ }
106
+ for (const result of Object.values(results)) {
107
+ result.rate = result.games ? result.wins / result.games : 0;
108
+ result.decided = result.wins + result.losses ? result.wins / (result.wins + result.losses) : 0;
109
+ }
110
+ return results;
111
+ }
112
+ /**
113
+ * Plays games of a simulation, one from each seed, and gives what each ended in, in the order of the seeds. Given as
114
+ * it is, they are played in this thread. Given as the address of a file (a module whose `default`, or `simulation`, is
115
+ * one: `defineSimulation`), each worker thread imports it and plays its share: in Node, from the built packages (a
116
+ * worker loads JavaScript, not the TypeScript of this repository)
117
+ *
118
+ * @beta
119
+ */
120
+ async function simulate(simulation, options) {
121
+ if (typeof simulation !== "string" && !(simulation instanceof URL)) {
122
+ const games = [];
123
+ for (const seed of options.seeds) {
124
+ const game = await playGame(simulation, seed);
125
+ options.onGame?.(game);
126
+ games.push(game);
127
+ }
128
+ return games;
129
+ }
130
+ const { Worker } = await import("node:worker_threads");
131
+ const { availableParallelism } = await import("node:os");
132
+ const module = String(simulation);
133
+ const count = Math.max(1, Math.min(options.workers ?? availableParallelism() - 1, options.seeds.length));
134
+ const script = new URL("simulate-worker.js", import.meta.url);
135
+ const results = /* @__PURE__ */ new Map();
136
+ const queue = [...options.seeds];
137
+ await Promise.all(Array.from({ length: count }, () => new Promise((resolve, reject) => {
138
+ const worker = new Worker(script);
139
+ const next = () => {
140
+ const seed = queue.shift();
141
+ if (seed === void 0) {
142
+ worker.terminate();
143
+ return resolve();
144
+ }
145
+ worker.postMessage({
146
+ module,
147
+ seed
148
+ });
149
+ };
150
+ worker.on("message", (message) => {
151
+ if (message.error !== void 0) {
152
+ worker.terminate();
153
+ return reject(new Error(message.error));
154
+ }
155
+ results.set(message.game.seed, message.game);
156
+ options.onGame?.(message.game);
157
+ next();
158
+ });
159
+ worker.on("error", reject);
160
+ next();
161
+ })));
162
+ return options.seeds.map((seed) => results.get(seed));
163
+ }
164
+ //#endregion
165
+ export { winRates as i, playGame as n, simulate as r, defineSimulation as t };
166
+
167
+ //# sourceMappingURL=simulate-oXBiw1Ff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulate-oXBiw1Ff.js","names":[],"sources":["../src/simulate.ts"],"sourcesContent":["import type { AiOptions } from './ai/options'\nimport type { AiStats } from './ai/report'\nimport type { BrainDef } from './ai/brain'\nimport type { PlayerId } from './entity'\nimport { createWorldFromMap, type GameModule } from './game'\nimport { type GameMap, MAP_CREEPS } from './map'\nimport type { World } from './world'\n\n/*\n * Many games at once, for balance (S43): games of a game played at full speed by the computer, from seeds, each\n * ending in plain data (who won, how long it took, what the game asked to count). The same seed gives the same game.\n * In this thread (`simulate(simulation, …)`), or in worker threads, each of which imports the game itself from a file\n * (`simulate(url, { workers })`): a game's rules are functions, which cannot be sent to another thread.\n */\n\n/**\n * What a value counted at the end of a game may be: plain data, which comes back from a worker as it went\n *\n * @beta\n */\nexport type Counted = number | string | boolean | null\n\n/**\n * A game to play many times (`simulate`): the rules, the map, who the computer plays and how, and what to count\n *\n * @beta\n */\nexport interface Simulation {\n\tgame: GameModule\n\tmap: GameMap\n\t/**\n\t * The players the computer plays: every player of the map with a start, the creeps apart, when left out. One left\n\t * out is the game's own (`start`) to move, or stands\n\t */\n\tcomputers?: PlayerId[]\n\t/**\n\t * The brain each player is played by, or its id (one the game lists in `GameModule.brains`, or the engine's\n\t * `core.melee`): two brains against each other, for a balance run. A player not named here is played by the first\n\t * brain the game lists\n\t */\n\tbrains?: Record<number, BrainDef | string>\n\t/**\n\t * How hard each player's computer plays, by name (`'hard'`): under what `ai` gives. The seat's own when left out\n\t */\n\tdifficulties?: Record<number, string>\n\t/** How the computer plays a player, over what it is given (`enemyBases`: where the others start) */\n\tai?: (player: PlayerId, world: World) => Partial<AiOptions>\n\t/** What the game does once a world is made, before the first tick: units of its own, a rule turned on */\n\tstart?: (world: World) => void\n\t/** Seconds of the game at most: twenty minutes when left out */\n\tseconds?: number\n\t/** Whether the game is over before that: once there is a winner, when left out */\n\tuntil?: (world: World) => boolean\n\t/** What to count at the end of each game, by name: units alive, gold gathered, the hour */\n\tmeasure?: (world: World) => Record<string, Counted>\n}\n\n/**\n * How a computer player played one game, beside who won (`SimulatedGame.computers`): the numbers that say whether a\n * brain plays well, not only whether it won (`AiStats`, the brain's own counts, by the minute where it says so)\n *\n * @beta\n */\nexport interface ComputerMeasures {\n\t/** The brain that played the seat, by id: none for the first the game lists, seated by none named */\n\tbrain?: string\n\t/** The team the seat played for */\n\tteam: number\n\t/** Whether its team won: false for a draw too */\n\twon: boolean\n\t/** Commands sent a minute of the game */\n\tcommandsAMinute: number\n\t/** Seconds its fighters stood idle, summed over the fighters */\n\tidleArmySeconds: number\n\t/** Orders sent to a unit again for the same thing within a few seconds */\n\tchurn: number\n\t/** Seconds its building waited on money */\n\tblockedOnMoney: number\n\t/** Commands the game refused */\n\trefused: number\n\t/** Commands held back past what a think may send */\n\theld: number\n}\n\n/**\n * How one game ended\n *\n * @beta\n */\nexport interface SimulatedGame {\n\tseed: number\n\t/** The winning team, or null for none in the time given */\n\twinner: number | null\n\t/** Seconds of the game it lasted, and ticks */\n\tseconds: number\n\tticks: number\n\t/** Why it stopped on an error of its rules, where it did (`World.failure`) */\n\tfailed?: string\n\t/** What `measure` counted */\n\tmeasured: Record<string, Counted>\n\t/** How each computer player played, by player: commands a minute, idle army seconds, churn, seconds blocked on money, commands refused */\n\tcomputers: Record<string, ComputerMeasures>\n\t/** Milliseconds it took to play here */\n\ttook: number\n}\n\n/** A simulation, as a module that a worker imports gives it: as it is written, for its type *\n * @beta\n */\nexport const defineSimulation = (simulation: Simulation): Simulation => simulation\n\n/** Plays one game of a simulation from a seed, in this thread *\n * @beta\n */\nexport async function playGame(simulation: Simulation, seed: number): Promise<SimulatedGame> {\n\tconst began = performance.now()\n\tconst { game, map } = simulation\n\tconst world = await createWorldFromMap({ map: structuredClone(map), game, seed, onError: 'end' })\n\ttry {\n\t\tconst starts = map.players.filter((player) => player.start && player.id !== MAP_CREEPS)\n\t\tconst computers = simulation.computers ?? starts.map((player) => player.id)\n\t\tsimulation.start?.(world)\n\t\tfor (const player of computers) {\n\t\t\tconst enemyBases = starts.filter((other) => other.id !== player && world.areEnemies(player, other.id)).map((other) => other.start!)\n\t\t\tconst named = simulation.brains?.[player]\n\t\t\tconst brain = typeof named === 'string' ? world.computers.brain(named) : named\n\t\t\tif (typeof named === 'string' && !brain)\n\t\t\t\tthrow new Error(`No brain is called \"${named}\": list it in GameModule.brains, or give the brain itself`)\n\t\t\tconst difficulty = simulation.difficulties?.[player]\n\t\t\tconst options: AiOptions = { enemyBases, ...(difficulty !== undefined && { difficulty }), ...simulation.ai?.(player, world) }\n\t\t\tworld.computers.seat(player, { ...options, brain })\n\t\t}\n\t\tconst until = simulation.until ?? ((done: World) => done.winner !== null)\n\t\tconst ticks = Math.ceil((simulation.seconds ?? 1200) * world.tickRate)\n\t\tlet tick = 0\n\t\twhile (tick < ticks && !world.failure && !until(world)) {\n\t\t\tworld.step()\n\t\t\ttick++\n\t\t}\n\t\tconst seconds = tick / world.tickRate\n\t\tconst measures: Record<string, ComputerMeasures> = {}\n\t\tfor (const player of computers) {\n\t\t\tconst computer = world.computers.get(player)?.computer as { stats?: AiStats; def?: { id: string } } | undefined\n\t\t\tconst stats = computer?.stats\n\t\t\tif (!stats) continue\n\t\t\tconst team = world.players.get(player)?.team ?? player\n\t\t\tmeasures[player] = {\n\t\t\t\t...(computer?.def?.id !== undefined && { brain: computer.def.id }),\n\t\t\t\tteam,\n\t\t\t\twon: world.winner !== null && world.winner === team,\n\t\t\t\tcommandsAMinute: seconds > 0 ? (stats.commands / seconds) * 60 : 0,\n\t\t\t\tidleArmySeconds: stats.idleArmySeconds,\n\t\t\t\tchurn: stats.churn,\n\t\t\t\tblockedOnMoney: stats.blockedOnMoney,\n\t\t\t\trefused: stats.refused,\n\t\t\t\theld: stats.held\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tseed,\n\t\t\twinner: world.winner,\n\t\t\tseconds,\n\t\t\tticks: tick,\n\t\t\t...(world.failure && { failed: world.failure.message }),\n\t\t\tmeasured: simulation.measure?.(world) ?? {},\n\t\t\tcomputers: measures,\n\t\t\ttook: performance.now() - began\n\t\t}\n\t} finally {\n\t\tworld.dispose()\n\t}\n}\n\n/**\n * How a brain did over some games (`winRates`): by seat, so a brain that played both sides of a game counts twice\n *\n * @beta\n */\nexport interface BrainResults {\n\t/** Seats it played */\n\tgames: number\n\twins: number\n\t/** Seats whose team another team beat */\n\tlosses: number\n\t/** Seats of games with no winner in the time given */\n\tdraws: number\n\t/** Wins over seats played */\n\trate: number\n\t/** Wins over games decided either way: draws left out */\n\tdecided: number\n}\n\n/**\n * The win rate of each brain over games of `simulate`, by brain id (`ComputerMeasures.brain`): what a balance run of two\n * brains reads. A seat that named no brain is left out\n *\n * const games = await simulate({ ...duel, brains: { 1: meleeBrain, 2: myBrain } }, { seeds })\n * winRates(games) // { 'core.melee': { rate: 0.4, … }, 'mygame.brain': { rate: 0.55, … } }\n *\n * @beta\n */\nexport function winRates(games: readonly SimulatedGame[]): Record<string, BrainResults> {\n\tconst results: Record<string, BrainResults> = {}\n\tfor (const game of games)\n\t\tfor (const measures of Object.values(game.computers)) {\n\t\t\tif (measures.brain === undefined) continue\n\t\t\tconst result = (results[measures.brain] ??= { games: 0, wins: 0, losses: 0, draws: 0, rate: 0, decided: 0 })\n\t\t\tresult.games++\n\t\t\tif (measures.won) result.wins++\n\t\t\telse if (game.winner === null) result.draws++\n\t\t\telse result.losses++\n\t\t}\n\tfor (const result of Object.values(results)) {\n\t\tresult.rate = result.games ? result.wins / result.games : 0\n\t\tresult.decided = result.wins + result.losses ? result.wins / (result.wins + result.losses) : 0\n\t}\n\treturn results\n}\n\n/**\n * How `simulate` plays its games\n *\n * @beta\n */\nexport interface SimulateOptions {\n\t/** The seeds to play from, a game each */\n\tseeds: number[]\n\t/**\n\t * Worker threads to play them in at once, where the simulation is given as a file: as many as the machine has\n\t * cores less one, when left out. In this thread, one after the other, for a simulation given as it is\n\t */\n\tworkers?: number\n\t/** Told of each game as it ends, in the order they end */\n\tonGame?: (game: SimulatedGame) => void\n}\n\n/**\n * Plays games of a simulation, one from each seed, and gives what each ended in, in the order of the seeds. Given as\n * it is, they are played in this thread. Given as the address of a file (a module whose `default`, or `simulation`, is\n * one: `defineSimulation`), each worker thread imports it and plays its share: in Node, from the built packages (a\n * worker loads JavaScript, not the TypeScript of this repository)\n *\n * @beta\n */\nexport async function simulate(simulation: Simulation | string | URL, options: SimulateOptions): Promise<SimulatedGame[]> {\n\tif (typeof simulation !== 'string' && !(simulation instanceof URL)) {\n\t\tconst games: SimulatedGame[] = []\n\t\tfor (const seed of options.seeds) {\n\t\t\tconst game = await playGame(simulation, seed)\n\t\t\toptions.onGame?.(game)\n\t\t\tgames.push(game)\n\t\t}\n\t\treturn games\n\t}\n\tconst { Worker } = await import('node:worker_threads')\n\tconst { availableParallelism } = await import('node:os')\n\tconst module = String(simulation)\n\tconst count = Math.max(1, Math.min(options.workers ?? availableParallelism() - 1, options.seeds.length))\n\t// the worker's file sits next to this one in the build: named so, a bundler leaves the address alone\n\tconst file = 'simulate-worker.js'\n\tconst script = new URL(file, import.meta.url)\n\tconst results = new Map<number, SimulatedGame>()\n\tconst queue = [...options.seeds]\n\tawait Promise.all(\n\t\tArray.from(\n\t\t\t{ length: count },\n\t\t\t() =>\n\t\t\t\tnew Promise<void>((resolve, reject) => {\n\t\t\t\t\tconst worker = new Worker(script)\n\t\t\t\t\tconst next = () => {\n\t\t\t\t\t\tconst seed = queue.shift()\n\t\t\t\t\t\tif (seed === undefined) {\n\t\t\t\t\t\t\tvoid worker.terminate()\n\t\t\t\t\t\t\treturn resolve()\n\t\t\t\t\t\t}\n\t\t\t\t\t\tworker.postMessage({ module, seed })\n\t\t\t\t\t}\n\t\t\t\t\tworker.on('message', (message: { game?: SimulatedGame; error?: string }) => {\n\t\t\t\t\t\tif (message.error !== undefined) {\n\t\t\t\t\t\t\tvoid worker.terminate()\n\t\t\t\t\t\t\treturn reject(new Error(message.error))\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresults.set(message.game!.seed, message.game!)\n\t\t\t\t\t\toptions.onGame?.(message.game!)\n\t\t\t\t\t\tnext()\n\t\t\t\t\t})\n\t\t\t\t\tworker.on('error', reject)\n\t\t\t\t\tnext()\n\t\t\t\t})\n\t\t)\n\t)\n\treturn options.seeds.map((seed) => results.get(seed)!)\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { n as playGame } from "./simulate-oXBiw1Ff.js";
2
+ import { parentPort } from "node:worker_threads";
3
+ //#region src/simulate-worker.ts
4
+ /**
5
+ * A worker thread of `simulate`: told a file and a seed, it imports the simulation the file gives (its `default`, or
6
+ * `simulation`) and plays a game of it, and says how it ended. Built next to `testing.js`, and not one of the names
7
+ * of the engine: `simulate` starts it
8
+ */
9
+ var loaded = /* @__PURE__ */ new Map();
10
+ parentPort?.on("message", async ({ module, seed }) => {
11
+ try {
12
+ let simulation = loaded.get(module);
13
+ if (!simulation) {
14
+ simulation = import(module).then((imported) => {
15
+ const found = imported.default ?? imported.simulation;
16
+ if (!found) throw new Error(`${module} gives no simulation: its default, or one named simulation`);
17
+ return found;
18
+ });
19
+ loaded.set(module, simulation);
20
+ }
21
+ parentPort.postMessage({ game: await playGame(await simulation, seed) });
22
+ } catch (error) {
23
+ parentPort.postMessage({ error: error instanceof Error ? error.stack ?? error.message : String(error) });
24
+ }
25
+ });
26
+ //#endregion
27
+
28
+ //# sourceMappingURL=simulate-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulate-worker.js","names":[],"sources":["../src/simulate-worker.ts"],"sourcesContent":["/**\n * A worker thread of `simulate`: told a file and a seed, it imports the simulation the file gives (its `default`, or\n * `simulation`) and plays a game of it, and says how it ended. Built next to `testing.js`, and not one of the names\n * of the engine: `simulate` starts it\n */\nimport { parentPort } from 'node:worker_threads'\nimport { playGame, type Simulation } from './simulate'\n\nconst loaded = new Map<string, Promise<Simulation>>()\n\nparentPort?.on('message', async ({ module, seed }: { module: string; seed: number }) => {\n\ttry {\n\t\tlet simulation = loaded.get(module)\n\t\tif (!simulation) {\n\t\t\tsimulation = import(module).then((imported: { default?: Simulation; simulation?: Simulation }) => {\n\t\t\t\tconst found = imported.default ?? imported.simulation\n\t\t\t\tif (!found) throw new Error(`${module} gives no simulation: its default, or one named simulation`)\n\t\t\t\treturn found\n\t\t\t})\n\t\t\tloaded.set(module, simulation)\n\t\t}\n\t\tparentPort!.postMessage({ game: await playGame(await simulation, seed) })\n\t} catch (error) {\n\t\tparentPort!.postMessage({ error: error instanceof Error ? (error.stack ?? error.message) : String(error) })\n\t}\n})\n"],"mappings":";;;;;;;;AAQA,IAAM,yBAAS,IAAI,IAAiC;AAEpD,YAAY,GAAG,WAAW,OAAO,EAAE,QAAQ,WAA6C;CACvF,IAAI;EACH,IAAI,aAAa,OAAO,IAAI,MAAM;EAClC,IAAI,CAAC,YAAY;GAChB,aAAa,OAAO,OAAO,CAAC,MAAM,aAAgE;IACjG,MAAM,QAAQ,SAAS,WAAW,SAAS;IAC3C,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,GAAG,OAAO,2DAA2D;IACjG,OAAO;GACR,CAAC;GACD,OAAO,IAAI,QAAQ,UAAU;EAC9B;EACA,WAAY,YAAY,EAAE,MAAM,MAAM,SAAS,MAAM,YAAY,IAAI,EAAE,CAAC;CACzE,SAAS,OAAO;EACf,WAAY,YAAY,EAAE,OAAO,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW,OAAO,KAAK,EAAE,CAAC;CAC3G;AACD,CAAC"}
@@ -0,0 +1,154 @@
1
+ import type { AiOptions } from './ai/options';
2
+ import type { BrainDef } from './ai/brain';
3
+ import type { PlayerId } from './entity';
4
+ import { type GameModule } from './game';
5
+ import { type GameMap } from './map';
6
+ import type { World } from './world';
7
+ /**
8
+ * What a value counted at the end of a game may be: plain data, which comes back from a worker as it went
9
+ *
10
+ * @beta
11
+ */
12
+ export type Counted = number | string | boolean | null;
13
+ /**
14
+ * A game to play many times (`simulate`): the rules, the map, who the computer plays and how, and what to count
15
+ *
16
+ * @beta
17
+ */
18
+ export interface Simulation {
19
+ game: GameModule;
20
+ map: GameMap;
21
+ /**
22
+ * The players the computer plays: every player of the map with a start, the creeps apart, when left out. One left
23
+ * out is the game's own (`start`) to move, or stands
24
+ */
25
+ computers?: PlayerId[];
26
+ /**
27
+ * The brain each player is played by, or its id (one the game lists in `GameModule.brains`, or the engine's
28
+ * `core.melee`): two brains against each other, for a balance run. A player not named here is played by the first
29
+ * brain the game lists
30
+ */
31
+ brains?: Record<number, BrainDef | string>;
32
+ /**
33
+ * How hard each player's computer plays, by name (`'hard'`): under what `ai` gives. The seat's own when left out
34
+ */
35
+ difficulties?: Record<number, string>;
36
+ /** How the computer plays a player, over what it is given (`enemyBases`: where the others start) */
37
+ ai?: (player: PlayerId, world: World) => Partial<AiOptions>;
38
+ /** What the game does once a world is made, before the first tick: units of its own, a rule turned on */
39
+ start?: (world: World) => void;
40
+ /** Seconds of the game at most: twenty minutes when left out */
41
+ seconds?: number;
42
+ /** Whether the game is over before that: once there is a winner, when left out */
43
+ until?: (world: World) => boolean;
44
+ /** What to count at the end of each game, by name: units alive, gold gathered, the hour */
45
+ measure?: (world: World) => Record<string, Counted>;
46
+ }
47
+ /**
48
+ * How a computer player played one game, beside who won (`SimulatedGame.computers`): the numbers that say whether a
49
+ * brain plays well, not only whether it won (`AiStats`, the brain's own counts, by the minute where it says so)
50
+ *
51
+ * @beta
52
+ */
53
+ export interface ComputerMeasures {
54
+ /** The brain that played the seat, by id: none for the first the game lists, seated by none named */
55
+ brain?: string;
56
+ /** The team the seat played for */
57
+ team: number;
58
+ /** Whether its team won: false for a draw too */
59
+ won: boolean;
60
+ /** Commands sent a minute of the game */
61
+ commandsAMinute: number;
62
+ /** Seconds its fighters stood idle, summed over the fighters */
63
+ idleArmySeconds: number;
64
+ /** Orders sent to a unit again for the same thing within a few seconds */
65
+ churn: number;
66
+ /** Seconds its building waited on money */
67
+ blockedOnMoney: number;
68
+ /** Commands the game refused */
69
+ refused: number;
70
+ /** Commands held back past what a think may send */
71
+ held: number;
72
+ }
73
+ /**
74
+ * How one game ended
75
+ *
76
+ * @beta
77
+ */
78
+ export interface SimulatedGame {
79
+ seed: number;
80
+ /** The winning team, or null for none in the time given */
81
+ winner: number | null;
82
+ /** Seconds of the game it lasted, and ticks */
83
+ seconds: number;
84
+ ticks: number;
85
+ /** Why it stopped on an error of its rules, where it did (`World.failure`) */
86
+ failed?: string;
87
+ /** What `measure` counted */
88
+ measured: Record<string, Counted>;
89
+ /** How each computer player played, by player: commands a minute, idle army seconds, churn, seconds blocked on money, commands refused */
90
+ computers: Record<string, ComputerMeasures>;
91
+ /** Milliseconds it took to play here */
92
+ took: number;
93
+ }
94
+ /** A simulation, as a module that a worker imports gives it: as it is written, for its type *
95
+ * @beta
96
+ */
97
+ export declare const defineSimulation: (simulation: Simulation) => Simulation;
98
+ /** Plays one game of a simulation from a seed, in this thread *
99
+ * @beta
100
+ */
101
+ export declare function playGame(simulation: Simulation, seed: number): Promise<SimulatedGame>;
102
+ /**
103
+ * How a brain did over some games (`winRates`): by seat, so a brain that played both sides of a game counts twice
104
+ *
105
+ * @beta
106
+ */
107
+ export interface BrainResults {
108
+ /** Seats it played */
109
+ games: number;
110
+ wins: number;
111
+ /** Seats whose team another team beat */
112
+ losses: number;
113
+ /** Seats of games with no winner in the time given */
114
+ draws: number;
115
+ /** Wins over seats played */
116
+ rate: number;
117
+ /** Wins over games decided either way: draws left out */
118
+ decided: number;
119
+ }
120
+ /**
121
+ * The win rate of each brain over games of `simulate`, by brain id (`ComputerMeasures.brain`): what a balance run of two
122
+ * brains reads. A seat that named no brain is left out
123
+ *
124
+ * const games = await simulate({ ...duel, brains: { 1: meleeBrain, 2: myBrain } }, { seeds })
125
+ * winRates(games) // { 'core.melee': { rate: 0.4, … }, 'mygame.brain': { rate: 0.55, … } }
126
+ *
127
+ * @beta
128
+ */
129
+ export declare function winRates(games: readonly SimulatedGame[]): Record<string, BrainResults>;
130
+ /**
131
+ * How `simulate` plays its games
132
+ *
133
+ * @beta
134
+ */
135
+ export interface SimulateOptions {
136
+ /** The seeds to play from, a game each */
137
+ seeds: number[];
138
+ /**
139
+ * Worker threads to play them in at once, where the simulation is given as a file: as many as the machine has
140
+ * cores less one, when left out. In this thread, one after the other, for a simulation given as it is
141
+ */
142
+ workers?: number;
143
+ /** Told of each game as it ends, in the order they end */
144
+ onGame?: (game: SimulatedGame) => void;
145
+ }
146
+ /**
147
+ * Plays games of a simulation, one from each seed, and gives what each ended in, in the order of the seeds. Given as
148
+ * it is, they are played in this thread. Given as the address of a file (a module whose `default`, or `simulation`, is
149
+ * one: `defineSimulation`), each worker thread imports it and plays its share: in Node, from the built packages (a
150
+ * worker loads JavaScript, not the TypeScript of this repository)
151
+ *
152
+ * @beta
153
+ */
154
+ export declare function simulate(simulation: Simulation | string | URL, options: SimulateOptions): Promise<SimulatedGame[]>;
@@ -0,0 +1,9 @@
1
+ import type { Entity } from './entity';
2
+ import type { Vec2 } from './math';
3
+ import type { OrderInit } from './registry';
4
+ import type { World } from './world';
5
+ /**
6
+ * The right-click: picks the order a unit should get for whatever was clicked.
7
+ * Returns undefined when there is nothing sensible for this unit to do.
8
+ */
9
+ export declare function smartOrder(world: World, entity: Entity, point: Vec2, target?: Entity): OrderInit | undefined;
@@ -0,0 +1,62 @@
1
+ import type { Entity, PlayerId } from './entity';
2
+ /**
3
+ * Uniform grid for "what is near this point" queries. The map is cut into square cells and every entity is
4
+ * filed under the cells it is in, so a query only looks at the few cells around the point instead of at every
5
+ * entity in the world.
6
+ *
7
+ * There are two layers:
8
+ * - Units move, so they are filed again from scratch every tick (`rebuild`), which is simpler and cheaper than
9
+ * following every unit from cell to cell. A unit is filed by its center only, so queries look a little
10
+ * further than asked, by the radius of the biggest unit, not to miss one whose edge is in range.
11
+ * - Structures (buildings, trees, mines) never move, so they are filed once when they appear and taken out when
12
+ * they go (`insertStatic`, `removeStatic`). They are filed under every cell their footprint covers, so a query
13
+ * finds them by any part of them without looking further than asked. That matters: padding queries by the
14
+ * size of the biggest building would make every query near a forest go through far more trees.
15
+ */
16
+ export declare class SpatialIndex {
17
+ private readonly cellSize;
18
+ private readonly cols;
19
+ private readonly rows;
20
+ private readonly cells;
21
+ private readonly staticCells;
22
+ /**
23
+ * By cell, the one owner of every unit filed in it this tick, or -1 for none or more than one: what a search that
24
+ * passes over a side's own leaves out cell by cell (`query`'s `skip`). Not to be gone by once an owner changed
25
+ * since (`ownerChanged`), until the next filing
26
+ */
27
+ private readonly soleOwner;
28
+ private ownersMoved;
29
+ /** Radius of the biggest unit filed this tick */
30
+ private maxRadius;
31
+ /** Counts queries. A structure that covers several cells is marked with it when found, so it is only found once */
32
+ private stamp;
33
+ constructor(width: number, depth: number, cellSize?: number);
34
+ private col;
35
+ private row;
36
+ /** Files every unit again. Called once per tick, before the systems run. Structures in `entities` are passed over */
37
+ rebuild(entities: Iterable<Entity>): void;
38
+ /** A unit filed this tick has another owner now: no cell's one owner is gone by until the next filing */
39
+ ownerChanged(): void;
40
+ /** Files a structure under every cell it covers, for as long as it stands */
41
+ insertStatic(entity: Entity): void;
42
+ removeStatic(entity: Entity): void;
43
+ private forCellsOf;
44
+ /**
45
+ * Entities that could be within `radius` of the point: every structure that has a part in the square around it,
46
+ * and every unit whose center is close enough for its edge to be. Callers do the exact distance check.
47
+ *
48
+ * The answer is written into `out`, which callers keep around and pass again so that nothing is allocated. While going
49
+ * through the answer, a caller that does something which can lead to another query with the same `out` (dealing damage
50
+ * can set off effects that look around) has to go through a copy instead: the other query would overwrite it.
51
+ *
52
+ * `skip`: owners whose units the caller passes over anyway (a side's own, as it picks a target): a cell of units of
53
+ * one such owner alone is left out. What is left is what the full answer had, in the same order, less some of theirs
54
+ */
55
+ query(x: number, z: number, radius: number, out?: Entity[], skip?: (owner: PlayerId) => boolean): Entity[];
56
+ /**
57
+ * Only the structures that have a part in the square around the point: buildings, trees, mines, every kind but a
58
+ * unit. For who looks for those alone, among crowds of units that would otherwise all be gone through. Into `out`,
59
+ * as `query`
60
+ */
61
+ queryStatic(x: number, z: number, radius: number, out?: Entity[]): Entity[];
62
+ }
@@ -0,0 +1,33 @@
1
+ import { Entity, type PlayerId } from './entity';
2
+ import type { Vec2 } from './math';
3
+ import type { SpawnOptions, World } from './world';
4
+ /**
5
+ * How entities come into the world and are moved about in it, as the world's own part (`World.spawn`, `place`,
6
+ * `setOwner`, `setHidden`, `setLifetime`): what a type is given as it is made, its place on the ground, the grid and the
7
+ * ways, whose it is, whether it is inside something, how long it lasts. The world's methods are this.world part's
8
+ */
9
+ export declare class Spawning {
10
+ private readonly world;
11
+ constructor(world: World);
12
+ spawn(typeId: string, owner: PlayerId, position: Vec2, options?: SpawnOptions): Entity;
13
+ /**
14
+ * Turns scenery into an actor, for good: something gave it a reason to be looked at every tick, like a buff
15
+ * that has to run out. Whatever does that to an entity calls this.world; the debug checks catch what forgets to.
16
+ */
17
+ wake(entity: Entity): void;
18
+ /** Puts a unit somewhere else, where it stands: the way through is not its concern. Structures stay where they were built */
19
+ place(entity: Entity, point: Vec2): void;
20
+ /**
21
+ * Gives an entity to another player, from now on in everything: whose it is, whom it fights, what it counts for.
22
+ * What it was doing is called off, and what it was making is given back to the one it belonged to
23
+ */
24
+ setOwner(entity: Entity, owner: PlayerId): void;
25
+ /**
26
+ * Takes a unit out of the world for a while, or puts it back: a worker inside a gold mine, a unit carried in
27
+ * something. While hidden it is not found, not seen and in nobody's way, and its orders wait. It comes back at
28
+ * `at`, or where it went in, on the nearest ground it can stand on. Structures cannot hide
29
+ */
30
+ setHidden(entity: Entity, hidden: boolean, at?: Vec2): void;
31
+ /** Gives an entity a time after which it is gone by itself, like a ward, or takes it away with `null` */
32
+ setLifetime(entity: Entity, seconds: number | null): void;
33
+ }