Last active
March 29, 2022 18:35
-
-
Save obelisk68/15ffdf1bfd82953361be0264b5ea4119 to your computer and use it in GitHub Desktop.
Ruby2D を使ったテトリス
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "ruby2d" | |
include Ruby2D::DSL | |
Wait = 18 | |
class Tetromino | |
def initialize | |
@pat = Array.new(4) | |
pats = [["1111"], ["11", "11"], ["011", "110"], ["110", "011"], | |
["100", "111"], ["001", "111"], ["010", "111"]] | |
@num = rand(1..pats.size) | |
@pat[0] = pats.map {|pt| pt.map {|st| st.chars.map(&:to_i)} }[@num - 1] | |
(1..3).each do |i| | |
@pat[i] = @pat[i - 1].reverse.transpose #右回転 | |
end | |
@dir = 0 | |
@x, @y = 3, 0 | |
end | |
attr_accessor :x, :y | |
def rotate(n) | |
@dir = (@dir + n) % 4 | |
end | |
def get | |
@pat[@dir].map {|row| row.map {|i| i * @num} } | |
end | |
def width | |
@pat[@dir].first.size | |
end | |
def height | |
@pat[@dir].size | |
end | |
end | |
class Field | |
Width, Height = 10, 20 | |
Margin = 30 | |
BlockMargin = 1 | |
BlockSide = 25 | |
S = BlockMargin * 2 + BlockSide | |
W = Margin * 2 + S * Width | |
H = Margin * 2 + S * Height | |
Color = ["#158FAC", "#F1F101", "#2FFF43", "#DF0F0F", | |
"#5858FF", "#FFB950", "#FF98F3"] | |
def initialize | |
set width: W, height: H, title: "Tetris Ruby2D" | |
Rectangle.new x: 0, y: 0, width: W, height: H, color: "#ABF8FC", z: 0 | |
Rectangle.new x: Margin, y: Margin, | |
width: W - 2 * Margin, height: H - 2 * Margin, | |
color: "black", z: 0 | |
@blocks = Height.times.map {|y| | |
Width.times.map {|x| | |
Square.new x: Margin + BlockMargin + S * x, | |
y: Margin + BlockMargin + S * y, | |
size: BlockSide, color: "red", z: 10 | |
} | |
} | |
@field = @blocks.map {Array.new(Width, 0)} | |
end | |
def render | |
Height.times do |y| | |
Width.times do |x| | |
@blocks[y][x].color = Color[@field[y][x] - 1] | |
@field[y][x].nonzero? ? @blocks[y][x].add : @blocks[y][x].remove | |
end | |
end | |
end | |
def birth | |
@piece = Tetromino.new | |
collision? ? raise("game over") : write_to_field | |
end | |
def write_to_field | |
x, y = @piece.x, @piece.y | |
@piece.get.map.with_index {|row, dy| | |
row.each_index {|dx| @field[y + dy][x + dx] = row[dx] if row[dx].nonzero?} | |
} | |
end | |
def delete_from_field | |
x, y = @piece.x, @piece.y | |
@piece.get.map.with_index {|row, dy| | |
row.each_index {|dx| @field[y + dy][x + dx] = 0 if row[dx].nonzero?} | |
} | |
end | |
def one_down | |
delete_from_field | |
collision_flag = false | |
@piece.y += 1 | |
if collision? | |
@piece.y -= 1 | |
collision_flag = true | |
end | |
write_to_field | |
return collision_flag | |
end | |
def collision? | |
x, y = @piece.x, @piece.y | |
return true if y + @piece.height > Height || x + @piece.width > Width | |
@piece.get.map.with_index {|row, dy| | |
row.map.with_index {|a, dx| a.nonzero? && @field[y + dy][x + dx].nonzero?}.any? | |
}.any? | |
end | |
def delete_blocks | |
Height.times do |y| | |
if @field[y].all?(&:nonzero?) | |
@field.delete_at(y) | |
@field.unshift(Array.new(Width, 0)) | |
return true | |
end | |
end | |
false | |
end | |
def move(dx) | |
delete_from_field | |
@piece.x += dx | |
@piece.x -= dx if @piece.x < 0 || collision? | |
write_to_field | |
end | |
def rotate | |
delete_from_field | |
@piece.rotate(1) | |
@piece.rotate(-1) if collision? | |
write_to_field | |
end | |
end | |
f = Field.new | |
t = 1 | |
deleting_continues = false #ブロックを消す作業が終っていなければtrue | |
collision_flag = false #これ以上テトロミノが落下できなければtrue | |
command = nil | |
wait = Wait | |
on :key_down do |event| | |
command = case event.key | |
when "left" then "left" | |
when "right" then "right" | |
when "up" then "rotate" | |
when "down" then "down" | |
else nil | |
end | |
end | |
on :controller_button_down do |event| | |
command = case event.button | |
when :left then "left" | |
when :right then "right" | |
when :a then "rotate" | |
when :down then "down" | |
else nil | |
end | |
end | |
f.birth | |
f.render | |
update do | |
unless collision_flag | |
case command | |
when "left" then f.move(-1) | |
when "right" then f.move(1) | |
when "rotate" then f.rotate | |
when "down" then wait = 2 | |
end | |
command = nil | |
end | |
collision_flag = f.one_down if (t % wait).zero? && !collision_flag #落下できるならひとつ落下 | |
#消せる行があるか | |
if (deleting_continues || collision_flag) && (t % 30).zero? | |
deleting_continues = f.delete_blocks #消せる行があれば一行消す | |
#すべて消し終わったあとの処理 | |
unless deleting_continues | |
wait = Wait | |
collision_flag = false | |
f.birth | |
end | |
end | |
t += 1 | |
f.render | |
end | |
show |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
nice