Skip to content

Instantly share code, notes, and snippets.

@simon2k
Created March 30, 2012 10:13
Show Gist options
  • Save simon2k/2250576 to your computer and use it in GitHub Desktop.
Save simon2k/2250576 to your computer and use it in GitHub Desktop.
sokoban
# http://www.rubyquiz.com/quiz5.html
# SOKOBAN
# gem install highline
require 'highline/system_extensions'
include HighLine::SystemExtensions
class String
# define methods:
# crate? garage? wall? man?
{ 'crate?' => /o|\*/, 'garage?' => /\.|\*/,'man?' => /@|\+/, 'wall?' => /#/ }.
each_pair { |name, regex| define_method(name) { match(regex) } }
def empty?
!wall? && !crate?
end
def colorize(colour)
"\e[#{colour}m#{self}\e[0m"
end
end
class Array
def colorize
colours = { '.' => 31, '#' => 35, '*' => 32, 'o' => 33, '@' => 34, '+' => 34 }
map { |e| colours[e] ? e.colorize(colours[e]) : e }
end
def clone
map(&:clone)
end
end
class Board
def initialize(filename)
@original_board = parse_boards(filename).shuffle.first
@board = @original_board.clone
@all_points = count_char '.'
@arrow
end
def display
system('clear')
puts 'SOKOBAN'.colorize(2)
puts '------------------------'
puts "Points: #{count_char('*')}/#{@all_points}".colorize(97)
puts '------------------------'
@board.each { |line| puts line.colorize.join }
end
def running?
@all_points != current_points
end
def wait_for_move
@arrow = Game::Arrows[get_character.chr.to_sym]
if @arrow && !next_field.wall?
move_crate if can_move_crate?
move_man if next_field.empty?
end
end
private
# define methods:
# move_man move_crate
{ :crate => %w{* o}, :man => %w{+ @} }.each_pair do |name, val|
define_method("move_#{name}") do
px, py = name.eql?(:crate) ? next_coordinaties : [x, y]
x, y = next_coordinaties(px, py)
@board[y][x] = @original_board[y][x].garage? ? val[0] : val[1]
@board[py][px] = @original_board[py][px].garage? ? '.' : ' '
end
end
def can_move_crate?
x, y = next_coordinaties
next_field.crate? && next_field(x, y).empty?
end
def count_char(chr)
@board.join.scan(chr).count
end
def current_points
count_char '*'
end
def next_coordinaties(x = x, y = y)
case @arrow
when :right then x += 1
when :left then x -= 1
when :down then y += 1
when :up then y -= 1
end
[x, y]
end
def next_field(x = x, y = y)
x, y = next_coordinaties(x, y)
@board[y][x].to_s
end
def parse_boards(filename)
File.open(filename).read.split("\n\n").
each.inject([]) do |all_boards, b|
all_boards << b.split("\n").each.inject([]) do |lines, line|
lines << line.scan(/./)
end
end
end
def x
@board[y].index { |e| e.man? }
end
def y
line = @board.detect { |r| r.include?('@') || r.include?('+') }
@board.index(line)
end
end
module Game
Arrows = { :j => :left,
:l => :right,
:k => :down,
:i => :up }
def self.start
board = Board.new('sokoban_levels.txt')
while board.running?
board.display
board.wait_for_move
end
board.display
puts 'Good job! :-)'
end
end
Game.start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment