Skip to content

Instantly share code, notes, and snippets.

@obelisk68
Last active October 26, 2018 06:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save obelisk68/ede984fd5766bd5293b220b3d035d621 to your computer and use it in GitHub Desktop.
Save obelisk68/ede984fd5766bd5293b220b3d035d621 to your computer and use it in GitHub Desktop.
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