mana-scribe 1.0.2 → 2.0.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/README.md CHANGED
@@ -1,20 +1,21 @@
1
1
  # mana-scribe
2
2
  [![Tests](https://github.com/matortheeternal/mana-scribe/actions/workflows/tests.yml/badge.svg)](https://github.com/matortheeternal/mana-scribe/actions/workflows/tests.yml) [![codecov](https://codecov.io/github/matortheeternal/mana-scribe/graph/badge.svg?token=Z81O4KMEOH)](https://codecov.io/github/matortheeternal/mana-scribe)
3
3
 
4
- Mana Scribe is a utility for working with Magic: The Gathering mana costs and activation costs.
4
+ Mana Scribe is a utility for working with Magic: The Gathering mana costs and activation costs.
5
5
 
6
6
  Supports both brace `{3}{R/U}` and shortform `3R/U` notation.
7
7
 
8
8
  ## Features
9
9
 
10
10
  - Parse MTG mana costs into structured objects
11
- - Supports all major symbols: generic, colored, hybrid, phyrexian, snow, energy, tap/untap, variable
11
+ - Supports all major symbols: generic, colored, 2-5 color hybrid, phyrexian, generic hybrid, phyrexian hybrid, snow, energy, tap/untap, variable
12
12
  - Works with both Scryfall braces and shortform notation
13
13
  - Compute:
14
14
  - Converted mana cost
15
15
  - Color identity
16
16
  - Devotion
17
- - Extensible design add new symbol types by subclassing
17
+ - Compare mana costs (equality, greater than, less than)
18
+ - Easily add custom colors, types of mana, or other extra symbols
18
19
 
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`.
20
21
 
@@ -44,35 +45,76 @@ console.log(cost.cmc); // 3
44
45
  console.log(cost.colors); // ['W','U','B']
45
46
  ```
46
47
 
47
- ## Activation costs
48
- `ActivationCost` offers the same functionality as the `ManaCost` class, but supports additional symbols such as Tap, Untap, and Energy.
48
+ ### Comparison example
49
+ The `ManaCost` class supports equality and comparison operators under superset semantics:
50
+
51
+ - `equals(other)` → true if the costs are exactly the same
52
+ - `greaterThan(other)` → true if this cost includes all symbols of other plus additional ones, or generic mana cost is higher.
53
+ - `lessThan(other)` → true if this cost is a strict subset of other, or generic mana cost is lower.
54
+
55
+ ```js
56
+ const a = ManaCost.parse('{3}{B}{B}{B}');
57
+ const b = ManaCost.parse('{1}{B}{B}{B}');
58
+
59
+ console.log(a.equals(b)); // false
60
+ console.log(a.greaterThan(b)); // true
61
+ console.log(b.lessThan(a)); // true
62
+ ```
63
+
64
+
65
+ ### Activation costs
66
+ `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.
49
67
 
50
68
  ```js
51
69
  import { ActivationCost } from 'mana-scribe';
52
70
 
53
71
  const cost = ActivationCost.parse('{1}{G}{T}');
54
- console.log(cost.symbols.map(s => s.type)); // ["generic", "colored", "tap"]
72
+ console.log(cost.symbols.map(s => s.type)); // ["genericMana", "coloredMana", "tap"]
55
73
  console.log(cost.toString(true)); // "{1}{G}{T}"
56
74
  ```
57
75
 
58
76
  ## Extending
59
77
 
60
- The library is class-based. Each symbol type is a subclass of a base Symbol class and implements:
78
+ ### Custom colors
79
+
80
+ You can add custom colors of mana with `symbolRegistry.addColor`.
81
+
82
+ ```js
83
+ import { symbolRegistry, ManaCost } from 'mana-scribe';
84
+
85
+ symbolRegistry.addColor({ id: 'P', name: 'Purple' });
86
+ const cost = ManaCost.parse('{3}{R/P}{P}');
87
+ console.log(cost.symbols.map(s => s.type)); // ["genericMana", "twoColorHybridMana", "coloredMana"]
88
+ console.log(cost.cmc); // 5
89
+ console.log(cost.colors); // ['R','P']
90
+ console.log(cost.getDevotionTo('P')); // 2
91
+ ```
61
92
 
62
- - `static match(str)` → returns a regex match object if it applies
63
- - `get colors()` → returns an array of the colors associated with the symbol
64
- - `cmcValue()` → returns an integer corresponding to how much this symbol contributes to a card's overall converted mana cost.
93
+ ### Custom mana types
65
94
 
66
- This makes it easy to add custom symbols or patch existing ones in your own project.
95
+ You can add custom types of mana with `symbolRegistry.addManaType`. This is for mana produced by specific sources, like snow mana.
67
96
 
68
- ## Project Status
97
+ ```js
98
+ import { symbolRegistry, ActivationCost } from 'mana-scribe';
99
+
100
+ symbolRegistry.addManaType({ id: 'A', name: 'Artificial' }); // mana produced by an artifact
101
+ const cost = ActivationCost.parse('{A}{A}{T}');
102
+ console.log(cost.symbols.map(s => s.type)); // ["typedMana", "typedMana", "tap"]
103
+ console.log(cost.colors); // []
104
+ ```
69
105
 
70
- This is an early but complete implementation.
106
+ ### Extra symbols
71
107
 
72
- Current priorities:
73
- - [x] Support core MTG symbols
74
- - [x] Add test coverage
75
- - [ ] Other improvements? TBD
108
+ You can add additional symbols for use in activation costs with `symbolRegistry.addExtraSym`. This is used for things like the energy symbol.
109
+
110
+ ```js
111
+ import { symbolRegistry, ActivationCost } from 'mana-scribe';
112
+
113
+ symbolRegistry.addManaType({ id: '\\@', name: 'Chaos' });
114
+ const cost = ActivationCost.parse('{@}');
115
+ console.log(cost.symbols.map(s => s.type)); // ["extra"]
116
+ console.log(cost.colors); // []
117
+ ```
76
118
 
77
119
  ## License
78
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mana-scribe",
3
- "version": "1.0.2",
3
+ "version": "2.0.0",
4
4
  "description": "Library for parsing and querying Magic: The Gathering mana costs — with support for CMC, devotion, and color identity.",
5
5
  "keywords": [
6
6
  "mtg",
@@ -29,6 +29,7 @@
29
29
  },
30
30
  "scripts": {
31
31
  "test": "jasmine --config=jasmine.json",
32
+ "debug:test": "node --inspect-brk node_modules/jasmine/bin/jasmine.js --config=jasmine.json",
32
33
  "coverage": "c8 npm test"
33
34
  },
34
35
  "files": [
@@ -0,0 +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
+
@@ -3,14 +3,15 @@ import ColoredManaSymbol from '../symbols/ColoredManaSymbol.js';
3
3
  import ColoredPhyrexianManaSymbol from '../symbols/ColoredPhyrexianManaSymbol.js';
4
4
  import ColorlessManaSymbol from '../symbols/ColorlessManaSymbol.js';
5
5
  import ColorlessPhyrexianManaSymbol from '../symbols/ColorlessPhyrexianManaSymbol.js';
6
- import EnergySymbol from '../symbols/EnergySymbol.js';
6
+ import ExtraSymbol from '../symbols/ExtraSymbol.js';
7
7
  import FiveColorHybridManaSymbol from '../symbols/FiveColorHybridManaSymbol.js';
8
8
  import FourColorHybridManaSymbol from '../symbols/FourColorHybridManaSymbol.js';
9
9
  import GenericHybridManaSymbol from '../symbols/GenericHybridManaSymbol.js';
10
10
  import GenericManaSymbol from '../symbols/GenericManaSymbol.js';
11
- import HalfColoredManaSymbol from '../symbols/HalfColoredManaSymbol.js';
11
+ import HalfManaSymbol from '../symbols/HalfManaSymbol.js';
12
+ import HybridPhyrexianManaSymbol from '../symbols/HybridPhyrexianManaSymbol.js';
12
13
  import InfiniteManaSymbol from '../symbols/InfiniteManaSymbol.js';
13
- import SnowManaSymbol from '../symbols/SnowManaSymbol.js';
14
+ import TypedManaSymbol from '../symbols/TypedManaSymbol.js';
14
15
  import TapSymbol from '../symbols/TapSymbol.js';
15
16
  import ThreeColorHybridManaSymbol from '../symbols/ThreeColorHybridManaSymbol.js';
16
17
  import TwoColorHybridManaSymbol from '../symbols/TwoColorHybridManaSymbol.js';
@@ -20,13 +21,13 @@ import VariableManaSymbol from '../symbols/VariableManaSymbol.js';
20
21
  export default class ActivationCost extends Cost {
21
22
  static get allowedSymbols() {
22
23
  return [
23
- ColoredPhyrexianManaSymbol, GenericHybridManaSymbol, FiveColorHybridManaSymbol,
24
- FourColorHybridManaSymbol, ThreeColorHybridManaSymbol, TwoColorHybridManaSymbol,
25
- ColorlessPhyrexianManaSymbol, HalfColoredManaSymbol,
26
- GenericManaSymbol, VariableManaSymbol,
27
- InfiniteManaSymbol, SnowManaSymbol,
28
- ColoredManaSymbol, ColorlessManaSymbol,
29
- EnergySymbol, TapSymbol, UntapSymbol,
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,
30
31
  ];
31
32
  }
32
33
  }
@@ -7,22 +7,25 @@ import FiveColorHybridManaSymbol from '../symbols/FiveColorHybridManaSymbol.js';
7
7
  import FourColorHybridManaSymbol from '../symbols/FourColorHybridManaSymbol.js';
8
8
  import GenericHybridManaSymbol from '../symbols/GenericHybridManaSymbol.js';
9
9
  import GenericManaSymbol from '../symbols/GenericManaSymbol.js';
10
- import HalfColoredManaSymbol from '../symbols/HalfColoredManaSymbol.js';
10
+ import HalfManaSymbol from '../symbols/HalfManaSymbol.js';
11
11
  import InfiniteManaSymbol from '../symbols/InfiniteManaSymbol.js';
12
- import SnowManaSymbol from '../symbols/SnowManaSymbol.js';
12
+ import TypedManaSymbol from '../symbols/TypedManaSymbol.js';
13
13
  import ThreeColorHybridManaSymbol from '../symbols/ThreeColorHybridManaSymbol.js';
14
14
  import TwoColorHybridManaSymbol from '../symbols/TwoColorHybridManaSymbol.js';
15
+ import HybridPhyrexianManaSymbol from '../symbols/HybridPhyrexianManaSymbol.js';
15
16
  import VariableManaSymbol from '../symbols/VariableManaSymbol.js';
17
+ import { arrayEquals, arrayGreaterThan, arrayLessThan } from '../arrayComparison.js';
16
18
 
17
19
  export default class ManaCost extends Cost {
18
20
  static get allowedSymbols() {
19
21
  return [
20
- ColoredPhyrexianManaSymbol, GenericHybridManaSymbol, FiveColorHybridManaSymbol,
21
- FourColorHybridManaSymbol, ThreeColorHybridManaSymbol, TwoColorHybridManaSymbol,
22
- ColorlessPhyrexianManaSymbol, HalfColoredManaSymbol,
23
- GenericManaSymbol, VariableManaSymbol,
24
- InfiniteManaSymbol, SnowManaSymbol,
25
- ColoredManaSymbol, ColorlessManaSymbol
22
+ HybridPhyrexianManaSymbol, ColoredPhyrexianManaSymbol,
23
+ GenericHybridManaSymbol, FiveColorHybridManaSymbol,
24
+ FourColorHybridManaSymbol, ThreeColorHybridManaSymbol,
25
+ TwoColorHybridManaSymbol, HalfManaSymbol,
26
+ ColorlessPhyrexianManaSymbol,
27
+ GenericManaSymbol, VariableManaSymbol, InfiniteManaSymbol,
28
+ TypedManaSymbol, ColoredManaSymbol, ColorlessManaSymbol
26
29
  ];
27
30
  }
28
31
 
@@ -35,4 +38,16 @@ export default class ManaCost extends Cost {
35
38
  return devotion + (sym.colors.includes(color) ? 1 : 0);
36
39
  }, 0);
37
40
  }
41
+
42
+ equals(other) {
43
+ return arrayEquals(this.symbols, other.symbols);
44
+ }
45
+
46
+ greaterThan(other) {
47
+ return arrayGreaterThan(this.symbols, other.symbols);
48
+ }
49
+
50
+ lessThan(other) {
51
+ return arrayLessThan(this.symbols, other.symbols);
52
+ }
38
53
  }
@@ -4,3 +4,17 @@ export class NotImplementedError extends Error {
4
4
  this.name = 'NotImplementedError';
5
5
  }
6
6
  }
7
+
8
+ export class SchemaError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = 'SchemaError';
12
+ }
13
+ }
14
+
15
+ export class AlreadyRegisteredError extends Error {
16
+ constructor(label, id) {
17
+ super(`${label} with id "${id}" already registered`);
18
+ this.name = 'AlreadyRegisteredError';
19
+ }
20
+ }
package/src/index.js CHANGED
@@ -1,4 +1,26 @@
1
1
  import ActivationCost from './costs/ActivationCost.js';
2
2
  import ManaCost from './costs/ManaCost.js';
3
+ import symbolRegistry from './services/SymbolRegistry.js';
3
4
 
4
- export { ActivationCost, ManaCost };
5
+ const baseColors = [
6
+ { id: 'W', name: 'White' },
7
+ { id: 'U', name: 'Blue' },
8
+ { id: 'B', name: 'Black' },
9
+ { id: 'R', name: 'Red' },
10
+ { id: 'G', name: 'Green' },
11
+ ];
12
+
13
+ const baseTypes = [
14
+ { id: 'S', name: 'Snow', },
15
+ { id: 'L', name: 'Legendary' },
16
+ ];
17
+
18
+ const extraSymbols = [
19
+ { id: 'E', name: 'Energy' }
20
+ ];
21
+
22
+ baseColors.forEach(color => symbolRegistry.addColor(color));
23
+ baseTypes.forEach(type => symbolRegistry.addManaType(type));
24
+ extraSymbols.forEach(type => symbolRegistry.addExtraSym(type));
25
+
26
+ export { ActivationCost, ManaCost, symbolRegistry };
@@ -0,0 +1,80 @@
1
+ import { AlreadyRegisteredError, SchemaError } from '../customErrors.js';
2
+
3
+ const ReservedIds = {
4
+ H: 'Phyrexian',
5
+ I: 'Infinite Mana',
6
+ Q: 'Untap',
7
+ T: 'Tap',
8
+ X: 'Variable Mana',
9
+ Y: 'Variable Mana',
10
+ Z: 'Variable Mana',
11
+ };
12
+
13
+ function isEscapedChar(str) {
14
+ return str.length === 2 && str[0] === '\\';
15
+ }
16
+
17
+ function validateSchema(item, label) {
18
+ if (!item.id || item.id.constructor !== String)
19
+ throw new SchemaError(`${label} must have a string property "id"`);
20
+ if (item.id.length !== 1 && !isEscapedChar(item.id))
21
+ throw new SchemaError(`${label} property "id" length must be 1`);
22
+ if (ReservedIds.hasOwnProperty(item.id))
23
+ throw new SchemaError(
24
+ `${label} id "${item.id}" is not allowed (used for ${ReservedIds[item.id]})`
25
+ );
26
+ if (!item.name || item.name.constructor !== String)
27
+ throw new SchemaError(`${label} must have a string property "name"`);
28
+ }
29
+
30
+ class SymbolRegistry {
31
+ constructor() {
32
+ this.colors = new Map();
33
+ this.manaTypes = new Map();
34
+ this.extraSymbols = new Map();
35
+ }
36
+
37
+ checkIfRegistered(key) {
38
+ if (this.colors.has(key))
39
+ throw new AlreadyRegisteredError('Color', key);
40
+ if (this.manaTypes.has(key))
41
+ throw new AlreadyRegisteredError('ManaType', key);
42
+ if (this.extraSymbols.has(key))
43
+ throw new AlreadyRegisteredError('ExtraSym', key);
44
+ }
45
+
46
+ addColor(item) {
47
+ validateSchema(item, 'Color');
48
+ const key = item.id;
49
+ this.checkIfRegistered(key);
50
+ this.colors.set(key, item);
51
+ }
52
+
53
+ addManaType(item) {
54
+ validateSchema(item, 'ManaType');
55
+ const key = item.id;
56
+ this.checkIfRegistered(key);
57
+ this.manaTypes.set(key, item);
58
+ }
59
+
60
+ addExtraSym(item) {
61
+ validateSchema(item, 'ExtraSym');
62
+ const key = item.id;
63
+ this.checkIfRegistered(key);
64
+ this.extraSymbols.set(key, item);
65
+ }
66
+
67
+ get colorKeys() {
68
+ return [...this.colors.keys()];
69
+ }
70
+
71
+ get manaTypeKeys() {
72
+ return [...this.manaTypes.keys()];
73
+ }
74
+
75
+ get extraSymKeys() {
76
+ return [...this.extraSymbols.keys()];
77
+ }
78
+ }
79
+
80
+ export default new SymbolRegistry();
@@ -0,0 +1,12 @@
1
+ import symbolRegistry from './SymbolRegistry.js';
2
+
3
+ export function msr(str) {
4
+ const colorKeys = symbolRegistry.colorKeys.join('');
5
+ const mTypeKeys = symbolRegistry.manaTypeKeys.join('');
6
+ const extraKeys = symbolRegistry.extraSymKeys.join('');
7
+ const pstr = str.raw[0]
8
+ .replaceAll('\\c', '[' + colorKeys + ']')
9
+ .replaceAll('\\T', '[' + mTypeKeys + ']')
10
+ .replaceAll('\\#', '[' + extraKeys + ']');
11
+ return new RegExp(`^({${pstr}}|${pstr})`, 'i');
12
+ }
@@ -1,9 +1,9 @@
1
1
  import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class ColoredManaSymbol extends Symbol {
4
5
  static match(str) {
5
- return str.match(/^{[WUBRG]}/i)
6
- || str.match(/^[WUBRG]/i);
6
+ return str.match(msr`\c`)
7
7
  }
8
8
 
9
9
  get colors() {
@@ -1,9 +1,9 @@
1
1
  import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class ColoredPhyrexianManaSymbol extends Symbol {
4
5
  static match(str) {
5
- return str.match(/^{(h\/[wubrg]|[wubrg]\/h)}/i)
6
- || str.match(/^(h\/[wubrg]|[wubrg]\/h)/i);
6
+ return str.match(msr`(H\/\c|\c\/H)`);
7
7
  }
8
8
 
9
9
  get colors() {
@@ -1,9 +1,9 @@
1
1
  import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class ColorlessManaSymbol extends Symbol {
4
5
  static match(str) {
5
- return str.match(/^{C}/i)
6
- || str.match(/^C/i);
6
+ return str.match(msr`C`);
7
7
  }
8
8
 
9
9
  get colors() {
@@ -1,9 +1,9 @@
1
1
  import ColorlessManaSymbol from './ColorlessManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class ColorlessPhyrexianManaSymbol extends ColorlessManaSymbol {
4
5
  static match(str) {
5
- return str.match(/^{(h\/c|c\/h|h)}/i)
6
- || str.match(/^(h\/c|c\/h|h)/i);
6
+ return str.match(msr`(H\/c|c\/H|H)`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -0,0 +1,12 @@
1
+ import NonManaSymbol from './NonManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
3
+
4
+ export default class ExtraSymbol extends NonManaSymbol {
5
+ static match(str) {
6
+ return str.match(msr`\#`);
7
+ }
8
+
9
+ get type() {
10
+ return 'extra';
11
+ }
12
+ }
@@ -1,9 +1,9 @@
1
1
  import HybridManaSymbol from './HybridManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class FiveColorHybridManaSymbol extends HybridManaSymbol {
