|
| 1 | +### recursive division maze |
| 2 | + |
| 3 | +# Begin with an empty field. |
| 4 | +# Bisect the field with a wall, either horizontally or vertically. Add a single passage through the wall. |
| 5 | +# Repeat step #2 with the areas on either side of the wall. |
| 6 | +# Continue, recursively, until the maze reaches the desired resolution. |
| 7 | +# Recommended: look at aspect ratio of each new "field" and choose vert or horiz |
| 8 | +# wall to avoid long skinny passages. |
| 9 | +# http://weblog.jamisbuck.org/2011/1/12/maze-generation-recursive-division-algorithm |
| 10 | +# likes following one |
| 11 | +# 'sequence' of subdivisions until a field is at the cell level, then backing to |
| 12 | +# some other field. |
| 13 | + |
| 14 | + |
| 15 | + |
| 16 | +## recursive backtracking maze |
| 17 | + |
| 18 | +# Make the initial cell the current cell and mark it as visited |
| 19 | +# While there are unvisited cells |
| 20 | + # If the current cell has any neighbours which have not been visited |
| 21 | + # Choose randomly one of the unvisited neighbours |
| 22 | + # Push the current cell to the stack |
| 23 | + # Remove the wall between the current cell and the chosen cell |
| 24 | + # Make the chosen cell the current cell and mark it as visited |
| 25 | + # Else if stack is not empty |
| 26 | + # Pop a cell from the stack |
| 27 | + # Make it the current cell |
| 28 | + # Else |
| 29 | + # Pick a random unvisited cell, make it the current cell and mark it as visited |
| 30 | + |
| 31 | +rbmaze<-function( dimx, dimy, slowly=FALSE, tcols, ...) { |
| 32 | +require(reshape2) |
| 33 | +mardef<-par()$mar |
| 34 | +par(mar=c(.1,.1,.1,.1)) |
| 35 | +#build init matrices; remember cells are ID'd by "top left" coord |
| 36 | +scell<-matrix(0,nrow=(dimy),ncol=(dimx) ) |
| 37 | +icell<-melt(scell) |
| 38 | +walls <- NULL #store indices of removed walls |
| 39 | +dewall<-vector(length=4) |
| 40 | +# select starting cell -- rumor has it that the starting cell |
| 41 | +# should be at 0,0; dunno why, tho. |
| 42 | +initx<-sample(1:dimx,1) |
| 43 | +inity<-sample(1:dimy,1) |
| 44 | +icell[icell[,1]==inity & icell[,2]==initx,3] <- 1 |
| 45 | +# let's build my stack. set scell[i,j] to -1 when all neighbors are visited |
| 46 | +stackcount<-1 |
| 47 | +scell[inity,initx]<-stackcount |
| 48 | +stackcount<-stackcount + 1 |
| 49 | +# now loop until all cells have been visited... I think |
| 50 | +while( any(0%in% icell[,3] )) { |
| 51 | +# pick a wall to remove; for initial cell, don't need to check whether |
| 52 | +# neighbor has been visited. BUT need to check for boundary |
| 53 | +# 1234= NESW |
| 54 | +##watch for index crash at edges -- gotta be a smoother way |
| 55 | + neighborx <- initx+c(-1,1) |
| 56 | + neighborx <- neighborx[neighborx>0 & neighborx <=dimx] |
| 57 | + neighbory <- inity+c(-1,1) |
| 58 | + neighbory <- neighbory[neighbory>0 & neighbory <=dimy] |
| 59 | +# find out how many neighborxy |
| 60 | +# values showed up and sample amongst them. |
| 61 | + visith <- icell[(icell[,1]==inity & abs(icell[,2]-initx)==1 &icell[,3]==0),] |
| 62 | + visitv <- icell[(abs(icell[,1]-inity)==1 & icell[,2]==initx &icell[,3]==0),] |
| 63 | + visits<-rbind(visith,visitv) |
| 64 | +# in case all neighbors are already visited, skip the "build" |
| 65 | + if( nrow(visits) ) { |
| 66 | + pickit <- sample(1:nrow(visits),1) |
| 67 | + newy <- visits[pickit,1] |
| 68 | + newx <- visits[pickit,2] |
| 69 | + scell[newy,newx]<-stackcount |
| 70 | +##see same-ish algorithm for "dewall" in pmaze |
| 71 | + kwall<- c(max(initx,newx), max(newy,inity), max(newx,initx)+1*(initx==newx), max(newy,inity) + 1*(inity==newy) ) |
| 72 | + walls<-rbind(walls, kwall) |
| 73 | + #Finished off the work when visits[] had rows |
| 74 | + } else { |
| 75 | +# just pick a new place to start; new approach is to back up the stackcount |
| 76 | + scell[inity,initx]<- -1 #no nonvisited neighbors; removes current |
| 77 | +# "stack count" value from array |
| 78 | + newcoord<-which(scell==max(scell),arr.ind=TRUE) |
| 79 | + newx<-newcoord[2] |
| 80 | + newy<-newcoord[1] |
| 81 | + } # end of if else |
| 82 | + stackcount<-stackcount+1 |
| 83 | + initx <- newx |
| 84 | + inity <- newy |
| 85 | + icell[icell[,1]==inity & icell[,2]==initx,3] <- 1 |
| 86 | + scell[inity,initx]<-stackcount |
| 87 | +} #end of while any 0 |
| 88 | +# |
| 89 | +## now plot the maze, using same techniques as in pmaze. |
| 90 | +# modify limits to make it easier to plot in/out labels |
| 91 | +plot( c(1,(dimx+1),(dimx+1),1,1), c(1,1,(dimy+1),dimy+1,1),t='l',axes=F,xlab='',ylab='',xlim=c(0,dimx+2),ylim=c(0,dimy+2) ) |
| 92 | + df = as.matrix(expand.grid( xvert= seq_len(dimx),yvert= seq_len(dimy))) |
| 93 | + dfv <- cbind(df,df[,1],df[,2]+1) |
| 94 | + df2 <- as.matrix(expand.grid(yvert= seq_len(dimy), xvert= seq_len(dimx))) |
| 95 | + dfh <- cbind(df2[,2],df2[,1],df2[,2]+1,df2[,1]) |
| 96 | +# concatenate each row w/ delimiter (so that 2_13 is not same as 21_3 ) |
| 97 | +#nb alternative methods as found on SO turn out to be much slower |
| 98 | +allwalls<-rbind(dfv,dfh) |
| 99 | +allrows<-unlist(sapply(1:nrow(allwalls),function(j) paste(allwalls[j,],collapse='_')) ) |
| 100 | +# the maze walls: |
| 101 | +allfoo <- unlist(sapply(1:nrow(walls),function(j) paste(walls[j,],collapse='_'))) |
| 102 | +thewalls<-setdiff(allrows,allfoo) |
| 103 | +dowalls<-allwalls[allrows%in%thewalls,] |
| 104 | +# original method, since I'm not sure "trees" exist in this maze yet. |
| 105 | +dowalls <- dowalls[order(rowSums(dowalls[,1:2])),] |
| 106 | + # timng is kinda cruddy here. Experiment with (if(slowly & !j%%10)) sort of thing |
| 107 | +for(j in 1:nrow(dowalls) ) { |
| 108 | + lines(dowalls[j,c(1,3)],dowalls[j,c(2,4)],...) |
| 109 | + if(slowly) Sys.sleep(slowly) |
| 110 | + } |
| 111 | +#cleanup |
| 112 | +par(mar=c(mardef)) |
| 113 | +return(invisible(walls)) |
| 114 | +} |
0 commit comments