Skip to content

Instantly share code, notes, and snippets.

@alpicola
Created December 11, 2010 03:41
Show Gist options
  • Select an option

  • Save alpicola/737131 to your computer and use it in GitHub Desktop.

Select an option

Save alpicola/737131 to your computer and use it in GitHub Desktop.
require 'ffi-ncurses'
class Tetris
Curses = FFI::NCurses
PIECES = [
[ # O
[[-1, 0], [0, 0], [-1, 1], [0, 1]]
],
[ # I
[[-2, 0], [-1, 0], [0, 0], [1, 0]],
[[0, -1], [0, 0], [0, 1], [0, 2]]
],
[ # S
[[0, 0], [1, 0], [-1, 1], [0, 1]],
[[0, -1], [0, 0], [1, 0], [1, 1]]
],
[ # Z
[[-1, 0], [0, 0], [0, 1], [1, 1]],
[[1, -1], [0, 0], [1, 0], [0, 1]]
],
[ # L
[[-1, 0], [0, 0], [1, 0], [-1, 1]],
[[0, -1], [0, 0], [0, 1], [1, 1]],
[[1, -1], [-1, 0], [0, 0], [1, 0]],
[[-1, -1], [0, -1], [0, 0], [0, 1]]
],
[ # J
[[-1, 0], [0, 0], [1, 0], [1, 1]],
[[0, -1], [1, -1], [0, 0], [0, 1]],
[[-1, -1], [-1, 0], [0, 0], [1, 0]],
[[0, -1], [0, 0], [-1, 1], [0, 1]]
],
[ # T
[[-1, 0], [0, 0], [1, 0], [0, 1]],
[[0, -1], [0, 0], [1, 0], [0, 1]],
[[0, -1], [-1, 0], [0, 0], [1, 0]],
[[0, -1], [-1, 0], [0, 0], [0, 1]]
]
]
def self.run
new.run
end
def initialize
@pile = Array.new(20) { Array.new(10, false) }
@piece = nil
@x = nil
@y = nil
@completed_lines = 0
@level = 0
@delay = 0
end
def run
Curses.initscr
Curses.clear
Curses.cbreak
Curses.noecho
Curses.timeout(0)
Curses.move(0, 0)
Curses.vline('|'.ord, 21)
Curses.move(0, 21)
Curses.vline('|'.ord, 21)
Curses.move(20, 0)
Curses.hline('-'.ord, 22)
Curses.move(1, 24)
Curses.addstr('Score:')
Curses.move(4, 24)
Curses.addstr('Keys:')
Curses.move(5, 24)
Curses.addstr('h,l: Left, Right')
Curses.move(6, 24)
Curses.addstr('j,k: Down, Rotate')
Curses.move(7, 24)
Curses.addstr("' ': Drop")
Curses.move(8, 24)
Curses.addstr(' q: Quit')
catch(:quit) do
prev = nil
loop do
now = Time.now
if !prev || now - prev > @delay
fall
prev = now
end
while (c = Curses.getch) != -1
c = c.chr rescue ''
if @piece
case c
when 'h'
@x -= 1
@x += 1 if overlapped?
when 'j'
@y += 1
@y -= 1 if overlapped?
when 'k'
@piece.rotate!(1)
@piece.rotate!(-1) if overlapped?
when 'l'
@x += 1
@x -= 1 if overlapped?
when ' '
fall while @piece
end
end
throw :quit if c == 'q'
end
render
sleep 0.05
end
end
ensure
Curses.endwin
end
private
def fall
if @piece
@y += 1
if overlapped?
@piece[0].each do |x, y|
@pile[@y+y-1][@x+x] = true
end
@piece = nil
@pile.reject! {|line| line.all? }
while @pile.length < 20
@pile.unshift(Array.new(10, false))
@completed_lines += 1
end
end
else
@x, @y = 5, 0
@piece = PIECES.sample.dup
@level = [@completed_lines / 10, 10].min
@delay = (11 - @level) * 0.05
Curses.move(2, 27)
Curses.addstr('%06d' % @completed_lines)
if overlapped?
Curses.timeout(-1)
Curses.move(8, 6)
Curses.addstr('GAME OVER')
Curses.move(0, 0)
Curses.refresh
c = Curses.getch
throw :quit if c == 'q'.ord
Curses.timeout(0)
initialize
end
end
end
def overlapped?
if @piece
@piece[0].any? do |x, y|
x += @x
y += @y
x < 0 || 10 <= x || y < 0 || 20 <= y || @pile[y][x]
end
end
end
def render
20.times do |y|
10.times do |x|
Curses.move(y, x*2+1)
if @pile[y][x]
Curses.addstr('[]')
else
Curses.addstr(' ')
end
end
end
if @piece
@piece[0].each do |x, y|
Curses.move(@y+y, (@x+x)*2+1)
Curses.addstr('[]')
end
end
Curses.move(0, 0)
Curses.refresh
end
end
if __FILE__ == $0
Tetris.run
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment