mana-scribe 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -7
- package/README.md +120 -120
- package/package.json +41 -41
- package/src/arrayComparison.js +50 -50
- package/src/costs/ActivationCost.js +33 -33
- package/src/costs/Cost.js +50 -48
- package/src/costs/ManaCost.js +53 -53
- package/src/customErrors.js +20 -20
- package/src/index.js +26 -26
- package/src/services/SymbolRegistry.js +80 -80
- package/src/services/regExpService.js +12 -12
- package/src/symbolOrder.js +9 -0
- package/src/symbols/ColoredManaSymbol.js +20 -20
- package/src/symbols/ColoredPhyrexianManaSymbol.js +20 -20
- package/src/symbols/ColorlessManaSymbol.js +20 -20
- package/src/symbols/ColorlessPhyrexianManaSymbol.js +12 -12
- package/src/symbols/ExtraSymbol.js +12 -12
- package/src/symbols/FiveColorHybridManaSymbol.js +12 -12
- package/src/symbols/FourColorHybridManaSymbol.js +12 -12
- package/src/symbols/GenericHybridManaSymbol.js +24 -24
- package/src/symbols/GenericManaSymbol.js +16 -16
- package/src/symbols/HalfManaSymbol.js +23 -23
- package/src/symbols/HybridManaSymbol.js +25 -25
- package/src/symbols/HybridPhyrexianManaSymbol.js +16 -16
- package/src/symbols/InfiniteManaSymbol.js +16 -16
- package/src/symbols/NonManaSymbol.js +11 -11
- package/src/symbols/Symbol.js +53 -48
- package/src/symbols/TapSymbol.js +12 -12
- package/src/symbols/ThreeColorHybridManaSymbol.js +12 -12
- package/src/symbols/TwoColorHybridManaSymbol.js +12 -12
- package/src/symbols/TypedManaSymbol.js +21 -21
- package/src/symbols/UntapSymbol.js +12 -12
- package/src/symbols/VariableManaSymbol.js +16 -16
package/LICENSE
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Copyright 2025 Colin A
|
|
2
|
-
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
-
|
|
5
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
-
|
|
7
|
-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1
|
+
Copyright 2025 Colin A
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
# mana-scribe
|
|
2
|
-
[](https://github.com/matortheeternal/mana-scribe/actions/workflows/tests.yml) [](https://codecov.io/github/matortheeternal/mana-scribe)
|
|
3
|
-
|
|
4
|
-
Mana Scribe is a utility for working with Magic: The Gathering mana costs and activation costs.
|
|
5
|
-
|
|
6
|
-
Supports both brace `{3}{R/U}` and shortform `3R/U` notation.
|
|
7
|
-
|
|
8
|
-
## Features
|
|
9
|
-
|
|
10
|
-
- Parse MTG mana costs into structured objects
|
|
11
|
-
- Supports all major symbols: generic, colored, 2-5 color hybrid, phyrexian, generic hybrid, phyrexian hybrid, snow, energy, tap/untap, variable
|
|
12
|
-
- Works with both Scryfall braces and shortform notation
|
|
13
|
-
- Compute:
|
|
14
|
-
- Converted mana cost
|
|
15
|
-
- Color identity
|
|
16
|
-
- Devotion
|
|
17
|
-
- Compare mana costs (equality, greater than, less than)
|
|
18
|
-
- Easily add custom colors, types of mana, or other extra symbols
|
|
19
|
-
|
|
20
|
-
Note: When the parser encounters an unrecognized symbol it stops parsing and returns the symbols it parsed so far. You can access the unparsed string component through the property `remainingStr`.
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install mana-scribe
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Usage
|
|
29
|
-
```js
|
|
30
|
-
import { ManaCost } from 'mana-scribe';
|
|
31
|
-
|
|
32
|
-
const cost = ManaCost.parse('{3}{R}{R}{R}');
|
|
33
|
-
console.log(cost.cmc); // 6
|
|
34
|
-
console.log(cost.colors); // ['R']
|
|
35
|
-
console.log(cost.getDevotionTo('R')); // 3
|
|
36
|
-
|
|
37
|
-
console.log(cost.toString(true)); // "{3}{R}{R}{R}"
|
|
38
|
-
console.log(cost.toString(false)); // "3RRR"
|
|
39
|
-
```
|
|
40
|
-
### Shortform example
|
|
41
|
-
```js
|
|
42
|
-
const cost = ManaCost.parse('2WU/B');
|
|
43
|
-
console.log(cost.cmc); // 4
|
|
44
|
-
console.log(cost.colors); // ['W','U','B']
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Comparison example
|
|
48
|
-
The `ManaCost` class supports equality and comparison operators under superset semantics:
|
|
49
|
-
|
|
50
|
-
- `equals(other)` → true if the costs are exactly the same
|
|
51
|
-
- `greaterThan(other)` → true if this cost includes all symbols of other plus additional ones, or generic mana cost is higher.
|
|
52
|
-
- `lessThan(other)` → true if this cost is a strict subset of other, or generic mana cost is lower.
|
|
53
|
-
|
|
54
|
-
```js
|
|
55
|
-
const a = ManaCost.parse('{3}{B}{B}{B}');
|
|
56
|
-
const b = ManaCost.parse('{1}{B}{B}{B}');
|
|
57
|
-
|
|
58
|
-
console.log(a.equals(b)); // false
|
|
59
|
-
console.log(a.greaterThan(b)); // true
|
|
60
|
-
console.log(b.lessThan(a)); // true
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
### Activation costs
|
|
65
|
-
`ActivationCost` offers the same functionality as the `ManaCost` class, but supports additional symbols such as Tap, Untap, and Energy. It does not have comparison functions.
|
|
66
|
-
|
|
67
|
-
```js
|
|
68
|
-
import { ActivationCost } from 'mana-scribe';
|
|
69
|
-
|
|
70
|
-
const cost = ActivationCost.parse('{1}{G}{T}');
|
|
71
|
-
console.log(cost.symbols.map(s => s.type)); // ["genericMana", "coloredMana", "tap"]
|
|
72
|
-
console.log(cost.toString(true)); // "{1}{G}{T}"
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Extending
|
|
76
|
-
|
|
77
|
-
### Custom colors
|
|
78
|
-
|
|
79
|
-
You can add custom colors of mana with `symbolRegistry.addColor`.
|
|
80
|
-
|
|
81
|
-
```js
|
|
82
|
-
import { symbolRegistry, ManaCost } from 'mana-scribe';
|
|
83
|
-
|
|
84
|
-
symbolRegistry.addColor({ id: 'P', name: 'Purple' });
|
|
85
|
-
const cost = ManaCost.parse('{3}{R/P}{P}');
|
|
86
|
-
console.log(cost.symbols.map(s => s.type)); // ["genericMana", "twoColorHybridMana", "coloredMana"]
|
|
87
|
-
console.log(cost.cmc); // 5
|
|
88
|
-
console.log(cost.colors); // ['R','P']
|
|
89
|
-
console.log(cost.getDevotionTo('P')); // 2
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Custom mana types
|
|
93
|
-
|
|
94
|
-
You can add custom types of mana with `symbolRegistry.addManaType`. This is for mana produced by specific sources, like snow mana.
|
|
95
|
-
|
|
96
|
-
```js
|
|
97
|
-
import { symbolRegistry, ActivationCost } from 'mana-scribe';
|
|
98
|
-
|
|
99
|
-
symbolRegistry.addManaType({ id: 'A', name: 'Artificial' }); // mana produced by an artifact
|
|
100
|
-
const cost = ActivationCost.parse('{A}{A}{T}');
|
|
101
|
-
console.log(cost.symbols.map(s => s.type)); // ["typedMana", "typedMana", "tap"]
|
|
102
|
-
console.log(cost.colors); // []
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
### Extra symbols
|
|
106
|
-
|
|
107
|
-
You can add additional symbols for use in activation costs with `symbolRegistry.addExtraSym`. This is used for things like the energy symbol.
|
|
108
|
-
|
|
109
|
-
```js
|
|
110
|
-
import { symbolRegistry, ActivationCost } from 'mana-scribe';
|
|
111
|
-
|
|
112
|
-
symbolRegistry.addManaType({ id: '\\@', name: 'Chaos' });
|
|
113
|
-
const cost = ActivationCost.parse('{@}');
|
|
114
|
-
console.log(cost.symbols.map(s => s.type)); // ["extra"]
|
|
115
|
-
console.log(cost.colors); // []
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
## License
|
|
119
|
-
|
|
120
|
-
This project is licensed under the MIT License. See LICENSE for more info.
|
|
1
|
+
# mana-scribe
|
|
2
|
+
[](https://github.com/matortheeternal/mana-scribe/actions/workflows/tests.yml) [](https://codecov.io/github/matortheeternal/mana-scribe)
|
|
3
|
+
|
|
4
|
+
Mana Scribe is a utility for working with Magic: The Gathering mana costs and activation costs.
|
|
5
|
+
|
|
6
|
+
Supports both brace `{3}{R/U}` and shortform `3R/U` notation.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Parse MTG mana costs into structured objects
|
|
11
|
+
- Supports all major symbols: generic, colored, 2-5 color hybrid, phyrexian, generic hybrid, phyrexian hybrid, snow, energy, tap/untap, variable
|
|
12
|
+
- Works with both Scryfall braces and shortform notation
|
|
13
|
+
- Compute:
|
|
14
|
+
- Converted mana cost
|
|
15
|
+
- Color identity
|
|
16
|
+
- Devotion
|
|
17
|
+
- Compare mana costs (equality, greater than, less than)
|
|
18
|
+
- Easily add custom colors, types of mana, or other extra symbols
|
|
19
|
+
|
|
20
|
+
Note: When the parser encounters an unrecognized symbol it stops parsing and returns the symbols it parsed so far. You can access the unparsed string component through the property `remainingStr`.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install mana-scribe
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
```js
|
|
30
|
+
import { ManaCost } from 'mana-scribe';
|
|
31
|
+
|
|
32
|
+
const cost = ManaCost.parse('{3}{R}{R}{R}');
|
|
33
|
+
console.log(cost.cmc); // 6
|
|
34
|
+
console.log(cost.colors); // ['R']
|
|
35
|
+
console.log(cost.getDevotionTo('R')); // 3
|
|
36
|
+
|
|
37
|
+
console.log(cost.toString(true)); // "{3}{R}{R}{R}"
|
|
38
|
+
console.log(cost.toString(false)); // "3RRR"
|
|
39
|
+
```
|
|
40
|
+
### Shortform example
|
|
41
|
+
```js
|
|
42
|
+
const cost = ManaCost.parse('2WU/B');
|
|
43
|
+
console.log(cost.cmc); // 4
|
|
44
|
+
console.log(cost.colors); // ['W','U','B']
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Comparison example
|
|
48
|
+
The `ManaCost` class supports equality and comparison operators under superset semantics:
|
|
49
|
+
|
|
50
|
+
- `equals(other)` → true if the costs are exactly the same
|
|
51
|
+
- `greaterThan(other)` → true if this cost includes all symbols of other plus additional ones, or generic mana cost is higher.
|
|
52
|
+
- `lessThan(other)` → true if this cost is a strict subset of other, or generic mana cost is lower.
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
const a = ManaCost.parse('{3}{B}{B}{B}');
|
|
56
|
+
const b = ManaCost.parse('{1}{B}{B}{B}');
|
|
57
|
+
|
|
58
|
+
console.log(a.equals(b)); // false
|
|
59
|
+
console.log(a.greaterThan(b)); // true
|
|
60
|
+
console.log(b.lessThan(a)); // true
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
### Activation costs
|
|
65
|
+
`ActivationCost` offers the same functionality as the `ManaCost` class, but supports additional symbols such as Tap, Untap, and Energy. It does not have comparison functions.
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
import { ActivationCost } from 'mana-scribe';
|
|
69
|
+
|
|
70
|
+
const cost = ActivationCost.parse('{1}{G}{T}');
|
|
71
|
+
console.log(cost.symbols.map(s => s.type)); // ["genericMana", "coloredMana", "tap"]
|
|
72
|
+
console.log(cost.toString(true)); // "{1}{G}{T}"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Extending
|
|
76
|
+
|
|
77
|
+
### Custom colors
|
|
78
|
+
|
|
79
|
+
You can add custom colors of mana with `symbolRegistry.addColor`.
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
import { symbolRegistry, ManaCost } from 'mana-scribe';
|
|
83
|
+
|
|
84
|
+
symbolRegistry.addColor({ id: 'P', name: 'Purple' });
|
|
85
|
+
const cost = ManaCost.parse('{3}{R/P}{P}');
|
|
86
|
+
console.log(cost.symbols.map(s => s.type)); // ["genericMana", "twoColorHybridMana", "coloredMana"]
|
|
87
|
+
console.log(cost.cmc); // 5
|
|
88
|
+
console.log(cost.colors); // ['R','P']
|
|
89
|
+
console.log(cost.getDevotionTo('P')); // 2
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Custom mana types
|
|
93
|
+
|
|
94
|
+
You can add custom types of mana with `symbolRegistry.addManaType`. This is for mana produced by specific sources, like snow mana.
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import { symbolRegistry, ActivationCost } from 'mana-scribe';
|
|
98
|
+
|
|
99
|
+
symbolRegistry.addManaType({ id: 'A', name: 'Artificial' }); // mana produced by an artifact
|
|
100
|
+
const cost = ActivationCost.parse('{A}{A}{T}');
|
|
101
|
+
console.log(cost.symbols.map(s => s.type)); // ["typedMana", "typedMana", "tap"]
|
|
102
|
+
console.log(cost.colors); // []
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Extra symbols
|
|
106
|
+
|
|
107
|
+
You can add additional symbols for use in activation costs with `symbolRegistry.addExtraSym`. This is used for things like the energy symbol.
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
import { symbolRegistry, ActivationCost } from 'mana-scribe';
|
|
111
|
+
|
|
112
|
+
symbolRegistry.addManaType({ id: '\\@', name: 'Chaos' });
|
|
113
|
+
const cost = ActivationCost.parse('{@}');
|
|
114
|
+
console.log(cost.symbols.map(s => s.type)); // ["extra"]
|
|
115
|
+
console.log(cost.colors); // []
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
This project is licensed under the MIT License. See LICENSE for more info.
|
package/package.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "mana-scribe",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Library for parsing and querying Magic: The Gathering mana costs — with support for CMC, devotion, and color identity.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"mtg",
|
|
7
|
-
"magic the gathering",
|
|
8
|
-
"mana",
|
|
9
|
-
"cost",
|
|
10
|
-
"parser"
|
|
11
|
-
],
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/matortheeternal/mana-scribe/issues"
|
|
14
|
-
},
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/matortheeternal/mana-scribe.git"
|
|
18
|
-
},
|
|
19
|
-
"license": "MIT",
|
|
20
|
-
"author": "mator",
|
|
21
|
-
"type": "module",
|
|
22
|
-
"main": "./src/index.js",
|
|
23
|
-
"exports": {
|
|
24
|
-
".": "./src/index.js"
|
|
25
|
-
},
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"c8": "^10.1.3",
|
|
28
|
-
"jasmine": "^5.12.0"
|
|
29
|
-
},
|
|
30
|
-
"scripts": {
|
|
31
|
-
"test": "jasmine --config=jasmine.json",
|
|
32
|
-
"debug:test": "node --inspect-brk node_modules/jasmine/bin/jasmine.js --config=jasmine.json",
|
|
33
|
-
"coverage": "c8 npm test"
|
|
34
|
-
},
|
|
35
|
-
"files": [
|
|
36
|
-
"src/",
|
|
37
|
-
"README.md",
|
|
38
|
-
"LICENSE",
|
|
39
|
-
"package.json"
|
|
40
|
-
]
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "mana-scribe",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "Library for parsing and querying Magic: The Gathering mana costs — with support for CMC, devotion, and color identity.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mtg",
|
|
7
|
+
"magic the gathering",
|
|
8
|
+
"mana",
|
|
9
|
+
"cost",
|
|
10
|
+
"parser"
|
|
11
|
+
],
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/matortheeternal/mana-scribe/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/matortheeternal/mana-scribe.git"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "mator",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"main": "./src/index.js",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./src/index.js"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"c8": "^10.1.3",
|
|
28
|
+
"jasmine": "^5.12.0"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "jasmine --config=jasmine.json",
|
|
32
|
+
"debug:test": "node --inspect-brk node_modules/jasmine/bin/jasmine.js --config=jasmine.json",
|
|
33
|
+
"coverage": "c8 npm test"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"src/",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE",
|
|
39
|
+
"package.json"
|
|
40
|
+
]
|
|
41
|
+
}
|
package/src/arrayComparison.js
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
function toFrequencyMap(arr) {
|
|
2
|
-
const map = new Map();
|
|
3
|
-
for (const el of arr) {
|
|
4
|
-
if (el.type === 'genericMana' || el.type === 'infiniteMana') {
|
|
5
|
-
map.set('generic', (map.get('generic') || 0) + el.cmcValue());
|
|
6
|
-
} else {
|
|
7
|
-
const key = el.toString();
|
|
8
|
-
map.set(key, (map.get(key) || 0) + 1);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return map;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function arrayEquals(arr1, arr2) {
|
|
15
|
-
const m1 = toFrequencyMap(arr1);
|
|
16
|
-
const m2 = toFrequencyMap(arr2);
|
|
17
|
-
|
|
18
|
-
if (m1.size !== m2.size) return false;
|
|
19
|
-
for (const [key, count] of m1.entries()) {
|
|
20
|
-
if ((m2.get(key) || 0) !== count) return false;
|
|
21
|
-
}
|
|
22
|
-
return true;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function arrayGreaterThan(arr1, arr2) {
|
|
26
|
-
const m1 = toFrequencyMap(arr1);
|
|
27
|
-
const m2 = toFrequencyMap(arr2);
|
|
28
|
-
|
|
29
|
-
let larger = false;
|
|
30
|
-
for (const [key, count] of m2.entries()) {
|
|
31
|
-
if ((m1.get(key) || 0) < count) return false;
|
|
32
|
-
larger = larger || m1.get(key) > count;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return larger || m1.size > m2.size;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function arrayLessThan(arr1, arr2) {
|
|
39
|
-
const m1 = toFrequencyMap(arr1);
|
|
40
|
-
const m2 = toFrequencyMap(arr2);
|
|
41
|
-
|
|
42
|
-
let smaller = false;
|
|
43
|
-
for (const [key, count] of m1.entries()) {
|
|
44
|
-
if ((m2.get(key) || 0) < count) return false;
|
|
45
|
-
smaller = smaller || (count < (m2.get(key) || 0));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return smaller || m1.size < m2.size;
|
|
49
|
-
}
|
|
50
|
-
|
|
1
|
+
function toFrequencyMap(arr) {
|
|
2
|
+
const map = new Map();
|
|
3
|
+
for (const el of arr) {
|
|
4
|
+
if (el.type === 'genericMana' || el.type === 'infiniteMana') {
|
|
5
|
+
map.set('generic', (map.get('generic') || 0) + el.cmcValue());
|
|
6
|
+
} else {
|
|
7
|
+
const key = el.toString();
|
|
8
|
+
map.set(key, (map.get(key) || 0) + 1);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return map;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function arrayEquals(arr1, arr2) {
|
|
15
|
+
const m1 = toFrequencyMap(arr1);
|
|
16
|
+
const m2 = toFrequencyMap(arr2);
|
|
17
|
+
|
|
18
|
+
if (m1.size !== m2.size) return false;
|
|
19
|
+
for (const [key, count] of m1.entries()) {
|
|
20
|
+
if ((m2.get(key) || 0) !== count) return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function arrayGreaterThan(arr1, arr2) {
|
|
26
|
+
const m1 = toFrequencyMap(arr1);
|
|
27
|
+
const m2 = toFrequencyMap(arr2);
|
|
28
|
+
|
|
29
|
+
let larger = false;
|
|
30
|
+
for (const [key, count] of m2.entries()) {
|
|
31
|
+
if ((m1.get(key) || 0) < count) return false;
|
|
32
|
+
larger = larger || m1.get(key) > count;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return larger || m1.size > m2.size;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function arrayLessThan(arr1, arr2) {
|
|
39
|
+
const m1 = toFrequencyMap(arr1);
|
|
40
|
+
const m2 = toFrequencyMap(arr2);
|
|
41
|
+
|
|
42
|
+
let smaller = false;
|
|
43
|
+
for (const [key, count] of m1.entries()) {
|
|
44
|
+
if ((m2.get(key) || 0) < count) return false;
|
|
45
|
+
smaller = smaller || (count < (m2.get(key) || 0));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return smaller || m1.size < m2.size;
|
|
49
|
+
}
|
|
50
|
+
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import Cost from './Cost.js';
|
|
2
|
-
import ColoredManaSymbol from '../symbols/ColoredManaSymbol.js';
|
|
3
|
-
import ColoredPhyrexianManaSymbol from '../symbols/ColoredPhyrexianManaSymbol.js';
|
|
4
|
-
import ColorlessManaSymbol from '../symbols/ColorlessManaSymbol.js';
|
|
5
|
-
import ColorlessPhyrexianManaSymbol from '../symbols/ColorlessPhyrexianManaSymbol.js';
|
|
6
|
-
import ExtraSymbol from '../symbols/ExtraSymbol.js';
|
|
7
|
-
import FiveColorHybridManaSymbol from '../symbols/FiveColorHybridManaSymbol.js';
|
|
8
|
-
import FourColorHybridManaSymbol from '../symbols/FourColorHybridManaSymbol.js';
|
|
9
|
-
import GenericHybridManaSymbol from '../symbols/GenericHybridManaSymbol.js';
|
|
10
|
-
import GenericManaSymbol from '../symbols/GenericManaSymbol.js';
|
|
11
|
-
import HalfManaSymbol from '../symbols/HalfManaSymbol.js';
|
|
12
|
-
import HybridPhyrexianManaSymbol from '../symbols/HybridPhyrexianManaSymbol.js';
|
|
13
|
-
import InfiniteManaSymbol from '../symbols/InfiniteManaSymbol.js';
|
|
14
|
-
import TypedManaSymbol from '../symbols/TypedManaSymbol.js';
|
|
15
|
-
import TapSymbol from '../symbols/TapSymbol.js';
|
|
16
|
-
import ThreeColorHybridManaSymbol from '../symbols/ThreeColorHybridManaSymbol.js';
|
|
17
|
-
import TwoColorHybridManaSymbol from '../symbols/TwoColorHybridManaSymbol.js';
|
|
18
|
-
import UntapSymbol from '../symbols/UntapSymbol.js';
|
|
19
|
-
import VariableManaSymbol from '../symbols/VariableManaSymbol.js';
|
|
20
|
-
|
|
21
|
-
export default class ActivationCost extends Cost {
|
|
22
|
-
static get allowedSymbols() {
|
|
23
|
-
return [
|
|
24
|
-
HybridPhyrexianManaSymbol, ColoredPhyrexianManaSymbol,
|
|
25
|
-
GenericHybridManaSymbol, FiveColorHybridManaSymbol,
|
|
26
|
-
FourColorHybridManaSymbol, ThreeColorHybridManaSymbol,
|
|
27
|
-
TwoColorHybridManaSymbol, HalfManaSymbol, ColorlessPhyrexianManaSymbol,
|
|
28
|
-
GenericManaSymbol, VariableManaSymbol, InfiniteManaSymbol,
|
|
29
|
-
TypedManaSymbol, ColoredManaSymbol, ColorlessManaSymbol,
|
|
30
|
-
ExtraSymbol, TapSymbol, UntapSymbol,
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
import Cost from './Cost.js';
|
|
2
|
+
import ColoredManaSymbol from '../symbols/ColoredManaSymbol.js';
|
|
3
|
+
import ColoredPhyrexianManaSymbol from '../symbols/ColoredPhyrexianManaSymbol.js';
|
|
4
|
+
import ColorlessManaSymbol from '../symbols/ColorlessManaSymbol.js';
|
|
5
|
+
import ColorlessPhyrexianManaSymbol from '../symbols/ColorlessPhyrexianManaSymbol.js';
|
|
6
|
+
import ExtraSymbol from '../symbols/ExtraSymbol.js';
|
|
7
|
+
import FiveColorHybridManaSymbol from '../symbols/FiveColorHybridManaSymbol.js';
|
|
8
|
+
import FourColorHybridManaSymbol from '../symbols/FourColorHybridManaSymbol.js';
|
|
9
|
+
import GenericHybridManaSymbol from '../symbols/GenericHybridManaSymbol.js';
|
|
10
|
+
import GenericManaSymbol from '../symbols/GenericManaSymbol.js';
|
|
11
|
+
import HalfManaSymbol from '../symbols/HalfManaSymbol.js';
|
|
12
|
+
import HybridPhyrexianManaSymbol from '../symbols/HybridPhyrexianManaSymbol.js';
|
|
13
|
+
import InfiniteManaSymbol from '../symbols/InfiniteManaSymbol.js';
|
|
14
|
+
import TypedManaSymbol from '../symbols/TypedManaSymbol.js';
|
|
15
|
+
import TapSymbol from '../symbols/TapSymbol.js';
|
|
16
|
+
import ThreeColorHybridManaSymbol from '../symbols/ThreeColorHybridManaSymbol.js';
|
|
17
|
+
import TwoColorHybridManaSymbol from '../symbols/TwoColorHybridManaSymbol.js';
|
|
18
|
+
import UntapSymbol from '../symbols/UntapSymbol.js';
|
|
19
|
+
import VariableManaSymbol from '../symbols/VariableManaSymbol.js';
|
|
20
|
+
|
|
21
|
+
export default class ActivationCost extends Cost {
|
|
22
|
+
static get allowedSymbols() {
|
|
23
|
+
return [
|
|
24
|
+
HybridPhyrexianManaSymbol, ColoredPhyrexianManaSymbol,
|
|
25
|
+
GenericHybridManaSymbol, FiveColorHybridManaSymbol,
|
|
26
|
+
FourColorHybridManaSymbol, ThreeColorHybridManaSymbol,
|
|
27
|
+
TwoColorHybridManaSymbol, HalfManaSymbol, ColorlessPhyrexianManaSymbol,
|
|
28
|
+
GenericManaSymbol, VariableManaSymbol, InfiniteManaSymbol,
|
|
29
|
+
TypedManaSymbol, ColoredManaSymbol, ColorlessManaSymbol,
|
|
30
|
+
ExtraSymbol, TapSymbol, UntapSymbol,
|
|
31
|
+
];
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/costs/Cost.js
CHANGED
|
@@ -1,48 +1,50 @@
|
|
|
1
|
-
import { NotImplementedError } from '../customErrors.js';
|
|
2
|
-
|
|
3
|
-
export default class Cost {
|
|
4
|
-
static get allowedSymbols() {
|
|
5
|
-
throw new NotImplementedError('allowedSymbols');
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
static parse(str) {
|
|
9
|
-
const cost = new this();
|
|
10
|
-
cost.parseSymbols(str);
|
|
11
|
-
return cost;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
constructor(symbols = []) {
|
|
15
|
-
this.symbols = symbols;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
get colors() {
|
|
19
|
-
const set = new Set(this.symbols.flatMap(sym => sym.colors));
|
|
20
|
-
return [...set];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
parseSymbols(str) {
|
|
24
|
-
let remainingStr = str.trim();
|
|
25
|
-
while (remainingStr.length) {
|
|
26
|
-
const symbol = this.parseNextSymbol(remainingStr);
|
|
27
|
-
if (!symbol) break;
|
|
28
|
-
symbol.apply(this.symbols);
|
|
29
|
-
if (remainingStr === symbol.remainingStr) break;
|
|
30
|
-
remainingStr = symbol.remainingStr;
|
|
31
|
-
}
|
|
32
|
-
this.remainingStr = remainingStr;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
parseNextSymbol(str) {
|
|
36
|
-
for (const symbol of this.constructor.allowedSymbols) {
|
|
37
|
-
const match = symbol.match(str);
|
|
38
|
-
if (!match) continue;
|
|
39
|
-
return symbol.parse(match, str);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
toString(useBraces = false) {
|
|
44
|
-
return this.symbols.
|
|
45
|
-
return
|
|
46
|
-
}).
|
|
47
|
-
|
|
48
|
-
}
|
|
1
|
+
import { NotImplementedError } from '../customErrors.js';
|
|
2
|
+
|
|
3
|
+
export default class Cost {
|
|
4
|
+
static get allowedSymbols() {
|
|
5
|
+
throw new NotImplementedError('allowedSymbols');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
static parse(str) {
|
|
9
|
+
const cost = new this();
|
|
10
|
+
cost.parseSymbols(str);
|
|
11
|
+
return cost;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
constructor(symbols = []) {
|
|
15
|
+
this.symbols = symbols;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get colors() {
|
|
19
|
+
const set = new Set(this.symbols.flatMap(sym => sym.colors));
|
|
20
|
+
return [...set];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
parseSymbols(str) {
|
|
24
|
+
let remainingStr = str.trim();
|
|
25
|
+
while (remainingStr.length) {
|
|
26
|
+
const symbol = this.parseNextSymbol(remainingStr);
|
|
27
|
+
if (!symbol) break;
|
|
28
|
+
symbol.apply(this.symbols);
|
|
29
|
+
if (remainingStr === symbol.remainingStr) break;
|
|
30
|
+
remainingStr = symbol.remainingStr;
|
|
31
|
+
}
|
|
32
|
+
this.remainingStr = remainingStr;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
parseNextSymbol(str) {
|
|
36
|
+
for (const symbol of this.constructor.allowedSymbols) {
|
|
37
|
+
const match = symbol.match(str);
|
|
38
|
+
if (!match) continue;
|
|
39
|
+
return symbol.parse(match, str);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
toString(useBraces = false) {
|
|
44
|
+
return this.symbols.sort((a, b) => {
|
|
45
|
+
return a.sortIndex - b.sortIndex;
|
|
46
|
+
}).map(sym => {
|
|
47
|
+
return sym.toString(useBraces);
|
|
48
|
+
}).join('');
|
|
49
|
+
}
|
|
50
|
+
}
|