Compare commits
No commits in common. "d72984851e77b09cbb8738c89de872d50b11ab21" and "ec6526deb5b3fa95531ff57eba0fe27feaa74d0c" have entirely different histories.
d72984851e
...
ec6526deb5
|
|
@ -1,115 +0,0 @@
|
||||||
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) {
|
|
||||||
const cell = board[rowIndex][colIndex];
|
|
||||||
const bottomCell = board[rowIndex + 1][colIndex];
|
|
||||||
cell
|
|
||||||
.getNodeAt(Direction.SOUTH)
|
|
||||||
.linkToNode(bottomCell.getNodeAt(Direction.NORTH));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const rowIndex of indexes) {
|
|
||||||
for (const colIndex of indexes.slice(0, -1)) {
|
|
||||||
const cell = board[rowIndex][colIndex];
|
|
||||||
const rightCell = board[rowIndex][colIndex + 1];
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
export enum CellType {
|
|
||||||
NORMAL = "NORMAL",
|
|
||||||
HOUSE = "HOUSE",
|
|
||||||
FACTORY = "FACTORY",
|
|
||||||
UNIVERSITY = "UNIVERSITY",
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
export enum Direction {
|
|
||||||
NORTH = "NORTH",
|
|
||||||
SOUTH = "SOUTH",
|
|
||||||
EAST = "EAST",
|
|
||||||
WEST = "WEST",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const directions: Direction[] = [
|
|
||||||
Direction.NORTH,
|
|
||||||
Direction.SOUTH,
|
|
||||||
Direction.EAST,
|
|
||||||
Direction.WEST,
|
|
||||||
];
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
export enum ExitType {
|
|
||||||
RAIL = "RAIL",
|
|
||||||
ROAD = "ROAD",
|
|
||||||
AMBIVALENT = "AMBIVALENT",
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1 @@
|
||||||
export * from "./constants/CellType";
|
|
||||||
export * from "./constants/Direction";
|
|
||||||
export * from "./constants/ExitType";
|
|
||||||
export * from "./constants/TrackType";
|
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";
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
import { CellType } from "../constants/CellType";
|
|
||||||
import { Direction } from "../constants/Direction";
|
|
||||||
import { ExternalNode } from "./ExternalNode";
|
|
||||||
|
|
||||||
export class Cell {
|
|
||||||
public readonly externalNodes: Map<Direction, ExternalNode>;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import { ExitType } from "../constants/ExitType";
|
|
||||||
|
|
||||||
export class Exit {
|
|
||||||
public readonly type: ExitType;
|
|
||||||
|
|
||||||
constructor(type: ExitType) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
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);
|
|
||||||
if (other instanceof ExternalNode) {
|
|
||||||
other.border = this.border;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public traverseBorder(): ExternalNode | Exit {
|
|
||||||
if (!this.border) {
|
|
||||||
throw Error(`Missing border for node`);
|
|
||||||
}
|
|
||||||
return (this.border as Border).traverseFrom(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import { Cell } from "./Cell";
|
|
||||||
|
|
||||||
export abstract class Node {
|
|
||||||
public readonly cell: Cell;
|
|
||||||
|
|
||||||
constructor(cell: Cell) {
|
|
||||||
this.cell = cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,12 +13,5 @@ module.exports = {
|
||||||
jest: true,
|
jest: true,
|
||||||
},
|
},
|
||||||
ignorePatterns: [".eslintrc.js", "dist/"],
|
ignorePatterns: [".eslintrc.js", "dist/"],
|
||||||
rules: {
|
rules: {},
|
||||||
"prettier/prettier": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
endOfLine: "auto",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
@use "node_modules/@picocss/pico/scss/pico" with (
|
@use "node_modules/@picocss/pico/scss/pico" with (
|
||||||
$theme-color: "pumpkin"
|
$theme-color: "pumpkin"
|
||||||
);
|
);
|
||||||
|
|
||||||
@import "./pages/game/GamePage.scss";
|
|
||||||
@import "./pages/game/components/GameBoard.scss";
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
|
import React from "react";
|
||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
import GamePage from "./pages/game/GamePage";
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const socket = io("http://localhost:3010");
|
const socket = io("http://localhost:3010");
|
||||||
|
const emitData = () =>
|
||||||
|
console.log(socket.emit("example-request", "custom-request"));
|
||||||
socket.on("example-response", (data) =>
|
socket.on("example-response", (data) =>
|
||||||
console.log(`Received response in front end with data: ${data}`),
|
console.log(`Received response in front end with data: ${data}`),
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<GamePage />
|
<header className="App-header">
|
||||||
|
<button onClick={emitData}>Emit Data</button>
|
||||||
|
Hello World! Front
|
||||||
|
</header>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import GameBoard from "./components/GameBoard";
|
|
||||||
|
|
||||||
const GamePage = () => {
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<h1>Game Page Title</h1>
|
|
||||||
<div className="game-panel">
|
|
||||||
<GameBoard />
|
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GamePage;
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
.game-board {
|
|
||||||
height: 850px;
|
|
||||||
width: 850px;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(7, auto);
|
|
||||||
gap: auto;
|
|
||||||
padding: 50px;
|
|
||||||
|
|
||||||
.cell {
|
|
||||||
width: 100px;
|
|
||||||
height: 100px;
|
|
||||||
border: 2px solid black;
|
|
||||||
border-radius: 10%;
|
|
||||||
|
|
||||||
&.house {
|
|
||||||
background-color: blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.university {
|
|
||||||
background-color: orange;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.factory {
|
|
||||||
background-color: grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
.exit:has(.exit-road) {
|
|
||||||
border: solid black;
|
|
||||||
border-width: 0 1px;
|
|
||||||
|
|
||||||
.exit-road {
|
|
||||||
margin: auto;
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
border: dashed black;
|
|
||||||
border-width: 0 1px;
|
|
||||||
transform: translate(calc(50% - 1px));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.exit:has(.exit-road) {
|
|
||||||
border: solid black;
|
|
||||||
border-width: 0 1px;
|
|
||||||
|
|
||||||
.exit-road {
|
|
||||||
margin: auto;
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
border: dashed black;
|
|
||||||
border-width: 0 1px;
|
|
||||||
transform: translate(calc(50% - 1px));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.exit:has(.exit-rail) {
|
|
||||||
border-width: 0 1px;
|
|
||||||
|
|
||||||
.exit-rail {
|
|
||||||
margin: auto;
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
border: solid black;
|
|
||||||
border-width: 0 1px;
|
|
||||||
transform: translate(calc(50% - 1px));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.exit:has(.exit-ambivalent) {
|
|
||||||
border-width: 0 1px;
|
|
||||||
border-style: dotted;
|
|
||||||
|
|
||||||
.exit-ambivalent {
|
|
||||||
margin: auto;
|
|
||||||
width: 0;
|
|
||||||
height: 100%;
|
|
||||||
border: dotted black;
|
|
||||||
border-width: 0 1px 0 0;
|
|
||||||
transform: translate(calc(50%));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.exit {
|
|
||||||
position: relative;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
|
|
||||||
&:has(.exit-north) {
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(calc(-50% - 1px), -100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:has(.exit-south) {
|
|
||||||
left: 50%;
|
|
||||||
top: 100%;
|
|
||||||
transform: translate(calc(-50% - 1px), 0%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:has(.exit-east) {
|
|
||||||
left: 100%;
|
|
||||||
top: 50%;
|
|
||||||
transform: rotate(90deg) translate(-50%, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:has(.exit-west) {
|
|
||||||
left: 0%;
|
|
||||||
top: 50%;
|
|
||||||
transform: rotate(90deg) translate(-50%, 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:has(.exit-north), &:has(.exit-south) {
|
|
||||||
+ .exit:has(.exit-east) {
|
|
||||||
left: 100%;
|
|
||||||
transform: rotate(90deg) translate(-150%, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:has(.exit-north), &:has(.exit-south) {
|
|
||||||
+ .exit:has(.exit-west) {
|
|
||||||
top: 50%;
|
|
||||||
transform: rotate(90deg) translate(-150%, 100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
import { buildBoard, Cell, directions, Exit } from "interface";
|
|
||||||
|
|
||||||
const GameBoard = () => {
|
|
||||||
const board: Cell[][] = buildBoard();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="game-board">
|
|
||||||
{board.flatMap((row) =>
|
|
||||||
row.map((cell) => (
|
|
||||||
<div className={"cell " + cell.cellType.toLowerCase()}>
|
|
||||||
{directions.map((direction) => {
|
|
||||||
const traversedNode = cell.getNodeAt(direction).traverseBorder();
|
|
||||||
const isExit = traversedNode instanceof Exit;
|
|
||||||
if (!isExit) return;
|
|
||||||
const className =
|
|
||||||
`exit-${direction.toLowerCase()}` +
|
|
||||||
` exit-${(traversedNode as Exit).type.toLowerCase()}`;
|
|
||||||
return (
|
|
||||||
<div className="exit">
|
|
||||||
<div className={className} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GameBoard;
|
|
||||||
Loading…
Reference in New Issue