Last active
August 29, 2015 13:58
-
-
Save myokoym/10006568 to your computer and use it in GitHub Desktop.
A game as Tetris using Gosu and Ruby.
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
#!ruby | |
# | |
# This program is licensed under the MIT license. (c) 2014 Masafumi Yokoyama | |
# | |
require "gosu" | |
module Tetris | |
SCALE = 20 | |
module Base | |
def draw_square(window, x1, y1, x2, y2, color, z_order=0) | |
window.draw_quad(x1, y1, color, | |
x2, y1, color, | |
x1, y2, color, | |
x2, y2, color, | |
z_order) | |
end | |
def draw_frame(window, x1, y1, x2, y2, color, z_order=0) | |
window.draw_line(x1, y1, color, | |
x2, y1, color, | |
z_order) | |
window.draw_line(x1, y1, color, | |
x1, y2, color, | |
z_order) | |
window.draw_line(x1, y2, color, | |
x2, y2, color, | |
z_order) | |
window.draw_line(x2, y1, color, | |
x2, y2, color, | |
z_order) | |
end | |
end | |
class Block | |
include Base | |
attr_reader :x, :y | |
def initialize(window, x, y, color=nil) | |
@window = window | |
@x = x | |
@y = y | |
@color = color || Gosu::Color::GRAY | |
end | |
def fall | |
@y += 1 | |
end | |
def move_left | |
@x -= 1 | |
end | |
def move_right | |
@x += 1 | |
end | |
def transfer(x, y) | |
@x += x | |
@y += y | |
end | |
def draw | |
x1 = @x * SCALE | |
y1 = @y * SCALE | |
x2 = (@x + 1) * SCALE | |
y2 = (@y + 1) * SCALE | |
draw_square(@window, | |
x1, | |
y1, | |
x2, | |
y2, | |
@color) | |
draw_frame(@window, | |
x1, | |
y1, | |
x2, | |
y2, | |
Gosu::Color::BLACK) | |
end | |
end | |
class Point | |
attr_accessor :x, :y | |
def initialize(x, y) | |
@x = x | |
@y = y | |
end | |
end | |
class Tetrimino | |
attr_reader :blocks | |
COLORS = [ | |
Gosu::Color.new(0xfff5f5f5), # whitesmoke | |
Gosu::Color.new(0xff4169e1), # royalblue | |
Gosu::Color.new(0xff228b22), # forestgreen | |
Gosu::Color.new(0xffdeb887), # burlywood | |
Gosu::Color.new(0xffff6347), # tomato | |
Gosu::Color.new(0xffadff2f), # greenyellow | |
Gosu::Color.new(0xff2f4f4f), # darkslategray | |
] | |
TETRIMINO_LIST = [ | |
{ | |
:points => [[ 0, 0], [ 0, 1], [ 0, 2], [ 0, -1]], | |
:color => COLORS[0], | |
}, | |
{ | |
:points => [[ 0, 0], [ 1, 0], [ 0, 1], [ 1, 1]], | |
:color => COLORS[1], | |
}, | |
{ | |
:points => [[ 0, 0], [ 1, 0], [ 0, 1], [-1, 1]], | |
:color => COLORS[2], | |
}, | |
{ | |
:points => [[ 0, 0], [-1, 0], [ 0, 1], [ 1, 1]], | |
:color => COLORS[3], | |
}, | |
{ | |
:points => [[-1, 0], [-1, 1], [ 0, 1], [ 1, 1]], | |
:color => COLORS[4], | |
}, | |
{ | |
:points => [[ 1, 0], [ 1, 1], [ 0, 1], [-1, 1]], | |
:color => COLORS[5], | |
}, | |
{ | |
:points => [[ 0, 0], [ 0, 1], [ 1, 1], [-1, 1]], | |
:color => COLORS[6], | |
}, | |
] | |
def initialize(window, x, y) | |
@points, @color = sample_tetrimino | |
@blocks = [] | |
@points.each do |point| | |
@blocks << Block.new(window, | |
x + point.x, | |
y + point.y, | |
@color) | |
end | |
end | |
def fall | |
@blocks.each {|block| block.fall } | |
end | |
def move_left | |
@blocks.each {|block| block.move_left } | |
end | |
def move_right | |
@blocks.each {|block| block.move_right } | |
end | |
def turn | |
@blocks.each_with_index do |block, i| | |
point = @points[i] | |
x = point.y | |
y = point.x * -1 | |
block.transfer(x - point.x, y - point.y) | |
@points[i].x = x | |
@points[i].y = y | |
end | |
end | |
def turn_reverse | |
@blocks.each_with_index do |block, i| | |
point = @points[i] | |
x = point.y * -1 | |
y = point.x | |
block.transfer(x - point.x, y - point.y) | |
@points[i].x = x | |
@points[i].y = y | |
end | |
end | |
def draw | |
@blocks.each {|block| block.draw } | |
end | |
private | |
def sample_tetrimino | |
tetrimino = TETRIMINO_LIST.sample | |
points = tetrimino[:points].collect do |point| | |
Point.new(point[0], point[1]) | |
end | |
[points, tetrimino[:color]] | |
end | |
end | |
class GameWindow < Gosu::Window | |
include Base | |
DEFAULT_X = 10 | |
DEFAULT_Y = DEFAULT_X * 2 | |
DEFAULT_WIDTH = DEFAULT_X * SCALE | |
DEFAULT_HEIGHT = DEFAULT_Y * SCALE | |
SPEED = 2 | |
def initialize(width=DEFAULT_WIDTH, height=DEFAULT_HEIGHT) | |
super(width, height, false) | |
@score = 0 | |
self.caption = @score.to_s | |
@background_color = Gosu::Color.new(0xff000000) | |
@fall_count = 100 | |
@blocks = [] | |
new_tetrimino | |
end | |
def update | |
if @fall_count <= 0 | |
if block_stop? | |
tetrimino_to_blocks | |
line_complete | |
new_tetrimino | |
else | |
@tetrimino.fall | |
end | |
@fall_count = 100 | |
end | |
@fall_count -= SPEED | |
@fall_count -= (SPEED * 2) if button_down?(Gosu::KbDown) | |
end | |
def draw | |
draw_background | |
@blocks.each {|block| block.draw } | |
@tetrimino.draw | |
end | |
def button_down(id) | |
case id | |
when Gosu::KbLeft | |
move_left | |
when Gosu::KbRight | |
move_right | |
when Gosu::KbUp | |
@tetrimino.turn | |
@tetrimino.turn_reverse if out_of_area? || block_stop? | |
when Gosu::KbEscape | |
close | |
end | |
end | |
private | |
def draw_background | |
draw_square(self, | |
0, 0, | |
self.width, self.height, | |
@background_color) | |
end | |
def new_tetrimino | |
window = self | |
x = (DEFAULT_X - 1) / 2 | |
y = -1 | |
@tetrimino = Tetrimino.new(self, | |
(DEFAULT_X - 1) / 2, | |
-1) | |
draw | |
game_over if block_stop? | |
end | |
def block_stop? | |
@tetrimino.blocks.any? do |falling_block| | |
stopped = false | |
if falling_block.y >= DEFAULT_Y - 1 | |
stopped = true | |
else | |
@blocks.each do |block| | |
if (falling_block.x == block.x) && | |
(falling_block.y == block.y - 1) | |
stopped = true | |
break | |
end | |
end | |
end | |
stopped | |
end | |
end | |
def out_of_area? | |
@tetrimino.blocks.any? do |falling_block| | |
falling_block.x < 0 || | |
falling_block.x >= DEFAULT_X || | |
falling_block.y < 0 || | |
falling_block.y >= DEFAULT_Y | |
end | |
end | |
def tetrimino_to_blocks | |
@tetrimino.blocks.each do |block| | |
@blocks << block | |
end | |
end | |
def move_left | |
@tetrimino.blocks.each do |falling_block| | |
return if falling_block.x <= 0 | |
return unless can_move_left? | |
end | |
@tetrimino.move_left | |
end | |
def move_right | |
@tetrimino.blocks.each do |falling_block| | |
return if falling_block.x >= DEFAULT_X - 1 | |
return unless can_move_right? | |
end | |
@tetrimino.move_right | |
end | |
def can_move_left? | |
@tetrimino.blocks.all? do |falling_block| | |
can_move = true | |
@blocks.each do |block| | |
if (falling_block.x == block.x + 1) && | |
(falling_block.y == block.y) | |
can_move = false | |
break | |
end | |
end | |
can_move | |
end | |
end | |
def can_move_right? | |
@tetrimino.blocks.all? do |falling_block| | |
can_move = true | |
@blocks.each do |block| | |
if (falling_block.x == block.x - 1) && | |
(falling_block.y == block.y) | |
can_move = false | |
break | |
end | |
end | |
can_move | |
end | |
end | |
def line_complete | |
0.upto(DEFAULT_Y - 1) do |y| | |
next unless @blocks.count {|block| block.y == y } == DEFAULT_X | |
@score += 1 | |
self.caption = @score.to_s | |
@blocks.reject! do |block| | |
block.y == y | |
end | |
@blocks.each do |block| | |
block.fall if block.y < y | |
end | |
end | |
end | |
def game_over | |
sleep(3) | |
@score = 0 | |
self.caption = @score.to_s | |
@blocks = [] | |
new_tetrimino | |
end | |
end | |
end | |
Tetris::GameWindow.new.show |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment