[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[QUIZ] Sokoban (#5

James Gray

10/29/2004 1:00:00 PM

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.grayproductions.net/...

3. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

Ruby isn't the only good thing to come out of Japan. The computer game Sokoban,
invented by Hiroyuki Imabayashi, was introduced by Thinking Rabbit of
Takarazuka, Japan in 1982. This simple game of logic puzzles was an instant
success. It won awards and spawned sequels. Over the years, Sokoban has been
ported to a huge number of platforms. Fan support remains strong and many of
those fans even produce new levels for the game.

This week's quiz is to implement the game of Sokoban with the interface of your
choosing and any extra features you would like to have.

Sokoban (which translates to "Warehouse Man") has simple rules, which basically
amount to push crates into their storage spots in the warehouse. The elements
of the levels are simple: The "man", crates, walls, open floor, and storage.
Different level designers use various symbols to represent these items in level
data files. Here's one possible mix:

@ for the man
o for crates
# for walls
<space> for open floor
. for storage

Now because a man or a crate can also be on a storage space, we need special
conditions to represent those setups:

* for crate on storage
+ for man on storage

Using this, we can build an extremely simple level:

#####
#.o@#
#####

This level is completely surrounded by walls, as all Sokoban levels must be.
Walls are, of course, impassable. In the center we have from left to right: A
storage space, a crate (on open floor), and the man (also on open floor).

(The original Sokoban levels were 19 x 16, but later levels have varied in
size.)

The game is played by moving the man up, down, left and right. When the man
moves towards a crate, he may push it along in front of him as long as there is
no wall or second crate behind the one being pushed. A level is solved when all
crates are on storage spaces.

Given those rules, we can solve our level above with a single move to the left,
yielding:

#####
#*@ #
#####

That simple system can lead to some surprisingly complicated mind benders, but
please don't take my word for it. Here are some levels to test your game engine
and your logic skills:

http://www.grayproductions.net/...sokoban_levels.txt

(Note: These levels are Copyrighted by Thinking Rabbit. You may play them but
not profit from them in any way.)

Be warned, Sokoban is extremely addictive!


12 Answers

Florian Gross

10/29/2004 1:16:00 PM

0

Ruby Quiz wrote:

> This week's quiz is to implement the game of Sokoban with the interface of your
> choosing and any extra features you would like to have.

Interesting. In fact I did a cross between Sokoban and Dr. Mario (a nice
variation of Tetris) for this year's first Ludum Dare 48 hour game
development contest.

See http://www.ludumdare.com/user/1011/Infe... for a screenshot
of this.

The game itself is currently still available from
http://noegnud.sourceforge.net/flgr/Infectob... (windows package)
and http://noegnud.sourceforge.net/flgr/Infectoban-... (linux
package, requires ruby-sdl).

I also took part in this year's second contest -- that time I used
Ruby/Gosu and did a cave exploration / flying / shooting style game with
random maps. See http://www.mechanicalcat.net/tech/ld48/home/...

Other Ruby entries for this year's second contest were a very addicting
jump-and-flee (http://www.mechanicalcat.net/tech/ld48/home...)
and a game with a random world with different species of monsters (see
http://www.mechanicalcat.net/tech/ld48/hom...).

For anybody who hasn't created games with Ruby yet: It is surprisingly
easy and combined with the productivity boost you get from the 48 hour
contests it is a *lot* of fun. :)

James Gray

10/29/2004 1:52:00 PM

0

On Oct 29, 2004, at 8:19 AM, Florian Gross wrote:

> For anybody who hasn't created games with Ruby yet: It is surprisingly
> easy and combined with the productivity boost you get from the 48 hour
> contests it is a *lot* of fun. :)

I'm glad you like this one.

I'm a pretty big game nut and I think there are a lot of interesting
problems in the game space, so left to my own devices we'll probably
see plenty game quizzes over time.

Of course, for those of you who don't like that you can just submit
tons of quizzes, so I don't get a chance to run my own... :D

James Edward Gray II



Laurent Sansonetti

10/29/2004 2:12:00 PM

0

There is an interesting implementation of the Sobokan game as a
Ruby-GNOME2 sample:

http://ruby-gnome2.sourceforge.jp/hiki.c...

Cheers,

On Fri, 29 Oct 2004 22:51:46 +0900, James Edward Gray II
<james@grayproductions.net> wrote:
> On Oct 29, 2004, at 8:19 AM, Florian Gross wrote:
>
> > For anybody who hasn't created games with Ruby yet: It is surprisingly
> > easy and combined with the productivity boost you get from the 48 hour
> > contests it is a *lot* of fun. :)
>
> I'm glad you like this one.
>
> I'm a pretty big game nut and I think there are a lot of interesting
> problems in the game space, so left to my own devices we'll probably
> see plenty game quizzes over time.
>
> Of course, for those of you who don't like that you can just submit
> tons of quizzes, so I don't get a chance to run my own... :D
>
> James Edward Gray II
>
>

