tps-ninja
Version:
Generate images from Tak Positional System (TPS) strings
231 lines (208 loc) • 5.01 kB
JavaScript
export const atoi = (coord) => [
"abcdefgh".indexOf(coord[0]),
parseInt(coord[1], 10) - 1,
];
export const itoa = (x, y) => "abcdefgh"[x] + (y + 1);
const OPPOSITE = {
N: "S",
S: "N",
E: "W",
W: "E",
};
const EDGE = {
N: "NS",
S: "NS",
E: "EW",
W: "EW",
};
export const Square = class {
constructor(x, y, size) {
this.piece = null;
this.color = null;
this.pieces = [];
this.isSelected = false;
this.isStanding = false;
this.roads = new Sides();
this.connected = new Sides({
disable: (side) => this.roads.set(side, false),
});
this.coord = itoa(x, y);
this.x = x;
this.y = y;
this.edges = new Sides({
enable: (side) => {
this.isEdge = true;
this["is" + EDGE[side]] = true;
this.isCorner = this.edges.length === 2;
},
});
this.isLight = x % 2 !== y % 2;
this.isEdge = false;
this.isCorner = false;
this.isNS = false;
this.isEW = false;
this.neighbors = new Sides();
this.edges.setSides({
N: y === size - 1,
S: y === 0,
E: x === size - 1,
W: x === 0,
});
if (this.isEdge) {
this.ring = 1;
} else {
const offset = (size - 1) / 2;
const getRing = (x) => 1 + Math.round(offset - Math.abs(x - offset));
this.ring = Math.min(getRing(x), getRing(y));
}
}
_getPiece() {
return this.pieces.length ? this.pieces[this.pieces.length - 1] : null;
}
_setPiece(piece) {
const prevColor = this.color;
const wasStanding = this.piece && this.isStanding;
this.piece = piece;
if (piece) {
this.color = piece.color;
this.isStanding = piece.isStanding;
} else {
this.color = null;
this.isStanding = false;
}
if (this.color !== prevColor || this.isStanding !== wasStanding) {
this._updateConnected();
}
}
_updateConnected() {
let neighbor, isConnected;
Object.keys(EDGE).forEach((side) => {
if (this.edges[side]) {
this.connected[side] = Boolean(this.piece && !this.isStanding);
} else if ((neighbor = this.neighbors[side])) {
isConnected = Boolean(
neighbor.color === this.color &&
this.piece &&
!this.isStanding &&
!neighbor.isStanding
);
this.connected[side] = isConnected;
neighbor.connected[OPPOSITE[side]] = isConnected;
}
});
}
setRoad(road) {
this.connected.forEach((side) => {
const isRoad = Boolean(
road &&
((this.edges[side] && road.edges[EDGE[side]]) ||
(this.neighbors[side] &&
road.squares.includes(this.neighbors[side].coord)))
);
if (!road || isRoad) {
this.roads[side] = isRoad;
}
});
}
setStackPiece(index, piece) {
piece.square = this;
this.pieces[index] = piece;
if (index === this.pieces.length - 1) {
this._setPiece(piece);
}
}
pushPiece(piece) {
piece.square = this;
this._setPiece(piece);
this.pieces.push(piece);
}
pushPieces(pieces) {
pieces.forEach((piece) => this.pushPiece(piece));
}
popPiece() {
const piece = this.pieces.pop();
if (piece) {
piece.square = null;
}
this._setPiece(this._getPiece());
return piece;
}
popPieces(count) {
while (count--) {
this.popPiece();
}
}
clear() {
while (this.pieces.length) {
this.popPiece();
}
}
};
class Sides {
constructor(options = {}) {
this.onEnable = options.enable || null;
this.onDisable = options.disable || null;
this._index = {
N: false,
S: false,
E: false,
W: false,
};
Object.keys(this._index).forEach((side) => {
Object.defineProperty(this, side, {
get: () => this.get(side),
set: (value) => {
this.set(side, value);
},
});
});
this.length = 0;
this._array = [];
[
"concat",
"filter",
"find",
"forEach",
"includes",
"indexOf",
"map",
"pop",
"push",
"shift",
"splice",
"unshift",
].forEach((fn) => (this[fn] = this._array[fn].bind(this._array)));
}
setSides(values) {
if (values) {
for (const side in values) {
this.set(side, values[side]);
}
}
}
get(side) {
return this._index[side];
}
set(side, value) {
if (this._index[side] !== value) {
if (value) {
this.push(typeof value === "boolean" ? side : value);
this._index[side] = value;
this.length = this._array.length;
if (this.onEnable) {
this.onEnable(side, value);
}
} else {
this.splice(
this.indexOf(typeof value === "boolean" ? side : this._index[side]),
1
);
this._index[side] = value;
this.length = this._array.length;
if (this.onDisable) {
this.onDisable(side, value);
}
}
}
}
}