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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more