--
Laurent


G.Durga Prasad

11/1/2004 11:41:00 AM

0

On Fri, 29 Oct 2004 22:19:03 +0900, Florian Gross <flgr@ccan.de> wrote:
> Ruby Quiz wrote:
>
> > This week's quiz is to implement the game of Sokoban with the interface of your
> > choosing and any extra features you would like to have.
>

Thank you for causing me to write my first ever game programme in any language.
I am including my solution here. (I don't know any better way }. I
could solve using the Curses Library example given in Pickaxe II
book.

# solution to [QUIZ] Sokoban (#5)
# G.D.Prasad , 1st Nov , 04 , 5.00 PM
# run as ---- ruby sokoban.rb sokobangame

#file sokoban.rb

require 'curses'
include Curses
$a= []
i=0
file = File.new(ARGV[0],"r")
while line =file.gets
lineArr=line.split("")
$a << lineArr
if lineArr.include?("@")
$x= lineArr.index("@")
$y= i
end
i += 1
end

$M = '@'
$C='o'
$W='#'
$F= " "
$S='.'
$CS='*'
$MS='+'

class Store
HEIGHT = 10
STORE =$a
def initialize
@top = (Curses::lines - HEIGHT)/2
draw
end
def left
$x=STORE[$y].index("@") || STORE[$y].index("+")
temp=STORE[$y][$x-1]
case temp
when $F
if STORE[$y][$x-1,2]==[$F,$M]
STORE[$y][$x-1,2]=[$M,$F]
elsif STORE[$y][$x-1,2]==[$F,$MS]
STORE[$y][$x-1,2]=[$M,$S]
end
when $C
if STORE[$y][$x-2] == $F
STORE[$y][$x-2,3]=[$C,$M,$F]
elsif STORE[$y][$x-2] == $S
STORE[$y][$x-2,3]=[$CS,$M,$F]
end
when $CS
if STORE[$y][$x-2,3] == [$S,$CS,$M]
STORE[$y][$x-2,3]=[$CS,$M,$F]
end
when $S
if STORE[$y][$x-2,3]==[$S,$S,$M]
STORE[$y][$x-2,3]=[$S,$MS,$F]
elsif STORE[$y][$x-1,2]==[$S,$MS]
STORE[$y][$x-1,2]=[$MS,$S]
else
STORE[$y][$x-1,2]=[$MS,$F]
end
else beep
end
end

def right
$x=STORE[$y].index("@") || STORE[$y].index("+")
temp=STORE[$y][$x+1]
case temp
when $F
if STORE[$y][$x,2]==[$M,$F]
STORE[$y][$x,2]=[$F,$M]
elsif STORE[$y][$x,2]==[$MS,$F]
STORE[$y][$x,2]=[$S,$M]
end
when $C
if STORE[$y][$x+2] == $F
STORE[$y][$x,3]=[$F,$M,$C]
elsif STORE[$y][$x+2] == $S
STORE[$y][$x,3]=[$F,$M,$CS]
end
when $CS
if STORE[$y][$x,3] == [$M,$CS,$S]
STORE[$y][$x,3]=[$F,$MS,$CS]
end
when $S
if STORE[$y][$x,3]==[$M,$S,$S]
STORE[$y][$x,3]=[$F,$MS,$S]
elsif STORE[$y][$x,2]==[$MS,$S]
STORE[$y][$x,2]=[$S,$MS]
elsif STORE[$y][$x,2]==[$M,$S]
STORE[$y][$x,2]=[$F,$MS]
end
else beep
end
end
def up
$x=STORE[$y].index("@") || STORE[$y].index("+")
temp=STORE[$y-1][$x]
case temp
when $F
if [ STORE[$y][$x],STORE[$y-1][$x] ]==[$M,$F]
STORE[$y][$x],STORE[$y-1][$x]=$F,$M
$y -= 1
elsif [ STORE[$y][$x],STORE[$y-1][$x] ]==[$MS,$F]
STORE[$y][$x],STORE[$y-1][$x]=$S,$M
$y -= 1
end
when $C
if STORE[$y-2][$x] == $F
STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]=$F,$M,$C
$y -= 1
elsif STORE[$y-2][$x] == $S
STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]=$F,$M,$CS
$y -= 1
end
when $CS
if [STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]]==
[$M,$CS,$S]
STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]=$F,$MS,$CS
$y -= 1
elsif
[STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]]== [$MS,$CS,$S]
STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]=$S,$MS,$CS
$y -= 1
end
when $S
if [STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]]==[$M,$S,$S]
STORE[$y][$x],STORE[$y-1][$x],STORE[$y-2][$x]=$F,$MS,$S
$y -= 1
elsif [STORE[$y][$x],STORE[$y-1][$x]]==[$MS,$S]
STORE[$y][$x],STORE[$y-1][$x]=$S,$MS
$y -= 1
elsif [STORE[$y][$x],STORE[$y-1][$x]]==[$M,$S]
STORE[$y][$x],STORE[$y-1][$x]=$F,$MS
$y -= 1
end
else beep
end
end
def down
$x=STORE[$y].index("@") || STORE[$y].index("+")
temp=STORE[$y+1][$x]
case temp
when $F
if [ STORE[$y][$x],STORE[$y+1][$x] ]==[$M,$F]
STORE[$y][$x],STORE[$y+1][$x]=$F,$M
$y += 1
elsif [ STORE[$y][$x],STORE[$y+1][$x] ]==[$MS,$F]
STORE[$y][$x],STORE[$y+1][$x]=$S,$M
$y += 1
end
when $C
if STORE[$y+2][$x] == $F
STORE[$y][$x],STORE[$y+1][$x],STORE[$y+2][$x]=$F,$M,$C
$y += 1
elsif STORE[$y+2][$x] == $S
STORE[$y][$x],STORE[$y+1][$x],STORE[$y+2][$x]=$F,$M,$CS
$y += 1
end
when $CS
if [STORE[$y][$x],STORE[$y+1][$x],STORE[$y+2][$x]]==
[$M,$CS,$S]
STORE[$y][$x],STORE[$y+1][$x],STORE[$y+2][$x]=$F,$MS,$CS
$y += 1
end
when $S
if [STORE[$y][$x],STORE[$y+1][$x],STORE[$y+2][$x]]==[$M,$S,$S]
STORE[$y][$x],STORE[$y+1][$x],STORE[$y+2][$x]=$F,$MS,$S
$y += 1
elsif [STORE[$y][$x],STORE[$y+1][$x]]==[$MS,$S]
STORE[$y][$x],STORE[$y+1][$x]=$S,$MS
$y += 1
elsif [STORE[$y][$x],STORE[$y+1][$x]]==[$M,$S]
STORE[$y][$x],STORE[$y+1][$x]=$F,$MS
$y += 1
end
else beep
end
end
def draw
setpos(@top-1, 0)
addstr(STORE.to_s)
refresh
end
end

