ルービックキューブ

今日中にできなかったら昼飯をおごるという約束でとりあえず作った。
全体をまわす処理ができていないのでカーソルはどこまでもいける。どこまでもな。

操作はカーソルキーでカーソル(白い四角)を動かして、シフト押しながらカーソルで回転。
まあ、アルゴリズムができただけで、終了判定もないわけだが、終了判定は難しすぎるのでパス。
お急ぎで作ったのでソースもぐちゃぐちゃでちょっと長いが、まあ、きちんと整理してサンプルにつけるように考えておこう。
なので証拠としてソースはあげておくが、これはあまり参考にしないように。

require 'dxruby'

def createSquare(v, t)
  v = v.flatten
  t = t.flatten
  pa = Polygon.new
  pa.vertex = [v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8]]
  pa.tutv = [t[0], t[1], t[2], t[3], t[4], t[5]]
  pb = Polygon.new
  pb.vertex = [v[0], v[1], v[2] ,v[6], v[7], v[8], v[9], v[10], v[11]]
  pb.tutv = [t[0], t[1], t[4], t[5], t[6], t[7]]
  [pa, pb]
end

def createWall(pos, size, tutv)
  point = [[-1, -1, -1], [1, -1, -1], [1, -1, 1], [-1, -1, 1], [-1, 1, -1], [1, 1, -1], [1, 1, 1], [-1, 1, 1]]
  point.map! do |p1|
    [p1[0] * size / 2 + pos[0], p1[1] * size / 2 + pos[1], p1[2] * size / 2 + pos[2]]
  end
  pt = createSquare([point[0], point[1], point[2], point[3]], tutv[0])
  pt.concat createSquare([point[3], point[2], point[6], point[7]], tutv[1])
  pt.concat createSquare([point[0], point[3], point[7], point[4]], tutv[2])
  pt.concat createSquare([point[1], point[0], point[4], point[5]], tutv[3])
  pt.concat createSquare([point[2], point[1], point[5], point[6]], tutv[4])
  pt.concat createSquare([point[7], point[6], point[5], point[4]], tutv[5])
  pt
end

tutv = []
for i in 1..6 do tutv.push([i * 32, 0, i * 32 + 31, 0, i * 32 + 31, 31, i * 32, 31]) end

image = Image.new( 32 * 8, 32 )
[[0, 0, 0], [0, 0, 200], [0, 200, 0], [0, 200, 200], [200, 0, 0], [200, 0, 200], [200, 200, 0], [200, 200, 200]].each_with_index do |c, i|
  image.boxFill(i * 32, 0, i * 32 + 31, 31, c).box(i * 32, 0, i * 32 + 31, 31, [0,0,0])
end

mesh = Mesh.new(createWall([0,0,0], 100, tutv), image)

class Parts
  attr_accessor :func, :angle, :matrix
  def initialize(x, y, z, mesh)
    @x = x
    @y = y
    @z = z
    @matrix = Matrix.createRotationX(0)
    @angle = 0
    @mesh = mesh
    @func = Matrix.method(:createRotationX)
  end
  def draw
    Window.drawMesh(Matrix.createTranslation(@x, @y, @z) * @matrix * @func.call(@angle) * 
                    Matrix.createTranslation(250, 250, -1500),  @mesh)
  end
end

cube = [[[],[],[]],[[],[],[]],[[],[],[]]]
# キューブの3次元配列
for y in -1..1
  for z in -1..1
    for x in -1..1
      cube[y+1][z+1].push(Parts.new(x * 100, y * 100, z * 100, mesh))
    end
  end
end

def get_x_men(cube, index)  # 縦に切った2次元配列を作る
  r = [[],[],[]]
  for i in 0..2
    for j in 0..2
      r[i].push(cube[i][j][index])
    end
  end
  r
end

def set_x_men(cube, index, r)  # 戻す
  for i in 0..2
    for j in 0..2
      cube[i][j][index] = r[i][j]
    end
  end
end

def rotation(c, v, func, cube)
  c.each do |line|
    line.each do |parts|
      parts.func = func
      parts.angle = 0
    end
  end
  0.step(90, 5) do |i|
    break if Input.update
    c.each do |line|
      line.each do |parts|
        parts.angle = i * v
      end
    end
    cube.flatten.each do |parts| parts.draw end
    Window.sync
    Window.update
  end
  c.each do |line|
    line.each do |parts|
      parts.angle = 0
      parts.matrix *= func.call(90 * v)
    end
  end
  for i in 0..(v == 1 ? 0 : 2)
    c = c.transpose.reverse
  end
  return c
end

Window.view_matrix = Matrix.createView(Matrix.createTranslation(400,-500,0) * Matrix.createRotationY(20) * Matrix.createRotationX(20))

cursor_image = Image.new(30,30,[255,255,255])
x = y = 0
Window.create
loop do
  break if Input.update
  dx = (Input.keyPush?(K_LEFT) ? -1:0) + (Input.keyPush?(K_RIGHT) ? 1:0)
  dy = (Input.keyPush?(K_UP) ? -1:0) + (Input.keyPush?(K_DOWN) ? 1:0)
  if Input.keyDown?(K_LSHIFT) or Input.keyDown?(K_RSHIFT) # シフト押しながらだと回転
    if Input.keyPush?(K_LEFT) or Input.keyPush?(K_RIGHT)  # 左右
      cube[y] = rotation(cube[y], dx, Matrix.method(:createRotationY), cube)
    elsif Input.keyPush?(K_UP) or Input.keyPush?(K_DOWN)  # 上下
      set_x_men(cube, x, rotation(get_x_men(cube, x), -dy, Matrix.method(:createRotationX), cube))
    end
  else
    x += dx
    y += dy
  end

  cube.flatten.each do |parts|
    parts.draw
  end
  Window.draw(x * 80 + 195, y * 80 + 185, cursor_image)
  Window.sync
  Window.update
end