mirror of
https://github.com/nullnyat/NullcatChan.git
synced 2025-04-29 05:37:19 +09:00
224 lines
6.7 KiB
TypeScript
224 lines
6.7 KiB
TypeScript
import * as gen from 'random-seed';
|
|
import { CellType } from './maze';
|
|
|
|
const cellVariants = {
|
|
void: {
|
|
digg: { left: null, right: null, top: null, bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
empty: {
|
|
digg: { left: 'left', right: 'right', top: 'top', bottom: 'bottom' },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
left: {
|
|
digg: { left: null, right: 'leftRight', top: 'leftTop', bottom: 'leftBottom' },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
right: {
|
|
digg: { left: 'leftRight', right: null, top: 'rightTop', bottom: 'rightBottom' },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
top: {
|
|
digg: { left: 'leftTop', right: 'rightTop', top: null, bottom: 'topBottom' },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
bottom: {
|
|
digg: { left: 'leftBottom', right: 'rightBottom', top: 'topBottom', bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
leftTop: {
|
|
digg: { left: null, right: 'leftRightTop', top: null, bottom: 'leftTopBottom' },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
leftBottom: {
|
|
digg: { left: null, right: 'leftRightBottom', top: 'leftTopBottom', bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
rightTop: {
|
|
digg: { left: 'leftRightTop', right: null, top: null, bottom: 'rightTopBottom' },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
rightBottom: {
|
|
digg: { left: 'leftRightBottom', right: null, top: 'rightTopBottom', bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
leftRightTop: {
|
|
digg: { left: null, right: null, top: null, bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
leftRightBottom: {
|
|
digg: { left: null, right: null, top: null, bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
leftTopBottom: {
|
|
digg: { left: null, right: null, top: null, bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
rightTopBottom: {
|
|
digg: { left: null, right: null, top: null, bottom: null },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
leftRight: {
|
|
digg: { left: null, right: null, top: 'leftRightTop', bottom: 'leftRightBottom' },
|
|
cross: { left: false, right: false, top: true, bottom: true },
|
|
},
|
|
topBottom: {
|
|
digg: { left: 'leftTopBottom', right: 'rightTopBottom', top: null, bottom: null },
|
|
cross: { left: true, right: true, top: false, bottom: false },
|
|
},
|
|
cross: {
|
|
digg: { left: 'cross', right: 'cross', top: 'cross', bottom: 'cross' },
|
|
cross: { left: false, right: false, top: false, bottom: false },
|
|
},
|
|
} as { [k in CellType]: {
|
|
digg: { left: CellType | null; right: CellType | null; top: CellType | null; bottom: CellType | null; };
|
|
cross: { left: boolean; right: boolean; top: boolean; bottom: boolean; };
|
|
} };
|
|
|
|
type Dir = 'left' | 'right' | 'top' | 'bottom';
|
|
|
|
export function genMaze(seed, complexity?) {
|
|
const rand = gen.create(seed);
|
|
|
|
let mazeSize;
|
|
if (complexity) {
|
|
if (complexity === 'veryEasy') mazeSize = 3 + rand(3);
|
|
if (complexity === 'easy') mazeSize = 8 + rand(8);
|
|
if (complexity === 'hard') mazeSize = 22 + rand(13);
|
|
if (complexity === 'veryHard') mazeSize = 40 + rand(20);
|
|
if (complexity === 'ai') mazeSize = 100;
|
|
} else {
|
|
mazeSize = 11 + rand(21);
|
|
}
|
|
|
|
const donut = rand(3) === 0;
|
|
const donutWidth = mazeSize / 3;
|
|
|
|
const straightMode = rand(10) === 0;
|
|
|
|
// maze (filled by 'empty')
|
|
const maze: CellType[][] = new Array(mazeSize);
|
|
for (let i = 0; i < mazeSize; i++) {
|
|
maze[i] = new Array(mazeSize).fill('empty');
|
|
}
|
|
|
|
if (donut) {
|
|
for (let y = 0; y < mazeSize; y++) {
|
|
for (let x = 0; x < mazeSize; x++) {
|
|
if (x >= donutWidth && x < mazeSize - donutWidth && y >= donutWidth && y < mazeSize - donutWidth) {
|
|
maze[x][y] = 'void';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function checkDiggable(x: number, y: number, dir: Dir) {
|
|
if (cellVariants[maze[x][y]].digg[dir] === null) return false;
|
|
|
|
const newPos =
|
|
dir === 'top' ? { x: x, y: y - 1 } :
|
|
dir === 'bottom' ? { x: x, y: y + 1 } :
|
|
dir === 'left' ? { x: x - 1, y: y } :
|
|
dir === 'right' ? { x: x + 1, y: y } :
|
|
{ x, y };
|
|
|
|
if (newPos.x < 0 || newPos.y < 0 || newPos.x >= mazeSize || newPos.y >= mazeSize) return false;
|
|
|
|
const cell = maze[newPos.x][newPos.y];
|
|
if (cell === 'void') return false;
|
|
if (cell === 'empty') return true;
|
|
if (cellVariants[cell].cross[dir] && checkDiggable(newPos.x, newPos.y, dir)) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function diggFrom(x: number, y: number, prevDir?: Dir) {
|
|
const isUpDiggable = checkDiggable(x, y, 'top');
|
|
const isRightDiggable = checkDiggable(x, y, 'right');
|
|
const isDownDiggable = checkDiggable(x, y, 'bottom');
|
|
const isLeftDiggable = checkDiggable(x, y, 'left');
|
|
|
|
if (!isUpDiggable && !isRightDiggable && !isDownDiggable && !isLeftDiggable) return;
|
|
|
|
const dirs: Dir[] = [];
|
|
if (isUpDiggable) dirs.push('top');
|
|
if (isRightDiggable) dirs.push('right');
|
|
if (isDownDiggable) dirs.push('bottom');
|
|
if (isLeftDiggable) dirs.push('left');
|
|
|
|
let dir: Dir;
|
|
if (straightMode) {
|
|
if (dirs.includes(prevDir)) {
|
|
dir = prevDir;
|
|
} else {
|
|
dir = dirs[rand(dirs.length)];
|
|
}
|
|
} else {
|
|
dir = dirs[rand(dirs.length)];
|
|
}
|
|
|
|
maze[x][y] = cellVariants[maze[x][y]].digg[dir];
|
|
|
|
if (dir === 'top') {
|
|
maze[x][y - 1] = maze[x][y - 1] === 'empty' ? 'bottom' : 'cross';
|
|
diggFrom(x, y - 1, dir);
|
|
return;
|
|
}
|
|
if (dir === 'right') {
|
|
maze[x + 1][y] = maze[x + 1][y] === 'empty' ? 'left' : 'cross';
|
|
diggFrom(x + 1, y, dir);
|
|
return;
|
|
}
|
|
if (dir === 'bottom') {
|
|
maze[x][y + 1] = maze[x][y + 1] === 'empty' ? 'top' : 'cross';
|
|
diggFrom(x, y + 1, dir);
|
|
return;
|
|
}
|
|
if (dir === 'left') {
|
|
maze[x - 1][y] = maze[x - 1][y] === 'empty' ? 'right' : 'cross';
|
|
diggFrom(x - 1, y, dir);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//#region start digg
|
|
const nonVoidCells = [];
|
|
|
|
for (let y = 0; y < mazeSize; y++) {
|
|
for (let x = 0; x < mazeSize; x++) {
|
|
const cell = maze[x][y];
|
|
if (cell !== 'void') nonVoidCells.push([x, y]);
|
|
}
|
|
}
|
|
|
|
const origin = nonVoidCells[rand(nonVoidCells.length)];
|
|
|
|
diggFrom(origin[0], origin[1]);
|
|
//#endregion
|
|
|
|
let hasEmptyCell = true;
|
|
while (hasEmptyCell) {
|
|
const nonEmptyCells = [];
|
|
|
|
for (let y = 0; y < mazeSize; y++) {
|
|
for (let x = 0; x < mazeSize; x++) {
|
|
const cell = maze[x][y];
|
|
if (cell !== 'empty' && cell !== 'void' && cell !== 'cross') nonEmptyCells.push([x, y]);
|
|
}
|
|
}
|
|
|
|
const pos = nonEmptyCells[rand(nonEmptyCells.length)];
|
|
|
|
diggFrom(pos[0], pos[1]);
|
|
|
|
hasEmptyCell = false;
|
|
for (let y = 0; y < mazeSize; y++) {
|
|
for (let x = 0; x < mazeSize; x++) {
|
|
if (maze[x][y] === 'empty') hasEmptyCell = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return maze;
|
|
}
|