TrainsAndRoads/interface/types/Cell.ts

107 lines
3.5 KiB
TypeScript

import { CellType } from "../constants/CellType";
import { Direction, directions } from "../constants/Direction";
import { ExitType } from "../constants/ExitType";
import { PieceId, pieceMap } from "../constants/Pieces";
import { TrackType } from "../constants/TrackType";
import { Exit } from "./Exit";
import { ExternalNode } from "./ExternalNode";
import { InternalNode } from "./InternalNode";
import { Piece } from "./Piece";
import { PlacedPiece } from "./PlacedPiece";
export class Cell {
public readonly externalNodes: Map<Direction, ExternalNode>;
public readonly cellType: CellType;
public placedPiece?: {
piece: PlacedPiece;
id: PieceId;
};
constructor(cellType: CellType) {
this.externalNodes = new Map([
[Direction.NORTH, new ExternalNode(this, Direction.NORTH)],
[Direction.SOUTH, new ExternalNode(this, Direction.SOUTH)],
[Direction.EAST, new ExternalNode(this, Direction.EAST)],
[Direction.WEST, new ExternalNode(this, Direction.WEST)],
]);
this.cellType = cellType;
}
public getNodeAt(direction: Direction): ExternalNode {
const node = this.externalNodes.get(direction);
if (!node) throw Error(`Could not find node at ${direction}`);
return node!;
}
public placePiece(pieceId: PieceId) {
if (this.placedPiece !== undefined) return;
const piece: Piece = pieceMap[pieceId];
this.validatePiecePlacement(piece);
this.placedPiece = {
piece: piece.toPlacedPiece(this),
id: pieceId,
};
}
private validatePiecePlacement(piece: Piece) {
const hasAnyConnection = Array.from(piece.tracks)
.map((track) => {
const trackExternalNodes = [
this.getNodeAt(track.joinedPoints.firstPoint),
];
if (!(track.joinedPoints.secondPoint instanceof InternalNode)) {
trackExternalNodes.push(
this.getNodeAt(track.joinedPoints.secondPoint),
);
}
return {
trackExternalNodes: trackExternalNodes,
trackType: track.type,
};
})
.some(({ trackExternalNodes, trackType }) => {
let isTrackConnected: boolean = false;
trackExternalNodes
.filter((node) => node.traverseBorder() instanceof Exit)
.forEach((node) => {
const exitType = (node.traverseBorder() as Exit).type;
isTrackConnected = true;
if (
exitType !== ExitType.AMBIVALENT &&
exitType.toString() !== trackType.toString()
) {
throw Error(
`Unable to place piece, invalid exit type at direction ${node.direction}`,
);
}
});
trackExternalNodes
.filter((node) => node.traverseBorder() instanceof ExternalNode)
.forEach((node) => {
const adjacentExternalNode = node.traverseBorder() as ExternalNode;
const adjacentTrack =
adjacentExternalNode.cell.placedPiece?.piece.findTrackForDirection(
adjacentExternalNode.direction,
);
if (adjacentTrack !== undefined) {
isTrackConnected = true;
if (adjacentTrack.type !== trackType) {
throw Error(
`Unable to place piece next to another due to conflicting track types at ${node.direction}`,
);
}
}
});
return isTrackConnected;
});
if (!hasAnyConnection) {
throw Error("No adjacent exit or piece available to connect to");
}
}
}