Public
Edited
Oct 2, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
unbiasedGridMaze = {
await visibility()
random
const cellList = [...Array(gridSize.w * gridSize.h).keys()]
shuffle(cellList)

const ltg = new simpleListToGenerator(cellList)

const maze = new GridMaze(gridSize.w, gridSize.h)
return genmaze({
cells: ltg.generator(),
getNeighbors(cell){
return maze.getNeighbors(cell)
},
neighborFilter(cell, neighbors) {
return neighbors.map(list=>{
const result = list.map(it=> false)
result[Math.floor(Math.random() * list.length)] = true
return result
})
},
setConnected(a, b){
maze.setConnected(a, b)
},
getState(){
return {
cells: cellList.slice(0, ltg.index),
openEdges: [...maze.connected.values()],
}
}
})
}
Insert cell
Insert cell
Insert cell
Insert cell
renderGridMaze(loopGridMaze)
Insert cell
loopGridMaze = {
// await visibility()
random1
const cellList = [...Array(gridSize.w * gridSize.h).keys()]
shuffle(cellList)

const ltg = new simpleListToGenerator(cellList)

const maze = new GridMaze(gridSize.w, gridSize.h)

const debugCell = new Map()

return genmaze({
cells: ltg.generator(),
getNeighbors(cell){
return maze.getNeighbors(cell)
},
neighborFilter(cell, neighbors) { // @edit loopable
// console.log(neighbors)
return neighbors.map(list=>{
let result = list.map(it=> false)
if (list.length > 1) { //
if (Math.random() < loopChance) {
let pairs = [] // prefer longer loops
for (let i = 0; i < list.length-1; i++){
for (let j = i; j < list.length; j++){
const isVerticalPair = Math.abs(list[i]-list[j]) === gridSize.w * 2
const isHorizontalPair = Math.abs(list[i]-list[j]) === 2
if (isVerticalPair || isHorizontalPair){
pairs.push([i, j])
}
}
}
if (pairs.length > 0) {
const amount = 1// Math.floor(Math.random() * pairs.length+1)
for (let i = 0; i < amount; i++) {
const idx = Math.floor(Math.random()*pairs.length)
const p = pairs[idx]
result[p[0]] = true
result[p[1]] = true
pairs.splice(idx, 1)
}
debugCell.set(cell, 'darkred')
} else {
// corner only
const diagonal = list[1]+list[0]-cell
if(!(maze.isConnected(list[0], diagonal) && maze.isConnected(list[1], diagonal))){
// avoid bigroom
result[0] = true
result[1] = true
debugCell.set(cell, 'darkblue')
} else {
// connecct only one of it or the cell will be unreachable
result[Math.floor(Math.random()*result.length)] = true
debugCell.set(cell, 'teal')
}
}
} else {
result[Math.floor(Math.random()*result.length)] = true
debugCell.set(cell, 'darkgreen')
}
} else {
result[Math.floor(Math.random()*result.length)] = true
debugCell.set(cell, 'gray')
}
return result
})
},
setConnected(a, b){
maze.setConnected(a, b)
},
getState(){
return {
cells: cellList.slice(0, ltg.index),
openEdges: [...maze.connected.values()],
debugCell,
}
}
})
}
Insert cell
Insert cell
Insert cell
function* genmaze({cells, getNeighbors, neighborFilter, setConnected, getState}){
let edges
let refmap = new Map()
for(const cell of cells){
let map = new Map()
const nbs = getNeighbors(cell)
refmap.set(cell, {
ref: null,
depth: 0,
})
if(nbs.length !== 0) {
for (const other of nbs) {
if(!refmap.has(other)) continue
const ref = find(refmap.get(other))
if(!map.has(ref)){
map.set(ref, [other])
} else {
map.get(ref).push(other)
}
}
const neighbors = [...map.values()]
const result = neighborFilter(cell, neighbors)
for (let i = 0; i<result.length; i++) {
for (let j = 0; j < result[i].length; j++) {
if(result[i][j]) {
setConnected(cell, neighbors[i][j])
union(refmap.get(cell), refmap.get(neighbors[i][0]))
}
}
}
} else {
console.log(`there is an isolated cell: ${cell}`)
}
yield getState()
}
yield getState()
}
Insert cell
class GridMaze {
constructor(width, height){
this.width = width
this.height = height
this.connected = new Map()
}
isConnected(cellA, cellB){
let diff = cellA - cellB
if (diff > 0) {
[cellA, cellB] = [cellB, cellA]
}
return this.connected.has(`${cellA}-${cellB}`)
}
setConnected(cellA, cellB){
let diff = cellA - cellB
if (diff > 0) {
[cellA, cellB] = [cellB, cellA]
}
const rowA = Math.floor(cellA/gridSize.w)
const columnA = cellA % gridSize.w
const rowB = Math.floor(cellB/gridSize.w)
const columnB = cellB % gridSize.w
const rowDiffer = rowA - rowB
const columnDiffer = columnA - columnB

const toLeft = rowDiffer === 0 && columnDiffer === -1
const toRight = rowDiffer === 0 && columnDiffer === 1
const toTop = rowDiffer === -1 && columnDiffer === 0
const toBottom = rowDiffer === 1 && columnDiffer === 0
if (toLeft || toRight || toTop || toBottom) {
this.connected.set(`${cellA}-${cellB}`, {x1: columnA+0.5, y1: rowA+0.5, x2: columnB+0.5, y2: rowB+0.5})
} else {
throw new Error(`${cellA} and ${cellB} are not neighbors but are called to connect.`)
}
}
getNeighbors(cell, type = 0) { // 0: all, 1: only connected, 2: only non-connected
const list = neighborsInGrid(cell, this.width, this.height)
if (type === 1) {
return list.filter(it=>this.isConnected(cell, it))
} else if (type === 2) {
return list.filter(it=>!this.isConnected(cell, it))
} else {
return list
}
}
getCells(){
return [...Array(this.width * this.height).keys()]
}
}
Insert cell
function neighborsInGrid(cell, w = gridSize.w, h = gridSize.h){
const list = []
const row = Math.floor(cell/w)
const column = cell % w
if (row > 0) {
list.push(cell - w)
}
if (row < h - 1) {
list.push(cell + w)
}
if (column > 0) {
list.push(cell - 1)
}
if (column < w - 1) {
list.push(cell + 1)
}
return list
}
Insert cell
Insert cell
Insert cell
function renderGridMaze({cells, openEdges, debugCell}){
const scale = 10
return htl.svg`<svg width="300" height="300" viewbox="-10 -10 ${gridSize.w * 10 + 20} ${gridSize.h * 10 + 20}">
<g stroke="black" stroke-width="1" fill="black">
${openEdges.map(it=>{
return htl.svg.fragment`<line stroke-width="8" x1=${it.x1*scale} y1=${it.y1*scale} x2=${it.x2*scale} y2=${it.y2*scale}></line>`
if(it.type === 'vertical'){
return htl.svg.fragment`<line x1=${it.x*10} y1=${it.y*10+1} x2=${it.x*10} y2=${(it.y+1)*10-1}></line>`
} else {
return htl.svg.fragment`<line x1=${it.x*10+1} y1=${it.y*10} x2=${(it.x+1)*10-1} y2=${it.y*10}></line>`
}
})}
${cells.map(it=>{
const row = Math.floor(it/gridSize.w)
const column = it % gridSize.w
let color = 'black'
// if (debugCell) color = debugCell.get(it)
const idx = cells.indexOf(it)
return htl.svg.fragment`<g stroke-width="0">
<rect x=${column*10+1} y=${row*10+1} width="8" height="8" fill=${color}></rect>
<text x=${column*10+5} y=${row*10+5} text-anchor="middle" font-size="4" fill="white">${idx}</text>
</g>`
})}
</g>
</svg>`
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more