/**
 * Some of this is borrowed from: https://github.com/bestguy/generate-maze/blob/master/src/generate-maze.js
 */ import _ from "lodash" ; import { Point2D } from "./Point2D" ; function mergeSetWith ( row , oldSet , newSet ) { const setToMerge = _ . filter ( row , { set : oldSet } ) ; setToMerge . forEach ( ( box ) => { box . set = newSet ; } ) ; } function populateMissingSets ( row ) { const noSets = _ . reject ( row , ( box ) => box . set ) ; const setsInUse = _ . chain ( row ) . map ( "set" ) . uniq ( ) . compact ( ) . value ( ) ; const allSets = _ . range ( 1 , row . length + 1 ) ; const availableSets = _ . chain ( allSets ) . difference ( setsInUse ) . shuffle ( ) . value ( ) ; noSets . forEach ( ( box , i ) => ( ( box as any ) . set = availableSets [ i ] ) ) ; } function mergeRandomSetsIn ( row , probability = 0.5 ) { const allBoxesButLast = _ . initial ( row ) ; allBoxesButLast . forEach ( ( current , x ) => { const next = row [ x + 1 ] ; const differentSets = current . set !== next . set ; const shouldMerge = Math . random ( ) <= probability ; if ( differentSets && shouldMerge ) { mergeSetWith ( row , next . set , current . set ) ; current . right = false ; next . left = false ; } } ) ; } function addSetExits ( row , nextRow ) { const setsInRow = _ . chain ( row ) . groupBy ( "set" ) . values ( ) . value ( ) ; const { ceil , random } = Math ; setsInRow . forEach ( ( set ) => { const exits = _ . sampleSize ( set , ceil ( random ( ) * set . length ) ) ; exits . forEach ( ( exit ) => { if ( exit ) { const below = nextRow [ exit . x ] ; exit . bottom = false ; below . top = false ; below . set = exit . set ; } } ) ; } ) ; } export type Cell = { x : number ; y : number ; top : boolean ; bottom : boolean ; left : boolean ; right : boolean ; set : number ; } ; export type Row = Cell [ ] ; export class Maze { public rows : Row [ ] ; constructor ( public width , public height , public closed = true ) { const rows = [ ] ; const range = _ . range ( width ) ; for ( let y = 0 ; y < height ; y += 1 ) { const row = range . map ( ( x ) => { return { x , y , top : closed || y > 0 , left : closed || x > 0 , bottom : closed || y < height - 1 , right : closed || x < width - 1 , } as any ; } ) ; rows . push ( row ) ; } _ . initial ( rows ) . forEach ( ( row , y ) => { populateMissingSets ( row ) ; mergeRandomSetsIn ( row ) ; addSetExits ( row , rows [ y + 1 ] ) ; } ) ; const lastRow = _ . last ( rows ) ; populateMissingSets ( lastRow ! ) ; mergeRandomSetsIn ( lastRow ! , 1 ) ; this . rows = rows ; } getRandomPoint ( ) { return new Point2D ( Math . floor ( Math . random ( ) * this . width ) , Math . floor ( Math . random ( ) * this . height ) ) ; } }