#!/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. $o = [] # Triangles that are used in middle boxes. $q = {} # Get the canonical parent for a particular cell. def s(d, b) p = [d, b] while $p[p] && $p[p] != p p = $p[p] end return p end # Merge two adjacent cells. def y(d, i, b, q) p = s(d, i) q = s(b, q) if p != q $p[p] = $p[q] = [p, q].min if d == b # A ..;;B;;;.. ..;;'';;.. # ..;;'' ;; '';;.. ..;;'' '';;.. # '';;.. ;; ..;;'' -> '';;.. ..;;'' # '';;;;;;'' '';;..;;'' 4.times{|l| $o[d + l][i + 8, 2] = "' ."[l] * 2} else o = [d, b].max if $q[p] && $q[q] # ..;;A;;;.. ..;;;;;;.. # ..;;'' ;; '';;.. ..;;'' ;; '';;.. # '';;.. B; ..;;;;;;.. -> '';;.. ;; ;;;;.. # '';;;;;;'' ;; '';;.. '';;;; ;; '';;.. # '';;.. ;; ..;;'' '';;.. ;; ..;;'' # '';;;;;;'' '';;;;;;'' 2.times{|l| $o[o + l][i + 2, 6] = " " * 6} else # ..;;A;;;.. ..;;;;;;.. # ..;;'' ;; '';;.. ..;;'' ;; '';;.. # '';;.. B; ..;;;;;;.. -> '';;.. ;; ,;;;;;;.. # '';;;;;;'' ;; '';;.. '';;;;;; ;; '';;.. # '';;.. ;; ..;;'' '';;.. ;; ..;;'' # '';;;;;;'' '';;;;;;'' $o[o][i + 4, 2] = " , "[(i % 16 / 8) ^ (o % 4 / 2), 2] $o[o + 1][i + 4, 2] = " " end end end end # {{{ init_canvas I = [ # Template "; .;;;. ", ";;' ; ';", ";;. ; .;", "; ';;;' ", # Suffix "';. ; .;", " ';;;' ", " ' " ].map{|l| l.split(//).map{|i| i * 2}.join} (z * 2).times{|l| $o += [I[l % 4] * Z]} # Remove extraneous edges from top row. $o[0] = $o[0].gsub(/;; /, " ") $o[1] = $o[1].gsub(/;;;;''/, "..;;''") # Add some padding to bottom row. 3.times{|l| $o += [I[l + 4] * Z]} # Pad right side edges. $o.size.times{|l| $o[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. i = rand(z - 5) * 2 j = rand(Z - 2) * 16 + (i % 4 < 2 ? 16 : 8) # See if we can expand further right. g = 0 f = rand(2) > 0 ? 2 : -2 while (g < 1 || rand(5) > 0) && (p = j + 8 * g) < Z * 16 - 24 && (q = i + f * g) < z * 2 - 7 && q > 1 && # {{{ is_available (0..2).all?{|l| !$q[[q + l * 2, p]] && !$q[[q + l * 2, p + 8]]} # }}} g += 1 end if g > 0 # Reserve cells. g.times{|l| 3.times{|u| [0, 8].each{|v| $q[[i + l * f + u * 2, j + l * 8 + v]] = 1 } } } # Remove walls. h = rand(2) * 2 - 1 e = h * f < 0 u = v = w = x = nil g.times{|l| # Merge A+B y(d = i + l * f + (h > 0 ? 0 : 4), b = j + l * 8, p = d, q = b + 8) # Merge D+E y(m = d + (e ? -2 * f : f), n = b + (e ? 8 : 0), r = m + f, s = n) if u # Merge B+C y(u, v, d, b) # Merge E+F y(w, x, m, n) end u = p v = q w = r x = s } # Merge G+H y(u = e ? i + 2 : w, v = e ? j : x + 8, u - f, v) end # }}} end } # }}} # {{{ remove_vertical_walls + get_diagonal_walls w = [] z.times{|l| Z.times{|i| y(q = l * 2, p = i * 16 - l % 2 * 8, q, p + (!$q[[q, p]] && !$q[[q, p + 8]] ? 8 : 0)) } (1..Z * 2 - 2).each{|i| w += l > 0 ? [[q = l * 2, p = i * 8, q - 2, p]] : [] } } w = w.shuffle # }}} # {{{ join_cells + make_maze_solvable 2.times{|l| w.each{|p, d, q, b| if (l < 1 || s(0, 0) != s(z * 2 - 2, Z * 16 - 8)) && !$q[[p, d]] && !$q[[q, b]] y(p, d, q, b) end } $q = {} } # }}} # {{{ mark_start_and_end y(-2, 0, 0, 0) y(p = z * 2, q = Z * 16 - 8, p - 2, q) # }}} $o.each{|l| puts l}