@civ-clone/core-strategy 0.1.0 → 0.1.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/README.md +9 -22
- package/Rules/Priority.d.ts +6 -3
- package/Rules/Priority.js.map +1 -1
- package/Rules/Priority.ts +3 -3
- package/Strategy.d.ts +6 -6
- package/Strategy.js +14 -16
- package/Strategy.js.map +1 -1
- package/Strategy.ts +19 -15
- package/StrategyNote.d.ts +4 -5
- package/StrategyNote.js +5 -20
- package/StrategyNote.js.map +1 -1
- package/StrategyNote.ts +13 -14
- package/StrategyNoteRegistry.d.ts +1 -0
- package/StrategyNoteRegistry.js +3 -0
- package/StrategyNoteRegistry.js.map +1 -1
- package/StrategyNoteRegistry.ts +4 -0
- package/StrategyRegistry.d.ts +1 -1
- package/StrategyRegistry.js +1 -2
- package/StrategyRegistry.js.map +1 -1
- package/StrategyRegistry.ts +2 -3
- package/package.json +1 -1
- package/tests/Strategy.test.ts +105 -43
- package/tests/StrategyRegistry.test.ts +10 -11
- package/tests/lib/Strategies.d.ts +9 -0
- package/tests/lib/Strategies.js +23 -0
- package/tests/lib/Strategies.js.map +1 -0
- package/tests/lib/Strategies.ts +17 -0
- package/tsconfig.json +1 -1
- package/Routine.d.ts +0 -15
- package/Routine.js +0 -38
- package/Routine.js.map +0 -1
- package/Routine.ts +0 -39
- package/tests/Routine.test.ts +0 -93
- package/tests/lib/Routines.d.ts +0 -9
- package/tests/lib/Routines.js +0 -23
- package/tests/lib/Routines.js.map +0 -1
- package/tests/lib/Routines.ts +0 -17
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# core-strategy
|
|
2
2
|
|
|
3
|
-
A framework for building modular AI. The idea being that `Strategy`s
|
|
4
|
-
|
|
3
|
+
A framework for building modular AI. The idea being that `Strategy`s can handle one of many `PlayerAction`s and it
|
|
4
|
+
should be easy enough to register new `Strategy`s to handle newly added `PlayerAction`s or specific custom `Unit`s and
|
|
5
|
+
their actions (`Caravan`, `Diplomat`, etc).
|
|
5
6
|
|
|
6
|
-
The consumer `AIClient`, `
|
|
7
|
-
[civ-clone/
|
|
7
|
+
The consumer `AIClient`, `StrategyAIClient`, is available at
|
|
8
|
+
[civ-clone/core-strategy-ai-client](https://github.com/civ-clone/core-strategy-ai-client).
|
|
8
9
|
|
|
9
10
|
Another aim for this set of classes is to be able to automate simple tasks (explore, improve terrain, go-to, etc.).
|
|
10
11
|
|
|
@@ -12,27 +13,13 @@ Lastly, there's the possibility for primitive Barbarian behaviour, without havin
|
|
|
12
13
|
|
|
13
14
|
## Current state
|
|
14
15
|
|
|
16
|
+
This is purely illustrative and might change quite substantially until I've got a reasonable working implementation.
|
|
17
|
+
|
|
15
18
|
```ts
|
|
16
19
|
import Strategy from '@civ-clone/core-strategy/Strategy';
|
|
17
20
|
|
|
18
21
|
// Export your base `Strategy`:
|
|
19
22
|
export class MyStrategy extends Strategy {
|
|
20
|
-
constructor(
|
|
21
|
-
dependencyForRoutine: SpecificThing = defaultInstanceOfSpecificThing
|
|
22
|
-
) {
|
|
23
|
-
super(
|
|
24
|
-
new MyRoutine(dependencyForRoutine),
|
|
25
|
-
new AnotherRoutine(dependencyForRoutine)
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
31
|
-
import Routine from '@civ-clone/core-strategy/Strategy';
|
|
32
|
-
import { instance as strategyNoteInstance } from '@civ-clone/core-strategy/StrategyNoteRegistry';
|
|
33
|
-
|
|
34
|
-
// which is comprised of `Routine`s like the following:
|
|
35
|
-
export class MyRoutine extends Routine {
|
|
36
23
|
// Inject dependencies (e.g. `Registry`s) into the `constructor` as usual
|
|
37
24
|
|
|
38
25
|
attempt(action: PlayerAction<SpecificItemClass>): boolean {
|
|
@@ -69,7 +56,7 @@ export class MyRoutine extends Routine {
|
|
|
69
56
|
import { generateKey } from '@civ-clone/core-strategy/StrategyNote';
|
|
70
57
|
|
|
71
58
|
export const myCustomKeyGenerator = (item: SpecificItemClass) =>
|
|
72
|
-
generateKey(item,
|
|
59
|
+
generateKey(item, MyStrategy.name);
|
|
73
60
|
|
|
74
61
|
// To control the priority of your `Routine`s you need to use `Priority` `Rule`s and you can even take the `Leader`s
|
|
75
62
|
// `Trait`s into consideration if you wish:
|
|
@@ -89,7 +76,7 @@ export const getRules = (
|
|
|
89
76
|
): Priority[] => [
|
|
90
77
|
new Priority(
|
|
91
78
|
new Criterion(
|
|
92
|
-
(player: Player,
|
|
79
|
+
(player: Player, strategy: Strategy) => routine instanceof MyRoutine
|
|
93
80
|
),
|
|
94
81
|
new Effect((player: Player) => {
|
|
95
82
|
const civilization = player.civilization(),
|
package/Rules/Priority.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
2
2
|
import PriorityValue from '@civ-clone/core-rule/Priority';
|
|
3
|
-
import Routine from '../Routine';
|
|
4
3
|
import Rule from '@civ-clone/core-rule/Rule';
|
|
5
|
-
|
|
4
|
+
import Strategy from '../Strategy';
|
|
5
|
+
export declare class Priority extends Rule<
|
|
6
|
+
[PlayerAction, Strategy],
|
|
7
|
+
PriorityValue
|
|
8
|
+
> {}
|
|
6
9
|
export default Priority;
|
package/Rules/Priority.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Priority.js","sourceRoot":"","sources":["Priority.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"Priority.js","sourceRoot":"","sources":["Priority.ts"],"names":[],"mappings":";;;AAEA,oDAA6C;AAG7C,MAAa,QAAS,SAAQ,cAA6C;CAAG;AAA9E,4BAA8E;AAE9E,kBAAe,QAAQ,CAAC"}
|
package/Rules/Priority.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
2
2
|
import PriorityValue from '@civ-clone/core-rule/Priority';
|
|
3
|
-
import Routine from '../Routine';
|
|
4
3
|
import Rule from '@civ-clone/core-rule/Rule';
|
|
4
|
+
import Strategy from '../Strategy';
|
|
5
5
|
|
|
6
|
-
export class Priority extends Rule<[
|
|
6
|
+
export class Priority extends Rule<[PlayerAction, Strategy], PriorityValue> {}
|
|
7
7
|
|
|
8
8
|
export default Priority;
|
package/Strategy.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
|
|
2
2
|
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
3
3
|
import Priority from '@civ-clone/core-rule/Priority';
|
|
4
|
-
import Routine from './Routine';
|
|
5
4
|
export interface IStrategy {
|
|
6
5
|
attempt(action: PlayerAction): boolean;
|
|
7
|
-
priority(
|
|
6
|
+
priority(action: PlayerAction): Priority;
|
|
8
7
|
}
|
|
9
8
|
export declare class Strategy implements IStrategy {
|
|
10
|
-
|
|
11
|
-
constructor(
|
|
9
|
+
private _ruleRegistry;
|
|
10
|
+
constructor(ruleRegistry?: RuleRegistry);
|
|
12
11
|
/**
|
|
13
12
|
* Checks to see if the `action` can be handled, returns `true` if it is, `false` otherwise.
|
|
14
13
|
*/
|
|
15
14
|
attempt(action: PlayerAction): boolean;
|
|
16
|
-
priority(
|
|
15
|
+
priority(action: PlayerAction): Priority;
|
|
16
|
+
protected ruleRegistry(): RuleRegistry;
|
|
17
17
|
}
|
|
18
18
|
export default Strategy;
|
package/Strategy.js
CHANGED
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
6
|
-
};
|
|
7
|
-
var _Strategy_routines;
|
|
8
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
3
|
exports.Strategy = void 0;
|
|
4
|
+
const RuleRegistry_1 = require("@civ-clone/core-rule/RuleRegistry");
|
|
10
5
|
const Priority_1 = require("@civ-clone/core-rule/Priority");
|
|
6
|
+
const Priority_2 = require("./Rules/Priority");
|
|
11
7
|
class Strategy {
|
|
12
|
-
constructor(
|
|
13
|
-
|
|
14
|
-
items.forEach((item) => __classPrivateFieldGet(this, _Strategy_routines, "f").push(item));
|
|
8
|
+
constructor(ruleRegistry = RuleRegistry_1.instance) {
|
|
9
|
+
this._ruleRegistry = ruleRegistry;
|
|
15
10
|
}
|
|
16
11
|
/**
|
|
17
12
|
* Checks to see if the `action` can be handled, returns `true` if it is, `false` otherwise.
|
|
18
13
|
*/
|
|
19
14
|
attempt(action) {
|
|
20
|
-
|
|
21
|
-
.sort((a, b) => a.priority(action.player()).value() -
|
|
22
|
-
b.priority(action.player()).value())
|
|
23
|
-
.some((routine) => routine.attempt(action));
|
|
15
|
+
throw new Error('This must be overwritten in the implementor.');
|
|
24
16
|
}
|
|
25
|
-
priority(
|
|
26
|
-
return new Priority_1.default(
|
|
17
|
+
priority(action) {
|
|
18
|
+
return new Priority_1.default(
|
|
19
|
+
// This takes the highest priority (lowest value) from all the applicable `PriorityRule`s
|
|
20
|
+
Math.min(...this._ruleRegistry
|
|
21
|
+
.process(Priority_2.default, action, this)
|
|
22
|
+
.map((priority) => priority.value()), Infinity));
|
|
23
|
+
}
|
|
24
|
+
ruleRegistry() {
|
|
25
|
+
return this._ruleRegistry;
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
exports.Strategy = Strategy;
|
|
30
|
-
_Strategy_routines = new WeakMap();
|
|
31
29
|
exports.default = Strategy;
|
|
32
30
|
//# sourceMappingURL=Strategy.js.map
|
package/Strategy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Strategy.js","sourceRoot":"","sources":["Strategy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Strategy.js","sourceRoot":"","sources":["Strategy.ts"],"names":[],"mappings":";;;AAAA,oEAG2C;AAE3C,4DAAqD;AACrD,+CAA4C;AAO5C,MAAa,QAAQ;IAGnB,YAAY,eAA6B,uBAAoB;QAC3D,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,MAAoB;QAC1B,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,QAAQ,CAAC,MAAoB;QAC3B,OAAO,IAAI,kBAAQ;QACjB,yFAAyF;QACzF,IAAI,CAAC,GAAG,CACN,GAAG,IAAI,CAAC,aAAa;aAClB,OAAO,CAAC,kBAAY,EAAE,MAAM,EAAE,IAAI,CAAC;aACnC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,EACtC,QAAQ,CACT,CACF,CAAC;IACJ,CAAC;IAES,YAAY;QACpB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;CACF;AA7BD,4BA6BC;AAED,kBAAe,QAAQ,CAAC"}
|
package/Strategy.ts
CHANGED
|
@@ -1,41 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
RuleRegistry,
|
|
3
|
+
instance as ruleRegistryInstance,
|
|
4
|
+
} from '@civ-clone/core-rule/RuleRegistry';
|
|
2
5
|
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
3
6
|
import Priority from '@civ-clone/core-rule/Priority';
|
|
4
|
-
import
|
|
7
|
+
import PriorityRule from './Rules/Priority';
|
|
5
8
|
|
|
6
9
|
export interface IStrategy {
|
|
7
10
|
attempt(action: PlayerAction): boolean;
|
|
8
|
-
priority(
|
|
11
|
+
priority(action: PlayerAction): Priority;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
export class Strategy implements IStrategy {
|
|
12
|
-
|
|
15
|
+
private _ruleRegistry: RuleRegistry;
|
|
13
16
|
|
|
14
|
-
constructor(
|
|
15
|
-
|
|
17
|
+
constructor(ruleRegistry: RuleRegistry = ruleRegistryInstance) {
|
|
18
|
+
this._ruleRegistry = ruleRegistry;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Checks to see if the `action` can be handled, returns `true` if it is, `false` otherwise.
|
|
20
23
|
*/
|
|
21
24
|
attempt(action: PlayerAction): boolean {
|
|
22
|
-
|
|
23
|
-
.sort(
|
|
24
|
-
(a, b) =>
|
|
25
|
-
a.priority(action.player()).value() -
|
|
26
|
-
b.priority(action.player()).value()
|
|
27
|
-
)
|
|
28
|
-
.some((routine) => routine.attempt(action));
|
|
25
|
+
throw new Error('This must be overwritten in the implementor.');
|
|
29
26
|
}
|
|
30
27
|
|
|
31
|
-
priority(
|
|
28
|
+
priority(action: PlayerAction): Priority {
|
|
32
29
|
return new Priority(
|
|
30
|
+
// This takes the highest priority (lowest value) from all the applicable `PriorityRule`s
|
|
33
31
|
Math.min(
|
|
34
|
-
...this
|
|
32
|
+
...this._ruleRegistry
|
|
33
|
+
.process(PriorityRule, action, this)
|
|
34
|
+
.map((priority) => priority.value()),
|
|
35
35
|
Infinity
|
|
36
36
|
)
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
protected ruleRegistry(): RuleRegistry {
|
|
41
|
+
return this._ruleRegistry;
|
|
42
|
+
}
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export default Strategy;
|
package/StrategyNote.d.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
id(): string;
|
|
3
|
-
};
|
|
1
|
+
import { IDataObject } from '@civ-clone/core-data-object/DataObject';
|
|
4
2
|
export interface IStrategyNote<Value = any> {
|
|
5
3
|
key(): string;
|
|
6
4
|
value(): Value;
|
|
7
5
|
}
|
|
8
6
|
export declare class StrategyNote<Value = any> implements IStrategyNote<Value> {
|
|
9
|
-
|
|
7
|
+
private _key;
|
|
8
|
+
private _value;
|
|
10
9
|
constructor(key: string, value: Value);
|
|
11
10
|
key(): string;
|
|
12
11
|
value(): Value;
|
|
13
12
|
}
|
|
14
13
|
export declare const generateKey: (
|
|
15
|
-
...items: (IDataObject | string)[]
|
|
14
|
+
...items: (Pick<IDataObject, 'id'> | string)[]
|
|
16
15
|
) => string;
|
|
17
16
|
export default StrategyNote;
|
package/StrategyNote.js
CHANGED
|
@@ -1,37 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
-
};
|
|
8
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
|
-
var _StrategyNote_key, _StrategyNote_value;
|
|
14
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
3
|
exports.generateKey = exports.StrategyNote = void 0;
|
|
16
4
|
const DataObject_1 = require("@civ-clone/core-data-object/DataObject");
|
|
17
5
|
class StrategyNote {
|
|
18
6
|
constructor(key, value) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
__classPrivateFieldSet(this, _StrategyNote_key, key, "f");
|
|
22
|
-
__classPrivateFieldSet(this, _StrategyNote_value, value, "f");
|
|
7
|
+
this._key = key;
|
|
8
|
+
this._value = value;
|
|
23
9
|
}
|
|
24
10
|
key() {
|
|
25
|
-
return
|
|
11
|
+
return this._key;
|
|
26
12
|
}
|
|
27
13
|
value() {
|
|
28
|
-
return
|
|
14
|
+
return this._value;
|
|
29
15
|
}
|
|
30
16
|
}
|
|
31
17
|
exports.StrategyNote = StrategyNote;
|
|
32
|
-
_StrategyNote_key = new WeakMap(), _StrategyNote_value = new WeakMap();
|
|
33
18
|
const generateKey = (...items) => items
|
|
34
|
-
.map((item) => item instanceof DataObject_1.
|
|
19
|
+
.map((item) => item instanceof DataObject_1.DataObject
|
|
35
20
|
? item.id()
|
|
36
21
|
: typeof item === 'string'
|
|
37
22
|
? item
|
package/StrategyNote.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StrategyNote.js","sourceRoot":"","sources":["StrategyNote.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StrategyNote.js","sourceRoot":"","sources":["StrategyNote.ts"],"names":[],"mappings":";;;AAAA,uEAGgD;AAOhD,MAAa,YAAY;IAIvB,YAAY,GAAW,EAAE,KAAY;QACnC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAEM,GAAG;QACR,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAEM,KAAK;QACV,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AAhBD,oCAgBC;AAEM,MAAM,WAAW,GAEV,CAAC,GAAG,KAA2C,EAAE,EAAE,CAC/D,KAAK;KACF,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACZ,IAAI,YAAY,uBAAU;IACxB,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE;IACX,CAAC,CAAC,OAAO,IAAI,KAAK,QAAQ;QAC1B,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CACpB;KACA,IAAI,CAAC,GAAG,CAAC,CAAC;AAXF,QAAA,WAAW,eAWT;AAEf,kBAAe,YAAY,CAAC"}
|
package/StrategyNote.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
DataObject,
|
|
3
|
+
IDataObject,
|
|
4
|
+
} from '@civ-clone/core-data-object/DataObject';
|
|
6
5
|
|
|
7
6
|
export interface IStrategyNote<Value = any> {
|
|
8
7
|
key(): string;
|
|
@@ -10,26 +9,26 @@ export interface IStrategyNote<Value = any> {
|
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
export class StrategyNote<Value = any> implements IStrategyNote<Value> {
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
private _key: string;
|
|
13
|
+
private _value: Value;
|
|
15
14
|
|
|
16
15
|
constructor(key: string, value: Value) {
|
|
17
|
-
this
|
|
18
|
-
this
|
|
16
|
+
this._key = key;
|
|
17
|
+
this._value = value;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
public key(): string {
|
|
22
|
-
return this
|
|
21
|
+
return this._key;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
public value(): Value {
|
|
26
|
-
return this
|
|
25
|
+
return this._value;
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
export const generateKey: (
|
|
31
|
-
...items: (IDataObject | string)[]
|
|
32
|
-
) =>
|
|
29
|
+
export const generateKey: (
|
|
30
|
+
...items: (Pick<IDataObject, 'id'> | string)[]
|
|
31
|
+
) => string = (...items: (Pick<IDataObject, 'id'> | string)[]) =>
|
|
33
32
|
items
|
|
34
33
|
.map((item) =>
|
|
35
34
|
item instanceof DataObject
|
|
@@ -12,6 +12,7 @@ export declare class StrategyNoteRegistry
|
|
|
12
12
|
extends EntityRegistry<StrategyNote>
|
|
13
13
|
implements IStrategyNoteRegistry
|
|
14
14
|
{
|
|
15
|
+
constructor();
|
|
15
16
|
getByKey<Value = any>(key: string): StrategyNote<Value> | undefined;
|
|
16
17
|
getOrCreateByKey<Value>(key: string, value: Value): StrategyNote<Value>;
|
|
17
18
|
register(...entities: StrategyNote[]): void;
|
package/StrategyNoteRegistry.js
CHANGED
|
@@ -4,6 +4,9 @@ exports.instance = exports.StrategyNoteRegistry = void 0;
|
|
|
4
4
|
const EntityRegistry_1 = require("@civ-clone/core-registry/EntityRegistry");
|
|
5
5
|
const StrategyNote_1 = require("./StrategyNote");
|
|
6
6
|
class StrategyNoteRegistry extends EntityRegistry_1.EntityRegistry {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(StrategyNote_1.default);
|
|
9
|
+
}
|
|
7
10
|
getByKey(key) {
|
|
8
11
|
return this.getBy('key', key)[0];
|
|
9
12
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StrategyNoteRegistry.js","sourceRoot":"","sources":["StrategyNoteRegistry.ts"],"names":[],"mappings":";;;AAAA,4EAGiD;AACjD,iDAA0C;AAQ1C,MAAa,oBACX,SAAQ,+BAA4B;IAGpC,QAAQ,CAAc,GAAW;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,gBAAgB,CAAQ,GAAW,EAAE,KAAY;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAEpC,IAAI,QAAQ,EAAE;YACZ,OAAO,QAAQ,CAAC;SACjB;QAED,MAAM,IAAI,GAAG,IAAI,sBAAY,CAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QAEjD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,CAAC,GAAG,QAAwB;QAClC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC1B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE;gBAC/B,MAAM,IAAI,SAAS,CACjB,oBAAoB,MAAM,CAAC,GAAG,EAAE,mBAAmB,CACpD,CAAC;aACH;YAED,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,QAAwB;QACjC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;YAE7C,IAAI,QAAQ,EAAE;gBACZ,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;aAC3B;YAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"StrategyNoteRegistry.js","sourceRoot":"","sources":["StrategyNoteRegistry.ts"],"names":[],"mappings":";;;AAAA,4EAGiD;AACjD,iDAA0C;AAQ1C,MAAa,oBACX,SAAQ,+BAA4B;IAGpC;QACE,KAAK,CAAC,sBAAY,CAAC,CAAC;IACtB,CAAC;IAED,QAAQ,CAAc,GAAW;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,gBAAgB,CAAQ,GAAW,EAAE,KAAY;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAEpC,IAAI,QAAQ,EAAE;YACZ,OAAO,QAAQ,CAAC;SACjB;QAED,MAAM,IAAI,GAAG,IAAI,sBAAY,CAAQ,GAAG,EAAE,KAAK,CAAC,CAAC;QAEjD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAEpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,QAAQ,CAAC,GAAG,QAAwB;QAClC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC1B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE;gBAC/B,MAAM,IAAI,SAAS,CACjB,oBAAoB,MAAM,CAAC,GAAG,EAAE,mBAAmB,CACpD,CAAC;aACH;YAED,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,GAAG,QAAwB;QACjC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;YAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;YAE7C,IAAI,QAAQ,EAAE;gBACZ,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;aAC3B;YAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AAjDD,oDAiDC;AAEY,QAAA,QAAQ,GAAyB,IAAI,oBAAoB,EAAE,CAAC;AAEzE,kBAAe,oBAAoB,CAAC"}
|
package/StrategyNoteRegistry.ts
CHANGED
|
@@ -14,6 +14,10 @@ export class StrategyNoteRegistry
|
|
|
14
14
|
extends EntityRegistry<StrategyNote>
|
|
15
15
|
implements IStrategyNoteRegistry
|
|
16
16
|
{
|
|
17
|
+
constructor() {
|
|
18
|
+
super(StrategyNote);
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
getByKey<Value = any>(key: string): StrategyNote<Value> | undefined {
|
|
18
22
|
return this.getBy('key', key)[0];
|
|
19
23
|
}
|
package/StrategyRegistry.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EntityRegistry from '@civ-clone/core-registry/EntityRegistry';
|
|
2
|
-
import Strategy from './Strategy';
|
|
3
2
|
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
3
|
+
import Strategy from './Strategy';
|
|
4
4
|
export declare class StrategyRegistry extends EntityRegistry<Strategy> {
|
|
5
5
|
constructor();
|
|
6
6
|
/**
|
package/StrategyRegistry.js
CHANGED
|
@@ -13,8 +13,7 @@ class StrategyRegistry extends EntityRegistry_1.default {
|
|
|
13
13
|
*/
|
|
14
14
|
attempt(action) {
|
|
15
15
|
return this.entries()
|
|
16
|
-
.sort((a, b) => a.priority(action.
|
|
17
|
-
b.priority(action.player()).value() ||
|
|
16
|
+
.sort((a, b) => a.priority(action).value() - b.priority(action).value() ||
|
|
18
17
|
Math.floor(Math.random() * 3 - 1))
|
|
19
18
|
.some((strategy) => strategy.attempt(action));
|
|
20
19
|
}
|
package/StrategyRegistry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StrategyRegistry.js","sourceRoot":"","sources":["StrategyRegistry.ts"],"names":[],"mappings":";;;AAAA,4EAAqE;
|
|
1
|
+
{"version":3,"file":"StrategyRegistry.js","sourceRoot":"","sources":["StrategyRegistry.ts"],"names":[],"mappings":";;;AAAA,4EAAqE;AAErE,yCAAkC;AAElC,MAAa,gBAAiB,SAAQ,wBAAwB;IAC5D;QACE,KAAK,CAAC,kBAAQ,CAAC,CAAC;IAClB,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,MAAoB;QAC1B,OAAO,IAAI,CAAC,OAAO,EAAE;aAClB,IAAI,CACH,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE;YACvD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CACpC;aACA,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;CACF;AAlBD,4CAkBC;AAEY,QAAA,QAAQ,GAAG,IAAI,gBAAgB,EAAE,CAAC;AAE/C,kBAAe,gBAAgB,CAAC"}
|
package/StrategyRegistry.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EntityRegistry from '@civ-clone/core-registry/EntityRegistry';
|
|
2
|
-
import Strategy from './Strategy';
|
|
3
2
|
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
3
|
+
import Strategy from './Strategy';
|
|
4
4
|
|
|
5
5
|
export class StrategyRegistry extends EntityRegistry<Strategy> {
|
|
6
6
|
constructor() {
|
|
@@ -15,8 +15,7 @@ export class StrategyRegistry extends EntityRegistry<Strategy> {
|
|
|
15
15
|
return this.entries()
|
|
16
16
|
.sort(
|
|
17
17
|
(a, b) =>
|
|
18
|
-
a.priority(action.
|
|
19
|
-
b.priority(action.player()).value() ||
|
|
18
|
+
a.priority(action).value() - b.priority(action).value() ||
|
|
20
19
|
Math.floor(Math.random() * 3 - 1)
|
|
21
20
|
)
|
|
22
21
|
.some((strategy) => strategy.attempt(action));
|
package/package.json
CHANGED
package/tests/Strategy.test.ts
CHANGED
|
@@ -1,80 +1,142 @@
|
|
|
1
|
-
import { High, Normal } from '@civ-clone/core-rule/Priorities';
|
|
2
|
-
import {
|
|
1
|
+
import { High, Low, Normal } from '@civ-clone/core-rule/Priorities';
|
|
2
|
+
import { StrategyA, StrategyB, StrategyFalse } from './lib/Strategies';
|
|
3
3
|
import { expect, spy } from 'chai';
|
|
4
4
|
import Criterion from '@civ-clone/core-rule/Criterion';
|
|
5
5
|
import Effect from '@civ-clone/core-rule/Effect';
|
|
6
6
|
import Player from '@civ-clone/core-player/Player';
|
|
7
|
-
import { PlayerAction } from './lib/PlayerActions';
|
|
8
7
|
import Priority from '@civ-clone/core-rule/Priority';
|
|
9
8
|
import PriorityRule from '../Rules/Priority';
|
|
10
|
-
import Routine from '../Routine';
|
|
11
9
|
import RuleRegistry from '@civ-clone/core-rule/RuleRegistry';
|
|
12
10
|
import Strategy from '../Strategy';
|
|
11
|
+
import PlayerActionBase from '@civ-clone/core-player/PlayerAction';
|
|
12
|
+
import StrategyRegistry from '../StrategyRegistry';
|
|
13
|
+
import TraitRegistry from '@civ-clone/core-civilization/TraitRegistry';
|
|
14
|
+
import Civilization from '@civ-clone/core-civilization/Civilization';
|
|
15
|
+
import { TraitFull, TraitHalf, TraitNone } from './lib/Traits';
|
|
16
|
+
import Leader from '@civ-clone/core-civilization/Leader';
|
|
17
|
+
import Trait from '@civ-clone/core-civilization/Trait';
|
|
13
18
|
|
|
14
19
|
describe('Strategy', () => {
|
|
15
|
-
const
|
|
20
|
+
const action = new PlayerActionBase(new Player(), null);
|
|
16
21
|
|
|
17
22
|
it('should default to `Priority(Infinity)`', () =>
|
|
18
|
-
expect(new Strategy().priority(
|
|
19
|
-
|
|
20
|
-
it('should stop calling `Routine`s after the first successful `attempt()`', async () => {
|
|
21
|
-
const routineA = new RoutineFalse(),
|
|
22
|
-
routineB = new RoutineTrue(),
|
|
23
|
-
routineC = new RoutineTrue(),
|
|
24
|
-
strategy = new Strategy(routineA, routineB, routineC),
|
|
25
|
-
spyA = spy.on(routineA, 'attempt'),
|
|
26
|
-
spyB = spy.on(routineB, 'attempt'),
|
|
27
|
-
spyC = spy.on(routineC, 'attempt');
|
|
28
|
-
|
|
29
|
-
expect(await strategy.attempt(new PlayerAction(testPlayer, null))).true;
|
|
30
|
-
expect(spyA).called();
|
|
31
|
-
expect(spyB).called();
|
|
32
|
-
expect(spyC).not.called();
|
|
33
|
-
});
|
|
23
|
+
expect(new Strategy().priority(action).value()).eq(Infinity));
|
|
34
24
|
|
|
35
|
-
it('should respect `
|
|
25
|
+
it('should respect `Strategy` `Priority`s', async () => {
|
|
36
26
|
const ruleRegistry = new RuleRegistry(),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
spyA = spy.on(
|
|
41
|
-
spyB = spy.on(
|
|
27
|
+
strategyRegistry = new StrategyRegistry(),
|
|
28
|
+
strategyA = new StrategyA(ruleRegistry),
|
|
29
|
+
strategyB = new StrategyB(ruleRegistry),
|
|
30
|
+
spyA = spy.on(strategyA, 'attempt'),
|
|
31
|
+
spyB = spy.on(strategyB, 'attempt');
|
|
32
|
+
|
|
33
|
+
strategyRegistry.register(strategyA, strategyB);
|
|
42
34
|
|
|
43
35
|
ruleRegistry.register(
|
|
44
36
|
...(
|
|
45
37
|
[
|
|
46
|
-
[
|
|
47
|
-
[
|
|
48
|
-
] as [typeof
|
|
38
|
+
[StrategyA, High],
|
|
39
|
+
[StrategyB, Normal],
|
|
40
|
+
] as [typeof Strategy, typeof Priority][]
|
|
49
41
|
).map(
|
|
50
|
-
([
|
|
42
|
+
([StrategyType, PriorityType]) =>
|
|
51
43
|
new PriorityRule(
|
|
52
44
|
new Criterion(
|
|
53
|
-
(
|
|
54
|
-
|
|
45
|
+
(action: PlayerActionBase, strategy: Strategy) =>
|
|
46
|
+
strategy instanceof StrategyType
|
|
55
47
|
),
|
|
56
48
|
new Effect(() => new PriorityType())
|
|
57
49
|
)
|
|
58
50
|
)
|
|
59
51
|
);
|
|
60
52
|
|
|
61
|
-
expect(await
|
|
53
|
+
expect(await strategyRegistry.attempt(action)).true;
|
|
62
54
|
expect(spyA).called();
|
|
63
55
|
expect(spyB).not.called();
|
|
64
56
|
});
|
|
65
57
|
|
|
66
|
-
it('should return false if there are no successfully executed `
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
spyA = spy.on(
|
|
72
|
-
spyB = spy.on(
|
|
73
|
-
spyC = spy.on(
|
|
58
|
+
it('should return false if there are no successfully executed `Strategy`s', async () => {
|
|
59
|
+
const strategyA = new StrategyFalse(),
|
|
60
|
+
strategyB = new StrategyFalse(),
|
|
61
|
+
strategyC = new StrategyFalse(),
|
|
62
|
+
strategyRegistry = new StrategyRegistry(),
|
|
63
|
+
spyA = spy.on(strategyA, 'attempt'),
|
|
64
|
+
spyB = spy.on(strategyB, 'attempt'),
|
|
65
|
+
spyC = spy.on(strategyC, 'attempt');
|
|
74
66
|
|
|
75
|
-
|
|
67
|
+
strategyRegistry.register(strategyA, strategyB, strategyC);
|
|
68
|
+
|
|
69
|
+
expect(await strategyRegistry.attempt(action)).false;
|
|
76
70
|
expect(spyA).called();
|
|
77
71
|
expect(spyB).called();
|
|
78
72
|
expect(spyC).called();
|
|
79
73
|
});
|
|
74
|
+
|
|
75
|
+
it('should be possible to prioritise based on `Priority` `Rule`s', async () => {
|
|
76
|
+
const traitRegistry = new TraitRegistry(),
|
|
77
|
+
ruleRegistry = new RuleRegistry(),
|
|
78
|
+
civilization = new Civilization(),
|
|
79
|
+
traitNone = new TraitNone(Leader),
|
|
80
|
+
traitHalf = new TraitHalf(Leader),
|
|
81
|
+
traitFull = new TraitFull(Leader),
|
|
82
|
+
testPlayer = action.player(),
|
|
83
|
+
strategyA = new StrategyA(ruleRegistry),
|
|
84
|
+
strategyB = new StrategyB(ruleRegistry);
|
|
85
|
+
|
|
86
|
+
ruleRegistry.register(
|
|
87
|
+
...(
|
|
88
|
+
[
|
|
89
|
+
[StrategyA, TraitFull, High],
|
|
90
|
+
[StrategyA, TraitHalf, Normal],
|
|
91
|
+
[StrategyA, TraitNone, Low],
|
|
92
|
+
[StrategyB, TraitFull, Low],
|
|
93
|
+
[StrategyB, TraitHalf, Normal],
|
|
94
|
+
[StrategyB, TraitNone, High],
|
|
95
|
+
] as [typeof Strategy, typeof Trait, typeof Priority][]
|
|
96
|
+
).map(
|
|
97
|
+
([RoutineType, TraitType, PriorityType]) =>
|
|
98
|
+
new PriorityRule(
|
|
99
|
+
new Criterion(
|
|
100
|
+
(action: PlayerActionBase, strategy: Strategy) =>
|
|
101
|
+
strategy instanceof RoutineType
|
|
102
|
+
),
|
|
103
|
+
new Criterion(
|
|
104
|
+
(action: PlayerActionBase) =>
|
|
105
|
+
action.player().civilization().leader() !== null
|
|
106
|
+
),
|
|
107
|
+
new Criterion((action: PlayerActionBase) =>
|
|
108
|
+
traitRegistry
|
|
109
|
+
.getByLeader(
|
|
110
|
+
action
|
|
111
|
+
.player()
|
|
112
|
+
.civilization()
|
|
113
|
+
.leader()!
|
|
114
|
+
.sourceClass() as typeof Leader
|
|
115
|
+
)
|
|
116
|
+
.some((trait) => trait instanceof TraitType)
|
|
117
|
+
),
|
|
118
|
+
new Effect(() => new PriorityType())
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
testPlayer.setCivilization(civilization);
|
|
124
|
+
civilization.setLeader(new Leader());
|
|
125
|
+
traitRegistry.register(traitFull);
|
|
126
|
+
|
|
127
|
+
expect(strategyA.priority(action).value()).equal(new High().value());
|
|
128
|
+
expect(strategyB.priority(action).value()).equal(new Low().value());
|
|
129
|
+
|
|
130
|
+
traitRegistry.unregister(traitFull);
|
|
131
|
+
traitRegistry.register(traitHalf);
|
|
132
|
+
|
|
133
|
+
expect(strategyA.priority(action).value()).equal(new Normal().value());
|
|
134
|
+
expect(strategyB.priority(action).value()).equal(new Normal().value());
|
|
135
|
+
|
|
136
|
+
traitRegistry.unregister(traitHalf);
|
|
137
|
+
traitRegistry.register(traitNone);
|
|
138
|
+
|
|
139
|
+
expect(strategyA.priority(action).value()).equal(new Low().value());
|
|
140
|
+
expect(strategyB.priority(action).value()).equal(new High().value());
|
|
141
|
+
});
|
|
80
142
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StrategyFalse, StrategyTrue } from './lib/Strategies';
|
|
2
2
|
import { expect, spy, use } from 'chai';
|
|
3
3
|
import Player from '@civ-clone/core-player/Player';
|
|
4
4
|
import { PlayerAction } from './lib/PlayerActions';
|
|
@@ -12,8 +12,8 @@ describe('StrategyRegistry', () => {
|
|
|
12
12
|
const testPlayer = new Player();
|
|
13
13
|
|
|
14
14
|
it('should filter inactive `Strategy`s', async () => {
|
|
15
|
-
const strategyA = new
|
|
16
|
-
strategyB = new Strategy(
|
|
15
|
+
const strategyA = new StrategyTrue(),
|
|
16
|
+
strategyB = new Strategy(),
|
|
17
17
|
strategyRegistry = new StrategyRegistry(),
|
|
18
18
|
spyA = spy.on(strategyA, 'attempt'),
|
|
19
19
|
spyB = spy.on(strategyB, 'attempt');
|
|
@@ -27,8 +27,8 @@ describe('StrategyRegistry', () => {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
it('should stop calling `Strategy`s after the first successful `attempt()`', async () => {
|
|
30
|
-
const strategyA = new
|
|
31
|
-
strategyB = new
|
|
30
|
+
const strategyA = new StrategyTrue(),
|
|
31
|
+
strategyB = new StrategyFalse(),
|
|
32
32
|
strategyRegistry = new StrategyRegistry(),
|
|
33
33
|
spyA = spy.on(strategyA, 'attempt'),
|
|
34
34
|
spyB = spy.on(strategyB, 'attempt');
|
|
@@ -42,9 +42,8 @@ describe('StrategyRegistry', () => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
it('should respect `Strategy` `Priority`s', async () => {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
strategyB = new Strategy(routine),
|
|
45
|
+
const strategyA = new StrategyTrue(),
|
|
46
|
+
strategyB = new StrategyTrue(),
|
|
48
47
|
strategyRegistry = new StrategyRegistry(),
|
|
49
48
|
spyA = spy.on(strategyA, 'attempt'),
|
|
50
49
|
spyB = spy.on(strategyB, 'attempt');
|
|
@@ -58,9 +57,9 @@ describe('StrategyRegistry', () => {
|
|
|
58
57
|
});
|
|
59
58
|
|
|
60
59
|
it('should return false if there are no successfully executed `Strategy`s', async () => {
|
|
61
|
-
const strategyA = new
|
|
62
|
-
strategyB = new
|
|
63
|
-
strategyC = new
|
|
60
|
+
const strategyA = new StrategyFalse(),
|
|
61
|
+
strategyB = new StrategyFalse(),
|
|
62
|
+
strategyC = new StrategyFalse(),
|
|
64
63
|
strategyRegistry = new StrategyRegistry(),
|
|
65
64
|
spyA = spy.on(strategyA, 'attempt'),
|
|
66
65
|
spyB = spy.on(strategyB, 'attempt'),
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import Strategy from '../../Strategy';
|
|
2
|
+
export declare class StrategyTrue extends Strategy {
|
|
3
|
+
attempt(): boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare class StrategyFalse extends Strategy {
|
|
6
|
+
attempt(): boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class StrategyA extends StrategyTrue {}
|
|
9
|
+
export declare class StrategyB extends StrategyTrue {}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StrategyB = exports.StrategyA = exports.StrategyFalse = exports.StrategyTrue = void 0;
|
|
4
|
+
const Strategy_1 = require("../../Strategy");
|
|
5
|
+
class StrategyTrue extends Strategy_1.default {
|
|
6
|
+
attempt() {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.StrategyTrue = StrategyTrue;
|
|
11
|
+
class StrategyFalse extends Strategy_1.default {
|
|
12
|
+
attempt() {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.StrategyFalse = StrategyFalse;
|
|
17
|
+
class StrategyA extends StrategyTrue {
|
|
18
|
+
}
|
|
19
|
+
exports.StrategyA = StrategyA;
|
|
20
|
+
class StrategyB extends StrategyTrue {
|
|
21
|
+
}
|
|
22
|
+
exports.StrategyB = StrategyB;
|
|
23
|
+
//# sourceMappingURL=Strategies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Strategies.js","sourceRoot":"","sources":["Strategies.ts"],"names":[],"mappings":";;;AAAA,6CAAsC;AAEtC,MAAa,YAAa,SAAQ,kBAAQ;IACjC,OAAO;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAJD,oCAIC;AAED,MAAa,aAAc,SAAQ,kBAAQ;IAClC,OAAO;QACZ,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAJD,sCAIC;AAED,MAAa,SAAU,SAAQ,YAAY;CAAG;AAA9C,8BAA8C;AAE9C,MAAa,SAAU,SAAQ,YAAY;CAAG;AAA9C,8BAA8C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Strategy from '../../Strategy';
|
|
2
|
+
|
|
3
|
+
export class StrategyTrue extends Strategy {
|
|
4
|
+
public attempt(): boolean {
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class StrategyFalse extends Strategy {
|
|
10
|
+
public attempt(): boolean {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class StrategyA extends StrategyTrue {}
|
|
16
|
+
|
|
17
|
+
export class StrategyB extends StrategyTrue {}
|
package/tsconfig.json
CHANGED
package/Routine.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { RuleRegistry } from '@civ-clone/core-rule/RuleRegistry';
|
|
2
|
-
import Player from '@civ-clone/core-player/Player';
|
|
3
|
-
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
4
|
-
import Priority from '@civ-clone/core-rule/Priority';
|
|
5
|
-
export interface IRoutine {
|
|
6
|
-
attempt(action: PlayerAction): boolean;
|
|
7
|
-
priority(player: Player): Priority;
|
|
8
|
-
}
|
|
9
|
-
export declare class Routine implements IRoutine {
|
|
10
|
-
#private;
|
|
11
|
-
constructor(ruleRegistry?: RuleRegistry);
|
|
12
|
-
attempt(action: PlayerAction): boolean;
|
|
13
|
-
priority(player: Player): Priority;
|
|
14
|
-
}
|
|
15
|
-
export default Routine;
|
package/Routine.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
-
};
|
|
8
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
|
-
var _Routine_ruleRegistry;
|
|
14
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.Routine = void 0;
|
|
16
|
-
const RuleRegistry_1 = require("@civ-clone/core-rule/RuleRegistry");
|
|
17
|
-
const Priority_1 = require("@civ-clone/core-rule/Priority");
|
|
18
|
-
const Priority_2 = require("./Rules/Priority");
|
|
19
|
-
class Routine {
|
|
20
|
-
constructor(ruleRegistry = RuleRegistry_1.instance) {
|
|
21
|
-
_Routine_ruleRegistry.set(this, void 0);
|
|
22
|
-
__classPrivateFieldSet(this, _Routine_ruleRegistry, ruleRegistry, "f");
|
|
23
|
-
}
|
|
24
|
-
attempt(action) {
|
|
25
|
-
throw new TypeError('`attempt` must be overridden.');
|
|
26
|
-
}
|
|
27
|
-
priority(player) {
|
|
28
|
-
return new Priority_1.default(
|
|
29
|
-
// This takes the highest priority (lowest value) from all the applicable `PriorityRule`s
|
|
30
|
-
Math.min(...__classPrivateFieldGet(this, _Routine_ruleRegistry, "f")
|
|
31
|
-
.process(Priority_2.default, player, this)
|
|
32
|
-
.map((priority) => priority.value()), Infinity));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
exports.Routine = Routine;
|
|
36
|
-
_Routine_ruleRegistry = new WeakMap();
|
|
37
|
-
exports.default = Routine;
|
|
38
|
-
//# sourceMappingURL=Routine.js.map
|
package/Routine.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Routine.js","sourceRoot":"","sources":["Routine.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,oEAG2C;AAG3C,4DAAqD;AACrD,+CAA4C;AAO5C,MAAa,OAAO;IAGlB,YAAY,eAA6B,uBAAoB;QAF7D,wCAA4B;QAG1B,uBAAA,IAAI,yBAAiB,YAAY,MAAA,CAAC;IACpC,CAAC;IAED,OAAO,CAAC,MAAoB;QAC1B,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;IACvD,CAAC;IAED,QAAQ,CAAC,MAAc;QACrB,OAAO,IAAI,kBAAQ;QACjB,yFAAyF;QACzF,IAAI,CAAC,GAAG,CACN,GAAG,uBAAA,IAAI,6BAAc;aAClB,OAAO,CAAC,kBAAY,EAAE,MAAM,EAAE,IAAI,CAAC;aACnC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,EACtC,QAAQ,CACT,CACF,CAAC;IACJ,CAAC;CACF;AAtBD,0BAsBC;;AAED,kBAAe,OAAO,CAAC"}
|
package/Routine.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RuleRegistry,
|
|
3
|
-
instance as ruleRegistryInstance,
|
|
4
|
-
} from '@civ-clone/core-rule/RuleRegistry';
|
|
5
|
-
import Player from '@civ-clone/core-player/Player';
|
|
6
|
-
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
7
|
-
import Priority from '@civ-clone/core-rule/Priority';
|
|
8
|
-
import PriorityRule from './Rules/Priority';
|
|
9
|
-
|
|
10
|
-
export interface IRoutine {
|
|
11
|
-
attempt(action: PlayerAction): boolean;
|
|
12
|
-
priority(player: Player): Priority;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class Routine implements IRoutine {
|
|
16
|
-
#ruleRegistry: RuleRegistry;
|
|
17
|
-
|
|
18
|
-
constructor(ruleRegistry: RuleRegistry = ruleRegistryInstance) {
|
|
19
|
-
this.#ruleRegistry = ruleRegistry;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
attempt(action: PlayerAction): boolean {
|
|
23
|
-
throw new TypeError('`attempt` must be overridden.');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
priority(player: Player): Priority {
|
|
27
|
-
return new Priority(
|
|
28
|
-
// This takes the highest priority (lowest value) from all the applicable `PriorityRule`s
|
|
29
|
-
Math.min(
|
|
30
|
-
...this.#ruleRegistry
|
|
31
|
-
.process(PriorityRule, player, this)
|
|
32
|
-
.map((priority) => priority.value()),
|
|
33
|
-
Infinity
|
|
34
|
-
)
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export default Routine;
|
package/tests/Routine.test.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { High, Low, Normal } from '@civ-clone/core-rule/Priorities';
|
|
2
|
-
import { RoutineA, RoutineB } from './lib/Routines';
|
|
3
|
-
import { TraitFull, TraitHalf, TraitNone } from './lib/Traits';
|
|
4
|
-
import Civilization from '@civ-clone/core-civilization/Civilization';
|
|
5
|
-
import Criterion from '@civ-clone/core-rule/Criterion';
|
|
6
|
-
import Effect from '@civ-clone/core-rule/Effect';
|
|
7
|
-
import Leader from '@civ-clone/core-civilization/Leader';
|
|
8
|
-
import Player from '@civ-clone/core-player/Player';
|
|
9
|
-
import Priority from '@civ-clone/core-rule/Priority';
|
|
10
|
-
import PriorityRule from '../Rules/Priority';
|
|
11
|
-
import Routine from '../Routine';
|
|
12
|
-
import RuleRegistry from '@civ-clone/core-rule/RuleRegistry';
|
|
13
|
-
import Trait from '@civ-clone/core-civilization/Trait';
|
|
14
|
-
import TraitRegistry from '@civ-clone/core-civilization/TraitRegistry';
|
|
15
|
-
import { expect } from 'chai';
|
|
16
|
-
|
|
17
|
-
describe('Routine', () => {
|
|
18
|
-
const testPlayer = new Player();
|
|
19
|
-
|
|
20
|
-
it('should default to `Normal` `Priority`', () => {
|
|
21
|
-
const routine = new Routine();
|
|
22
|
-
|
|
23
|
-
expect(routine.priority(testPlayer).value()).equal(Infinity);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should be possible to prioritise based on `Priority` `Rule`s', async () => {
|
|
27
|
-
const traitRegistry = new TraitRegistry(),
|
|
28
|
-
ruleRegistry = new RuleRegistry(),
|
|
29
|
-
civilization = new Civilization(),
|
|
30
|
-
traitNone = new TraitNone(Leader),
|
|
31
|
-
traitHalf = new TraitHalf(Leader),
|
|
32
|
-
traitFull = new TraitFull(Leader),
|
|
33
|
-
routine = new RoutineA(ruleRegistry),
|
|
34
|
-
inverseRoutine = new RoutineB(ruleRegistry);
|
|
35
|
-
|
|
36
|
-
ruleRegistry.register(
|
|
37
|
-
...(
|
|
38
|
-
[
|
|
39
|
-
[RoutineA, TraitFull, High],
|
|
40
|
-
[RoutineA, TraitHalf, Normal],
|
|
41
|
-
[RoutineA, TraitNone, Low],
|
|
42
|
-
[RoutineB, TraitFull, Low],
|
|
43
|
-
[RoutineB, TraitHalf, Normal],
|
|
44
|
-
[RoutineB, TraitNone, High],
|
|
45
|
-
] as [typeof Routine, typeof Trait, typeof Priority][]
|
|
46
|
-
).map(
|
|
47
|
-
([RoutineType, TraitType, PriorityType]) =>
|
|
48
|
-
new PriorityRule(
|
|
49
|
-
new Criterion(
|
|
50
|
-
(player: Player, routine: Routine) =>
|
|
51
|
-
routine instanceof RoutineType
|
|
52
|
-
),
|
|
53
|
-
new Criterion(
|
|
54
|
-
(player: Player) => player.civilization().leader() !== null
|
|
55
|
-
),
|
|
56
|
-
new Criterion((player: Player) =>
|
|
57
|
-
traitRegistry
|
|
58
|
-
.getByLeader(
|
|
59
|
-
player.civilization().leader()!.sourceClass() as typeof Leader
|
|
60
|
-
)
|
|
61
|
-
.some((trait) => trait instanceof TraitType)
|
|
62
|
-
),
|
|
63
|
-
new Effect(() => new PriorityType())
|
|
64
|
-
)
|
|
65
|
-
)
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
testPlayer.setCivilization(civilization);
|
|
69
|
-
civilization.setLeader(new Leader());
|
|
70
|
-
traitRegistry.register(traitFull);
|
|
71
|
-
|
|
72
|
-
expect(routine.priority(testPlayer).value()).equal(new High().value());
|
|
73
|
-
expect(inverseRoutine.priority(testPlayer).value()).equal(
|
|
74
|
-
new Low().value()
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
traitRegistry.unregister(traitFull);
|
|
78
|
-
traitRegistry.register(traitHalf);
|
|
79
|
-
|
|
80
|
-
expect(routine.priority(testPlayer).value()).equal(new Normal().value());
|
|
81
|
-
expect(inverseRoutine.priority(testPlayer).value()).equal(
|
|
82
|
-
new Normal().value()
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
traitRegistry.unregister(traitHalf);
|
|
86
|
-
traitRegistry.register(traitNone);
|
|
87
|
-
|
|
88
|
-
expect(routine.priority(testPlayer).value()).equal(new Low().value());
|
|
89
|
-
expect(inverseRoutine.priority(testPlayer).value()).equal(
|
|
90
|
-
new High().value()
|
|
91
|
-
);
|
|
92
|
-
});
|
|
93
|
-
});
|
package/tests/lib/Routines.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import Routine from '../../Routine';
|
|
2
|
-
export declare class RoutineTrue extends Routine {
|
|
3
|
-
attempt(): boolean;
|
|
4
|
-
}
|
|
5
|
-
export declare class RoutineFalse extends Routine {
|
|
6
|
-
attempt(): boolean;
|
|
7
|
-
}
|
|
8
|
-
export declare class RoutineA extends RoutineTrue {}
|
|
9
|
-
export declare class RoutineB extends RoutineTrue {}
|
package/tests/lib/Routines.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RoutineB = exports.RoutineA = exports.RoutineFalse = exports.RoutineTrue = void 0;
|
|
4
|
-
const Routine_1 = require("../../Routine");
|
|
5
|
-
class RoutineTrue extends Routine_1.default {
|
|
6
|
-
attempt() {
|
|
7
|
-
return true;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
exports.RoutineTrue = RoutineTrue;
|
|
11
|
-
class RoutineFalse extends Routine_1.default {
|
|
12
|
-
attempt() {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
exports.RoutineFalse = RoutineFalse;
|
|
17
|
-
class RoutineA extends RoutineTrue {
|
|
18
|
-
}
|
|
19
|
-
exports.RoutineA = RoutineA;
|
|
20
|
-
class RoutineB extends RoutineTrue {
|
|
21
|
-
}
|
|
22
|
-
exports.RoutineB = RoutineB;
|
|
23
|
-
//# sourceMappingURL=Routines.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Routines.js","sourceRoot":"","sources":["Routines.ts"],"names":[],"mappings":";;;AAAA,2CAAoC;AAEpC,MAAa,WAAY,SAAQ,iBAAO;IAC/B,OAAO;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAJD,kCAIC;AAED,MAAa,YAAa,SAAQ,iBAAO;IAChC,OAAO;QACZ,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAJD,oCAIC;AAED,MAAa,QAAS,SAAQ,WAAW;CAAG;AAA5C,4BAA4C;AAE5C,MAAa,QAAS,SAAQ,WAAW;CAAG;AAA5C,4BAA4C"}
|
package/tests/lib/Routines.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import Routine from '../../Routine';
|
|
2
|
-
|
|
3
|
-
export class RoutineTrue extends Routine {
|
|
4
|
-
public attempt(): boolean {
|
|
5
|
-
return true;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class RoutineFalse extends Routine {
|
|
10
|
-
public attempt(): boolean {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class RoutineA extends RoutineTrue {}
|
|
16
|
-
|
|
17
|
-
export class RoutineB extends RoutineTrue {}
|