Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
3D maze
class CubeMaze
class Wall
def initialize(x, y, h, ob)
@wall = Array.new(h + 1) {Array.new(y) {Array.new(x, 1)}}
@x, @y = x, y
@cube = ob
end
attr_reader :wall
def break(num, n)
x, y, h = get_wall_coodinate(num)
c1, c2 = get_wall_numbers(x, y, h - 1, n)
if c1 != c2
@wall[h][y][x] = 0
c1, c2 = c2, c1 if c1 > c2
@cube.replace(c2, c1)
end
end
def get_wall_coodinate(num)
h = num / @x / @y + 1
y = num % (@x * @y) / @x
x = num % @x
[x, y, h]
end
def get_wall_numbers(x, y, h, n)
f = [0, 0, 1].rotate(-n)
x1, y1, h1 = get_cell_coordinate(x, y, h, n)
c1 = cells[h1 + f[2]][y1 + f[1]][x1 + f[0]]
c2 = cells[h1][y1][x1]
[c1, c2]
end
def get_cell_coordinate(x, y, h, n)
case n
when 1
x, y, h = h, x, y
when 2
x, y, h = y, h, x
end
[x, y, h]
end
def cells
@cube.cells
end
end
def initialize(x, y, z)
@cells = Array.new(z) do |i|
Array.new(y) {|j| Array.new(x) {|k| x * y * i + x * j + k}}
end
@xy = Wall.new(x, y, z, self)
@yz = Wall.new(y, z, x, self)
@zx = Wall.new(z, x, y, self)
@left_wall = [*0...3 * x * y * z - (x * y + y * z + z * x)]
@x, @y, @z = x, y, z
end
attr_reader :cells, :xy, :yz, :zx
def break_wall
wall_num = @left_wall.sample
@left_wall.delete(wall_num)
case wall_num
when 0 ...(s1 = @x * @y * (@z - 1))
@xy.break(wall_num, 0)
when s1...(s2 = s1 + @y * @z * (@x - 1))
@yz.break(wall_num - s1, 1)
when s2...(s2 + @z * @x * (@y - 1))
@zx.break(wall_num - s2, 2)
else
raise "error"
end
end
def replace(from, to)
each_cell do |x, y, z|
@cells[z][y][x] = to if @cells[z][y][x] == from
end
end
def each_cell
@z.times {|i| @y.times {|j| @x.times {|k| yield(k, j, i)}}}
end
def finish
each_cell do |x, y, z|
return false unless @cells[z][y][x] == @cells[0][0][0]
end
true
end
def generate
break_wall until finish
[@xy.wall, @yz.wall, @zx.wall]
end
end
require_relative '3d_maze_generate'
require_relative 'miniopengl'
module WalkMaze
def self.go(x, y, z, xy, yz, zx)
MiniOpenGL.app width: 500, height: 500, depth_buffer: :on, title: "3D maze" do
px, py, pz = x - 1, y - 1, z - 1 #迷路のスタート位置
dir = [-1, 0, 0] #最初の向きの設定
draw do
glEnable(GL_BLEND) #混合処理を可能にします
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
self.color_alpha = 0.93
glEnable(GL_DEPTH_TEST) #隠面消去
clear
grid = ->(x1, y1, z1, n) {
(z1 + 1).times do |i|
(y1 + 1).times do |j|
coodinates = [0, j, i, x, j, i]
line3(*coodinates.rotate(-n))
end
(x1 + 1).times do |j|
coodinates = [j, 0, i, j, y, i]
line3(*coodinates.rotate(-n))
end
end
}
color(1, 1, 1)
grid.(x, y, z, 0)
grid.(y, z, x, 1)
#ひとつの壁の端にわずかに隙間を作る
modify = ->(points) {
result = []
points.each_slice(3) do |po|
result += po.zip(points[0, 3]).map {|a| (a[0] - a[1]).zero? ? a[0] + 0.002 : a[0] - 0.002}
end
result
}
#壁の描画
color(0, 0.8, 0)
(z + 1).times do |z1|
y.times do |y1|
x.times do |x1|
next if xy[z1][y1][x1].zero?
vtx = [x1, y1, z1, x1, y1 + 1, z1, x1 + 1, y1 + 1, z1, x1 + 1, y1, z1]
draw_vertex(GL_QUADS, 3, modify.(vtx))
end
end
end
color(0, 0, 0.8)
(x + 1).times do |x1|
z.times do |z1|
y.times do |y1|
next if yz[x1][z1][y1].zero?
vtx = [x1, y1, z1, x1, y1 + 1, z1, x1, y1 + 1, z1 + 1, x1, y1, z1 + 1]
draw_vertex(GL_QUADS, 3, modify.(vtx))
end
end
end
color(0.8, 0, 0)
(y + 1).times do |y1|
x.times do |x1|
z.times do |z1|
next if zx[y1][x1][z1].zero?
vtx = [x1, y1, z1, x1, y1, z1 + 1, x1 + 1, y1, z1 + 1, x1 + 1, y1, z1]
draw_vertex(GL_QUADS, 3, modify.(vtx))
end
end
end
stack do
color(1, 1, 1)
translate(0.5, 0.5, 0.5)
glutSolidSphere(0.3, 20, 16)
end
display
end #--draw end
look = ->{
x2, y2, z2 = px + 0.5, py + 0.5, pz + 0.5
look_at(x2, y2, z2, x2 + dir[0], y2 + dir[1], z2 + dir[2], 0, 0, 1)
}
reshape do |w, h|
viewport(0, 0, w, h)
init_projection
perspective(120, w / h.to_f, 0.2, 20)
init_modelview
look.()
end
#dirの方向へ行けるなら移動
move = ->(po, dir) {
next_po = po.zip(dir).map {|x| x[0] + x[1]}
dir1 = dir.map {|c| (c == 1) ? 1 : 0}
f = if dir[0].nonzero?
yz[po[0] + dir1[0]][po[2] + dir1[2]][po[1] + dir1[1]].zero?
elsif dir[1].nonzero?
zx[po[1] + dir1[1]][po[0] + dir1[0]][po[2] + dir1[2]].zero?
else
xy[po[2] + dir1[2]][po[1] + dir1[1]][po[0] + dir1[0]].zero?
end
px, py, pz = next_po if f
p [px, py, pz]
}
is_end = ->{ px.zero? && py.zero? && pz.zero? }
key_in do |key|
case key
when " "
move.([px, py, pz], dir)
when "e"
exit if is_end.()
end
init_modelview
look.()
redisplay
end
key_in2 do |key|
case key
when GLUT_KEY_LEFT
dir = [-dir[1], dir[0], 0]
when GLUT_KEY_RIGHT
dir = [dir[1], -dir[0], 0]
when GLUT_KEY_UP
move.([px, py, pz], [0, 0, 1])
when GLUT_KEY_DOWN
move.([px, py, pz], [0, 0, -1])
end
init_modelview
look.()
redisplay
end
end
end
end
x, y, z = 3, 4, 5
xy, yz, zx = CubeMaze.new(x, y, z).generate
WalkMaze.go(x, y, z, xy, yz, zx)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.