init_screen
begin
crmode
noecho
stdscr.keypad(true)

store = Store.new

loop do
case getch
when ?Q, ?q : break
when Key::LEFT : store.left
when Key::RIGHT : store.right
when Key::UP : store.up
when Key::DOWN : store.down
else beep
end
store.draw
end
ensure
close_screen
end

---------------------------------------
#file sokobangame
#####
# #
#o #
### o##
# o o #
### # ## # ######
# # ## ##### ..#
# o o ..#
##### ### #@## ..#
# #########
#######


Dave Burt

11/1/2004 12:53:00 PM

0

"G.Durga Prasad" <gdprasad@gmail.com> wrote...
> require 'curses'

I'm rubying on Windows. Is there a way I can do this? Or something even a
little bit like it? I can't find any kind of terminal library for Ruby for
Windows.

Thanks,
Dave


James Gray

11/1/2004 2:29:00 PM

0

On Nov 1, 2004, at 5:41 AM, G.Durga Prasad wrote:

> Thank you for causing me to write my first ever game programme in any
> language.

I was very surprised when my company got contracted last year to write
a simple game. People get paid to do this? I couldn't believe it, but
I wrote it and they did pay me. Even more interesting, the game they
wanted wasn't much more complicated than what we're playing with here.

James Edward Gray II



