@datamoc/mw_games 0.7.1 → 0.7.2
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/CHANGELOG.md +964 -0
- package/README.md +27 -2
- package/dist/actors/Affix.d.ts +7 -1
- package/dist/actors/Affix.d.ts.map +1 -1
- package/dist/actors/Affix.js +4 -2
- package/dist/actors/Affix.js.map +1 -1
- package/dist/actors/StatBlock.d.ts +20 -0
- package/dist/actors/StatBlock.d.ts.map +1 -1
- package/dist/actors/StatBlock.js +14 -3
- package/dist/actors/StatBlock.js.map +1 -1
- package/dist/actors/index.d.ts +2 -2
- package/dist/actors/index.d.ts.map +1 -1
- package/dist/actors/index.js +1 -1
- package/dist/actors/index.js.map +1 -1
- package/dist/ai/index.d.ts +118 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +203 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/lua.d.ts +47 -0
- package/dist/ai/lua.d.ts.map +1 -0
- package/dist/ai/lua.js +195 -0
- package/dist/ai/lua.js.map +1 -0
- package/dist/ai/search.d.ts +59 -0
- package/dist/ai/search.d.ts.map +1 -0
- package/dist/ai/search.js +100 -0
- package/dist/ai/search.js.map +1 -0
- package/dist/board/Engine.d.ts +14 -0
- package/dist/board/Engine.d.ts.map +1 -1
- package/dist/board/Engine.js +33 -45
- package/dist/board/Engine.js.map +1 -1
- package/dist/board/index.d.ts +1 -1
- package/dist/board/index.d.ts.map +1 -1
- package/dist/board/index.js +1 -1
- package/dist/board/index.js.map +1 -1
- package/dist/core/Csv.d.ts +10 -0
- package/dist/core/Csv.d.ts.map +1 -1
- package/dist/core/Csv.js +34 -3
- package/dist/core/Csv.js.map +1 -1
- package/dist/core/Entity.d.ts +2 -2
- package/dist/core/Entity.d.ts.map +1 -1
- package/dist/core/Entity.js +18 -4
- package/dist/core/Entity.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mw_games.global.js +96 -96
- package/dist/mwl/actors.d.ts +3 -1
- package/dist/mwl/actors.d.ts.map +1 -1
- package/dist/mwl/actors.js +5 -0
- package/dist/mwl/actors.js.map +1 -1
- package/dist/mwl/catalog.d.ts.map +1 -1
- package/dist/mwl/catalog.js +2 -10
- package/dist/mwl/catalog.js.map +1 -1
- package/dist/mwl/compiler.d.ts +14 -0
- package/dist/mwl/compiler.d.ts.map +1 -1
- package/dist/mwl/compiler.js +23 -0
- package/dist/mwl/compiler.js.map +1 -1
- package/dist/mwl/conditions.d.ts +9 -0
- package/dist/mwl/conditions.d.ts.map +1 -0
- package/dist/mwl/conditions.js +162 -0
- package/dist/mwl/conditions.js.map +1 -0
- package/dist/mwl/content.d.ts +25 -0
- package/dist/mwl/content.d.ts.map +1 -1
- package/dist/mwl/content.js +46 -34
- package/dist/mwl/content.js.map +1 -1
- package/dist/mwl/fengari.d.ts +8 -0
- package/dist/mwl/fengari.d.ts.map +1 -0
- package/dist/mwl/fengari.js +136 -0
- package/dist/mwl/fengari.js.map +1 -0
- package/dist/mwl/hooks.d.ts +1 -1
- package/dist/mwl/index.d.ts +16 -7
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +7 -3
- package/dist/mwl/index.js.map +1 -1
- package/dist/mwl/readers.d.ts +18 -0
- package/dist/mwl/readers.d.ts.map +1 -0
- package/dist/mwl/readers.js +58 -0
- package/dist/mwl/readers.js.map +1 -0
- package/dist/mwl/report.d.ts +9 -0
- package/dist/mwl/report.d.ts.map +1 -0
- package/dist/mwl/report.js +36 -0
- package/dist/mwl/report.js.map +1 -0
- package/dist/mwl/runtime.d.ts +37 -1
- package/dist/mwl/runtime.d.ts.map +1 -1
- package/dist/mwl/runtime.js +170 -19
- package/dist/mwl/runtime.js.map +1 -1
- package/dist/mwl/schema.d.ts +13 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/schema.js +195 -8
- package/dist/mwl/schema.js.map +1 -1
- package/dist/mwl/scripts.d.ts +15 -0
- package/dist/mwl/scripts.d.ts.map +1 -0
- package/dist/mwl/scripts.js +23 -0
- package/dist/mwl/scripts.js.map +1 -0
- package/dist/mwl/utils.d.ts +8 -0
- package/dist/mwl/utils.d.ts.map +1 -0
- package/dist/mwl/utils.js +37 -0
- package/dist/mwl/utils.js.map +1 -0
- package/dist/two-d/render/Types2D.d.ts +19 -4
- package/dist/two-d/render/Types2D.d.ts.map +1 -1
- package/dist/two-d/render/Types2D.js +25 -0
- package/dist/two-d/render/Types2D.js.map +1 -1
- package/dist/two-d/render/index.d.ts +2 -2
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +1 -1
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +19 -3
- package/tools/mwl.mjs +32 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lua.d.ts","sourceRoot":"","sources":["../../src/ai/lua.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,EAAmB,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEvH,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,qBAAqB,CAAC;CACxC;AAED,MAAM,WAAW,YAAa,SAAQ,wBAAwB;IAC7D,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,KAAK,IAAI,CAAC;CACrD;AAED;;;;;;;GAOG;AACH,qBAAa,KAAK;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2C;IAClE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmE;IAC1F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,YAAY,OAAO,GAAE,YAAiB,EAQrC;IAED,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAK1C;IAED,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU,CAkD1D;IAED;;;;OAIG;IACH,MAAM,CACL,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,YAAY,EAAE,OAAO,EACnC,OAAO,EAAE,gBAAgB,GACvB,eAAe,CAAC,OAAO,YAAY,EAAE,OAAO,EAAE,OAAO,YAAY,EAAE,OAAO,CAAC,CA4B7E;IAED,WAAW,IAAI,eAAe,CAE7B;IAED,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAM3C;IAED,OAAO,IAAI,IAAI,CAKd;CACD;AA0ED,wBAAgB,WAAW,CAAC,OAAO,GAAE,YAAiB,GAAG,KAAK,CAE7D;AAED,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/ai/lua.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { createFengariScriptHost } from '../mwl/fengari.js';
|
|
2
|
+
import {} from './index.js';
|
|
3
|
+
import { alphaBetaSearch } from './search.js';
|
|
4
|
+
/**
|
|
5
|
+
* Optional Lua provider with the same decision shape as `JavaScriptAI`.
|
|
6
|
+
*
|
|
7
|
+
* A Lua function receives `(perception, state)` and returns either an action
|
|
8
|
+
* table or `{ action = action_table, state = state_table, events = {...} }`.
|
|
9
|
+
* The host is supplied so games can choose their own VM policy and tests can
|
|
10
|
+
* inject a fake without loading Fengari.
|
|
11
|
+
*/
|
|
12
|
+
export class LuaAI {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.agents = new Map();
|
|
15
|
+
this.states = new Map();
|
|
16
|
+
this.loaded = new Set();
|
|
17
|
+
this.options = options;
|
|
18
|
+
this.host =
|
|
19
|
+
options.host ??
|
|
20
|
+
createFengariScriptHost({
|
|
21
|
+
instructionLimit: options.maxSteps ?? options.instructionLimit,
|
|
22
|
+
seed: options.seed,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
register(agent) {
|
|
26
|
+
if (this.agents.has(agent.id))
|
|
27
|
+
throw new Error(`Lua AI agent already registered: ${agent.id}`);
|
|
28
|
+
if (!agent.source.trim())
|
|
29
|
+
throw new Error(`Lua AI agent has empty source: ${agent.id}`);
|
|
30
|
+
this.agents.set(agent.id, agent);
|
|
31
|
+
this.states.set(agent.id, {});
|
|
32
|
+
}
|
|
33
|
+
decide(agentId, input) {
|
|
34
|
+
const agent = this.agents.get(agentId);
|
|
35
|
+
if (!agent)
|
|
36
|
+
throw new Error(`unknown Lua AI agent: ${agentId}`);
|
|
37
|
+
if (input.signal?.aborted) {
|
|
38
|
+
return {
|
|
39
|
+
agent: agentId,
|
|
40
|
+
action: null,
|
|
41
|
+
state: this.states.get(agentId) ?? {},
|
|
42
|
+
status: 'cancelled',
|
|
43
|
+
steps: 0,
|
|
44
|
+
events: [],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (!this.loaded.has(agentId)) {
|
|
48
|
+
this.host.execute(agent.source);
|
|
49
|
+
this.loaded.add(agentId);
|
|
50
|
+
}
|
|
51
|
+
const functionName = agent.functionName ?? 'decide';
|
|
52
|
+
const state = input.state ?? this.states.get(agentId) ?? {};
|
|
53
|
+
try {
|
|
54
|
+
const result = this.host.call(functionName, [input.perception, state], {
|
|
55
|
+
mwg_seed: input.seed ?? this.options.seed ?? 0x6d7767,
|
|
56
|
+
});
|
|
57
|
+
const normalized = normalizeResult(agentId, result, state);
|
|
58
|
+
this.states.set(agentId, normalized.state);
|
|
59
|
+
const decision = {
|
|
60
|
+
agent: agentId,
|
|
61
|
+
action: normalized.action,
|
|
62
|
+
state: normalized.state,
|
|
63
|
+
status: normalized.action ? 'action' : 'idle',
|
|
64
|
+
steps: 1,
|
|
65
|
+
events: normalized.events,
|
|
66
|
+
};
|
|
67
|
+
this.options.onDecision?.(decision);
|
|
68
|
+
return decision;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (/instruction limit exceeded/i.test(String(error))) {
|
|
72
|
+
const decision = {
|
|
73
|
+
agent: agentId,
|
|
74
|
+
action: null,
|
|
75
|
+
state: state,
|
|
76
|
+
status: 'budget-exceeded',
|
|
77
|
+
steps: input.maxSteps ?? this.options.maxSteps ?? 0,
|
|
78
|
+
events: [],
|
|
79
|
+
};
|
|
80
|
+
this.options.onDecision?.(decision);
|
|
81
|
+
return decision;
|
|
82
|
+
}
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Search a game whose state and operations are exposed by named Lua functions.
|
|
88
|
+
* Defaults are `current_player`, `legal_moves`, `apply_move`, `is_terminal` and
|
|
89
|
+
* `evaluate`, each receiving the state and, for `apply_move`, the move as arguments.
|
|
90
|
+
*/
|
|
91
|
+
search(agentId, state, options) {
|
|
92
|
+
const agent = this.agents.get(agentId);
|
|
93
|
+
if (!agent)
|
|
94
|
+
throw new Error(`unknown Lua AI agent: ${agentId}`);
|
|
95
|
+
if (!this.loaded.has(agentId)) {
|
|
96
|
+
this.host.execute(agent.source);
|
|
97
|
+
this.loaded.add(agentId);
|
|
98
|
+
}
|
|
99
|
+
const names = {
|
|
100
|
+
player: agent.search?.player ?? 'current_player',
|
|
101
|
+
moves: agent.search?.moves ?? 'legal_moves',
|
|
102
|
+
apply: agent.search?.apply ?? 'apply_move',
|
|
103
|
+
terminal: agent.search?.terminal ?? 'is_terminal',
|
|
104
|
+
evaluate: agent.search?.evaluate ?? 'evaluate',
|
|
105
|
+
};
|
|
106
|
+
const call = (name, args) => this.host.call(name, args, { mwg_seed: this.options.seed ?? 0x6d7767 });
|
|
107
|
+
return alphaBetaSearch({
|
|
108
|
+
currentPlayer: (current) => toNumber(call(names.player, [current]), `Lua AI ${agentId} player`),
|
|
109
|
+
moves: (current) => toMoves(call(names.moves, [current]), agentId),
|
|
110
|
+
apply: (current, move) => call(names.apply, [current, move]),
|
|
111
|
+
isTerminal: (current) => toBoolean(call(names.terminal, [current]), `Lua AI ${agentId} terminal`),
|
|
112
|
+
evaluate: (current, perspective) => toNumber(call(names.evaluate, [current, perspective]), `Lua AI ${agentId} evaluation`),
|
|
113
|
+
}, state, options);
|
|
114
|
+
}
|
|
115
|
+
exportState() {
|
|
116
|
+
return { version: 1, agents: Object.fromEntries([...this.states].map(([id, state]) => [id, clone(state)])) };
|
|
117
|
+
}
|
|
118
|
+
importState(envelope) {
|
|
119
|
+
if (envelope.version !== 1)
|
|
120
|
+
throw new Error(`unsupported AI state version: ${envelope.version}`);
|
|
121
|
+
for (const [id, state] of Object.entries(envelope.agents)) {
|
|
122
|
+
if (!this.agents.has(id))
|
|
123
|
+
throw new Error(`AI state names unknown agent: ${id}`);
|
|
124
|
+
this.states.set(id, clone(state));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
dispose() {
|
|
128
|
+
this.host.dispose();
|
|
129
|
+
this.agents.clear();
|
|
130
|
+
this.states.clear();
|
|
131
|
+
this.loaded.clear();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function normalizeResult(agent, result, initialState) {
|
|
135
|
+
if (result === null)
|
|
136
|
+
return { action: null, state: clone(initialState), events: [] };
|
|
137
|
+
if (typeof result !== 'object' || Array.isArray(result))
|
|
138
|
+
throw new Error(`Lua AI ${agent} must return an action table`);
|
|
139
|
+
const value = result;
|
|
140
|
+
const hasEnvelope = 'action' in value || 'state' in value || 'events' in value;
|
|
141
|
+
const action = (hasEnvelope ? value.action : value);
|
|
142
|
+
if (action !== null &&
|
|
143
|
+
action !== undefined &&
|
|
144
|
+
(typeof action !== 'object' || Array.isArray(action) || typeof action.type !== 'string')) {
|
|
145
|
+
throw new Error(`Lua AI ${agent} returned an invalid action`);
|
|
146
|
+
}
|
|
147
|
+
const events = hasEnvelope && value.events && typeof value.events === 'object'
|
|
148
|
+
? tableEntries(value.events).map((entry) => {
|
|
149
|
+
if (typeof entry !== 'object' ||
|
|
150
|
+
entry === null ||
|
|
151
|
+
Array.isArray(entry) ||
|
|
152
|
+
typeof entry.name !== 'string') {
|
|
153
|
+
throw new Error(`Lua AI ${agent} returned an invalid event`);
|
|
154
|
+
}
|
|
155
|
+
return entry;
|
|
156
|
+
})
|
|
157
|
+
: [];
|
|
158
|
+
const state = hasEnvelope && value.state && typeof value.state === 'object' && !Array.isArray(value.state)
|
|
159
|
+
? value.state
|
|
160
|
+
: initialState;
|
|
161
|
+
return { action: action ?? null, state: clone(state), events };
|
|
162
|
+
}
|
|
163
|
+
function tableEntries(value) {
|
|
164
|
+
if (Array.isArray(value))
|
|
165
|
+
return value;
|
|
166
|
+
if (value === null || typeof value !== 'object')
|
|
167
|
+
return [];
|
|
168
|
+
return Object.keys(value)
|
|
169
|
+
.filter((key) => /^\d+$/.test(key))
|
|
170
|
+
.sort((a, b) => Number(a) - Number(b))
|
|
171
|
+
.map((key) => value[key]);
|
|
172
|
+
}
|
|
173
|
+
function toMoves(value, agent) {
|
|
174
|
+
const moves = tableEntries(value);
|
|
175
|
+
if (value !== null && typeof value !== 'object')
|
|
176
|
+
throw new Error(`Lua AI ${agent} legal moves must be a table`);
|
|
177
|
+
return moves;
|
|
178
|
+
}
|
|
179
|
+
function toBoolean(value, name) {
|
|
180
|
+
if (typeof value !== 'boolean')
|
|
181
|
+
throw new Error(`${name} must return a boolean`);
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
function toNumber(value, name) {
|
|
185
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
186
|
+
throw new Error(`${name} must return a finite number`);
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
function clone(value) {
|
|
190
|
+
return JSON.parse(JSON.stringify(value));
|
|
191
|
+
}
|
|
192
|
+
export function createLuaAI(options = {}) {
|
|
193
|
+
return new LuaAI(options);
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=lua.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lua.js","sourceRoot":"","sources":["../../src/ai/lua.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAiC,MAAM,mBAAmB,CAAC;AAE3F,OAAO,EAA+D,MAAM,YAAY,CAAC;AACzF,OAAO,EAAE,eAAe,EAA2E,MAAM,aAAa,CAAC;AAiBvH;;;;;;;GAOG;AACH,MAAM,OAAO,KAAK;IAOjB,YAAY,OAAO,GAAiB,EAAE;QALrB,WAAM,GAAG,IAAI,GAAG,EAAgC,CAAC;QACjD,WAAM,GAAG,IAAI,GAAG,EAAwD,CAAC;QAEzE,WAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QAG3C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,IAAI;YACR,OAAO,CAAC,IAAI;gBACZ,uBAAuB,CAAC;oBACvB,gBAAgB,EAAE,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,gBAAgB;oBAC9D,IAAI,EAAE,OAAO,CAAC,IAAI;iBAClB,CAAC,CAAC;IACL,CAAC;IAED,QAAQ,CAAC,KAA2B;QACnC,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/F,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACxF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,KAAsB;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QAChE,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC3B,OAAO;gBACN,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE;gBACrC,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,EAAE;aACV,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;QACD,MAAM,YAAY,GAAG,KAAK,CAAC,YAAY,IAAI,QAAQ,CAAC;QACpD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5D,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE;gBACtE,QAAQ,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,QAAQ;aACrD,CAAC,CAAC;YACH,MAAM,UAAU,GAAG,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;YAC3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;YAC3C,MAAM,QAAQ,GAAe;gBAC5B,KAAK,EAAE,OAAO;gBACd,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,KAAK,EAAE,UAAU,CAAC,KAAK;gBACvB,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;gBAC7C,KAAK,EAAE,CAAC;gBACR,MAAM,EAAE,UAAU,CAAC,MAAM;aACzB,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,QAAQ,CAAC,CAAC;YACpC,OAAO,QAAQ,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,6BAA6B,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACvD,MAAM,QAAQ,GAAe;oBAC5B,KAAK,EAAE,OAAO;oBACd,MAAM,EAAE,IAAI;oBACZ,KAAK,EAAE,KAAqD;oBAC5D,MAAM,EAAE,iBAAiB;oBACzB,KAAK,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC;oBACnD,MAAM,EAAE,EAAE;iBACV,CAAC;gBACF,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,QAAQ,CAAC,CAAC;gBACpC,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IAED;;;;OAIG;IACH,MAAM,CACL,OAAe,EACf,KAAmC,EACnC,OAAyB;QAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;QACD,MAAM,KAAK,GAAG;YACb,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,gBAAgB;YAChD,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,aAAa;YAC3C,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,YAAY;YAC1C,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,IAAI,aAAa;YACjD,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,IAAI,UAAU;SAC9C,CAAC;QACF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,IAAoC,EAAgC,EAAE,CACjG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC;QACzE,OAAO,eAAe,CACrB;YACC,aAAa,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,OAAO,SAAS,CAAC;YAC/F,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,CAAC;YAClE,KAAK,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC5D,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,OAAO,WAAW,CAAC;YACjG,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,CAClC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,UAAU,OAAO,aAAa,CAAC;SACvF,EACD,KAAK,EACL,OAAO,CACP,CAAC;IACH,CAAC;IAED,WAAW;QACV,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9G,CAAC;IAED,WAAW,CAAC,QAAyB;QACpC,IAAI,QAAQ,CAAC,OAAO,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACjG,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3D,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,EAAE,EAAE,CAAC,CAAC;YACjF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACnC,CAAC;IACF,CAAC;IAED,OAAO;QACN,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACD;AAED,SAAS,eAAe,CACvB,KAAa,EACb,MAAoC,EACpC,YAA0D;IAM1D,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,8BAA8B,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,MAAsD,CAAC;IACrE,MAAM,WAAW,GAAG,QAAQ,IAAI,KAAK,IAAI,OAAO,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,CAAC;IAC/E,MAAM,MAAM,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAqD,CAAC;IACxG,IACC,MAAM,KAAK,IAAI;QACf,MAAM,KAAK,SAAS;QACpB,CAAC,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,EACvF,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,6BAA6B,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,MAAM,GACX,WAAW,IAAI,KAAK,CAAC,MAAM,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAC9D,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACzC,IACC,OAAO,KAAK,KAAK,QAAQ;gBACzB,KAAK,KAAK,IAAI;gBACd,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACpB,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC7B,CAAC;gBACF,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,4BAA4B,CAAC,CAAC;YAC9D,CAAC;YACD,OAAO,KAAgD,CAAC;QACzD,CAAC,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,KAAK,GACV,WAAW,IAAI,KAAK,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;QAC3F,CAAC,CAAE,KAAK,CAAC,KAAsD;QAC/D,CAAC,CAAC,YAAY,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,YAAY,CAAC,KAAmC;IACxD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC3D,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;SACvB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SAClC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;SACrC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,OAAO,CAAC,KAAmC,EAAE,KAAa;IAClE,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,8BAA8B,CAAC,CAAC;IAChH,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,SAAS,CAAC,KAAmC,EAAE,IAAY;IACnE,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wBAAwB,CAAC,CAAC;IACjF,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,KAAmC,EAAE,IAAY;IAClE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,8BAA8B,CAAC,CAAC;IACjH,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,KAAK,CAAI,KAAQ;IACzB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAM,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAO,GAAiB,EAAE;IACrD,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AIValue } from './index.ts';
|
|
2
|
+
export interface AlphaBetaGame<State, Move> {
|
|
3
|
+
/** The side whose turn it is. Alpha-beta assumes a two-player zero-sum game. */
|
|
4
|
+
readonly currentPlayer: (state: State) => number;
|
|
5
|
+
/** Legal moves from this state, in the game's preferred stable order. */
|
|
6
|
+
readonly moves: (state: State) => readonly Move[];
|
|
7
|
+
/** Apply a move and return the next state. Do not mutate the input state. */
|
|
8
|
+
readonly apply: (state: State, move: Move) => State;
|
|
9
|
+
readonly isTerminal: (state: State) => boolean;
|
|
10
|
+
/** Score from the perspective of the root player. Higher is better. */
|
|
11
|
+
readonly evaluate: (state: State, rootPlayer: number) => number;
|
|
12
|
+
}
|
|
13
|
+
export interface AlphaBetaOptions {
|
|
14
|
+
readonly depth: number;
|
|
15
|
+
readonly maxNodes?: number;
|
|
16
|
+
readonly signal?: AbortSignal;
|
|
17
|
+
readonly onNode?: (depth: number, maximizing: boolean) => void;
|
|
18
|
+
}
|
|
19
|
+
export interface AlphaBetaResult<State, Move> {
|
|
20
|
+
readonly move: Move | null;
|
|
21
|
+
readonly score: number;
|
|
22
|
+
readonly depth: number;
|
|
23
|
+
readonly nodes: number;
|
|
24
|
+
readonly cutoffs: number;
|
|
25
|
+
readonly status: 'complete' | 'cancelled' | 'budget-exceeded';
|
|
26
|
+
readonly state: State;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Deterministic minimax with alpha-beta pruning for a game-owned state adapter.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { alphaBetaSearch } from '@datamoc/mw_games/ai';
|
|
34
|
+
*
|
|
35
|
+
* const result = alphaBetaSearch({
|
|
36
|
+
* currentPlayer: () => 1,
|
|
37
|
+
* moves: (state: number) => state < 2 ? [1] : [],
|
|
38
|
+
* apply: (state: number, move: number) => state + move,
|
|
39
|
+
* isTerminal: (state: number) => state >= 2,
|
|
40
|
+
* evaluate: (state: number) => state,
|
|
41
|
+
* }, 0, { depth: 2 });
|
|
42
|
+
* console.log(result.move); // 1
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function alphaBetaSearch<State, Move>(game: AlphaBetaGame<State, Move>, state: State, options: AlphaBetaOptions): AlphaBetaResult<State, Move>;
|
|
46
|
+
export interface LuaAlphaBetaFunctions {
|
|
47
|
+
readonly player?: string;
|
|
48
|
+
readonly moves?: string;
|
|
49
|
+
readonly apply?: string;
|
|
50
|
+
readonly terminal?: string;
|
|
51
|
+
readonly evaluate?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface LuaSearchValueAdapter {
|
|
54
|
+
readonly toMoves: (value: AIValue) => readonly AIValue[];
|
|
55
|
+
readonly toBoolean: (value: AIValue) => boolean;
|
|
56
|
+
readonly toNumber: (value: AIValue) => number;
|
|
57
|
+
readonly toPlayer: (value: AIValue) => number;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/ai/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,aAAa,CAAC,KAAK,EAAE,IAAI;IACzC,gFAAgF;IAChF,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC;IACjD,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,EAAE,CAAC;IAClD,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC;IACpD,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;IAC/C,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CAChE;AAED,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/D;AAED,MAAM,WAAW,eAAe,CAAC,KAAK,EAAE,IAAI;IAC3C,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,iBAAiB,CAAC;IAC9D,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACtB;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAC1C,IAAI,EAAE,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,EAChC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,gBAAgB,GACvB,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAwE9B;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,OAAO,EAAE,CAAC;IACzD,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IAChD,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;CAC9C"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const DEFAULT_MAX_NODES = 100_000;
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic minimax with alpha-beta pruning for a game-owned state adapter.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { alphaBetaSearch } from '@datamoc/mw_games/ai';
|
|
8
|
+
*
|
|
9
|
+
* const result = alphaBetaSearch({
|
|
10
|
+
* currentPlayer: () => 1,
|
|
11
|
+
* moves: (state: number) => state < 2 ? [1] : [],
|
|
12
|
+
* apply: (state: number, move: number) => state + move,
|
|
13
|
+
* isTerminal: (state: number) => state >= 2,
|
|
14
|
+
* evaluate: (state: number) => state,
|
|
15
|
+
* }, 0, { depth: 2 });
|
|
16
|
+
* console.log(result.move); // 1
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function alphaBetaSearch(game, state, options) {
|
|
20
|
+
if (!Number.isInteger(options.depth) || options.depth < 1)
|
|
21
|
+
throw new RangeError('depth must be a positive integer');
|
|
22
|
+
const maxNodes = options.maxNodes ?? DEFAULT_MAX_NODES;
|
|
23
|
+
if (!Number.isInteger(maxNodes) || maxNodes < 1)
|
|
24
|
+
throw new RangeError('maxNodes must be a positive integer');
|
|
25
|
+
const rootPlayer = game.currentPlayer(state);
|
|
26
|
+
let nodes = 0;
|
|
27
|
+
let cutoffs = 0;
|
|
28
|
+
let stopped;
|
|
29
|
+
const visit = (current, remaining, alpha, beta) => {
|
|
30
|
+
if (options.signal?.aborted) {
|
|
31
|
+
stopped = 'cancelled';
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
if (++nodes > maxNodes) {
|
|
35
|
+
stopped = 'budget-exceeded';
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
const maximizing = game.currentPlayer(current) === rootPlayer;
|
|
39
|
+
options.onNode?.(remaining, maximizing);
|
|
40
|
+
if (remaining === 0 || game.isTerminal(current))
|
|
41
|
+
return game.evaluate(current, rootPlayer);
|
|
42
|
+
const moves = game.moves(current);
|
|
43
|
+
if (!moves.length)
|
|
44
|
+
return game.evaluate(current, rootPlayer);
|
|
45
|
+
let best = maximizing ? -Infinity : Infinity;
|
|
46
|
+
for (const move of moves) {
|
|
47
|
+
const score = visit(game.apply(current, move), remaining - 1, alpha, beta);
|
|
48
|
+
if (stopped)
|
|
49
|
+
return score;
|
|
50
|
+
if (maximizing) {
|
|
51
|
+
best = Math.max(best, score);
|
|
52
|
+
alpha = Math.max(alpha, best);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
best = Math.min(best, score);
|
|
56
|
+
beta = Math.min(beta, best);
|
|
57
|
+
}
|
|
58
|
+
if (beta <= alpha) {
|
|
59
|
+
cutoffs++;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return best;
|
|
64
|
+
};
|
|
65
|
+
const rootMoves = game.moves(state);
|
|
66
|
+
let chosen = null;
|
|
67
|
+
let score = game.evaluate(state, rootPlayer);
|
|
68
|
+
if (!game.isTerminal(state) && rootMoves.length) {
|
|
69
|
+
const maximizing = game.currentPlayer(state) === rootPlayer;
|
|
70
|
+
let bestScore = maximizing ? -Infinity : Infinity;
|
|
71
|
+
let alpha = -Infinity;
|
|
72
|
+
let beta = Infinity;
|
|
73
|
+
for (const move of rootMoves) {
|
|
74
|
+
const child = game.apply(state, move);
|
|
75
|
+
const candidate = visit(child, options.depth - 1, alpha, beta);
|
|
76
|
+
if (stopped)
|
|
77
|
+
break;
|
|
78
|
+
if (chosen === null || (maximizing ? candidate > bestScore : candidate < bestScore)) {
|
|
79
|
+
chosen = move;
|
|
80
|
+
bestScore = candidate;
|
|
81
|
+
}
|
|
82
|
+
if (maximizing)
|
|
83
|
+
alpha = Math.max(alpha, bestScore);
|
|
84
|
+
else
|
|
85
|
+
beta = Math.min(beta, bestScore);
|
|
86
|
+
}
|
|
87
|
+
if (chosen !== null)
|
|
88
|
+
score = bestScore;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
move: stopped ? null : chosen,
|
|
92
|
+
score,
|
|
93
|
+
depth: options.depth,
|
|
94
|
+
nodes,
|
|
95
|
+
cutoffs,
|
|
96
|
+
status: stopped ?? 'complete',
|
|
97
|
+
state,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/ai/search.ts"],"names":[],"mappings":"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"}
|
package/dist/board/Engine.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AlphaBetaGame } from '../ai/search.ts';
|
|
1
2
|
import { type ChessMove, type ChessState } from './chess.ts';
|
|
2
3
|
export interface ChessEngineOptions {
|
|
3
4
|
/** plies to search; three is a useful small browser default */
|
|
@@ -43,4 +44,17 @@ export declare function chooseMove(state: ChessState, options?: ChessEngineOptio
|
|
|
43
44
|
* ```
|
|
44
45
|
*/
|
|
45
46
|
export declare function search(state: ChessState, options?: ChessEngineOptions): ChessSearchResult;
|
|
47
|
+
/**
|
|
48
|
+
* The chess rules adapter consumed by the shared `ai.alphaBetaSearch` primitive.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { chessGame, startingChess } from '@datamoc/mw_games/board';
|
|
53
|
+
* import { alphaBetaSearch } from '@datamoc/mw_games/ai';
|
|
54
|
+
*
|
|
55
|
+
* const result = alphaBetaSearch(chessGame, startingChess(), { depth: 2 });
|
|
56
|
+
* console.log(result.move !== null); // true
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare const chessGame: AlphaBetaGame<ChessState, ChessMove>;
|
|
46
60
|
//# sourceMappingURL=Engine.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/board/Engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAMN,KAAK,SAAS,EAEd,KAAK,UAAU,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IAClC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,kBAAuB,GAAG,SAAS,GAAG,IAAI,CAEhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,kBAAuB,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/board/Engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,EAMN,KAAK,SAAS,EAEd,KAAK,UAAU,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IAClC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACd;AAYD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,kBAAuB,GAAG,SAAS,GAAG,IAAI,CAEhG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,GAAE,kBAAuB,GAAG,iBAAiB,CAK7F;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,EAAE,aAAa,CAAC,UAAU,EAAE,SAAS,CAW1D,CAAC"}
|
package/dist/board/Engine.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { alphaBetaSearch } from '../ai/search.js';
|
|
1
2
|
import { applyMove, cloneChess, gameResult, inCheck, legalMoves, } from './chess.js';
|
|
2
3
|
const MATE = 100_000;
|
|
3
4
|
const PIECE_VALUE = {
|
|
@@ -44,60 +45,47 @@ export function chooseMove(state, options = {}) {
|
|
|
44
45
|
*/
|
|
45
46
|
export function search(state, options = {}) {
|
|
46
47
|
const depth = Math.max(1, Math.floor(options.depth ?? 3));
|
|
47
|
-
const maxNodes = options.maxNodes === undefined ?
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
const maxNodes = options.maxNodes === undefined ? 100_000 : Math.max(1, Math.floor(options.maxNodes));
|
|
49
|
+
const result = alphaBetaSearch(chessGame, state, { depth, maxNodes });
|
|
50
|
+
return { move: result.move, score: result.score, nodes: result.nodes };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The chess rules adapter consumed by the shared `ai.alphaBetaSearch` primitive.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { chessGame, startingChess } from '@datamoc/mw_games/board';
|
|
58
|
+
* import { alphaBetaSearch } from '@datamoc/mw_games/ai';
|
|
59
|
+
*
|
|
60
|
+
* const result = alphaBetaSearch(chessGame, startingChess(), { depth: 2 });
|
|
61
|
+
* console.log(result.move !== null); // true
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export const chessGame = {
|
|
65
|
+
currentPlayer: (state) => (state.turn === 'white' ? 1 : -1),
|
|
66
|
+
moves: (state) => orderedMoves(state, legalMoves(state)),
|
|
67
|
+
apply: (state, move) => {
|
|
58
68
|
const next = cloneChess(state);
|
|
59
69
|
applyMove(next, move);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
return { move: bestMove, score: bestScore, nodes };
|
|
68
|
-
function negamax(position, remaining, low, high, ply) {
|
|
69
|
-
if (nodes >= maxNodes)
|
|
70
|
-
return evaluate(position);
|
|
71
|
-
nodes++;
|
|
72
|
-
const result = gameResult(position);
|
|
73
|
-
if (result !== 'ongoing')
|
|
74
|
-
return terminalScore(position, ply);
|
|
75
|
-
if (remaining === 0)
|
|
76
|
-
return evaluate(position);
|
|
77
|
-
let value = -Infinity;
|
|
78
|
-
for (const move of orderedMoves(position, legalMoves(position))) {
|
|
79
|
-
const next = cloneChess(position);
|
|
80
|
-
applyMove(next, move);
|
|
81
|
-
value = Math.max(value, -negamax(next, remaining - 1, -high, -low, ply + 1));
|
|
82
|
-
low = Math.max(low, value);
|
|
83
|
-
if (low >= high)
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
return value;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
function evaluate(state) {
|
|
70
|
+
return next;
|
|
71
|
+
},
|
|
72
|
+
isTerminal: (state) => gameResult(state) !== 'ongoing',
|
|
73
|
+
evaluate: (state, rootPlayer) => gameResult(state) === 'ongoing' ? evaluate(state, rootPlayer) : terminalScore(state, rootPlayer),
|
|
74
|
+
};
|
|
75
|
+
function evaluate(state, rootPlayer) {
|
|
90
76
|
let score = 0;
|
|
91
77
|
for (const piece of state.board) {
|
|
92
78
|
if (piece)
|
|
93
79
|
score += (piece.side === 'white' ? 1 : -1) * PIECE_VALUE[piece.kind];
|
|
94
80
|
}
|
|
95
|
-
return
|
|
81
|
+
return rootPlayer === 1 ? score : -score;
|
|
96
82
|
}
|
|
97
|
-
function terminalScore(state,
|
|
98
|
-
|
|
83
|
+
function terminalScore(state, rootPlayer) {
|
|
84
|
+
const result = gameResult(state);
|
|
85
|
+
if (result === 'stalemate' || !inCheck(state, state.turn))
|
|
99
86
|
return 0;
|
|
100
|
-
|
|
87
|
+
const winner = result === 'white-wins' ? 1 : -1;
|
|
88
|
+
return winner === rootPlayer ? MATE : -MATE;
|
|
101
89
|
}
|
|
102
90
|
function orderedMoves(state, moves) {
|
|
103
91
|
return moves.slice().sort((a, b) => moveOrder(state, b) - moveOrder(state, a));
|
package/dist/board/Engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.js","sourceRoot":"","sources":["../../src/board/Engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,SAAS,EACT,UAAU,EACV,UAAU,EACV,OAAO,EACP,UAAU,GAIV,MAAM,YAAY,CAAC;AAepB,MAAM,IAAI,GAAG,OAAO,CAAC;AACrB,MAAM,WAAW,GAAuC;IACvD,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,GAAG;IACX,MAAM,EAAE,GAAG;IACX,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,CAAC;CACP,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,UAAU,CAAC,KAAiB,EAAE,OAAO,GAAuB,EAAE;IAC7E,OAAO,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,MAAM,CAAC,KAAiB,EAAE,OAAO,GAAuB,EAAE;IACzE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"Engine.js","sourceRoot":"","sources":["../../src/board/Engine.ts"],"names":[],"mappings":"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"}
|
package/dist/board/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { startingChess, parseFen, cloneChess, legalMoves, applyMove, inCheck, gameResult, sq, squareName, } from './chess.ts';
|
|
2
2
|
export type { ChessSide, ChessKind, ChessPiece, ChessSquare, PromotionKind, ChessMove, ChessCastling, ChessState, ChessResult, } from './chess.ts';
|
|
3
|
-
export { chooseMove, search } from './Engine.ts';
|
|
3
|
+
export { chessGame, chooseMove, search } from './Engine.ts';
|
|
4
4
|
export type { ChessEngineOptions, ChessSearchResult } from './Engine.ts';
|
|
5
5
|
export { BoardGrid, startingCheckers, checkersMoves, applyCheckersMove, startingGo, playGo, passGo, goResult, goScore, startingBackgammon, rollBackgammonDice, backgammonMoves, applyBackgammonMove, createDeck, shuffleDeck, deal, trickWinner, dealSolitaire, drawSolitaire, moveSolitaireTableau, moveSolitaireToFoundation, solitaireWon, rollDice, rollExpression, DiceCup, scoreDice, } from './Classics.ts';
|
|
6
6
|
export type { BoardOwner, BoardPiece, CheckersSide, CheckersPiece, CheckersMove, CheckersState, GoStone, GoState, DiceCategory, TrickPlay, BackgammonState, BackgammonMove, CardSuit, CardRank, Card, SolitaireState, } from './Classics.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,SAAS,EACT,SAAS,EACT,UAAU,EACV,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,UAAU,EACV,WAAW,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,OAAO,EACP,SAAS,GACT,MAAM,eAAe,CAAC;AACvB,YAAY,EACX,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,aAAa,EACb,OAAO,EACP,OAAO,EACP,YAAY,EACZ,SAAS,EACT,eAAe,EACf,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,cAAc,GACd,MAAM,eAAe,CAAC;AACvB,OAAO,EACN,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,eAAe,GACf,MAAM,cAAc,CAAC;AACtB,YAAY,EACX,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACX,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,cAAc,EACd,gBAAgB,GAChB,MAAM,kBAAkB,CAAC"}
|
package/dist/board/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { startingChess, parseFen, cloneChess, legalMoves, applyMove, inCheck, gameResult, sq, squareName, } from './chess.js';
|
|
2
|
-
export { chooseMove, search } from './Engine.js';
|
|
2
|
+
export { chessGame, chooseMove, search } from './Engine.js';
|
|
3
3
|
export { BoardGrid, startingCheckers, checkersMoves, applyCheckersMove, startingGo, playGo, passGo, goResult, goScore, startingBackgammon, rollBackgammonDice, backgammonMoves, applyBackgammonMove, createDeck, shuffleDeck, deal, trickWinner, dealSolitaire, drawSolitaire, moveSolitaireTableau, moveSolitaireToFoundation, solitaireWon, rollDice, rollExpression, DiceCup, scoreDice, } from './Classics.js';
|
|
4
4
|
export { startingTactics, addTacticalUnit, canPlaceTacticalUnit, tacticalMoves, moveTacticalUnit, setTacticalOverwatch, triggerTacticalOverwatch, tacticalAttack, endTacticalTurn, } from './Tactics.js';
|
|
5
5
|
export { FactionFog } from './FogOfWar.js';
|
package/dist/board/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/board/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,aAAa,EACb,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,OAAO,EACP,UAAU,EACV,EAAE,EACF,UAAU,GACV,MAAM,YAAY,CAAC;AAYpB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE5D,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,MAAM,EACN,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,IAAI,EACJ,WAAW,EACX,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,OAAO,EACP,SAAS,GACT,MAAM,eAAe,CAAC;AAmBvB,OAAO,EACN,eAAe,EACf,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,eAAe,GACf,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7F,OAAO,EACN,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,GACf,MAAM,kBAAkB,CAAC"}
|
package/dist/core/Csv.d.ts
CHANGED
|
@@ -61,4 +61,14 @@ export interface CsvOptions {
|
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
63
63
|
export declare function parseCSV<T = Record<string, string>>(source: string, options?: CsvOptions): T[];
|
|
64
|
+
export interface CsvCoercionOptions {
|
|
65
|
+
readonly column?: string;
|
|
66
|
+
readonly rowIndex?: number;
|
|
67
|
+
readonly listDelimiter?: string;
|
|
68
|
+
readonly mapDelimiter?: string;
|
|
69
|
+
readonly acceptYesNo?: boolean;
|
|
70
|
+
readonly error?: (message: string) => string;
|
|
71
|
+
}
|
|
72
|
+
/** Coerce one typed cell, shared by CSV and declarative content readers. */
|
|
73
|
+
export declare function coerceCsvValue(raw: string, type: CsvColumnType, options?: CsvCoercionOptions): unknown;
|
|
64
74
|
//# sourceMappingURL=Csv.d.ts.map
|
package/dist/core/Csv.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Csv.d.ts","sourceRoot":"","sources":["../../src/core/Csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7E,MAAM,WAAW,UAAU;IAC1B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAExC,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,CAAC,EAAE,CAwBlG"}
|
|
1
|
+
{"version":3,"file":"Csv.d.ts","sourceRoot":"","sources":["../../src/core/Csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7E,MAAM,WAAW,UAAU;IAC1B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAExC,mFAAmF;IACnF,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,CAAC,EAAE,CAwBlG;AA+BD,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;AAED,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAyC1G"}
|