4
5
  static match(str) {
5
- return super.match(str, 5, /^{[WUBRG](\/[WUBRG]){4}}/i)
6
- || super.match(str, 5, /^[WUBRG](\/[WUBRG]){4}/i);
6
+ return super.match(str, 5, msr`\c(\/\c){4}`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -1,9 +1,9 @@
1
1
  import HybridManaSymbol from './HybridManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class FourColorHybridManaSymbol extends HybridManaSymbol {
4
5
  static match(str) {
5
- return super.match(str, 4, /^{[WUBRG](\/[WUBRG]){3}}/i)
6
- || super.match(str, 4, /^[WUBRG](\/[WUBRG]){3}/i);
6
+ return super.match(str, 4, msr`\c(\/\c){3}`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -1,9 +1,9 @@
1
1
  import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class GenericHybridManaSymbol extends Symbol {
4
5
  static match(str) {
5
- return str.match(/^{\d\/([WUBRGC])}/i)
6
- || str.match(/^\d\/([WUBRGC])/i)
6
+ return str.match(msr`\d\/(\c|c)`);
7
7
  }
8
8
 
9
9
  get colors() {
@@ -1,9 +1,9 @@
1
1
  import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class GenericManaSymbol extends Symbol {
4
5
  static match(str) {
5
- return str.match(/^{[0-9][0-9]?}/)
6
- || str.match(/^[0-9][0-9]?/);
6
+ return str.match(msr`[0-9][0-9]?`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -0,0 +1,23 @@
1
+ import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
3
+ import { symbolRegistry } from '../index.js';
4
+
5
+ export default class HalfManaSymbol extends Symbol {
6
+ static match(str) {
7
+ return str.match(msr`\|(\c|\T)`);
8
+ }
9
+
10
+ get colors() {
11
+ const c = this.raw[1];
12
+ const colorIds = symbolRegistry.colorKeys;
13
+ return colorIds.includes(c) ? [c] : [];
14
+ }
15
+
16
+ get type() {
17
+ return 'halfMana';
18
+ }
19
+
20
+ cmcValue() {
21
+ return 0.5;
22
+ }
23
+ }
@@ -0,0 +1,16 @@
1
+ import HybridManaSymbol from './HybridManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
3
+
4
+ export default class HybridPhyrexianManaSymbol extends HybridManaSymbol {
5
+ static match(str) {
6
+ return super.match(str, 3, msr`(H\/\c\/\c|\c\/H\/\c|\c\/\c\/H)`);
7
+ }
8
+
9
+ get colors() {
10
+ return this.raw.split('/').filter(c => c !== 'H');
11
+ }
12
+
13
+ get type() {
14
+ return 'hybridPhyrexianMana';
15
+ }
16
+ }
@@ -1,9 +1,9 @@
1
1
  import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class InfiniteManaSymbol extends Symbol {
4
5
  static match(str) {
5
- return str.match(/^{I}/i)
6
- || str.match(/^I/i)
6
+ return str.match(msr`I`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -1,9 +1,9 @@
1
1
  import NonManaSymbol from './NonManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class TapSymbol extends NonManaSymbol {
4
5
  static match(str) {
5
- return str.match(/^{T}/i)
6
- || str.match(/^T/i);
6
+ return str.match(msr`T`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -1,9 +1,9 @@
1
1
  import HybridManaSymbol from './HybridManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class ThreeColorHybridManaSymbol extends HybridManaSymbol {
4
5
  static match(str) {
5
- return super.match(str, 3, /^{[WUBRG](\/[WUBRG]){2}}/i)
6
- || super.match(str, 3, /^[WUBRG](\/[WUBRG]){2}/i);
6
+ return super.match(str, 3, msr`\c(\/\c){2}`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -1,9 +1,9 @@
1
1
  import HybridManaSymbol from './HybridManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class TwoColorHybridManaSymbol extends HybridManaSymbol {
4
5
  static match(str) {
5
- return super.match(str, 2, /^{[WUBRG]\/[WUBRG]}/i)
6
- || super.match(str, 2, /^[WUBRG]\/[WUBRG]/i);
6
+ return super.match(str, 2, msr`\c\/\c`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -0,0 +1,21 @@
1
+ import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
3
+
4
+ // used for typed mana, like snow or legendary
5
+ export default class TypedManaSymbol extends Symbol {
6
+ static match(str) {
7
+ return str.match(msr`\T`);
8
+ }
9
+
10
+ get colors() {
11
+ return [];
12
+ }
13
+
14
+ get type() {
15
+ return 'typedMana';
16
+ }
17
+
18
+ cmcValue() {
19
+ return 1;
20
+ }
21
+ }
@@ -1,9 +1,9 @@
1
1
  import NonManaSymbol from './NonManaSymbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class UntapSymbol extends NonManaSymbol {
4
5
  static match(str) {
5
- return str.match(/^{Q}/i)
6
- || str.match(/^Q/i);
6
+ return str.match(msr`Q`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -1,9 +1,9 @@
1
1
  import Symbol from './Symbol.js';
2
+ import { msr } from '../services/regExpService.js';
2
3
 
3
4
  export default class VariableManaSymbol extends Symbol {
4
5
  static match(str) {
5
- return str.match(/^{[XYZ]}/i)
6
- || str.match(/^[XYZ]/i);
6
+ return str.match(msr`[XYZ]`);
7
7
  }
8
8
 
9
9
  get type() {
@@ -1,12 +0,0 @@
1
- import NonManaSymbol from './NonManaSymbol.js';
2
-
3
- export default class EnergySymbol extends NonManaSymbol {
4
- static match(str) {
5
- return str.match(/^{E}/i)
6
- || str.match(/^E/i);
7
- }
8
-
9
- get type() {
10
- return 'energy';
11
- }
12
- }
@@ -1,21 +0,0 @@
1
- import Symbol from './Symbol.js';
2
-
3
- export default class HalfColoredManaSymbol extends Symbol {
4
- static match(str) {
5
- return str.match(/^{\|[WUBRGS]}/i)
6
- || str.match(/^\|[WUBRGS]/i);
7
- }
8
-
9
- get colors() {
10
- const c = this.raw[1];
11
- return c === 'S' ? [] : [c];
12
- }
13
-
14
- get type() {
15
- return 'halfColoredMana';
16
- }
17
-
18
- cmcValue() {
19
- return 0.5;
20
- }
21
- }
@@ -1,20 +0,0 @@
1
- import Symbol from './Symbol.js';
2
-
3
- export default class SnowManaSymbol extends Symbol {
4
- static match(str) {
5
- return str.match(/^{S}/i)
6
- || str.match(/^S/i);
7
- }
8
-
9
- get colors() {
10
- return [];
11
- }
12
-
13
- get type() {
14
- return 'snowMana';
15
- }
16
-
17
- cmcValue() {
18
- return 1;
19
- }
20
- }