Created
June 23, 2018 12:51
-
-
Save mxrnx/06a628eafafdfffdc34e80984c6cdc6a to your computer and use it in GitHub Desktop.
A tiny, self-sustained implementation of Sokoban
This file contains hidden or 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
require 'io/console' | |
# minimal Sokoban implementation | |
# by knarka | |
# released into the public domain | |
class Sokoban | |
def initialize | |
@levels = DATA.readlines.map { |r| Level.new r } | |
main | |
end | |
private | |
def main | |
loop do | |
win if @level.nil? && @levels.empty? | |
@level = @levels.shift if @level.nil? | |
@level.draw | |
@level.handle STDIN.getch | |
@level = nil if @level.done? | |
end | |
end | |
def win | |
puts 'you win!' | |
exit | |
end | |
class Level | |
def initialize(raw) | |
i = -1 | |
@map = raw.split('-').map do |l| | |
i += 1 | |
width ||= l.length | |
raise 'uneven level' unless l.length == width | |
l.chars.map do |c| | |
@pos = [i, l.index('@')] if ['@', '+'].include?(c) && @pos.nil? | |
c | |
end | |
end | |
end | |
def done? | |
@map.each { |l| l.each { |c| return false if c == 'o' } } | |
true | |
end | |
def draw | |
@map.each { |l| puts l.join } | |
puts | |
end | |
def handle(key) | |
case key | |
when 'j' then move 0, :+ | |
when 'k' then move 0, :- | |
when 'l' then move 1, :+ | |
when 'h' then move 1, :- | |
when 'q' then abort 'you lose' | |
end | |
end | |
private | |
def move(index, operator) | |
dpos = @pos.clone | |
dpos[index] = dpos[index].public_send(operator, 1) | |
case get_field(dpos) | |
when '.', ' ' | |
move_player @pos, dpos | |
@pos = dpos | |
when 'o' | |
dcube = @pos.clone | |
dcube[index] = dcube[index].public_send(operator, 2) | |
if ['.', ' '].include? get_field(dcube) | |
move_cube dpos, dcube | |
move_player @pos, dpos | |
@pos = dpos | |
end | |
end | |
end | |
def get_field(pos) | |
@map[pos[0]][pos[1]] | |
end | |
def set_field(pos, value) | |
@map[pos[0]][pos[1]] = value | |
end | |
def move_player(old, new) | |
set_field(old, get_field(old) == '@' ? ' ' : '.') | |
set_field(new, get_field(new) == ' ' ? '@' : '+') | |
end | |
def move_cube(old, new) | |
set_field(old, ' ') | |
set_field(new, get_field(new) == '.' ? '*' : 'o') | |
end | |
end | |
end | |
Sokoban.new | |
__END__ | |
#####-#.o@#-##### | |
#####-#. #-#o@o#-# .#-##### |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment