From e9305893cbc5cfc805ad1e8815c299fd27074086 Mon Sep 17 00:00:00 2001 From: MiguelMLorente Date: Sat, 23 Nov 2024 22:49:51 +0100 Subject: [PATCH] Create board object builder and supporting classes and types --- interface/BoardBuilder.ts | 110 +++++++++++++++++++++++++++++++ interface/constants/CellType.ts | 6 ++ interface/constants/Direction.ts | 6 ++ interface/constants/ExitType.ts | 5 ++ interface/index.ts | 9 +++ interface/types/Border.ts | 22 +++++++ interface/types/Cell.ts | 24 +++++++ interface/types/Exit.ts | 9 +++ interface/types/ExternalNode.ts | 24 +++++++ interface/types/Node.ts | 9 +++ 10 files changed, 224 insertions(+) create mode 100644 interface/BoardBuilder.ts create mode 100644 interface/constants/CellType.ts create mode 100644 interface/constants/Direction.ts create mode 100644 interface/constants/ExitType.ts create mode 100644 interface/types/Border.ts create mode 100644 interface/types/Cell.ts create mode 100644 interface/types/Exit.ts create mode 100644 interface/types/ExternalNode.ts create mode 100644 interface/types/Node.ts diff --git a/interface/BoardBuilder.ts b/interface/BoardBuilder.ts new file mode 100644 index 0000000..c4ed092 --- /dev/null +++ b/interface/BoardBuilder.ts @@ -0,0 +1,110 @@ +import { CellType } from "./constants/CellType"; +import { Direction } from "./constants/Direction"; +import { ExitType } from "./constants/ExitType"; +import { Cell } from "./types/Cell"; +import { Exit } from "./types/Exit"; + +const boardSize = 7; +const universityLocations = [ + [2, 0], + [4, 1], + [3, 3], +]; +const factoryLocations = [ + [0, 0], + [5, 2], + [4, 6], +]; +const houseLocations = [ + [0, 2], + [1, 5], + [2, 4], + [5, 4], +]; +const mapPosition = (position: number[], type: CellType) => { + return { + row: position[0], + col: position[1], + type: type, + }; +}; +const specialUniversityCells = universityLocations.map((position) => + mapPosition(position, CellType.UNIVERSITY), +); +const specialFactoryCells = factoryLocations.map((position) => + mapPosition(position, CellType.FACTORY), +); +const specialHouseCells = houseLocations.map((position) => + mapPosition(position, CellType.HOUSE), +); +const specialCells = specialUniversityCells + .concat(specialFactoryCells) + .concat(specialHouseCells); + +const specialExitIndexes1 = [1, 5]; +const specialExitIndexes2 = [3]; + +function createBoard(): Cell[][] { + const indexes = Array.from(Array(boardSize).keys()); + return indexes.map((rowIndex) => + indexes.map((colIndex) => { + const specialCell = specialCells.find( + (specialCell) => + specialCell.row === rowIndex && specialCell.col === colIndex, + ); + return new Cell(specialCell ? specialCell.type : CellType.NORMAL); + }), + ); +} + +function connectAdjacentCells(board: Cell[][]) { + const indexes = Array.from(Array(boardSize).keys()); + for (const rowIndex of indexes.slice(0, -1)) { + for (const colIndex of indexes.slice(0, -1)) { + const cell = board[rowIndex][colIndex]; + const rightCell = board[rowIndex][colIndex + 1]; + const bottomCell = board[rowIndex + 1][colIndex]; + + cell + .getNodeAt(Direction.SOUTH) + .linkToNode(bottomCell.getNodeAt(Direction.NORTH)); + cell + .getNodeAt(Direction.EAST) + .linkToNode(rightCell.getNodeAt(Direction.WEST)); + } + } +} + +function addExits(board: Cell[][]) { + // Add exits to top row + board[0].forEach((cell, colIndex) => { + let exitType = ExitType.AMBIVALENT; + if (specialExitIndexes1.includes(colIndex)) exitType = ExitType.ROAD; + if (specialExitIndexes2.includes(colIndex)) exitType = ExitType.RAIL; + cell.getNodeAt(Direction.NORTH).linkToNode(new Exit(exitType)); + }); + + // Add exits to bottom row + board[boardSize - 1].forEach((cell, colIndex) => { + let exitType = ExitType.AMBIVALENT; + if (specialExitIndexes1.includes(colIndex)) exitType = ExitType.ROAD; + if (specialExitIndexes2.includes(colIndex)) exitType = ExitType.RAIL; + cell.getNodeAt(Direction.SOUTH).linkToNode(new Exit(exitType)); + }); + + // Add exits to left and right columns + board.forEach((row, rowIndex) => { + let exitType = ExitType.AMBIVALENT; + if (specialExitIndexes1.includes(rowIndex)) exitType = ExitType.RAIL; + if (specialExitIndexes2.includes(rowIndex)) exitType = ExitType.ROAD; + row[0].getNodeAt(Direction.WEST).linkToNode(new Exit(exitType)); + row[boardSize - 1].getNodeAt(Direction.EAST).linkToNode(new Exit(exitType)); + }); +} + +export function buildBoard(): Cell[][] { + const board: Cell[][] = createBoard(); + connectAdjacentCells(board); + addExits(board); + return board; +} diff --git a/interface/constants/CellType.ts b/interface/constants/CellType.ts new file mode 100644 index 0000000..a22a68e --- /dev/null +++ b/interface/constants/CellType.ts @@ -0,0 +1,6 @@ +export enum CellType { + NORMAL = "NORMAL", + HOUSE = "HOUSE", + FACTORY = "FACTORY", + UNIVERSITY = "UNIVERSITY", +} diff --git a/interface/constants/Direction.ts b/interface/constants/Direction.ts new file mode 100644 index 0000000..1f54f0c --- /dev/null +++ b/interface/constants/Direction.ts @@ -0,0 +1,6 @@ +export enum Direction { + NORTH = "NORTH", + SOUTH = "SOUTH", + EAST = "EAST", + WEST = "WEST", +} diff --git a/interface/constants/ExitType.ts b/interface/constants/ExitType.ts new file mode 100644 index 0000000..2a6bf4c --- /dev/null +++ b/interface/constants/ExitType.ts @@ -0,0 +1,5 @@ +export enum ExitType { + RAIL = "RAIL", + ROAD = "ROAD", + AMBIVALENT = "AMBIVALENT", +} diff --git a/interface/index.ts b/interface/index.ts index ac13701..a686c28 100644 --- a/interface/index.ts +++ b/interface/index.ts @@ -1 +1,10 @@ +export * from "./constants/CellType"; +export * from "./constants/Direction"; +export * from "./constants/ExitType"; export * from "./constants/TrackType"; +export * from "./types/Border"; +export * from "./types/Cell"; +export * from "./types/Exit"; +export * from "./types/ExternalNode"; +export * from "./types/Node"; +export * from "./BoardBuilder"; diff --git a/interface/types/Border.ts b/interface/types/Border.ts new file mode 100644 index 0000000..f5618c9 --- /dev/null +++ b/interface/types/Border.ts @@ -0,0 +1,22 @@ +import { Exit } from "./Exit"; +import { ExternalNode } from "./ExternalNode"; + +export class Border { + private readonly firstNode: ExternalNode; + private readonly secondNode: ExternalNode | Exit; + + constructor(firstNode: ExternalNode, secondNode: ExternalNode | Exit) { + this.firstNode = firstNode; + this.secondNode = secondNode; + } + + public traverseFrom(node: ExternalNode): ExternalNode | Exit { + if (node === this.firstNode) { + return this.secondNode; + } + if (node === this.secondNode) { + return this.firstNode; + } + throw Error("Unable to traverse border"); + } +} diff --git a/interface/types/Cell.ts b/interface/types/Cell.ts new file mode 100644 index 0000000..822ce34 --- /dev/null +++ b/interface/types/Cell.ts @@ -0,0 +1,24 @@ +import { CellType } from "../constants/CellType"; +import { Direction } from "../constants/Direction"; +import { ExternalNode } from "./ExternalNode"; + +export class Cell { + public readonly externalNodes: Map; + public readonly cellType: CellType; + + 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 this.externalNodes.get(direction) as ExternalNode; + } +} diff --git a/interface/types/Exit.ts b/interface/types/Exit.ts new file mode 100644 index 0000000..38fe187 --- /dev/null +++ b/interface/types/Exit.ts @@ -0,0 +1,9 @@ +import { ExitType } from "../constants/ExitType"; + +export class Exit { + public readonly type: ExitType; + + constructor(type: ExitType) { + this.type = type; + } +} diff --git a/interface/types/ExternalNode.ts b/interface/types/ExternalNode.ts new file mode 100644 index 0000000..c951c54 --- /dev/null +++ b/interface/types/ExternalNode.ts @@ -0,0 +1,24 @@ +import { Direction } from "../constants/Direction"; +import { Border } from "./Border"; +import { Cell } from "./Cell"; +import { Exit } from "./Exit"; +import { Node } from "./Node"; + +export class ExternalNode extends Node { + public readonly direction: Direction; + private border?: Border; + + constructor(cell: Cell, direction: Direction) { + super(cell); + this.direction = direction; + } + + public linkToNode(other: ExternalNode | Exit) { + this.border = new Border(this, other); + } + + public traverseBorder(): ExternalNode | Exit { + if (!this.border) throw Error(`Missing border for node`); + return (this.border as Border).traverseFrom(this); + } +} diff --git a/interface/types/Node.ts b/interface/types/Node.ts new file mode 100644 index 0000000..b185ad2 --- /dev/null +++ b/interface/types/Node.ts @@ -0,0 +1,9 @@ +import { Cell } from "./Cell"; + +export abstract class Node { + public readonly cell: Cell; + + constructor(cell: Cell) { + this.cell = cell; + } +}