@gobstones/gobstones-gbb-parser
Version:
A Parser/Stringifier for GBB (Gobstones Board) file format
1 lines • 381 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../node_modules/@gobstones/gobstones-core/dist/index.js","../src/translations/en.ts","../src/translations/es.ts","../src/translations/index.ts","../src/parser/models.ts","../node_modules/nearley/lib/nearley.js","../src/parser/errors.ts","../node_modules/moo/moo.js","../src/grammar/gbb-lexer.js","../src/grammar/nearley-helper.js","../src/grammar/gbb-grammar.js","../src/grammar/tokenizer.ts","../src/grammar/index.ts","../src/parser/parser.ts","../src/stringifier/models.ts","../src/stringifier/errors.ts","../src/stringifier/stringifier.ts","../src/index.ts"],"sourcesContent":["import require$$0 from 'events';\n\n/**\n * The base class of the error hierarchy that is thrown when\n * an invalid operation is performed in the [[Board/Board | Board]]\n * class and it's associated [[Board/Cell | Cell]].\n */\nclass BoardError extends Error {\n constructor(name, message) {\n super(message);\n this.name = name;\n this.isError = true;\n Object.setPrototypeOf(this, BoardError.prototype);\n }\n}\n/**\n * This error is thrown when attempting to create a board\n * with invalid data.\n */\nclass InvalidBoardDescription extends BoardError {\n constructor(height, width, cellLocation) {\n super('InvalidBoardDescription', `The values used to create the board are invalid. ` +\n ` height: ${height}, width: ${width}, cell location: ${cellLocation}`);\n this.height = height;\n this.width = width;\n this.cellLocation = cellLocation;\n Object.setPrototypeOf(this, InvalidBoardDescription.prototype);\n }\n}\n/**\n * This error is thrown when attempting to read a cell, a\n * column or a row but an invalid location is given.\n */\nclass InvalidCellReading extends BoardError {\n constructor(attempt, failingCoordinate) {\n super('InvalidCellReading', `The attempt of ${attempt} failed for coordinate ` +\n `[${failingCoordinate[0]}, ${failingCoordinate[1]}].`);\n this.attempt = attempt;\n this.failingCoordinate = failingCoordinate;\n Object.setPrototypeOf(this, InvalidCellReading.prototype);\n }\n}\n/**\n * This error is thrown when attempting to move the head, but an\n * invalid location is given.\n */\nclass LocationFallsOutsideBoard extends BoardError {\n constructor(attempt, failingCoordinate, previousCoordinate) {\n super('LocationFallsOutsideBoard', `The attempt of ${attempt} from [${previousCoordinate[0]}, ` +\n `${previousCoordinate[1]}] falls outside the board on ` +\n `coordinate [${failingCoordinate[0]}, ${failingCoordinate[1]}].`);\n this.attempt = attempt;\n this.failingCoordinate = failingCoordinate;\n this.previousCoordinate = previousCoordinate;\n Object.setPrototypeOf(this, LocationFallsOutsideBoard.prototype);\n }\n}\n/**\n * This error is thrown when attempting to change the size of the board,\n * but an invalid size is given\n */\nclass InvalidSizeChange extends BoardError {\n constructor(attempt, previousWidth, previousHeight, newWidth, newHeight) {\n super('InvalidSizeChange', `The attempt of changing size by ${attempt} from width ${previousWidth}, ` +\n `and height ${previousHeight} ends in an invalid board of width ` +\n `${newWidth} and height ${newHeight}.`);\n this.attempt = attempt;\n this.previousWidth = previousWidth;\n this.previousHeight = previousHeight;\n this.newWidth = newWidth;\n this.newHeight = newHeight;\n Object.setPrototypeOf(this, InvalidSizeChange.prototype);\n }\n}\n/**\n * This error is thrown when attempting to change the stones amount\n * with an invalid amount of stone (negative amount).\n */\nclass InvalidStonesAmount extends BoardError {\n constructor(attempt, color, amount, previousCellState) {\n super('InvalidStonesAmount', `The attempt of ${attempt} failed for color ${color} given ${amount}.`);\n this.attempt = attempt;\n this.color = color;\n this.amount = amount;\n this.previousCellState = previousCellState;\n Object.setPrototypeOf(this, InvalidStonesAmount.prototype);\n }\n}\n\n/**\n * This enum represent the valid Gobstones Colors.\n * It's accompanied by a namespace with the same name, that provides additional\n * functionality, such as asking for the first or the last color, or iterate over\n * the elements of this enum.\n *\n * Note that directions are sorted in the following order, from first to last.\n * * Color.Blue\n * * Color.Black\n * * Color.Red\n * * Color.Green\n *\n * Always prefer using the enum over the string values it represents,\n * even as object keys.\n */\nvar Color;\n(function (Color) {\n Color[\"Blue\"] = \"a\";\n Color[\"Black\"] = \"n\";\n Color[\"Red\"] = \"r\";\n Color[\"Green\"] = \"v\";\n})(Color || (Color = {}));\n/**\n * This namespace provides additional functionality that extends the simple\n * Color enum, by providing some helper functions.\n */\n(function (Color) {\n /**\n * The smallest Color possible, currently [[Color.Blue]]\n *\n * @returns The smallest color.\n */\n Color.min = () => Color.Blue;\n /**\n * The biggest Color possible, currently [[Color.Green]]\n *\n * @returns The biggest color.\n */\n Color.max = () => Color.Green;\n /**\n * The next Color of a given Color. Colors are sorted\n * in the following way, from first to last:\n * * Color.Blue\n * * Color.Black\n * * Color.Red\n * * Color.Green\n *\n * And they are cyclic, that is, the next color of Green is Blue.\n *\n * @param color The color to obtain the next value from.\n *\n * @returns The next color of the given one.\n */\n Color.next = (color) => {\n switch (color) {\n case Color.Blue:\n return Color.Black;\n case Color.Black:\n return Color.Red;\n case Color.Red:\n return Color.Green;\n case Color.Green:\n return Color.Blue;\n /* istanbul ignore next */\n default:\n return undefined;\n }\n };\n /**\n * The next Color of a given Color. Color are sorted\n * in the following way, from last to first:\n * * Color.Green\n * * Color.Red\n * * Color.Black\n * * Color.Blue\n *\n * And they are cyclic, that is, the previous color of Blue is Green.\n *\n * @param color The color to obtain the previous value from.\n *\n * @returns The previous color of the given one.\n */\n Color.previous = (color) => {\n switch (color) {\n case Color.Blue:\n return Color.Green;\n case Color.Black:\n return Color.Blue;\n case Color.Red:\n return Color.Black;\n case Color.Green:\n return Color.Red;\n /* istanbul ignore next */\n default:\n return undefined;\n }\n };\n /**\n * Iterate over all the colors, in their defined order, from the smallest,\n * to the biggest, performing the callback over each color. A function that\n * expects a color and returns void is expected as an argument.\n *\n * @param f The callback to call on each iteration.\n */\n function foreach(f) {\n let current = Color.min();\n while (current !== Color.max()) {\n f(current);\n current = Color.next(current);\n }\n f(current);\n }\n Color.foreach = foreach;\n})(Color || (Color = {}));\n\n/**\n * This enum represent the valid Gobstones Directions.\n * It's accompanied by a namespace with the same name, that provides additional\n * functionality, such as asking for the first or the last direction, or iterate over\n * the elements of this enum.\n *\n * Note that directions are sorted in the following order, from first to last.\n * * Direction.North\n * * Direction.East\n * * Direction.South\n * * Direction.West\n *\n * Always prefer using the enum over the string values it represents,\n * even as object keys.\n */\nvar Direction;\n(function (Direction) {\n Direction[\"North\"] = \"n\";\n Direction[\"East\"] = \"e\";\n Direction[\"South\"] = \"s\";\n Direction[\"West\"] = \"w\";\n})(Direction || (Direction = {}));\n/**\n * This namespace provides additional functionality that extends the simple\n * Direction enum, by providing some helper functions.\n */\n(function (Direction) {\n /**\n * The smallest Direction possible, currently [[Direction.North]]\n *\n * @returns The smallest direction.\n */\n Direction.min = () => Direction.North;\n /**\n * The biggest Direction possible, currently [[Direction.West]]\n *\n * @returns The biggest direction.\n */\n Direction.max = () => Direction.West;\n /**\n * The next Direction of a given Direction. Directions are sorted\n * in the following way, from first to last:\n * * Direction.North\n * * Direction.East\n * * Direction.South\n * * Direction.West\n *\n * And they are cyclic, that is, the next direction of West is North.\n *\n * @param dir The direction to obtain the next value from.\n *\n * @returns The next direction of the given one.\n */\n Direction.next = (dir) => {\n switch (dir) {\n case Direction.North:\n return Direction.East;\n case Direction.East:\n return Direction.South;\n case Direction.South:\n return Direction.West;\n case Direction.West:\n return Direction.North;\n /* istanbul ignore next */\n default:\n return undefined;\n }\n };\n /**\n * The next Direction of a given Direction. Directions are sorted\n * in the following way, from last to first:\n * * Direction.West\n * * Direction.South\n * * Direction.East\n * * Direction.North\n *\n * And they are cyclic, that is, the previous direction of North is West.\n *\n * @param dir The direction to obtain the previous value from.\n *\n * @returns The previous direction of the given one.\n */\n Direction.previous = (color) => {\n switch (color) {\n case Direction.North:\n return Direction.West;\n case Direction.East:\n return Direction.North;\n case Direction.South:\n return Direction.East;\n case Direction.West:\n return Direction.South;\n /* istanbul ignore next */\n default:\n return undefined;\n }\n };\n /**\n * The opposite Direction of a given Direction. Directions are opposed\n * to each other in pairs, those being:\n * * Direction.West is opposite to Direction.East and vice versa\n * * Direction.North is opposite to Direction.South and vice versa\n *\n * @param dir The direction to obtain the opposite value from.\n *\n * @returns The opposite direction of the given one.\n */\n Direction.opposite = (color) => {\n switch (color) {\n case Direction.North:\n return Direction.South;\n case Direction.East:\n return Direction.West;\n case Direction.South:\n return Direction.North;\n case Direction.West:\n return Direction.East;\n /* istanbul ignore next */\n default:\n return undefined;\n }\n };\n /**\n * Answer wether or not the given direction is vertical,\n * that is, one of Direction.North or Direction.South.\n *\n * @param dir The direction to find out if it's vertical.\n *\n * @returns `true` if it's vertical, `false` otherwise.\n */\n Direction.isVertical = (dir) => dir === Direction.North || dir === Direction.South;\n /**\n * Answer wether or not the given direction is horizontal,\n * that is, one of Direction.East or Direction.West.\n *\n * @param dir The direction to find out if it's horizontal.\n *\n * @returns `true` if it's horizontal, `false` otherwise.\n */\n Direction.isHorizontal = (dir) => !Direction.isVertical(dir);\n /**\n * Iterate over all the directions, in their defined order, from the smallest,\n * to the biggest, performing the callback over each direction. A function that\n * expects a direction and returns void is expected as an argument.\n *\n * @param f The callback to call on each iteration.\n */\n Direction.foreach = (f) => {\n let current = Direction.min();\n while (current !== Direction.max()) {\n f(current);\n current = Direction.next(current);\n }\n f(current);\n };\n})(Direction || (Direction = {}));\n\nvar TypedEmitter = require$$0.EventEmitter;\n\n/**\n * This is just a helper module that re-export the useful\n * [binier/tiny-typed-emitter](https://github.com/binier/tiny-typed-emitter).\n * You can check out information about this module at their README.\n *\n * @see https://github.com/binier/tiny-typed-emitter\n *\n * @author Alan Rodas Bonjour <alanrodas@gmail.com>\n *\n * @packageDocumentation\n */\n/**\n * This is a rename of EventEmitter that allows for type checking\n * of the event's emitting in a class. Just extend your event\n * throwing classes with TypeEmitter with the events signature as a\n * generic type, and expect that calling emit throws errors when not\n * typechecking. The on event over instances of the class will also\n * throws errors when invalid event names are used.\n *\n * @see [binier/tiny-typed-emitter](https://github.com/binier/tiny-typed-emitter)\n * to read more information about how all this works.\n */\nconst TypedEmitter$1 = TypedEmitter;\n\n/**\n * This module provides the function [[deepEquals]] that allows to test\n * if two object are semantically equal. The module is loosely based on\n * [inspect-js/node-deep-equal](https://github.com/inspect-js/node-deep-equal)\n * but removing all dependencies.\n *\n * The function is intended for comparison of basic types, simple non classed\n * objects, arrays, and built-in basic classed objects such as Set, Map, RegExp,\n * Date, Buffer and Error.\n *\n * Note that deep equality is costly, and should be avoided whenever possible. Yet\n * is some scenarios, it may be useful to count with such a function. In that sense,\n * we provide a 'cheap' (in terms of dependency overhead) alternative to most\n * third-party implementations, that can be used through the whole project.\n *\n * Note that the implementation is kind of ugly and heavily procedural. The idea\n * behind the code is to return a result as fast as possible. Also note that it might\n * not consider the most edgy cases. If you have trouble with a specific case,\n * please consider sending a Pull Request or raising an Issue in this project's\n * repository.\n *\n * @author Alan Rodas Bonjour <alanrodas@gmail.com>\n *\n * @packageDocumentation\n */\n/**\n * Answer wether or not two elements are equal, considering them\n * equal when they have the same type and all their internal elements are\n * the same, or when they represent the same concept (two regular expressions\n * that match the same string, to date for the same moment, to sets with same\n * elements in it, and so on).\n *\n * Most simple cases should return true as expected, such as:\n * * `deepEquals(1, 1.0)`\n * * `deepEquals({a: 1, b: {c: 3, d: 4}}, {b: {c: 3, d: 4}, a: 1})`\n * * `deepEquals([1,2,3], [1, 1+1, 2+1])`\n * * `deepEquals(new Set([1,2,3]), new Set([3,2,1]))`\n *\n * There is one special case, that we support and that might not be expected\n * in standard TS/JS behavior, which is `NaN` comparison. Here you might find\n * that `deepEquals(NaN, NaN)` is `true`, even though in JS NaN is not equal\n * to anything, even itself.\n *\n * Note that parameters are statically typed when running in TypeScript,\n * thus not allowing for things such as `deepEquals(4, '4.0')` to be typed,\n * unless explicitly casted away. In that case even, the comparison is performed\n * not considering type coercion, thus, returning false.\n *\n * If you want to see all supported and unsupported cases, we recommend you to check\n * out the test cases.\n *\n *\n * @param first The element to compare to.\n * @param second The element to compare against.\n *\n * @return `true` if both elements are equal, `false` otherwise.\n */\nconst deepEquals = (first, second) => {\n const compare = (a, b) => {\n // Return true if they are the same object\n if (a === b)\n return true;\n // and false if they don't have the same type\n else if (typeof a !== typeof b)\n return false;\n // Check for types and call a specific comparer\n // depending on the type\n if (typeof a === 'number' && typeof b === 'number')\n return numberEquals(a, b);\n // Cases where they are both objects start here, many\n // different things are considered object in JS, so\n // we need to disambiguate.\n if (typeof a === 'object' && typeof b === 'object') {\n // If they belong to different classes, then they are not equal,\n // one of them might not have a class, so consider that case too.\n if (a.constructor && b.constructor && a.constructor !== b.constructor)\n return false;\n // Use array comparison if both are arrays\n if (Array.isArray(a) && Array.isArray(b))\n return arrayEquals(a, b, compare);\n // If both are Sets\n if (a instanceof Set && b instanceof Set)\n return setEquals(a, b);\n // If both are Maps\n if (a instanceof Map && b instanceof Map)\n return mapEquals(a, b, compare);\n // If both are Errors\n if (a instanceof Error && b instanceof Error)\n return errorEquals(a, b);\n // If both are RegExp\n if (a instanceof RegExp && b instanceof RegExp)\n return regexpEquals(a, b);\n // If both are Dates\n if (a instanceof Date && b instanceof Date)\n return dateEquals(a, b);\n // If both are Buffers\n if (isBuffer(a) && isBuffer(b))\n return bufferEquals(a, b);\n // Reached this case we consider a plain object (or class\n // with plain properties that can be accessed)\n return objectEquals(a, b, compare);\n }\n return false;\n };\n return compare(first, second);\n};\n/**\n * Answer if two numbers are equal. Two numbers are equal\n * if they happen to be the same number, or, if both are NaN.\n *\n * @param a The first number\n * @param b The second number\n *\n * @returns true when both numbers are the same, or both are NaN.\n */\nconst numberEquals = (a, b) => {\n if (Number.isNaN(a) && Number.isNaN(b))\n return true;\n else\n return a === b;\n};\n/**\n * Answer if two arrays are equal. Two arrays are equal when they both\n * have the exact same number of elements, and they have the same element\n * in each position. To consider if two elements inside the array are equal\n * the [[innerComparer]] is used. The expected value for [[innerComparer]]\n * is the recursive comparer function in deepEquals.\n *\n * @param a The first array\n * @param b The second array\n * @param innerComparer The function for testing if two inner elements are equal\n *\n * @returns `true` if both arrays are equal, `false` otherwise.\n */\nconst arrayEquals = (aArr, bArr, innerComparer) => {\n // Two arrays should have the same length\n if (aArr.length !== bArr.length)\n return false;\n // And the same element in each position, which is\n // compared by deep equality\n for (let i = 0; i < aArr.length; i++) {\n // In case the value in a position is not equal,\n // they are not equal\n if (!innerComparer(aArr[i], bArr[i]))\n return false;\n }\n // They are only equal after full comparison\n return true;\n};\n/**\n * Answer if two objects are equal. Two objects are equal when they both\n * have the exact same number of properties, with same names, and they\n * have the same value in each property. To consider if two values of a property\n * are equal the [[innerComparer]] is used. The expected value for [[innerComparer]]\n * is the recursive comparer function in deepEquals.\n *\n * @param a The first object\n * @param b The second object\n * @param innerComparer The function for testing if two inner elements are equal\n *\n * @returns `true` if both object are equal, `false` otherwise.\n */\nconst objectEquals = (aArr, bArr, innerComparer) => {\n // Obtain the object keys, sorted\n const aKeys = Object.keys(aArr).sort();\n const bKeys = Object.keys(bArr).sort();\n // They should have the same amount of keys\n if (aKeys.length !== bKeys.length)\n return false;\n // And perform a cheap key test (they should both have same keys)\n for (let i = 0; i < aKeys.length; i++) {\n if (aKeys[i] !== bKeys[i])\n return false;\n }\n // If they do, perform a more expensive deep equal test in all values\n // eslint-disable-next-line @typescript-eslint/prefer-for-of\n for (let i = 0; i < aKeys.length; i++) {\n const aValue = aArr[aKeys[i]];\n const bValue = bArr[aKeys[i]];\n if (!innerComparer(aValue, bValue))\n return false;\n }\n // They must be equal when this is reached\n return true;\n};\n/**\n * Answer if two Sets are equal. Two Sets are equal when they both\n * have the exact same number of elements, and they have the same\n * elements.\n *\n * @param a The first object\n * @param b The second object\n *\n * @returns `true` if both object are equal, `false` otherwise.\n */\nconst setEquals = (a, b) => {\n if (a.size !== b.size)\n return false;\n const aIterator = a.entries();\n let aNext = aIterator.next();\n while (aNext && !aNext.done) {\n if (!b.has(aNext.value[1]))\n return false;\n aNext = aIterator.next();\n }\n return true;\n};\n/**\n * Answer if two Maps are equal. Two Maps are equal when they both\n * have the exact same number of keys, with same key names, and they\n * have the same value in each key. To consider if two values of a key\n * are equal the [[innerComparer]] is used. The expected value for [[innerComparer]]\n * is the recursive comparer function in deepEquals.\n *\n * @param a The first map\n * @param b The second map\n * @param innerComparer The function for testing if two inner elements are equal\n *\n * @returns `true` if both Maps are equal, `false` otherwise.\n */\nconst mapEquals = (a, b, innerComparer) => {\n if (a.size !== b.size)\n return false;\n const aEntries = a.entries();\n let aNext = aEntries.next();\n while (!aNext.done) {\n // Should have a key with same name or value\n if (!b.has(aNext.value[0]))\n return false;\n if (!innerComparer(aNext.value[1], b.get(aNext.value[0])))\n return false;\n aNext = aEntries.next();\n }\n return true;\n};\n/**\n * Answer if two Errors are equal. Two Errors are equal when they both\n * have the exact name and message.\n *\n * @param a The first Error\n * @param b The second Error\n *\n * @returns `true` if both Errors are equal, `false` otherwise.\n */\nconst errorEquals = (a, b) => a.name === b.name && a.message === b.message;\n/**\n * Answer if two RegExps are equal. Two RegExps are equal when they both\n * have the exact source and flags.\n *\n * @param a The first RegExp\n * @param b The second RegExp\n *\n * @returns `true` if both RegExp are equal, `false` otherwise.\n */\nconst regexpEquals = (a, b) => a.source === b.source && a.flags === b.flags;\n/**\n * Answer if two Dates are equal. Two Date are equal when they both\n * have the exact time.\n *\n * @param a The first Date\n * @param b The second Date\n *\n * @returns `true` if both Date are equal, `false` otherwise.\n */\nconst dateEquals = (a, b) => a.getTime() === b.getTime();\n/**\n * Answer if two Buffers are equal. Two Buffers are equal when they both\n * have the exact element at each position.\n *\n * @param a The first Buffer\n * @param b The second Buffer\n *\n * @returns `true` if both Buffers are equal, `false` otherwise.\n */\nconst bufferEquals = (a, b) => {\n if (a.length !== b.length)\n return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i])\n return false;\n }\n return true;\n};\n/**\n * Answer if an element is a Buffer.\n *\n * @param x The element to test if it's a buffer\n *\n * @returns `true` if the element is a Buffer, `false` otherwise.\n */\nconst isBuffer = (x) => !!(x.constructor && x.constructor.isBuffer && x.constructor.isBuffer(x));\n\n/**\n * This module provides the [[Matchers]] class, that contains all the matchers\n * for the expectations. All matchers are centralized in this module for\n * bigger extensibility.\n *\n * Additionally, it provides the [[MatcherCall]] interface, that allows to\n * register the result of a call to a specific matcher.\n *\n * @author Alan Rodas Bonjour <alanrodas@gmail.com>\n *\n * @packageDocumentation\n */\n/**\n * This object contains a series of matchers, that is, a series of functions\n * that can be called with the actual value (and in cases a series of arguments)\n * and returns a boolean, `true` if the value satisfies the matcher, and `false`\n * otherwise.\n *\n * Having the matchers separated from the instances that use the matchers allow for\n * greater extensibility.\n */\nclass Matchers {\n // Generic\n /** Answers if the actual value is the same as expected, using strict compare */\n static toBe(actual, expected) {\n return actual === expected;\n }\n /** Answers if the actual value is the same as expected, using a deep compare mechanism */\n static toBeLike(actual, expected) {\n return deepEquals(actual, expected);\n }\n /** Answers if the actual value is defined (as in not equal to undefined) */\n static toBeDefined(actual) {\n return actual !== undefined;\n }\n /** Answers if the actual value is undefined */\n static toBeUndefined(actual) {\n return actual === undefined;\n }\n /** Answers if the actual value is null (strict null, not undefined) */\n static toBeNull(actual) {\n // eslint-disable-next-line no-null/no-null\n return actual === null;\n }\n /** Answers if the actual value is a truthy value */\n static toBeTruthy(actual) {\n return !!actual;\n }\n /** Answers if the actual value is a falsy value */\n static toBeFalsy(actual) {\n return !actual;\n }\n /**\n * Answers if the actual value has a type matching the expected type.\n * This comparison is performed using the `typeof` operation over the value,\n * with additional logic added to support 'array' as a type.\n * @example `toHaveType([1,2,3], 'array')` returns `true` as expected.\n */\n static toHaveType(actual, expectedType) {\n return ((expectedType !== 'object' && typeof actual === expectedType) ||\n (expectedType === 'array' && typeof actual === 'object' && Array.isArray(actual)) ||\n (expectedType === 'object' && !Array.isArray(actual) && typeof actual === expectedType));\n }\n // Numbers\n /** Answer if the actual value is greater than the expected value. */\n static toBeGreaterThan(actual, expected) {\n return typeof actual === 'number' && actual > expected;\n }\n /** Answer if the actual value is greater than or equal than the expected value. */\n static toBeGreaterThanOrEqual(actual, expected) {\n return typeof actual === 'number' && actual >= expected;\n }\n /** Answer if the actual value is lower than the expected value. */\n static toBeLowerThan(actual, expected) {\n return typeof actual === 'number' && actual < expected;\n }\n /** Answer if the actual value is lower than or equal than the expected value. */\n static toBeLowerThanOrEqual(actual, expected) {\n return typeof actual === 'number' && actual <= expected;\n }\n /** Answer if the actual value is between the from and to values (inclusive). */\n static toBeBetween(actual, from, to) {\n return typeof actual === 'number' && from <= actual && actual <= to;\n }\n /** Answer if the actual value is infinity (positive or negative). */\n static toBeInfinity(actual) {\n return typeof actual === 'number' && (actual === Infinity || actual === -Infinity);\n }\n /** Answer if the actual value is not a number. */\n static toBeNaN(actual) {\n return typeof actual === 'number' && Number.isNaN(actual);\n }\n /**\n * Answer if the actual value is close to the expected value, by at least the number\n * of digits given.\n * @example `toBeCloseTo(4.0005, 4.0009, 3)` returns `true`, as there are 3\n * digits that are equal between actual and expected.\n * If no amount of digits is given, 5 is taken by default.\n */\n static toBeCloseTo(actual, expected, numDigits) {\n return (typeof actual === 'number' &&\n Math.abs(expected - actual) < Math.pow(10, -numDigits) / 10);\n }\n // String\n /** Answer if the actual value has expected as a substring. */\n static toHaveSubstring(actual, expected) {\n return typeof actual === 'string' && actual.indexOf(expected) >= 0;\n }\n /** Answer if the actual value starts with the expected string. */\n static toStartWith(actual, expected) {\n return typeof actual === 'string' && actual.startsWith(expected);\n }\n /** Answer if the actual value ends with the expected string. */\n static toEndWith(actual, expected) {\n return typeof actual === 'string' && actual.endsWith(expected);\n }\n /** Answer if the actual value matches the given regexp. */\n static toMatch(actual, expected) {\n return typeof actual === 'string' && expected.test(actual);\n }\n // Arrays\n /** Answer if the actual value has a length of expected number. */\n static toHaveLength(actual, expected) {\n return typeof actual === 'object' && actual instanceof Array && actual.length === expected;\n }\n /** Answer if the actual value contains the expected element. */\n static toContain(actual, expected) {\n return typeof actual === 'object' && Array.isArray(actual) && actual.indexOf(expected) >= 0;\n }\n /**\n * Answer if the actual value has a the expected element at a given position.\n * Returns false if the position does not exist.\n */\n static toHaveAtPosition(actual, expected, position) {\n return (typeof actual === 'object' &&\n Array.isArray(actual) &&\n actual.length > position &&\n position >= 0 &&\n actual[position] === expected);\n }\n /** Answer if all the element of the actual value satisfy a given criteria. */\n static allToSatisfy(actual, criteria) {\n return (typeof actual === 'object' &&\n Array.isArray(actual) &&\n actual.reduce((r, a) => criteria(a) && r, true));\n }\n /** Answer if any of the element of the actual value satisfy a given criteria. */\n static anyToSatisfy(actual, criteria) {\n return (typeof actual === 'object' &&\n Array.isArray(actual) &&\n actual.reduce((r, a) => criteria(a) || r, false));\n }\n /** Answer if a given amount of elements of the actual value satisfy a given criteria. */\n static amountToSatisfy(actual, amount, criteria) {\n return (typeof actual === 'object' &&\n Array.isArray(actual) &&\n actual.reduce((r, a) => (criteria(a) ? r + 1 : r), 0) === amount);\n }\n // Objects\n /** Answer if the actual element has the given amount of properties. */\n static toHavePropertyCount(actual, amount) {\n return (typeof actual === 'object' &&\n Object.keys(actual).filter((e) => Object.hasOwnProperty.call(actual, e)).length ===\n amount);\n }\n /** Answer if an object has at least all keys in the least. Combine with\n * toHaveNoOtherThan to ensure exact key existence */\n static toHaveAtLeast(actual, keys) {\n if (typeof actual !== 'object')\n return false;\n for (const key of keys) {\n if (!actual[key])\n return false;\n }\n return true;\n }\n /** Answer if an object has no other than the given keys (although not all given\n * need to be present). Combine with toHaveAtLeast to ensure exact key existence */\n static toHaveNoOtherThan(actual, keys) {\n if (typeof actual !== 'object')\n return false;\n for (const key of Object.keys(actual)) {\n if (keys.indexOf(key) < 0) {\n return false;\n }\n }\n return true;\n }\n /** Answer if the actual element has a property with the given name. */\n static toHaveProperty(actual, propertyName) {\n return (typeof actual === 'object' && Object.prototype.hasOwnProperty.call(actual, propertyName));\n }\n /** Answer if the actual element is an instance of a given class (using instanceof). */\n // eslint-disable-next-line @typescript-eslint/ban-types\n static toBeInstanceOf(actual, classConstructor) {\n return typeof actual === 'object' && actual instanceof classConstructor;\n }\n}\n\n/**\n * This abstract class provides finished expectation behavior for\n * all actions based on the fact that it's subclass provides\n * an implementation for [[getResult]].\n */\nclass FinishedExpectation {\n /** @inheritdoc [[IFinishedExpectation.orThrow]] */\n orThrow(error) {\n if (!this.getResult()) {\n throw error;\n }\n }\n /** @inheritdoc [[IFinishedExpectation.orYield]] */\n orYield(value) {\n return !this.getResult() ? value : undefined;\n }\n /** @inheritdoc [[IFinishedExpectation.andDoOr]] */\n andDoOr(actionWhenTrue, actionWhenFalse) {\n if (this.getResult()) {\n actionWhenTrue();\n }\n else {\n actionWhenFalse();\n }\n }\n /** @inheritdoc [[IFinishedExpectation.andDo]] */\n andDo(action) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function, no-empty-function\n this.andDoOr(action, () => { });\n }\n /** @inheritdoc [[IFinishedExpectation.orDo]] */\n orDo(action) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function, no-empty-function\n this.andDoOr(() => { }, action);\n }\n}\n\n/**\n * This module provides the [[Expectation]] class that implements\n * all interfaces for expectations.\n *\n * @author Alan Rodas Bonjour <alanrodas@gmail.com>\n *\n * @packageDocumentation\n */\nclass Expectation extends FinishedExpectation {\n /**\n * Create a new expectation for the given element.\n *\n * @param element The element to query to.\n */\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n constructor(element) {\n super();\n this.states = [];\n this.element = element;\n this.isNot = false;\n }\n /** @inheritdoc [[IGenericExpectation.not]] */\n get not() {\n this.isNot = !this.isNot;\n return this;\n }\n /** @inheritdoc [[IGenericExpectation.toBe]] */\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n toBe(value) {\n return this.runMatcher('toBe', [value]);\n }\n /** @inheritdoc [[IGenericExpectation.toBeLike]] */\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n toBeLike(value) {\n return this.runMatcher('toBeLike', [value]);\n }\n /** @inheritdoc [[IGenericExpectation.toBeNull]] */\n toBeNull() {\n return this.runMatcher('toBeNull', []);\n }\n /** @inheritdoc [[IGenericExpectation.toBeDefined]] */\n toBeDefined() {\n return this.runMatcher('toBeDefined', []);\n }\n /** @inheritdoc [[IGenericExpectation.toBeUndefined]] */\n toBeUndefined() {\n return this.runMatcher('toBeUndefined', []);\n }\n /** @inheritdoc [[IGenericExpectation.toBeTruthy]] */\n toBeTruthy() {\n return this.runMatcher('toBeTruthy', []);\n }\n /** @inheritdoc [[IGenericExpectation.toBeFalsy]] */\n toBeFalsy() {\n return this.runMatcher('toBeFalsy', []);\n }\n /** @inheritdoc [[IGenericExpectation.toHaveType]] */\n toHaveType(typeName) {\n return this.runMatcher('toHaveType', [typeName]);\n }\n // INumberExpectation\n /** @inheritdoc [[INumberExpectation.toBeGreaterThan]] */\n toBeGreaterThan(value) {\n return this.runMatcher('toBeGreaterThan', [value]);\n }\n /** @inheritdoc [[INumberExpectation.toBeGreaterThanOrEqual]] */\n toBeGreaterThanOrEqual(value) {\n return this.runMatcher('toBeGreaterThanOrEqual', [value]);\n }\n /** @inheritdoc [[INumberExpectation.toBeLowerThan]] */\n toBeLowerThan(value) {\n return this.runMatcher('toBeLowerThan', [value]);\n }\n /** @inheritdoc [[INumberExpectation.toBeLowerThanOrEqual]] */\n toBeLowerThanOrEqual(value) {\n return this.runMatcher('toBeLowerThanOrEqual', [value]);\n }\n /** @inheritdoc [[INumberExpectation.toBeBetween]] */\n toBeBetween(from, to) {\n return this.runMatcher('toBeBetween', [from, to]);\n }\n /** @inheritdoc [[INumberExpectation.toBeInfinity]] */\n toBeInfinity() {\n return this.runMatcher('toBeInfinity', []);\n }\n /** @inheritdoc [[INumberExpectation.toBeNaN]] */\n toBeNaN() {\n return this.runMatcher('toBeNaN', []);\n }\n /** @inheritdoc [[INumberExpectation.toBeCloseTo]] */\n toBeCloseTo(value, digits = 5) {\n return this.runMatcher('toBeCloseTo', [value, digits]);\n }\n // IStringExpectation\n /** @inheritdoc [[IStringExpectation.toHaveSubstring]] */\n toHaveSubstring(substring) {\n return this.runMatcher('toHaveSubstring', [substring]);\n }\n /** @inheritdoc [[IStringExpectation.toStartWith]] */\n toStartWith(start) {\n return this.runMatcher('toStartWith', [start]);\n }\n /** @inheritdoc [[IStringExpectation.toEndWith]] */\n toEndWith(end) {\n return this.runMatcher('toEndWith', [end]);\n }\n /** @inheritdoc [[IStringExpectation.toMatch]] */\n toMatch(regexp) {\n return this.runMatcher('toMatch', [regexp]);\n }\n // IArrayExpectation\n /** @inheritdoc [[IArrayExpectation.toHaveLength]] */\n toHaveLength(count) {\n return this.runMatcher('toHaveLength', [count]);\n }\n /** @inheritdoc [[IArrayExpectation.toContain]] */\n toContain(value) {\n return this.runMatcher('toContain', [value]);\n }\n /** @inheritdoc [[IArrayExpectation.toHaveAtPosition]] */\n toHaveAtPosition(value, position) {\n return this.runMatcher('toHaveAtPosition', [value, position]);\n }\n /** @inheritdoc [[IArrayExpectation.allToSatisfy]] */\n allToSatisfy(criteria) {\n return this.runMatcher('allToSatisfy', [criteria]);\n }\n /** @inheritdoc [[IArrayExpectation.anyToSatisfy]] */\n anyToSatisfy(criteria) {\n return this.runMatcher('anyToSatisfy', [criteria]);\n }\n /** @inheritdoc [[IArrayExpectation.amountToSatisfy]] */\n amountToSatisfy(count, criteria) {\n return this.runMatcher('amountToSatisfy', [count, criteria]);\n }\n // IObjectExpectation\n /** @inheritdoc [[IObjectExpectation.toHavePropertyCount]] */\n toHavePropertyCount(count) {\n return this.runMatcher('toHavePropertyCount', [count]);\n }\n /** @inheritdoc [[IObjectExpectation.toHaveAtLeast]] */\n toHaveAtLeast(keys) {\n return this.runMatcher('toHaveAtLeast', keys, false);\n }\n /** @inheritdoc [[IObjectExpectation.toHaveNoOtherThan]] */\n toHaveNoOtherThan(keys) {\n return this.runMatcher('toHaveNoOtherThan', keys, false);\n }\n /** @inheritdoc [[IObjectExpectation.toHaveProperty]] */\n toHaveProperty(propertyName) {\n return this.runMatcher('toHaveProperty', [propertyName]);\n }\n /** @inheritdoc [[IObjectExpectation.toBeInstanceOf]] */\n // eslint-disable-next-line @typescript-eslint/ban-types\n toBeInstanceOf(classConstructor) {\n return this.runMatcher('toBeInstanceOf', [classConstructor]);\n }\n // IFinishedExpectation\n /** @inheritdoc [[IFinishedExpectation.getResult]] */\n getResult() {\n return this.result;\n }\n /**\n * Set the given value as the result of this\n * expectation. The result is directly set, when\n * no previous result existed, or joined with a\n * logic conjunction with the previous result if\n * a value already exists.\n *\n * @value The value to set.\n */\n setResult(value) {\n if (this.result === undefined) {\n this.result = value;\n }\n else {\n this.result = this.result && value;\n }\n }\n /**\n * Run a matcher with the given name, passing the\n * querying element as a first argument, and all additional\n * given arguments. The result of running the matcher is stores,\n * and a new state is pushed to this particular matcher.\n *\n * @param matcherName The matcher name to run\n * @param args The arguments to pass to the matcher\n */\n runMatcher(matcherName, args, sparse = true) {\n const matcherArgs = sparse ? [this.element, ...args] : [this.element, args];\n const matcherResult = Matchers[matcherName].call(this, ...matcherArgs);\n const result = this.isNot ? !matcherResult : matcherResult;\n this.states.push({\n matcher: matcherName,\n args,\n result\n });\n this.setResult(result);\n return this;\n }\n}\n\n/**\n * This module provides the [[JoinedExpectation]] class that provides\n * a way to create an expectation that has a result the result of\n * applying the joiner to every expectation in the result.\n *\n * @author Alan Rodas Bonjour <alanrodas@gmail.com>\n *\n * @packageDocumentation\n */\n/**\n * A joined expectation consist of multiple expectations joined by a specific\n * joiner function. A JoinedExpectation implements [[FinishedExpectation]],\n * where the result is calculated using the given joiner function.\n *\n * Currently two join forms are provided, [[Expectations/Expectations.expect.and]],\n * and [[Expectations/Expectations.expect.or]].\n */\nclass JoinedExpectation extends FinishedExpectation {\n /**\n * Create a new instance of a JoinedExpectation for the given set\n * of expectations, using the provided joiner.\n *\n * @param expectations The expectations that ought to be joined.\n * @param joiner The joiner to use to calculate the result.\n */\n constructor(expectations, joiner) {\n super();\n this.result = joiner(expectations);\n }\n /** @inheritdoc [[IFinishedExpectancy.getResult]] */\n getResult() {\n return this.result;\n }\n}\n\n// eslint-disable-next-line max-len\n// eslint-disable-next-line prefer-arrow/prefer-arrow-functions, @typescript-eslint/explicit-module-boundary-types\nfunction expect(element) {\n return new Expectation(element);\n}\n/**\n * This namespace provides additional hany operations for the expect function.\n */\n(function (expect) {\n /**\n * Create a new [[JoinedExpectation]] where all the expectations need to have a `true` result\n * in order for the result of the joined one to be also `true`. That is, an expectation\n * that joins it's components with a logical and.\n * @param expectations A list of expectations that need to be fulfilled in order to\n * return `true` as result.\n */\n expect.and = (...expectations) => new JoinedExpectation(expectations, (exp) => exp.reduce((r, e) => r && e.getResult(), true));\n /**\n * Create a new [[JoinedExpectation]] where any of the expectations need to have a `true` result\n * in order for the result of the joined one to be also `true`. That is, an expectation\n * that joins it's components with a logical or.\n * @param expectations A list of expectations where one need to be fulfilled in order to\n * return `true` as result.\n */\n expect.or = (...expectations) => new JoinedExpectation(expectations, (exp) => exp.reduce((r, e) => r || e.getResult(), false));\n})(expect || (expect = {}));\n\n/**\n * This object contains the default values for a [[Board]] and it's cells.\n * When a specific value is not given, the defaults are used.\n */\nconst Defaults = {\n [Color.Blue]: 0,\n [Color.Black]: 0,\n [Color.Red]: 0,\n [Color.Green]: 0\n};\nclass Cell extends TypedEmitter$1 {\n /**\n * Create a new instance of a cell with the given cell information.\n * A cell should be given the [[Board]] it belongs to, as a cell cannot exist\n * without a board. Additionally, at least the [x, y] coordinate of the cell\n * within the board should be passed as the cell's information, and optionally,\n * the amount of stones of the different colors in case they are not zero.\n *\n * When creating a cell and passing color information, you should prefer using\n * the Color enum as a key, instead of the enum value as a string.\n * @example\n * ```\n * new Cell(board, { x: 3, y: 2, [Color.Red]: 5, [Color.Green]: 1 });\n * ```\n * This allows for abstracting away the inner representation of the enum, and allow\n * for changes in the future without impacting your code.\n *\n * @param board The board this cell belongs to.\n * @param cellInfo The information for this cell, at least the X and Y coordinates,\n * and optionally, amount of stones for each color.\n */\n constructor(board, cellInfo) {\n var _a, _b, _c, _d;\n super();\n this.board = board;\n this.locationX = cellInfo.x;\n this.locationY = cellInfo.y;\n this.blueStones = (_a = cellInfo[Color.Blue]) !== null && _a !== void 0 ? _a : Defaults[Color.Blue];\n this.blackStones = (_b = cellInfo[Color.Black]) !== null && _b !== void 0 ? _b : Defaults[Color.Black];\n this.redStones = (_c = cellInfo[Color.Red]) !== null && _c !== void 0 ? _c : Defaults[Color.Red];\n this.greenStones = (_d = cellInfo[Color.Green]) !== null && _d !== void 0 ? _d : Defaults[Color.Green];\n }\n /* ************* Cloning ************** */\n /**\n * Clone this cell. Pass a board in order to set the\n * associated board of the cloned cell to that element.\n *\n * @param cloneBoard The Board the cloned cell will be associated to.\n * @returns A new [[Cell]]\n */\n clone(newBoard) {\n return new Cell(newBoard, {\n x: this.x,\n y: this.y,\n [Color.Blue]: this.getStonesOf(Color.Blue),\n [Color.Black]: this.getStonesOf(Color.Black),\n [Color.Red]: this.getStonesOf(Color.Red),\n [Color.Green]: this.getStonesOf(Color.Green)\n });\n }\n /* ************* Accessors ************** */\n /**\n * Get or set the X location of this cell within the board.\n *\n * @warning The getter can be used always. The setter on the other hand\n * although exported, should not be used, as it's usage is reserved\n * for internal actions of the board only (It's used exclusively on\n * recalculating coordinates when the board resizes). Avoid the\n * setter at all cost.\n *\n * @param value The new value for the X coordinate.\n *\n * @returns This cells X coordinate within the board\n */\n get x() {\n return this.locationX;\n }\n set x(value) {\n this.locationX = value;\n }\n /**\n * Get or set the Y location of this cell within the board.\n *\n * @warning The getter can be used always. The setter on the other hand\n * although exported, should not be used, as it's usage is reserved\n * for internal actions of the board only (It's used exclusively on\n * recalculating coordinates when the board resizes). Avoid the\n * setter at all cost.\n *\n * @param value The new value for the Y coordinate.\n *\n * @returns This cells Y coordinate within the board\n */\n get y() {\n return this.locationY;\n }\n set y(value) {\n this.locationY = value;\n }\n /**\n * Get the amount of [[Color.Blue | blue]] stones of this cell.\n * Or instead, set the amount of stones.\n *\n * @deprecated This is retain only for compatibility reasons.\n * If you need to access the amount of stones of a color,\n * use [[getStonesOf]] instead, passing the color as an argument.\n * So the preferred method for blue stones should be\n * ```\n * cell.getStonesOf(Color.Blue);\n * ```\n * For setting the stones of a color, use [[setStonesOf]]