Mauricio Fernández

11/1/2004 5:29:00 PM

0

On Mon, Nov 01, 2004 at 09:58:50PM +0900, Dave Burt wrote:
> "G.Durga Prasad" <gdprasad@gmail.com> wrote...
> > require 'curses'
>
> I'm rubying on Windows. Is there a way I can do this? Or something even a
> little bit like it? I can't find any kind of terminal library for Ruby for
> Windows.

Compile pdcurses, then build curses.so linking against it.

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com



Florian Gross

11/1/2004 5:29:00 PM

0

Florian Gross wrote:

> Ruby Quiz wrote:
>
>> This week's quiz is to implement the game of Sokoban with the
>> interface of your
>> choosing and any extra features you would like to have.
>
>
> Interesting. In fact I did a cross between Sokoban and Dr. Mario (a nice
> variation of Tetris) for this year's first Ludum Dare 48 hour game
> development contest.

And here's my solution to this quiz. I used Ruby/Gosu again. It's very
simplistic and doesn't even include a turn counter, but it should still
be playable and work correctly.

You can use cursor left / right / up / down to move. ESC restarts the
current level.

Windows executable: http://noegnud.sourceforge.net/.flgr/s...
Linux package: http://noegnud.sourceforge.net/.flgr/soko...

I've also attached the source code to this mail for convenience.
require 'gosu'

include Gosu

module ZLevel
FloorFirst, DropZoneFirst, WallFirst = 0, 4, 8
TileSpan = 4

Player, Boulder = 12, 13
end

class Game < Window
def initialize
@screen_width, @screen_height = 800, 600
@scrolling = 0.95
super(@screen_width, @screen_height, false, 20)
self.caption = "Sokoban"

@images = Hash.new do |hash, key|
hash[key] = Image.load_tiles(self, "media/#{key}.png", 50, 50, false)
end

@level_number = 0

@deforms = Hash.new do |hash, key|
zlevel = rand(ZLevel::TileSpan)
zoom_x = rand / 5 + 1.05
zoom_y = rand / 5 + 1.05
angle = rand(4) * 90 + rand(10) - 5
hash[key] = [zlevel, angle, zoom_x, zoom_y]
end

load_levels
reload_level
end

def load_levels
@levels = File.read("media/levels.txt").split(/^$/).map do |data|
player = nil

y = 0
tiles = data.split("\n").map do |line|
if x = line.index(/[@+]/) then
player = [x, y]
end

