-
-
Save icebreaker/156be660281ab5dcb5b4 to your computer and use it in GitHub Desktop.
Quick and dirty Pong clone written in 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
#!/usr/bin/env ruby | |
# | |
# Pong clone written in Ruby. | |
# | |
# 2014, Mihail Szabolcs | |
# Released into the Public Domain. | |
# | |
# Keys: | |
# | |
# Escape - quit | |
# Up Arrow - move up | |
# Down Arrow - move down | |
# | |
# That's all folks :) | |
# | |
# | |
require 'curses' | |
module Pong | |
FPS = 33 # 30 fps = 1000.0 / 30.0 =~ 33 ms | |
BLOCK = 254 # block character | |
ESC = 27 # escape ascii code | |
class Shape | |
attr_reader :w, :h | |
def initialize | |
@shape = yield.map { |l| l.split('') } | |
@w = @shape.map(&:length).max | |
@h = @shape.count | |
end | |
def draw(x, y) | |
@shape.each_with_index do |line, j| | |
line.each_with_index do |col, i| | |
if col != ' ' | |
Curses.setpos(y + j, x + i) | |
Curses.addch(BLOCK) | |
end | |
end | |
end | |
end | |
end | |
class Logo | |
def initialize | |
@logo = Shape.new do | |
[ "0000000 0000000 00 0 0000000", | |
"0 0 0 0 0 0 0 0 ", | |
"0 0000 0 0 0 0 0 0 0000 ", | |
"0 0 0 0 0 0 0 0 0", | |
"0000 0000000 0 00 0000000" ] | |
end | |
@x = ((Curses.cols - @logo.w) / 2.0).ceil | |
@y = ((Curses.lines - @logo.h) / 2.0).ceil | |
end | |
def draw | |
@logo.draw(@x, @y) | |
end | |
end | |
class Digits | |
attr_reader :x, :y | |
def initialize | |
@digits = [] | |
@digits << Shape.new do | |
[ "0000000", | |
"0 0", | |
"0 0", | |
"0 0", | |
"0000000"] | |
end | |
@digits << Shape.new do | |
[ " 00 ", | |
" 000 ", | |
" 0000 ", | |
" 00 ", | |
"0000000"] | |
end | |
@digits << Shape.new do | |
[ "0000000", | |
" 00 ", | |
" 00 ", | |
" 00 ", | |
"0000000"] | |
end | |
@digits << Shape.new do | |
[ "0000000", | |
" 0", | |
" 00000", | |
" 0", | |
"0000000"] | |
end | |
@digits << Shape.new do | |
[ "0 0", | |
"0 0", | |
"0000000", | |
" 0", | |
" 0"] | |
end | |
@digits << Shape.new do | |
[ "0000000", | |
"0 ", | |
"0000000", | |
" 0", | |
"0000000"] | |
end | |
@digits << Shape.new do | |
[ "0000000", | |
"0 ", | |
"0000000", | |
"0 0", | |
"0000000"] | |
end | |
@digits << Shape.new do | |
[ "0000000", | |
" 00", | |
" 00 ", | |
" 00 ", | |
" 00 "] | |
end | |
@digits << Shape.new do | |
[ "0000000", | |
"0 0", | |
"0000000", | |
"0 0", | |
"0000000"] | |
end | |
@digits << Shape.new do | |
[ "0000000", | |
"0 0", | |
"0000000", | |
" 0", | |
"0000000"] | |
end | |
@w = @digits.first.w | |
@h = @digits.first.h | |
end | |
def draw(x, y, n) | |
@digits[n].draw(x, y) | |
end | |
end | |
class Score | |
def initialize(x, y) | |
@x = x | |
@y = y | |
@digits = Digits.new | |
@score = 0 | |
end | |
def draw | |
@digits.draw(@x, @y, @score) | |
end | |
def inc | |
@score += 1 | |
self | |
end | |
def won? | |
@score > 9 | |
end | |
end | |
class Separator | |
def initialize | |
@x = ((Curses.cols - 1) / 2).ceil | |
@y = 0 | |
end | |
def draw | |
(0..Curses.lines-1).each do |i| | |
if i % 2 == 1 | |
Curses.setpos(@y + i, @x) | |
Curses.addch(BLOCK) | |
end | |
end | |
end | |
end | |
class Ball | |
attr_reader :x, :y | |
def initialize | |
reset! | |
end | |
def reset! | |
@x = ((Curses.cols - 1) / 2).ceil | |
@y = ((Curses.lines - 1) / 2).ceil | |
@dx = rand(10) < 5 ? 1 : -1 | |
@dy = rand(10) < 5 ? 1 : -1 | |
@c = @x.ceil | |
end | |
def draw | |
Curses.setpos(@y, @x) | |
Curses.addch(BLOCK) | |
end | |
def coming_to_right? | |
@x > @c and @dx == 1 | |
end | |
def is_out_left? | |
@x == 0 | |
end | |
def is_out_right? | |
@x == Curses.cols - 1 | |
end | |
def update | |
# if @x == 0 || @x == Curses.cols - 1 | |
# @dx = -@dx | |
# end | |
if @y == 0 || @y == Curses.lines - 1 | |
@dy = -@dy | |
end | |
if (x = @x - 1) == 0 || (x = @x + 1) == Curses.cols - 1 | |
Curses.setpos(@y, x) | |
if Curses.inch == BLOCK | |
@dx = -@dx | |
Curses.setpos(@y - 1, x) | |
if Curses.inch != BLOCK | |
@dy = -1 | |
else | |
Curses.setpos(@y + 1, x) | |
if Curses.inch != BLOCK | |
@dy = 1 | |
end | |
end | |
end | |
Curses.setpos(@y, @x) | |
end | |
@x += @dx | |
@y += @dy | |
end | |
end | |
class Player | |
attr_reader :score | |
def initialize | |
@n = 4 | |
@h = (@n / 2).ceil | |
@x ||= 0 | |
@y = ((Curses.lines - @n) / 2).ceil | |
@score = Score.new((Curses.cols / 2.0).ceil + (@x == 0 ? -(@n * 3) : @n), 1) | |
end | |
def cy | |
@y + @h | |
end | |
def draw | |
(0..@n-1).each do |i| | |
Curses.setpos(@y + i, @x) | |
Curses.addch(BLOCK) | |
end | |
@score.draw | |
end | |
def up(n) | |
@y -= n if @y > 0 | |
end | |
def down(n) | |
@y += n if @y + @n < Curses.lines | |
end | |
end | |
class Computer < Player | |
def initialize | |
@x = Curses.cols - 1 | |
super | |
end | |
def can_move? | |
rand(66).to_i < 60 | |
end | |
end | |
class Main | |
def initialize | |
srand(Time.now.to_f) | |
Curses.init_screen() | |
Curses.noecho() | |
Curses.nonl() | |
Curses.cbreak() | |
Curses.curs_set(0) | |
Curses.stdscr.keypad(true) | |
end | |
def run | |
Curses.stdscr.nodelay = false | |
Curses.clear() | |
Logo.new.draw | |
Curses.refresh() | |
case Curses.getch | |
when ESC | |
return | |
end | |
Curses.stdscr.nodelay = true | |
ball = Ball.new | |
separator = Separator.new | |
player = Player.new | |
computer = Computer.new | |
loop do | |
t = Time.now.to_f | |
Curses.clear() | |
separator.draw | |
ball.draw | |
player.draw | |
computer.draw | |
Curses.refresh() | |
case Curses.getch | |
when Curses::Key::UP | |
player.up(1) | |
when Curses::Key::DOWN | |
player.down(1) | |
when ESC | |
break | |
end | |
ball.update | |
if ball.coming_to_right? and computer.can_move? | |
if ball.y < computer.cy | |
computer.up(1) | |
elsif ball.y > computer.cy | |
computer.down(1) | |
end | |
end | |
if ball.is_out_left? | |
if computer.score.inc.won? | |
break | |
else | |
ball.reset! | |
end | |
elsif ball.is_out_right? | |
if player.score.inc.won? | |
break | |
else | |
ball.reset! | |
end | |
end | |
dt = (Time.now.to_f - t) * 1000.0 | |
sleep((FPS - dt) * 0.001) if dt < FPS | |
end | |
run # :D | |
end | |
end | |
end | |
Pong::Main.new.run if $0 == __FILE__ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment