#!/usr/bin/ruby # Maze dimensions. i = ARGV z = [i[0] && i[0].to_i - 3 || 18, 14].max Z = [i[1] && i[1].to_i - 2 || 64, 32].max / 16 z = (z - (z + 2) % 4) / 2 if i[2] srand i[2].to_i end # Parent pointers. $p = {} # Output buffer. $canvas = [] # Triangles that are used in middle boxes. $q = {} # Get the canonical parent for a particular cell. def canonical_parent(y, x) p = [y, x] while $p[p] && $p[p] != p p = $p[p] end return p end # Merge two adjacent cells. def merge_cells(y1, x1, y2, x2) p = canonical_parent(y1, x1) q = canonical_parent(y2, x2) if p != q $p[p] = $p[q] = [p, q].min if y1 == y2 # A ..;;B;;;.. ..;;'';;.. # ..;;'' ;; '';;.. ..;;'' '';;.. # '';;.. ;; ..;;'' -> '';;.. ..;;'' # '';;;;;;'' '';;..;;'' 4.times{|i| $canvas[y1 + i][x1 + 8, 2] = "' ."[i] * 2} else y = [y1, y2].max if $q[p] && $q[q] # ..;;A;;;.. ..;;;;;;.. # ..;;'' ;; '';;.. ..;;'' ;; '';;.. # '';;.. B; ..;;;;;;.. -> '';;.. ;; ;;;;.. # '';;;;;;'' ;; '';;.. '';;;; ;; '';;.. # '';;.. ;; ..;;'' '';;.. ;; ..;;'' # '';;;;;;'' '';;;;;;'' 2.times{|i| $canvas[y + i][x1 + 2, 6] = " " * 6} else # ..;;A;;;.. ..;;;;;;.. # ..;;'' ;; '';;.. ..;;'' ;; '';;.. # '';;.. B; ..;;;;;;.. -> '';;.. ;; ,;;;;;;.. # '';;;;;;'' ;; '';;.. '';;;;;; ;; '';;.. # '';;.. ;; ..;;'' '';;.. ;; ..;;'' # '';;;;;;'' '';;;;;;'' $canvas[y][x1 + 4, 2] = " , "[(x1 % 16 / 8) ^ (y % 4 / 2), 2] $canvas[y + 1][x1 + 4, 2] = " " end end end end # {{{ init_canvas I = [ # Template "; .;;;. ", ";;' ; ';", ";;. ; .;", "; ';;;' ", # Suffix "';. ; .;", " ';;;' ", " ' " ].map{|l| l.split(//).map{|i| i * 2}.join} (z * 2).times{|l| $canvas += [I[l % 4] * Z]} # Remove extraneous edges from top row. $canvas[0] = $canvas[0].gsub(/;; /, " ") $canvas[1] = $canvas[1].gsub(/;;;;''/, "..;;''") # Add some padding to bottom row. 3.times{|l| $canvas += [I[l + 4] * Z]} # Pad right side edges. $canvas.size.times{|l| $canvas[l] += " .' "[l % 4] * 2} # Initialize disjoint regions. z.times{|l| (Z * 2).times{|i| p = [l * 2, i * 8] $p[p] = p } } # }}} # {{{ reserve_enough_boxes O = (Z * 2) * z + 1 O.times{ if $q.size < O * 0.4 # {{{ reserve_boxes # Set initial position. y0 = rand(z - 5) * 2 x0 = rand(Z - 2) * 16 + (y0 % 4 < 2 ? 16 : 8) # See if we can expand further right. size = 0 dy = rand(2) > 0 ? 2 : -2 while (size < 1 || rand(5) > 0) && (p = x0 + 8 * size) < Z * 16 - 24 && (q = y0 + dy * size) < z * 2 - 7 && q > 1 && # {{{ is_available (0..2).all?{|l| !$q[[q + l * 2, p]] && !$q[[q + l * 2, p + 8]]} # }}} size += 1 end if size > 0 # Reserve cells. size.times{|l| 3.times{|i| [0, 8].each{|k| $q[[y0 + l * dy + i * 2, x0 + l * 8 + k]] = 1 } } } # Remove walls. dx = rand(2) > 0 ? 8 : -8 d = dx * dy < 0 py1 = px1 = py2 = px2 = nil size.times{|l| # Merge A+B merge_cells(cy1 = y0 + l * dy + (dx > 0 ? 0 : 4), cx1 = x0 + l * 8, ny1 = cy1, nx1 = cx1 + 8) # Merge D+E merge_cells(cy2 = cy1 + (d ? -2 * dy : dy), cx2 = cx1 + (d ? 8 : 0), ny2 = cy2 + dy, nx2 = cx2) if py1 # Merge B+C merge_cells(py1, px1, cy1, cx1) # Merge E+F merge_cells(py2, px2, cy2, cx2) end py1 = ny1 px1 = nx1 py2 = ny2 px2 = nx2 } # Merge G+H merge_cells(cy = d ? y0 + 2 : py2, cx = d ? x0 : px2 + 8, cy - dy, cx) end # }}} end } # }}} # {{{ remove_vertical_walls + get_diagonal_walls walls = [] z.times{|l| Z.times{|i| merge_cells(y = l * 2, x = i * 16 - l % 2 * 8, y, x + (!$q[[y, x]] && !$q[[y, x + 8]] ? 8 : 0)) } (1..Z * 2 - 2).each{|i| walls += l > 0 ? [[y = l * 2, x = i * 8, y - 2, x]] : [] } } walls = walls.shuffle # }}} # {{{ join_cells + make_maze_solvable 2.times{|l| walls.each{|y1, x1, y2, x2| if (l < 1 || canonical_parent(0, 0) != canonical_parent(z * 2 - 2, Z * 16 - 8)) && !$q[[y1, x1]] && !$q[[y2, x2]] merge_cells(y1, x1, y2, x2) end } $q = {} } # }}} # {{{ mark_start_and_end merge_cells(-2, 0, 0, 0) merge_cells(y = z * 2, x = Z * 16 - 8, y - 2, x) # }}} $canvas.each{|l| puts l}