とりあえずアップしとくサンプル

ゲームオーバー判定が無いが。

# DXRuby0.0.5 サンプル ブロック落とし
require 'dxruby'

Window.caption = "ブロック落としモドキ"

# ブロックデータ
blank = [0, 0, 0, 0]
$block = [[blank, [0, 1, 1, 0], [0, 1, 1, 0], blank], [blank, [1, 1, 1, 1], [0, 0, 0, 0], blank],
          [blank, [0, 0, 1, 1], [0, 1, 1, 0], blank], [blank, [0, 1, 1, 0], [0, 0, 1, 1], blank],
          [blank, [0, 1, 1, 1], [0, 1, 0, 0], blank], [blank, [0, 1, 1, 1], [0, 0, 0, 1], blank],
          [blank, [0, 1, 1, 1], [0, 0, 1, 0], blank]]
blockimage = []
# 画像データの作成
(1..7). each do |i|
  data = Image.new(16, 16)
  # ブロックの種類が多いので計算で色を作ってみた
  # こんなことするより画像ファイルから読むほうをオススメします
  data.box(0, 0, 15, 15, [255, (i / 4)*220, ((i % 4) / 2)*220, (i % 2)*220])
  data.line(0, 0, 14, 0, [255, (i / 4)*128+127, ((i % 4) / 2)*128+127, (i % 2)*128+127])
  data.line(0, 0, 0, 14, [255, (i / 4)*128+127, ((i % 4) / 2)*128+127, (i % 2)*128+127])
  data.line(15, 1, 15, 15, [255, (i / 4)*128, ((i % 4) / 2)*128, (i % 2)*128])
  data.line(1, 15, 15, 15, [255, (i / 4)*128, ((i % 4) / 2)*128, (i % 2)*128])
  blockimage.push(data)
end
stageimage = Image.new(192, 336).box(0, 0, 191, 335, [255, 255, 255, 255]).
                                 box(16, 0, 175,  319, [255,0,0,0])
$field = Array.new(20){[1] + [nil]*10 + [1]}.push([1]*12)

# 指定回数右回転
def rotation(num, angle)
  b = $block[num]
  angle.times do
    a = [[], [], [], []]
    for y in 0..3 do
      for x in 0..3 do
        a[x].push(b[3 - y][x])
      end
    end
    b = a
  end
  return b
end

# 当たり判定
def check(b, x, y)
  for i in 0..3 do
    for j in 0..3 do
      if b[i][j] == 1 and $field[(y+15) / 16 + i][x / 16 + j] != nil then
        return true
      end
    end
  end
  return false
end

x = 64 # 横
y = 0  # 縦
kind = rand(7) # ブロックの種類
angle = 0      # 角度

# メインループ
Window.loop do
  Window.draw(0, 0, stageimage) # 枠描画

  # 回転操作
  if Input.padPush?(P_BUTTON0) and check(rotation(kind, angle-1), x, y) == false then
    angle = angle == 0 ? 3 : angle - 1
  end
  if Input.padPush?(P_BUTTON1) and check(rotation(kind, angle+1), x, y) == false then
    angle = angle == 3 ? 0 : angle + 1
  end

  b = rotation(kind, angle) # 回転したデータを作成

  x = x - 16 if Input.padPush?(P_LEFT) and check(b, x-16, y) == false
  x = x + 16 if Input.padPush?(P_RIGHT) and check(b, x+16, y) == false
  y = y + 1
  y = y + 3 if Input.padDown?(P_DOWN)

  # 当たり判定
  if check(b, x, y) == true then # 当たっていたら
    for i in 0..3 do
      for j in 0..3 do  # フィールド配列に設定
        $field[(y+15) / 16 + i - 1][x / 16 + j] = kind if b[i][j] == 1
      end
    end
    x = 64
    y = 0
    kind = rand(7)
    angle = 0
    for i in 0..19 do # 揃った行を削除&上に空行追加
      if $field[i].index(nil) == nil then
        $field.delete_at(i)
        $field.unshift([1] + [nil]*10 + [1])
      end
    end
  else
    # 画像描画
    for i in 0..3 do
      for j in 0..3 do
        Window.draw(j * 16 + x, i * 16 + y, blockimage[kind]) if b[i][j] == 1
      end
    end
  end

  # フィールド描画
  for i in 0..19 do
    for j in 1..10 do
      Window.draw(j * 16, i * 16, blockimage[$field[i][j]]) if $field[i][j]
    end
  end
  break if Input.keyDown?(K_ESCAPE)
end

テトリスもどきは検索するとソースがいっぱい出てくるが、作る人によってほんとにマチマチなのは、おそらくデータ構造によってプログラムが大きく変わるからだ。
つまり、初めの設計が全てである。
この点に置いて、初心者がテトリスは簡単そうだから練習に作ろう、というのはなかなか難しいのではないかと思うわけだ。
テキトーな構造で作りはじめるととんでもなく苦労することになる。


ややこしいことは避けようと思ったのだが、短くするためにアノ手コノ手になってしまった。
根本的に、データ構造の制限によりあまり短くはならない。
もっとわかりにくい構造にすれば、まだまだ行けるのだろう。
まあ、短さとわかりにくさなら7行テトリスがあることだし、競ってもしょうがなくて、ここではブログにソースを載せる限界を100行前後と勝手に定めて、そのあたりに納めるように努力してみた。