UNPKG

tps-ninja

Version:

Generate images from Tak Positional System (TPS) strings

182 lines (149 loc) 4.24 kB
import { atoi, itoa } from "./Square.js"; const directionModifier = { "+": [0, 1], "-": [0, -1], ">": [1, 0], "<": [-1, 0], }; export const Ply = class { constructor(notation) { const matchData = notation.match( /(\d)?([CS])?([a-h])([1-8])(([<>+-])([1-8]+)?(\*)?)?([?!'"]+)?/i ); if (!matchData) { throw new Error("Invalid move"); } [ this.ptn, this.pieceCount, this.specialPiece, this.column, this.row, this.movement, this.direction, this.distribution, this.wallSmash, this.evalText, ] = matchData; if (this.specialPiece) { this.specialPiece = this.specialPiece.toUpperCase(); } this.column = this.column.toLowerCase(); this.specialPiece = this.specialPiece === "F" ? "" : this.specialPiece; this.pieceType = this.specialPiece === "C" ? "cap" : "flat"; [this.x, this.y] = atoi(this.column + this.row); if (this.movement && !this.pieceCount) { this.pieceCount = String(1); } if (this.movement && !this.distribution) { this.distribution = String(this.pieceCount || 1); } this.squares = [this.column + this.row]; if (this.movement) { const [xOffset, yOffset] = directionModifier[this.direction]; let x = this.x; let y = this.y; this.stackDistribution = this.distribution.split("").map((drop) => { x += xOffset; y += yOffset; this.squares.push(itoa(x, y)); return parseInt(drop, 10); }); } } stackTotal() { if (!this.distribution) { return 1; } return this.distribution.split("").reduce((a, i) => a + parseInt(i, 10), 0); } isValidStackDistribution() { if (!this.pieceCount && !this.distribution) { return true; } return parseInt(this.pieceCount, 10) === this.stackTotal(); } isValid() { this.errors = []; if (this.movement && !this.isValidStackDistribution()) { this.errors.push("Invalid stack distribution"); } if (this.pieceCount > 1 && (!this.distribution || !this.direction)) { this.errors.push("Invalid movement: " + this.ptn); } return !this.errors.length; } toMoveset(reverse = false) { const types = { C: "cap", S: "wall", F: "flat" }; if (!this.isValid()) { throw new Error(this.errors[0]); } if (!this.movement) return [ { action: reverse ? "pop" : "push", x: this.x, y: this.y, type: types[this.specialPiece] || "flat", }, ]; const firstMove = { action: reverse ? "push" : "pop", count: parseInt(this.pieceCount, 10), x: this.x, y: this.y, }; const [xOffset, yOffset] = directionModifier[this.direction]; const moveSet = this.stackDistribution.map((n, i) => { return { action: reverse ? "pop" : "push", count: n, x: this.x + xOffset * (i + 1), y: this.y + yOffset * (i + 1), }; }); if (this.wallSmash) { moveSet[moveSet.length - 1].flatten = true; } return [firstMove, ...moveSet]; } toUndoMoveset() { return this.toMoveset(true).reverse(); } transform(size, [rotate, flip]) { rotate = rotate % 4; flip = flip % 2; let ptn = this.ptn; let x = this.x; let y = this.y; let direction = this.direction; if (rotate === 1) { [x, y] = [y, size - 1 - x]; if (direction) { direction = { "+": ">", "-": "<", ">": "-", "<": "+" }[direction]; } } else if (rotate === 2) { x = size - 1 - x; y = size - 1 - y; if (direction) { direction = { "+": "-", "-": "+", ">": "<", "<": ">" }[direction]; } } else if (rotate === 3) { [x, y] = [size - 1 - y, x]; if (direction) { direction = { "+": "<", "-": ">", ">": "+", "<": "-" }[direction]; } } if (flip) { x = size - 1 - x; if (direction) { direction = { ">": "<", "<": ">" }[direction] || direction; } } ptn = ptn.replace(itoa(this.x, this.y), itoa(x, y)); if (direction) { ptn = ptn.replace(this.direction, direction); } return new Ply(ptn); } };