@civ-clone/core-strategy 0.1.2 → 0.1.4
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/StrategyRegistry.d.ts +2 -1
- package/StrategyRegistry.js +4 -2
- package/StrategyRegistry.js.map +1 -1
- package/StrategyRegistry.ts +7 -2
- package/package.json +2 -1
- package/tests/StrategyRegistry.test.ts +53 -35
package/StrategyRegistry.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ import EntityRegistry from '@civ-clone/core-registry/EntityRegistry';
|
|
|
2
2
|
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
3
3
|
import Strategy from './Strategy';
|
|
4
4
|
export declare class StrategyRegistry extends EntityRegistry<Strategy> {
|
|
5
|
-
|
|
5
|
+
private _randomNumberGenerator;
|
|
6
|
+
constructor(randomNumberGenerator?: () => number);
|
|
6
7
|
/**
|
|
7
8
|
* Iterates all `Strategy`s ordered first by `Priority`, then by a random number so that alternative strategies are
|
|
8
9
|
* tried.
|
package/StrategyRegistry.js
CHANGED
|
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.instance = exports.StrategyRegistry = void 0;
|
|
4
4
|
const EntityRegistry_1 = require("@civ-clone/core-registry/EntityRegistry");
|
|
5
5
|
const Strategy_1 = require("./Strategy");
|
|
6
|
+
const core_random_1 = require("@civ-clone/core-random");
|
|
6
7
|
class StrategyRegistry extends EntityRegistry_1.default {
|
|
7
|
-
constructor() {
|
|
8
|
+
constructor(randomNumberGenerator = core_random_1.instance) {
|
|
8
9
|
super(Strategy_1.default);
|
|
10
|
+
this._randomNumberGenerator = randomNumberGenerator;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Iterates all `Strategy`s ordered first by `Priority`, then by a random number so that alternative strategies are
|
|
@@ -14,7 +16,7 @@ class StrategyRegistry extends EntityRegistry_1.default {
|
|
|
14
16
|
attempt(action) {
|
|
15
17
|
return this.entries()
|
|
16
18
|
.sort((a, b) => a.priority(action).value() - b.priority(action).value() ||
|
|
17
|
-
Math.floor(
|
|
19
|
+
Math.floor(this._randomNumberGenerator() * 3 - 1))
|
|
18
20
|
.some((strategy) => strategy.attempt(action));
|
|
19
21
|
}
|
|
20
22
|
}
|
package/StrategyRegistry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StrategyRegistry.js","sourceRoot":"","sources":["StrategyRegistry.ts"],"names":[],"mappings":";;;AAAA,4EAAqE;AAErE,yCAAkC;
|
|
1
|
+
{"version":3,"file":"StrategyRegistry.js","sourceRoot":"","sources":["StrategyRegistry.ts"],"names":[],"mappings":";;;AAAA,4EAAqE;AAErE,yCAAkC;AAClC,wDAAiE;AAEjE,MAAa,gBAAiB,SAAQ,wBAAwB;IAG5D,YAAY,wBAAsC,sBAAW;QAC3D,KAAK,CAAC,kBAAQ,CAAC,CAAC;QAEhB,IAAI,CAAC,sBAAsB,GAAG,qBAAqB,CAAC;IACtD,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,sBAAsB,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CACpD;aACA,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;CACF;AAtBD,4CAsBC;AAEY,QAAA,QAAQ,GAAG,IAAI,gBAAgB,EAAE,CAAC;AAE/C,kBAAe,gBAAgB,CAAC"}
|
package/StrategyRegistry.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import EntityRegistry from '@civ-clone/core-registry/EntityRegistry';
|
|
2
2
|
import PlayerAction from '@civ-clone/core-player/PlayerAction';
|
|
3
3
|
import Strategy from './Strategy';
|
|
4
|
+
import { instance as rngInstance } from '@civ-clone/core-random';
|
|
4
5
|
|
|
5
6
|
export class StrategyRegistry extends EntityRegistry<Strategy> {
|
|
6
|
-
|
|
7
|
+
private _randomNumberGenerator: () => number;
|
|
8
|
+
|
|
9
|
+
constructor(randomNumberGenerator: () => number = rngInstance) {
|
|
7
10
|
super(Strategy);
|
|
11
|
+
|
|
12
|
+
this._randomNumberGenerator = randomNumberGenerator;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
/**
|
|
@@ -16,7 +21,7 @@ export class StrategyRegistry extends EntityRegistry<Strategy> {
|
|
|
16
21
|
.sort(
|
|
17
22
|
(a, b) =>
|
|
18
23
|
a.priority(action).value() - b.priority(action).value() ||
|
|
19
|
-
Math.floor(
|
|
24
|
+
Math.floor(this._randomNumberGenerator() * 3 - 1)
|
|
20
25
|
)
|
|
21
26
|
.some((strategy) => strategy.attempt(action));
|
|
22
27
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@civ-clone/core-strategy",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"repository": "git@github.com:civ-clone/core-strategy.git",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@civ-clone/core-civilization": "^0.1.0",
|
|
35
35
|
"@civ-clone/core-data-object": "^0.1.0",
|
|
36
36
|
"@civ-clone/core-player": "^0.1.2",
|
|
37
|
+
"@civ-clone/core-random": "^0.1.0",
|
|
37
38
|
"@civ-clone/core-registry": "^0.1.0",
|
|
38
39
|
"@civ-clone/core-rule": "^0.1.1"
|
|
39
40
|
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { StrategyFalse, StrategyTrue } from './lib/Strategies';
|
|
2
2
|
import { expect, spy, use } from 'chai';
|
|
3
|
+
import Criterion from '@civ-clone/core-rule/Criterion';
|
|
4
|
+
import Effect from '@civ-clone/core-rule/Effect';
|
|
3
5
|
import Player from '@civ-clone/core-player/Player';
|
|
4
6
|
import { PlayerAction } from './lib/PlayerActions';
|
|
7
|
+
import PriorityRule from '../Rules/Priority';
|
|
8
|
+
import PriorityValue from '@civ-clone/core-rule/Priority';
|
|
9
|
+
import RuleRegistry from '@civ-clone/core-rule/RuleRegistry';
|
|
5
10
|
import Strategy from '../Strategy';
|
|
6
11
|
import StrategyRegistry from '../StrategyRegistry';
|
|
7
12
|
import * as spies from 'chai-spies';
|
|
@@ -11,49 +16,63 @@ use(spies);
|
|
|
11
16
|
describe('StrategyRegistry', () => {
|
|
12
17
|
const testPlayer = new Player();
|
|
13
18
|
|
|
14
|
-
it('should
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
it('should stop calling `Strategy`s after the first successful `attempt()`', async () => {
|
|
20
|
+
// `A` is given the higher priority so that it definitely runs first.
|
|
21
|
+
// Equal priorities are ordered by a random draw, which would make "the
|
|
22
|
+
// second one was not reached" true only some of the time.
|
|
23
|
+
const ruleRegistry = new RuleRegistry(),
|
|
24
|
+
strategyA = new StrategyTrue(ruleRegistry),
|
|
25
|
+
strategyB = new StrategyTrue(ruleRegistry),
|
|
26
|
+
strategyRegistry = new StrategyRegistry();
|
|
22
27
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
ruleRegistry.register(
|
|
29
|
+
new PriorityRule(
|
|
30
|
+
new Criterion(
|
|
31
|
+
(action: PlayerAction, strategy: Strategy): boolean =>
|
|
32
|
+
strategy === strategyA
|
|
33
|
+
),
|
|
34
|
+
new Effect((): PriorityValue => new PriorityValue(1))
|
|
35
|
+
)
|
|
36
|
+
);
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
const strategyA = new StrategyTrue(),
|
|
31
|
-
strategyB = new StrategyFalse(),
|
|
32
|
-
strategyRegistry = new StrategyRegistry(),
|
|
33
|
-
spyA = spy.on(strategyA, 'attempt'),
|
|
38
|
+
const spyA = spy.on(strategyA, 'attempt'),
|
|
34
39
|
spyB = spy.on(strategyB, 'attempt');
|
|
35
40
|
|
|
36
41
|
strategyRegistry.register(strategyA, strategyB);
|
|
37
42
|
|
|
38
|
-
expect(
|
|
39
|
-
|
|
40
|
-
expect(
|
|
41
|
-
expect(spyB).not.called;
|
|
43
|
+
expect(strategyRegistry.attempt(new PlayerAction(testPlayer, null))).true;
|
|
44
|
+
expect(spyA).to.have.been.called();
|
|
45
|
+
expect(spyB).to.not.have.been.called();
|
|
42
46
|
});
|
|
43
47
|
|
|
44
48
|
it('should respect `Strategy` `Priority`s', async () => {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
const ruleRegistry = new RuleRegistry(),
|
|
50
|
+
strategyA = new StrategyTrue(ruleRegistry),
|
|
51
|
+
strategyB = new StrategyTrue(ruleRegistry),
|
|
52
|
+
strategyRegistry = new StrategyRegistry();
|
|
53
|
+
|
|
54
|
+
// Without this the two are tied and the order is a coin flip. Giving `B`
|
|
55
|
+
// the lower value — which is the higher priority — is the whole point of
|
|
56
|
+
// the test, and it is what makes the assertion below meaningful rather
|
|
57
|
+
// than true three times in four.
|
|
58
|
+
ruleRegistry.register(
|
|
59
|
+
new PriorityRule(
|
|
60
|
+
new Criterion(
|
|
61
|
+
(action: PlayerAction, strategy: Strategy): boolean =>
|
|
62
|
+
strategy === strategyB
|
|
63
|
+
),
|
|
64
|
+
new Effect((): PriorityValue => new PriorityValue(1))
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const spyA = spy.on(strategyA, 'attempt'),
|
|
49
69
|
spyB = spy.on(strategyB, 'attempt');
|
|
50
70
|
|
|
51
71
|
strategyRegistry.register(strategyA, strategyB);
|
|
52
72
|
|
|
53
|
-
expect(
|
|
54
|
-
|
|
55
|
-
expect(spyA).not.called;
|
|
56
|
-
expect(spyB).called;
|
|
73
|
+
expect(strategyRegistry.attempt(new PlayerAction(testPlayer, null))).true;
|
|
74
|
+
expect(spyB).to.have.been.called();
|
|
75
|
+
expect(spyA).to.not.have.been.called();
|
|
57
76
|
});
|
|
58
77
|
|
|
59
78
|
it('should return false if there are no successfully executed `Strategy`s', async () => {
|
|
@@ -67,10 +86,9 @@ describe('StrategyRegistry', () => {
|
|
|
67
86
|
|
|
68
87
|
strategyRegistry.register(strategyA, strategyB, strategyC);
|
|
69
88
|
|
|
70
|
-
expect(
|
|
71
|
-
|
|
72
|
-
expect(
|
|
73
|
-
expect(
|
|
74
|
-
expect(spyC).called;
|
|
89
|
+
expect(strategyRegistry.attempt(new PlayerAction(testPlayer, null))).false;
|
|
90
|
+
expect(spyA).to.have.been.called();
|
|
91
|
+
expect(spyB).to.have.been.called();
|
|
92
|
+
expect(spyC).to.have.been.called();
|
|
75
93
|
});
|
|
76
94
|
});
|