y += 1
line.tr("@+", " .").split(//)
end

Level.new(tiles, player)
end
end

def next_level
@level_number += 1
reload_level
end

def reload_level
@level = Marshal.load(Marshal.dump(@levels[@level_number]))
@view_x = @level.px * 50 - @screen_width / 2
@view_y = @level.py * 50 - @screen_height / 2
self.close unless @level
@deforms.clear
end

def update
next_level if @level.finished?
end

def set_view(x, y)
new_view_x = x - @screen_width / 2
new_view_y = y - @screen_height / 2

of, nf = @scrolling, 1.0 - @scrolling
@view_x = (@view_x * of + new_view_x * nf).round
@view_y = (@view_y * of + new_view_y * nf).round
end

def draw
set_view(@level.px * 50, @level.py * 50)

# Draw map
@level.tiles.each_with_index do |line, ty|
line.each_with_index do |tile, tx|
ti = tile_to_index(tile)
x, y = tx * 50 + 25, ty * 50 + 25
zoff, angle, zoom_x, zoom_y = *@deforms[x + y << 3]

zlevel = zoff + case tile_to_index(tile)
when 0 then ZLevel::WallFirst
when 1 then ZLevel::FloorFirst
when 2 then ZLevel::DropZoneFirst
end

dx, dy = x - @view_x / 2, y - @view_y / 2

@images["tiles"][ti].draw_rot(dx, dy, zlevel, angle,
0.5, 0.5, zoom_x, zoom_y)

if @level.boulder?(tx, ty) then
@images["objects"][1].draw_rot(dx, dy, ZLevel::Boulder)
end
end
end

# Draw player
dx, dy = @level.px * 50 - @view_x / 2, @level.py * 50 - @view_y / 2
@images["objects"][0].draw(dx, dy, ZLevel::Player)
end

def tile_to_index(tile)
"# .".index(tile.tr("*o", ". "))
end

def button_down(button_id)
case button_id
when Button::KbLeft then @level.go_left
when Button::KbRight then @level.go_right
when Button::KbUp then @level.go_up
when Button::KbDown then @level.go_down
when Button::KbEscape then reload_level
end
end
end

class Level
attr_reader :tiles, :width, :height

def initialize(tiles, player)
@tiles, @player = tiles, player
@height = tiles.size * 50
width = tiles.map { |line| line.size }.max
@tiles.map! do |line|
line + Array.new(width - line.size) { " " }
end
@width = width * 50
end

def boulder?(x, y) "o*".index(@tiles[y][x]) end
def wall?(x, y) "#".index(@tiles[y][x]) end
def solid?(x, y) wall?(x, y) or boulder?(x, y) end
def free?(x, y) not solid?(x, y) end

def finished?
@tiles.all? do |line|
not line.any? do |tile|
"o.".index(tile)
end
end
end

def px() @player[0] end
def py() @player[1] end

def free_boulder(bx, by)
@tiles[by][bx] = case @tiles[by][bx]
when "o" then " "
when "*" then "."
end
end

def put_boulder(bx, by)
@tiles[by][bx] = case @tiles[by][bx]
when " " then "o"
when "." then "*"
end
end

def move_boulder(bx, by, vx, vy)
nx, ny = bx + vx, by + vy
if free?(nx, ny) then
free_boulder(bx, by)
put_boulder(nx, ny)
return true
end
return false
end

def move_player(vx, vy)
nx, ny = px + vx, py + vy
pp = lambda { |l,y| p [l,y]; y }
success = (free?(nx, ny) or
(boulder?(nx, ny) and move_boulder(nx, ny, vx, vy)))

if success then
@player = [nx, ny]
return true
end
end

def go_left() move_player(-1, 0) end
def go_right() move_player(+1, 0) end
def go_up() move_player(0, -1) end
def go_down() move_player(0, +1) end
end

Game.new.show

James Gray

11/4/2004 1:34:00 AM

0

On Nov 1, 2004, at 5:41 AM, G.Durga Prasad wrote:

> Thank you for causing me to write my first ever game programme in any
> language.
> I am including my solution here. (I don't know any better way }. I
> could solve using the Curses Library example given in Pickaxe II
> book.

There's a minor bug in here somewhere. When I play the first level and
push a crate into the middle row of storage, I can't then move above it
and push it down, if that makes any sense.

Hope that helps.

James Edward Gray II



Stan Pierce

10/2/2011 5:48:00 AM

0


"Tom" <random_chaotic@yahoo.com> wrote in message
news:462f7160-0b2c-46be-b4e1-0168c18a091b@k6g2000yql.googlegroups.com...
On Oct 1, 10:32 am, "Michael Laudahn eOpposition"
<nuke.islamis...@your.earliest.conv> wrote:
> *Message and author protected by the 1st amendment (free speech) and
> similar
> constitutional provisions. Violations of this provision through actions by
> judiciary and other government criminals will be prosecuted as soon as
> conditions permit.*
>


So Michael, are you a European socialist or a right winger?




You appear to be avoiding the question.


I notice that the Dutch are very good about celebrating Canada and how
we liberated them from the Nazis, sacrificing our lives.

Why isn't Geert there? Did his father work for the Nazis?

How about your father? Was he one of Hitler's boys?


# You brainless twit. Does your mother know you are still out of bed. Go
away and play somewhere else.