Skip to content

Instantly share code, notes, and snippets.

@myokoym
Last active August 29, 2015 13:58
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 myokoym/10006568 to your computer and use it in GitHub Desktop.
Save myokoym/10006568 to your computer and use it in GitHub Desktop.
A game as Tetris using Gosu and Ruby.
#!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