2024-07-19 18:00:43 +02:00
|
|
|
"use client"
|
|
|
|
|
|
|
|
import React, {
|
2024-07-25 11:35:58 +02:00
|
|
|
useEffect
|
2024-07-19 18:00:43 +02:00
|
|
|
} from "react";
|
|
|
|
|
|
|
|
import anime from 'animejs/lib/anime.es.js';
|
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
type Pos = [number, number];
|
|
|
|
|
2024-07-19 18:00:43 +02:00
|
|
|
const cellSize = 32; // Size of each cell in the SVG
|
2024-07-25 11:35:58 +02:00
|
|
|
const offset = 0.2 * cellSize; // Offset for the outline
|
|
|
|
let radius = 0.5 * cellSize - offset;
|
|
|
|
let radius_inner = offset;
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
const findClusters = (shape: Pos, walls: Pos[]) => {
|
2024-07-19 18:00:43 +02:00
|
|
|
const [width, height] = shape;
|
2024-07-25 11:35:58 +02:00
|
|
|
const clusters: Pos[][] = [];
|
2024-07-19 18:00:43 +02:00
|
|
|
const visited = new Set<string>();
|
|
|
|
|
|
|
|
const directions = [
|
|
|
|
[0, 1], [1, 0], [0, -1], [-1, 0]
|
|
|
|
];
|
|
|
|
|
|
|
|
const inBounds = (x: number, y: number) => x >= 0 && x < width && y >= 0 && y < height;
|
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
const dfs = (x: number, y: number, cluster: Pos[]) => {
|
2024-07-19 18:00:43 +02:00
|
|
|
if (!inBounds(x, y) || visited.has(`${x},${y}`) || !walls.some(([bx, by]) => bx === x && by === y)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
visited.add(`${x},${y}`);
|
|
|
|
cluster.push([x, y]);
|
|
|
|
for (const [dx, dy] of directions) {
|
|
|
|
dfs(x + dx, y + dy, cluster);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (const [x, y] of walls) {
|
|
|
|
if (!visited.has(`${x},${y}`)) {
|
2024-07-25 11:35:58 +02:00
|
|
|
const cluster: Pos[] = [];
|
2024-07-19 18:00:43 +02:00
|
|
|
dfs(x, y, cluster);
|
|
|
|
clusters.push(cluster);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return clusters;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Generate the path for a single cluster using a perimeter tracing algorithm with inner and outer boundaries
|
2024-07-25 11:35:58 +02:00
|
|
|
const createPath = (cluster: Pos[]) => {
|
2024-07-19 18:00:43 +02:00
|
|
|
const visitedCorners = new Set<string>();
|
2024-07-25 11:35:58 +02:00
|
|
|
const visitedCells = new Set<string>();
|
|
|
|
|
|
|
|
|
|
|
|
/* Corners:
|
|
|
|
|
|
|
|
Reference points
|
|
|
|
|
|
|
|
+-4-----+-------+
|
|
|
|
| 0 7
|
|
|
|
| | |
|
|
|
|
| | |
|
|
|
|
+-2-----+-----1-+
|
|
|
|
| | |
|
|
|
|
| | |
|
|
|
|
8 3 |
|
|
|
|
+-------+----11-+
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
*/
|
|
|
|
type PathMove = { next: [Pos, number], path: string };
|
|
|
|
|
|
|
|
const move = (x: number, y: number, corner: number): PathMove | undefined => {
|
|
|
|
let refPoint0 = [0.5 * cellSize, offset];
|
|
|
|
let refPoint1 = [cellSize - offset, 0.5 * cellSize];
|
|
|
|
let refPoint2 = [offset, 0.5 * cellSize];
|
|
|
|
let refPoint3 = [0.5 * cellSize, cellSize - offset];
|
|
|
|
|
|
|
|
let refPoint4 = [offset, 0];
|
|
|
|
let refPoint7 = [cellSize, offset];
|
|
|
|
let refPoint8 = [0, cellSize - offset];
|
|
|
|
let refPoint11 = [cellSize - offset, cellSize];
|
2024-07-19 18:00:43 +02:00
|
|
|
|
|
|
|
switch (corner) {
|
|
|
|
case 0:
|
2024-07-25 11:35:58 +02:00
|
|
|
// exit if wall to the top
|
|
|
|
if (cluster.some(([bx, by]) => bx === x && by === y - 1)) return;
|
2024-07-19 18:00:43 +02:00
|
|
|
// check if there is a wall to the left
|
|
|
|
if (cluster.some(([bx, by]) => bx === x - 1 && by === y)) {
|
2024-07-25 11:35:58 +02:00
|
|
|
return { next: [[x - 1, y], 7], path: `l ${- cellSize / 2} 0` };
|
2024-07-19 18:00:43 +02:00
|
|
|
} else {
|
2024-07-25 11:35:58 +02:00
|
|
|
const px = x * cellSize + refPoint2[0];
|
|
|
|
const py = y * cellSize + refPoint2[1];
|
|
|
|
return { next: [[x, y], 2], path: `A ${radius} ${radius} 0 0 0 ${px} ${py}` };
|
2024-07-19 18:00:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case 1:
|
2024-07-25 11:35:58 +02:00
|
|
|
// exit if wall to the right
|
|
|
|
if (cluster.some(([bx, by]) => bx === x + 1 && by === y)) return;
|
2024-07-19 18:00:43 +02:00
|
|
|
// check if there is a wall to the top
|
|
|
|
if (cluster.some(([bx, by]) => bx === x && by === y - 1)) {
|
2024-07-25 11:35:58 +02:00
|
|
|
return { next: [[x, y - 1], 11], path: `l 0 ${- cellSize / 2}` };
|
2024-07-19 18:00:43 +02:00
|
|
|
} else {
|
2024-07-25 11:35:58 +02:00
|
|
|
const px = x * cellSize + refPoint0[0];
|
|
|
|
const py = y * cellSize + refPoint0[1];
|
|
|
|
return { next: [[x, y], 0], path: `A ${radius} ${radius} 0 0 0 ${px} ${py}` };
|
2024-07-19 18:00:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case 2:
|
2024-07-25 11:35:58 +02:00
|
|
|
// exit if wall to the left
|
|
|
|
if (cluster.some(([bx, by]) => bx === x - 1 && by === y)) return;
|
2024-07-19 18:00:43 +02:00
|
|
|
// check if there is a wall to the bottom
|
|
|
|
if (cluster.some(([bx, by]) => bx === x && by === y + 1)) {
|
2024-07-25 11:35:58 +02:00
|
|
|
return { next: [[x, y + 1], 4], path: `l 0,${cellSize / 2}` };
|
2024-07-19 18:00:43 +02:00
|
|
|
} else {
|
2024-07-25 11:35:58 +02:00
|
|
|
const px = x * cellSize + refPoint3[0];
|
|
|
|
const py = y * cellSize + refPoint3[1];
|
|
|
|
return { next: [[x, y], 3], path: `A ${radius},${radius} 0 0 0 ${px},${py}` };
|
2024-07-19 18:00:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
case 3:
|
2024-07-25 11:35:58 +02:00
|
|
|
// exit if wall to the bottom
|
|
|
|
if (cluster.some(([bx, by]) => bx === x && by === y + 1)) return;
|
2024-07-19 18:00:43 +02:00
|
|
|
// check if there is a wall to the right
|
|
|
|
if (cluster.some(([bx, by]) => bx === x + 1 && by === y)) {
|
2024-07-25 11:35:58 +02:00
|
|
|
return { next: [[x + 1, y], 8], path: `l ${cellSize / 2},0` };
|
2024-07-19 18:00:43 +02:00
|
|
|
} else {
|
2024-07-25 11:35:58 +02:00
|
|
|
const px = x * cellSize + refPoint1[0];
|
|
|
|
const py = y * cellSize + refPoint1[1];
|
|
|
|
return { next: [[x, y], 1], path: `A ${radius},${radius} 0 0 0 ${px},${py}` };
|
2024-07-19 18:00:43 +02:00
|
|
|
}
|
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
case 4:
|
|
|
|
// check if there is a wall to the left
|
|
|
|
if (cluster.some(([bx, by]) => bx === x - 1 && by === y)) {
|
|
|
|
const px = (x - 1) * cellSize + refPoint7[0];
|
|
|
|
const py = y * cellSize + refPoint7[1];
|
|
|
|
return { next: [[x - 1, y], 7], path: `A ${radius_inner},${radius_inner} 0 0 1 ${px},${py}` };
|
|
|
|
} else {
|
|
|
|
return { next: [[x, y], 2], path: `l 0 ${cellSize / 2}` };
|
|
|
|
}
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
case 7:
|
|
|
|
// check if there is a wall to the top
|
|
|
|
if (cluster.some(([bx, by]) => bx === x && by === y - 1)) {
|
|
|
|
const px = x * cellSize + refPoint11[0];
|
|
|
|
const py = (y - 1) * cellSize + refPoint11[1];
|
|
|
|
return { next: [[x, y - 1], 11], path: `A ${radius_inner},${radius_inner} 0 0 1 ${px},${py}` };
|
|
|
|
} else {
|
|
|
|
return { next: [[x, y], 0], path: `l ${- cellSize / 2},0` };
|
|
|
|
}
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
case 8:
|
|
|
|
// check if there is a wall to the bottom
|
|
|
|
if (cluster.some(([bx, by]) => bx === x && by === y + 1)) {
|
|
|
|
const px = x * cellSize + refPoint4[0];
|
|
|
|
const py = (y + 1) * cellSize + refPoint4[1];
|
|
|
|
return { next: [[x, y + 1], 4], path: `A ${radius_inner},${radius_inner} 0 0 1 ${px},${py}` };
|
|
|
|
} else {
|
|
|
|
return { next: [[x, y], 3], path: `l ${cellSize / 2},0` };
|
|
|
|
}
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
case 11:
|
|
|
|
// check if there is a wall to the right
|
|
|
|
if (cluster.some(([bx, by]) => bx === x + 1 && by === y)) {
|
|
|
|
const px = (x + 1) * cellSize + refPoint8[0];
|
|
|
|
const py = y * cellSize + refPoint8[1];
|
|
|
|
return { next: [[x + 1, y], 8], path: `A ${radius_inner},${radius_inner} 0 0 1 ${px},${py}` };
|
|
|
|
} else {
|
|
|
|
return { next: [[x, y], 1], path: `l 0,${- cellSize / 2}` };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
const pathCommands = cluster.flatMap((startCell) => {
|
|
|
|
// iterate through all possible starting points in all cells (unless already visited)
|
|
|
|
const paths = [0, 1, 2, 3].map(startRefPoint => {
|
|
|
|
let [x, y] = startCell;
|
|
|
|
if (visitedCorners.has(`${x},${y},${startRefPoint}`)) return "";
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
let refPoint = startRefPoint;
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
let refPointLoc = [[0.5 * cellSize, offset],
|
|
|
|
[cellSize - offset, 0.5 * cellSize],
|
|
|
|
[offset, 0.5 * cellSize],
|
|
|
|
[0.5 * cellSize, cellSize - offset]
|
|
|
|
];
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
const px = x * cellSize + refPointLoc[refPoint][0];
|
|
|
|
const py = y * cellSize + refPointLoc[refPoint][1];
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
let pathCommands = [];
|
|
|
|
pathCommands.push([`M ${px},${py}`]);
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
do {
|
|
|
|
const node = `${x},${y},${refPoint}`;
|
|
|
|
if (visitedCorners.has(node)) {
|
|
|
|
break;
|
|
|
|
}
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
visitedCorners.add(node);
|
|
|
|
visitedCells.add(`${x},${y}`);
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
let pathMove = move(x, y, refPoint);
|
|
|
|
if (!pathMove) return "";
|
|
|
|
let path = "";
|
|
|
|
({ next: [[x, y], refPoint], path: path } = pathMove);
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
pathCommands.push([path]);
|
|
|
|
} while (!(x === startCell[0] && y === startCell[1] && refPoint === startRefPoint));
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
pathCommands.push(["Z"]); // Close the path
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
const joined = pathCommands.join(" ");
|
|
|
|
return joined;
|
|
|
|
});
|
|
|
|
return paths;
|
|
|
|
});
|
2024-07-19 18:00:43 +02:00
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
console.log(pathCommands);
|
|
|
|
return pathCommands.join(" ");
|
2024-07-19 18:00:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
function Bot({ position, color, width }: { position: Pos, color: string, width: number }) {
|
2024-07-19 18:00:43 +02:00
|
|
|
const leftSide = position[0] < width / 2;
|
|
|
|
const inHomezone = () => {
|
|
|
|
switch (color) {
|
|
|
|
case "blue":
|
|
|
|
return leftSide;
|
|
|
|
|
|
|
|
case "red":
|
|
|
|
return !leftSide;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
anime.timeline()
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox .blue',
|
|
|
|
opacity: [0, 1],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 2000,
|
|
|
|
}, 3500)
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox .red',
|
|
|
|
opacity: [0, 1],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 2000,
|
|
|
|
}, 3500);;
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<use transform={`translate(${(position[0]) * cellSize}, ${(position[1]) * cellSize}) scale(3)`} href={inHomezone() ? `#ghost` : `#pacman`} className={color} />
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-07-25 11:35:58 +02:00
|
|
|
|
2024-07-19 18:00:43 +02:00
|
|
|
function Maze({ game_uuid, shape, walls, food, a, b, x, y, whowins, gameover }:
|
|
|
|
{
|
|
|
|
game_uuid: string,
|
2024-07-25 11:35:58 +02:00
|
|
|
shape: Pos,
|
|
|
|
walls: Pos[],
|
|
|
|
food: Pos[],
|
|
|
|
a: Pos,
|
|
|
|
b: Pos,
|
|
|
|
x: Pos,
|
|
|
|
y: Pos,
|
2024-07-19 18:00:43 +02:00
|
|
|
whowins: number | null,
|
|
|
|
gameover: boolean
|
|
|
|
}
|
|
|
|
) {
|
|
|
|
const [width, height] = shape;
|
|
|
|
const clusters = findClusters(shape, walls);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (game_uuid) {
|
|
|
|
let pathAnimation = anime.timeline()
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox #maze path',
|
|
|
|
strokeDashoffset: [anime.setDashoffset, 0],
|
|
|
|
easing: 'easeInCubic',
|
|
|
|
duration: 2000,
|
|
|
|
delay: function (el, i) { return i * 25 },
|
|
|
|
direction: 'alternate',
|
|
|
|
loop: false
|
|
|
|
})
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox #maze path',
|
2024-07-25 11:35:58 +02:00
|
|
|
//fill: ['rgb(214, 219, 220)', "#faa"], // ffa
|
|
|
|
fillOpacity: [0, 0.7], // ffa
|
2024-07-19 18:00:43 +02:00
|
|
|
easing: 'linear',
|
|
|
|
duration: 2000
|
|
|
|
}, 2000)
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox .foodblue',
|
|
|
|
opacity: [0, 1],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 2000,
|
|
|
|
}, 3000)
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox .foodred',
|
|
|
|
opacity: [0, 1],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 2000,
|
|
|
|
}, 3000)
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox .blue',
|
|
|
|
opacity: [0, 1],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 2000,
|
|
|
|
}, 3500)
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox .red',
|
|
|
|
opacity: [0, 1],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 2000,
|
|
|
|
}, 3500);
|
|
|
|
}
|
2024-07-25 11:35:58 +02:00
|
|
|
}, [walls, game_uuid]);
|
2024-07-19 18:00:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
console.log(gameover, whowins);
|
|
|
|
if (gameover && whowins === 0) {
|
|
|
|
let pathAnimation = anime.timeline()
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox #maze path',
|
|
|
|
fill: ["#faa", "rgb(94, 158, 217)"],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 200
|
|
|
|
});
|
|
|
|
};
|
|
|
|
if (gameover && whowins === 1) {
|
|
|
|
let pathAnimation = anime.timeline()
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox #maze path',
|
|
|
|
fill: ["#faa", "rgb(235, 90, 90)"],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 200
|
|
|
|
});
|
|
|
|
};
|
|
|
|
if (gameover && whowins === 2) {
|
|
|
|
let pathAnimation = anime.timeline()
|
|
|
|
.add({
|
|
|
|
targets: '#mazebox #maze path',
|
|
|
|
fill: ["#faa", "#fff"],
|
|
|
|
easing: 'linear',
|
|
|
|
duration: 200
|
|
|
|
});
|
|
|
|
};
|
|
|
|
}, [gameover, whowins]);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div id="mazebox">
|
|
|
|
<svg
|
|
|
|
width={width * cellSize}
|
|
|
|
height={height * cellSize}
|
|
|
|
viewBox={`0 0 ${width * cellSize} ${height * cellSize}`}
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
>
|
|
|
|
<style type="text/css">{`
|
|
|
|
line {
|
|
|
|
stroke: #000000;
|
|
|
|
stroke-linecap: round;
|
|
|
|
stroke-width: 3;
|
|
|
|
}
|
|
|
|
.foodblue {
|
|
|
|
stroke: black;
|
|
|
|
fill: rgb(94, 158, 217);
|
|
|
|
}
|
|
|
|
.foodred {
|
|
|
|
stroke: black;
|
|
|
|
fill: rgb(235, 90, 90);
|
|
|
|
}
|
|
|
|
.blue {
|
|
|
|
fill: rgb(94, 158, 217);
|
|
|
|
}
|
|
|
|
.red {
|
|
|
|
fill: rgb(235, 90, 90);
|
|
|
|
}
|
|
|
|
|
|
|
|
.gameover {
|
|
|
|
fill: #FFC903;
|
|
|
|
stroke: #ED1B22;
|
|
|
|
stroke-width: 10px;
|
|
|
|
stroke-linejoin: round;
|
|
|
|
paint-order: stroke;
|
|
|
|
}
|
|
|
|
`}</style>
|
|
|
|
|
|
|
|
|
|
|
|
<defs>
|
2024-07-25 11:35:58 +02:00
|
|
|
<linearGradient id="grad" gradientUnits="userSpaceOnUse">
|
|
|
|
<stop stopColor="blue" offset="0" />
|
|
|
|
<stop stopColor="blue" offset="50%" />
|
|
|
|
<stop stopColor="red" offset="50%" />
|
|
|
|
<stop stopColor="red" offset="100%" />
|
|
|
|
</linearGradient>
|
2024-07-19 18:00:43 +02:00
|
|
|
<g id="pacman">
|
|
|
|
<path d="M 9.98 7.73
|
|
|
|
A 4.38 4.38 0 1 1 9.98 3.8
|
|
|
|
L 6.05 5.8
|
|
|
|
Z"
|
|
|
|
/>
|
|
|
|
</g>
|
|
|
|
|
|
|
|
<g id="ghost">
|
|
|
|
<path d="M 2 6
|
|
|
|
C 2 3.79 3.8 2 6 2
|
|
|
|
C 8.21 2 10 3.8 10 6
|
|
|
|
L 10 10
|
|
|
|
L 9.01 8.81
|
|
|
|
L 8.01 10
|
|
|
|
L 7 8.81
|
|
|
|
L 6.01 10
|
|
|
|
L 5.01 8.81
|
|
|
|
L 4 10
|
|
|
|
L 3 8.81
|
|
|
|
L 2 10
|
|
|
|
L 2 6
|
|
|
|
Z
|
|
|
|
M 4.39 6.54
|
|
|
|
C 4.9 6.54 5.31 6.03 5.31 5.38
|
|
|
|
C 5.31 4.74 4.9 4.22 4.39 4.22
|
|
|
|
C 3.88 4.22 3.47 4.74 3.47 5.38
|
|
|
|
C 3.47 6.03 3.88 6.54 4.39 6.54
|
|
|
|
Z
|
|
|
|
M 6.9 6.54
|
|
|
|
C 7.41 6.54 7.82 6.03 7.82 5.38
|
|
|
|
C 7.82 4.74 7.41 4.22 6.9 4.22
|
|
|
|
C 6.39 4.22 5.98 4.74 5.98 5.38
|
|
|
|
C 5.98 6.03 6.39 6.54 6.9 6.54
|
|
|
|
Z
|
|
|
|
M 4.25 6
|
|
|
|
C 4.44 6 4.6 5.79 4.6 5.53
|
|
|
|
C 4.6 5.27 4.44 5.05 4.25 5.05
|
|
|
|
C 4.05 5.05 3.89 5.27 3.89 5.53
|
|
|
|
C 3.89 5.79 4.05 6 4.25 6
|
|
|
|
Z
|
|
|
|
M 6.76 6
|
|
|
|
C 6.95 6 7.11 5.79 7.11 5.53
|
|
|
|
C 7.11 5.27 6.95 5.05 6.76 5.05
|
|
|
|
C 6.56 5.05 6.4 5.27 6.4 5.53
|
|
|
|
C 6.4 5.79 6.56 6 6.76 6
|
|
|
|
Z"></path>
|
|
|
|
</g>
|
|
|
|
|
|
|
|
</defs>
|
|
|
|
|
|
|
|
<g id="maze">
|
|
|
|
{walls.map(([x, y], index) => (
|
|
|
|
<rect
|
|
|
|
key={`${x},${y}`}
|
|
|
|
x={x * cellSize}
|
|
|
|
y={y * cellSize}
|
|
|
|
width={cellSize}
|
|
|
|
height={cellSize}
|
2024-07-25 11:35:58 +02:00
|
|
|
opacity="0"
|
|
|
|
// fill="lightblue"
|
|
|
|
stroke="lightgrey"
|
2024-07-19 18:00:43 +02:00
|
|
|
/>
|
|
|
|
))}
|
|
|
|
{clusters.map((cluster, index) => (
|
|
|
|
<path
|
|
|
|
className="maze"
|
|
|
|
key={`${cluster[0]},${cluster[1]}-${cluster.length}`}
|
|
|
|
d={createPath(cluster)}
|
2024-07-25 11:35:58 +02:00
|
|
|
// stroke="lightblue"
|
|
|
|
stroke="url(#grad)"
|
2024-07-19 18:00:43 +02:00
|
|
|
strokeWidth="2"
|
2024-07-25 11:35:58 +02:00
|
|
|
fill="#ffa"
|
2024-07-19 18:00:43 +02:00
|
|
|
strokeLinecap="round"
|
|
|
|
strokeLinejoin="bevel"
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</g>
|
|
|
|
{food.map(([x, y], index) => (
|
|
|
|
<circle key={index} cx={(0.5 + x) * cellSize} cy={(0.5 + y) * cellSize} r={cellSize / 5} className={x < width / 2 ? "foodblue" : "foodred"}></circle>
|
|
|
|
))}
|
|
|
|
|
|
|
|
<Bot position={a} key="botA" color="blue" width={width}></Bot>
|
|
|
|
<Bot position={b} key="botB" color="blue" width={width}></Bot>
|
|
|
|
<Bot position={x} key="botX" color="red" width={width}></Bot>
|
|
|
|
<Bot position={y} key="botY" color="red" width={width}></Bot>
|
|
|
|
|
|
|
|
{
|
|
|
|
gameover ? (<text fontSize="100" className="gameover"
|
2024-07-25 11:35:58 +02:00
|
|
|
x="50%" y="50%"
|
|
|
|
dominantBaseline="middle"
|
|
|
|
textAnchor="middle"
|
2024-07-19 18:00:43 +02:00
|
|
|
>
|
2024-07-25 11:35:58 +02:00
|
|
|
GAME OVER
|
|
|
|
</text>) : null
|
|
|
|
}
|
2024-07-19 18:00:43 +02:00
|
|
|
|
|
|
|
</svg>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
export default Maze;
|
|
|
|
|