RSokoban 0.71 → 0.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/NEWS +22 -0
- data/README.rdoc +19 -19
- data/RSokoban-0.73.gem +0 -0
- data/TODO +25 -18
- data/VERSION +1 -0
- data/bin/rsokoban +65 -1
- data/lib/rsokoban/exception.rb +4 -0
- data/lib/rsokoban/game.rb +42 -29
- data/lib/rsokoban/level.rb +67 -30
- data/lib/rsokoban/level_loader.rb +4 -4
- data/lib/rsokoban/level_set.rb +20 -10
- data/lib/rsokoban/map.rb +51 -0
- data/lib/rsokoban/move_recorder.rb +38 -0
- data/lib/rsokoban/option.rb +29 -12
- data/lib/rsokoban/raw_level.rb +15 -4
- data/lib/rsokoban/ui/base_ui.rb +37 -0
- data/lib/rsokoban/ui/console.rb +43 -33
- data/lib/rsokoban/ui/curses_console.rb +45 -36
- data/lib/rsokoban/ui/player_action.rb +74 -0
- data/lib/rsokoban/ui/tk_ui.rb +474 -0
- data/lib/rsokoban/ui.rb +10 -0
- data/lib/rsokoban.rb +4 -5
- data/skins/default/crate.bmp +0 -0
- data/skins/default/crate_store.bmp +0 -0
- data/skins/default/floor.bmp +0 -0
- data/skins/default/man_down.bmp +0 -0
- data/skins/default/man_left.bmp +0 -0
- data/skins/default/man_right.bmp +0 -0
- data/skins/default/man_store_down.bmp +0 -0
- data/skins/default/man_store_left.bmp +0 -0
- data/skins/default/man_store_right.bmp +0 -0
- data/skins/default/man_store_up.bmp +0 -0
- data/skins/default/man_up.bmp +0 -0
- data/skins/default/outside.bmp +0 -0
- data/skins/default/readme +1 -0
- data/skins/default/store.bmp +0 -0
- data/skins/default/wall.bmp +0 -0
- data/test/original.xsb +0 -2
- data/test/tc_level.rb +37 -37
- data/test/tc_level_loader.rb +14 -2
- data/test/tc_level_set.rb +8 -0
- data/test/tc_map.rb +40 -0
- data/test/tc_move_recorder.rb +100 -0
- data/test/tc_raw_level.rb +5 -5
- data/test/test.rb +3 -1
- data/test/test_file2.xsb +2 -0
- data/test/ui/tc_console.rb +27 -13
- data/test/ui/tc_player_action.rb +156 -0
- metadata +38 -25
- data/lib/rsokoban/ui/ui.rb +0 -44
data/NEWS
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
version 0.73 2011-??-??
|
2
|
+
|
3
|
+
* New: undo
|
4
|
+
|
5
|
+
* New: default GUI with Tk library
|
6
|
+
|
7
|
+
* Ruby requiered version down to 1.8.7
|
8
|
+
|
9
|
+
* Curses and Portable user interface display a few more information
|
10
|
+
|
11
|
+
|
12
|
+
version 0.72 2011-01-21
|
13
|
+
|
14
|
+
* New console output using curses
|
15
|
+
|
16
|
+
* New command line options
|
17
|
+
--curses : Use curses as user interface. Default option.
|
18
|
+
--portable : Use a simplest user interface, should work on any platform.
|
19
|
+
--help-output : Display help on available user interfaces.
|
20
|
+
|
21
|
+
* Installing via RubyGem
|
22
|
+
|
data/README.rdoc
CHANGED
@@ -1,35 +1,31 @@
|
|
1
1
|
= Welcome to RSokoban !
|
2
2
|
|
3
|
-
RSokoban is a clone of the famous {Sokoban
|
4
|
-
I wrote this program just to improve my skills in Ruby.
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# $ $ #
|
12
|
-
### # ## # ######
|
13
|
-
# # ## ##### ..#
|
14
|
-
# $ $ ..#
|
15
|
-
##### ### #@## ..#
|
16
|
-
# #########
|
17
|
-
#######
|
3
|
+
RSokoban is a clone of the famous {Sokoban}[http://en.wikipedia.org/wiki/Sokoban] game.
|
4
|
+
I wrote this program just to improve my skills in Ruby.
|
5
|
+
|
6
|
+
*Features*
|
7
|
+
* User interface comes in 3 flavors (tk, curses, straight text mode)
|
8
|
+
* Installing via RubyGem
|
9
|
+
* Undo
|
10
|
+
* Use xsb file format to load sets of levels
|
18
11
|
|
19
12
|
Enjoy the game.
|
20
13
|
|
21
14
|
== Documentation
|
22
15
|
Players can look at {the wiki}[https://github.com/lkdjiin/RSokoban/wiki].
|
23
16
|
|
24
|
-
Developpers can look at Level and
|
17
|
+
Developpers can look at Level, Game and TkUI.
|
25
18
|
|
26
19
|
|
27
20
|
== Dependancies
|
28
|
-
Ruby >= 1.
|
21
|
+
Ruby >= 1.8.7
|
29
22
|
|
30
23
|
== Installing RSokoban
|
31
|
-
Installing via
|
32
|
-
|
24
|
+
Installing via RubyGem is easy doing in one command:
|
25
|
+
|
26
|
+
gem install RSokoban
|
27
|
+
|
28
|
+
Or you can install RSokoban from the source. To do this, go to the RSokoban folder and build the gem :
|
33
29
|
|
34
30
|
gem build rsokoban.gemspec
|
35
31
|
|
@@ -41,6 +37,10 @@ Now start playing :
|
|
41
37
|
|
42
38
|
rsokoban
|
43
39
|
|
40
|
+
Or you can play without installing it. Go to the RSokoban/bin folder and type:
|
41
|
+
|
42
|
+
./rsokoban
|
43
|
+
|
44
44
|
== Questions and/or Comments
|
45
45
|
Feel free to email {Xavier Nayrac}[mailto:xavier.nayrac@gmail.com]
|
46
46
|
with any questions.
|
data/RSokoban-0.73.gem
ADDED
Binary file
|
data/TODO
CHANGED
@@ -1,34 +1,41 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
doing gem and upload it
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
(done)--curses (default)
|
7
|
-
(done)--help-output
|
8
|
-
(done)curses: level completed (yes/no) > y or n
|
9
|
-
(done)gem
|
4
|
+
--- next ---
|
5
|
+
tk: view description of level set in 'load set' dialog
|
10
6
|
|
11
|
-
|
7
|
+
tk: add a scrollbar to the 'load set' dialog.
|
12
8
|
|
13
|
-
|
9
|
+
tk: adjust window size to map size
|
14
10
|
|
15
|
-
|
11
|
+
tk: level preview
|
16
12
|
|
17
|
-
|
13
|
+
redo
|
18
14
|
|
19
|
-
|
15
|
+
--gnome
|
20
16
|
|
21
|
-
|
17
|
+
skining (several size of tile, resizing ?)
|
22
18
|
|
23
|
-
|
19
|
+
Console UI should provide a way to know names of sets
|
24
20
|
|
25
|
-
|
21
|
+
save & reload
|
26
22
|
|
27
|
-
|
23
|
+
multi player
|
24
|
+
|
25
|
+
save score
|
26
|
+
|
27
|
+
don't restrict map size
|
28
|
+
|
29
|
+
solver (plugin ?)
|
28
30
|
|
29
31
|
format fichier niveau xml (.slc)
|
32
|
+
|
30
33
|
--windows ?
|
31
|
-
|
34
|
+
|
32
35
|
--kde ?
|
36
|
+
|
33
37
|
--fxruby ?
|
34
|
-
|
38
|
+
|
39
|
+
mouse support
|
40
|
+
|
41
|
+
i18n
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.73
|
data/bin/rsokoban
CHANGED
@@ -1,10 +1,74 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# This file is part of RSokoban, a clone of the famous sokoban game.
|
4
|
+
# Copyright 2011 Xavier Nayrac
|
5
|
+
#
|
6
|
+
# RSokoban is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
3
19
|
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
20
|
+
|
21
|
+
$RSOKOBAN_PATH = File.expand_path(File.dirname(__FILE__)) + '/..'
|
22
|
+
|
4
23
|
$RSOKOBAN_DATA_PATH = File.expand_path(File.dirname(__FILE__) + '/../data')
|
24
|
+
|
5
25
|
require 'rsokoban'
|
6
26
|
|
27
|
+
tk_failed=<<EOS
|
28
|
+
Sorry, failed to load tk or tkimg library.
|
29
|
+
Please, be sure that libtcltk-ruby and libtk-img are correctly installed on
|
30
|
+
your system.
|
31
|
+
Now trying to start with curses library:
|
32
|
+
rsokoban --curses
|
33
|
+
|
34
|
+
EOS
|
35
|
+
|
36
|
+
curses_failed=<<EOS
|
37
|
+
Sorry, failed to load curses library.
|
38
|
+
Please, be sure that curses are correctly installed on your system.
|
39
|
+
You can try the game with the portable UI.
|
40
|
+
rsokoban --portable
|
41
|
+
|
42
|
+
EOS
|
43
|
+
|
44
|
+
# If player don't specify an explicit user interface,
|
45
|
+
# one will use tk.
|
7
46
|
option = Option.new
|
8
47
|
|
48
|
+
# I try to load the needed tk bindings. If it fails,
|
49
|
+
# one can try with curses.
|
50
|
+
if option[:ui] == :tk
|
51
|
+
begin
|
52
|
+
require 'tk'
|
53
|
+
require 'tkextlib/tkimg'
|
54
|
+
rescue LoadError => e
|
55
|
+
puts tk_failed
|
56
|
+
option.interface = :curses
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# I try to load the needed curses binding. If it fails,
|
61
|
+
# inform user and abandon.
|
62
|
+
if option[:ui] == :curses
|
63
|
+
begin
|
64
|
+
require 'curses'
|
65
|
+
rescue LoadError => e
|
66
|
+
puts curses_failed
|
67
|
+
exit
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
9
71
|
game = RSokoban::Game.new option[:ui]
|
10
|
-
|
72
|
+
|
73
|
+
# It seems I have somme troubles to unify GUI and non-GUI.
|
74
|
+
game.run if [:curses, :portable].include?(option[:ui])
|
data/lib/rsokoban/exception.rb
CHANGED
data/lib/rsokoban/game.rb
CHANGED
@@ -4,10 +4,10 @@ module RSokoban
|
|
4
4
|
class Game
|
5
5
|
|
6
6
|
# Construct a new game that you can later run.
|
7
|
-
# @param [:curses|:portable] ui_as_symbol the user interface for the game
|
7
|
+
# @param [:curses|:portable|:tk] ui_as_symbol the user interface for the game
|
8
8
|
def initialize ui_as_symbol
|
9
|
-
@
|
10
|
-
@
|
9
|
+
@level_loader = LevelLoader.new "original.xsb"
|
10
|
+
@level_number = 1
|
11
11
|
case ui_as_symbol
|
12
12
|
when :curses
|
13
13
|
require "rsokoban/ui/curses_console"
|
@@ -15,36 +15,45 @@ module RSokoban
|
|
15
15
|
when :portable
|
16
16
|
require "rsokoban/ui/console"
|
17
17
|
@ui = UI::Console.new
|
18
|
+
when :tk
|
19
|
+
require "rsokoban/ui/tk_ui"
|
20
|
+
@ui = UI::TkUI.new
|
18
21
|
end
|
19
22
|
end
|
20
23
|
|
21
24
|
# I am the game loop.
|
22
25
|
def run
|
23
|
-
|
26
|
+
player_action = start_level
|
24
27
|
loop do
|
25
|
-
if
|
26
|
-
|
28
|
+
if player_action.level_number?
|
29
|
+
player_action = load_level player_action.action
|
27
30
|
next
|
28
|
-
elsif
|
29
|
-
|
30
|
-
action = load_a_new_set action
|
31
|
+
elsif player_action.set_name?
|
32
|
+
player_action = load_a_new_set player_action.action
|
31
33
|
next
|
32
|
-
elsif
|
34
|
+
elsif player_action.quit?
|
33
35
|
break
|
34
|
-
elsif
|
35
|
-
|
36
|
+
elsif player_action.next?
|
37
|
+
player_action = next_level
|
36
38
|
next
|
37
|
-
elsif
|
38
|
-
|
39
|
+
elsif player_action.retry?
|
40
|
+
player_action = try_again
|
39
41
|
next
|
40
|
-
elsif
|
41
|
-
result = @level.move(action)
|
42
|
+
elsif player_action.move?
|
43
|
+
result = @level.move(player_action.action)
|
44
|
+
elsif player_action.undo?
|
45
|
+
result = @level.undo
|
42
46
|
end
|
43
47
|
|
48
|
+
move_index = result =~ /\d+/
|
44
49
|
if result.start_with?('WIN')
|
45
|
-
|
50
|
+
player_action = @ui.get_action(:type=>:win, :map=>@level.map, :move=>result[move_index..-1])
|
46
51
|
else
|
47
|
-
|
52
|
+
if move_index
|
53
|
+
player_action = @ui.get_action(:type=>:display, :map=>@level.map, :move=>result[move_index..-1])
|
54
|
+
else
|
55
|
+
player_action = @ui.get_action(:type=>:display, :map=>@level.map, :error=>result)
|
56
|
+
end
|
48
57
|
end
|
49
58
|
end
|
50
59
|
end
|
@@ -54,7 +63,7 @@ module RSokoban
|
|
54
63
|
# Load and start the next level of the set
|
55
64
|
# @return [Object] the user's {action}[Console#get_action]
|
56
65
|
def next_level
|
57
|
-
@
|
66
|
+
@level_number += 1
|
58
67
|
start_level
|
59
68
|
end
|
60
69
|
|
@@ -68,22 +77,25 @@ module RSokoban
|
|
68
77
|
# @param [String] setname the name of the set (with .xsb extension)
|
69
78
|
# @return [Object] the user's {action}[Console#get_action]
|
70
79
|
def load_a_new_set setname
|
80
|
+
title = nil
|
81
|
+
error = nil
|
71
82
|
begin
|
72
|
-
@
|
73
|
-
@
|
74
|
-
@level = @
|
75
|
-
|
83
|
+
@level_loader = LevelLoader.new setname
|
84
|
+
@level_number = 1
|
85
|
+
@level = @level_loader.level(@level_number)
|
86
|
+
title = @level.title
|
76
87
|
rescue NoFileError
|
77
|
-
|
88
|
+
error = "Error, no such file : #{setname}"
|
78
89
|
end
|
79
|
-
@ui.get_action(
|
90
|
+
@ui.get_action(:type=>:start, :map=>@level.map, :title=>title, :error=>error, :set=>@level_loader.set.title,
|
91
|
+
:number=>@level_number, :total=>@level_loader.set.size)
|
80
92
|
end
|
81
93
|
|
82
94
|
# Load a level from the current set.
|
83
95
|
# @param [Fixnum] num the number of the set (base 1)
|
84
96
|
# @return [Object] the user's {action}[Console#get_action]
|
85
97
|
def load_level num
|
86
|
-
@
|
98
|
+
@level_number = num
|
87
99
|
start_level
|
88
100
|
end
|
89
101
|
|
@@ -91,10 +103,11 @@ module RSokoban
|
|
91
103
|
# @return [Object] the user's {action}[Console#get_action]
|
92
104
|
def start_level
|
93
105
|
begin
|
94
|
-
@level = @
|
95
|
-
@ui.get_action(
|
106
|
+
@level = @level_loader.level(@level_number)
|
107
|
+
@ui.get_action(:type=>:start, :map=>@level.map, :title=>@level.title, :set=>@level_loader.set.title,
|
108
|
+
:number=>@level_number, :total=>@level_loader.set.size)
|
96
109
|
rescue LevelNumberTooHighError
|
97
|
-
@ui.get_action(
|
110
|
+
@ui.get_action(:type=>:end_of_set, :map=>Map.new)
|
98
111
|
end
|
99
112
|
end
|
100
113
|
|
data/lib/rsokoban/level.rb
CHANGED
@@ -21,13 +21,14 @@ module RSokoban
|
|
21
21
|
# @param [RawLevel] rawLevel
|
22
22
|
def initialize rawLevel
|
23
23
|
@title = rawLevel.title
|
24
|
-
@floor = init_floor rawLevel.
|
25
|
-
@man = init_man rawLevel.
|
24
|
+
@floor = init_floor rawLevel.map
|
25
|
+
@man = init_man rawLevel.map
|
26
26
|
@crates = []
|
27
27
|
@storages = []
|
28
|
-
init_crates_and_storages rawLevel.
|
28
|
+
init_crates_and_storages rawLevel.map
|
29
29
|
@move = 0
|
30
|
-
@
|
30
|
+
@map = nil
|
31
|
+
@move_recorder = MoveRecorder.new
|
31
32
|
end
|
32
33
|
|
33
34
|
# Two Level objects are equals if their @title, @floor, @man, @crates and @storages are equals.
|
@@ -44,14 +45,14 @@ module RSokoban
|
|
44
45
|
self == obj
|
45
46
|
end
|
46
47
|
|
47
|
-
# Get an instant
|
48
|
-
# @return [
|
49
|
-
def
|
50
|
-
@
|
48
|
+
# Get an instant map of the game.
|
49
|
+
# @return [Map] the map, after X turns of game.
|
50
|
+
def map
|
51
|
+
@map = init_floor @floor
|
51
52
|
draw_crates
|
52
53
|
draw_storages
|
53
54
|
draw_man
|
54
|
-
@
|
55
|
+
@map
|
55
56
|
end
|
56
57
|
|
57
58
|
# Move the man one box up.
|
@@ -90,15 +91,51 @@ module RSokoban
|
|
90
91
|
return 'ERROR wall behind crate' if wall_behind_crate?(direction)
|
91
92
|
return 'ERROR double crate' if double_crate?(direction)
|
92
93
|
@move += 1
|
94
|
+
|
93
95
|
@man.send(direction)
|
94
96
|
if @crates.include?(Crate.new(@man.x, @man.y))
|
95
97
|
i = @crates.index(Crate.new(@man.x, @man.y))
|
96
98
|
@crates[i].send(direction)
|
99
|
+
@move_recorder.record direction, :push
|
100
|
+
else
|
101
|
+
@move_recorder.record direction
|
97
102
|
end
|
98
103
|
return "WIN move #{@move}" if win?
|
99
104
|
"OK move #{@move}"
|
100
105
|
end
|
101
106
|
|
107
|
+
# Undo last move
|
108
|
+
def undo
|
109
|
+
begin
|
110
|
+
case @move_recorder.undo
|
111
|
+
when :up then @man.down
|
112
|
+
when :down then @man.up
|
113
|
+
when :left then @man.right
|
114
|
+
when :right then @man.left
|
115
|
+
when :UP
|
116
|
+
i = @crates.index(Crate.new(@man.x, @man.y-1))
|
117
|
+
@crates[i].down
|
118
|
+
@man.down
|
119
|
+
when :DOWN
|
120
|
+
i = @crates.index(Crate.new(@man.x, @man.y+1))
|
121
|
+
@crates[i].up
|
122
|
+
@man.up
|
123
|
+
when :LEFT
|
124
|
+
i = @crates.index(Crate.new(@man.x-1, @man.y))
|
125
|
+
@crates[i].right
|
126
|
+
@man.right
|
127
|
+
when :RIGHT
|
128
|
+
i = @crates.index(Crate.new(@man.x+1, @man.y))
|
129
|
+
@crates[i].left
|
130
|
+
@man.left
|
131
|
+
end
|
132
|
+
@move -= 1
|
133
|
+
rescue EmptyMoveQueueError
|
134
|
+
# Nothing to do
|
135
|
+
end
|
136
|
+
"OK move #{@move}"
|
137
|
+
end
|
138
|
+
|
102
139
|
private
|
103
140
|
|
104
141
|
# @return true if all crates are on a storage location
|
@@ -173,29 +210,29 @@ module RSokoban
|
|
173
210
|
box == CRATE or box == CRATE_ON_STORAGE
|
174
211
|
end
|
175
212
|
|
176
|
-
# Draw the man for
|
213
|
+
# Draw the man for map output
|
177
214
|
def draw_man
|
178
215
|
box = what_is_on @man.x, @man.y
|
179
|
-
|
180
|
-
|
216
|
+
put_man_in_map if box == FLOOR
|
217
|
+
put_man_on_storage_in_map if box == STORAGE
|
181
218
|
end
|
182
219
|
|
183
|
-
def
|
184
|
-
@
|
220
|
+
def put_man_in_map
|
221
|
+
@map[@man.y][@man.x] = MAN
|
185
222
|
end
|
186
223
|
|
187
|
-
def
|
188
|
-
@
|
224
|
+
def put_man_on_storage_in_map
|
225
|
+
@map[@man.y][@man.x] = MAN_ON_STORAGE
|
189
226
|
end
|
190
227
|
|
191
|
-
# Draw the crates for
|
228
|
+
# Draw the crates for map output
|
192
229
|
def draw_crates
|
193
|
-
@crates.each {|crate| @
|
230
|
+
@crates.each {|crate| @map[crate.y][crate.x] = what_is_on(crate.x, crate.y) }
|
194
231
|
end
|
195
232
|
|
196
|
-
# Draw the storages location for
|
233
|
+
# Draw the storages location for map output
|
197
234
|
def draw_storages
|
198
|
-
@storages.each {|st| @
|
235
|
+
@storages.each {|st| @map[st.y][st.x] = what_is_on(st.x, st.y) }
|
199
236
|
end
|
200
237
|
|
201
238
|
# Get the content of box x, y
|
@@ -220,21 +257,21 @@ module RSokoban
|
|
220
257
|
|
221
258
|
# Removes all storages locations, all crates and the man, leaving only walls and floor.
|
222
259
|
#
|
223
|
-
# @param [
|
224
|
-
# @return [
|
225
|
-
def init_floor
|
260
|
+
# @param [Map] map
|
261
|
+
# @return [Map] map with only walls and floor
|
262
|
+
def init_floor map
|
226
263
|
floor = []
|
227
|
-
|
264
|
+
map.each {|x| floor.push x.tr("#{STORAGE}#{CRATE}#{MAN}#{CRATE_ON_STORAGE}", FLOOR) }
|
228
265
|
floor
|
229
266
|
end
|
230
267
|
|
231
268
|
# Find the man's position, at the begining of the level.
|
232
269
|
#
|
233
|
-
# @param [
|
270
|
+
# @param [Map] map
|
234
271
|
# @return [Man] an initialised man
|
235
|
-
def init_man
|
272
|
+
def init_man map
|
236
273
|
x = y = 0
|
237
|
-
|
274
|
+
map.each {|line|
|
238
275
|
if line.include?(MAN)
|
239
276
|
x = line.index(MAN)
|
240
277
|
break
|
@@ -246,10 +283,10 @@ module RSokoban
|
|
246
283
|
|
247
284
|
# Find position of crates and storages, at the begining of the level.
|
248
285
|
#
|
249
|
-
# @param [
|
250
|
-
def init_crates_and_storages
|
286
|
+
# @param [Map] map
|
287
|
+
def init_crates_and_storages map
|
251
288
|
y = 0
|
252
|
-
|
289
|
+
map.each do |line|
|
253
290
|
count = 0
|
254
291
|
line.each_char do |c|
|
255
292
|
@crates.push Crate.new(count, y) if c == CRATE
|
@@ -7,10 +7,10 @@ module RSokoban
|
|
7
7
|
class LevelLoader
|
8
8
|
attr_reader :level, :set
|
9
9
|
|
10
|
-
# @
|
10
|
+
# @param [String] filename le nom du fichier où trouver les niveaux.
|
11
11
|
# Ce fichier est cherché dans le dossier data/.
|
12
|
-
# @
|
13
|
-
# @see LevelSet
|
12
|
+
# @raise [RSokoban::NoFileError] si le fichier n'existe pas
|
13
|
+
# @see LevelSet overview of .xsb file format
|
14
14
|
def initialize filename
|
15
15
|
filename = "#{$RSOKOBAN_DATA_PATH}/" + filename
|
16
16
|
raise NoFileError unless File.exist?(filename)
|
@@ -35,7 +35,7 @@ module RSokoban
|
|
35
35
|
line = file.readline.chomp
|
36
36
|
end
|
37
37
|
line = line.chomp.sub(/;/, '').sub(/\s*/, '')
|
38
|
-
@set.rawLevels.push RawLevel.new(line, raw)
|
38
|
+
@set.rawLevels.push RawLevel.new(line, raw) unless raw.empty?
|
39
39
|
|
40
40
|
line = file.readline
|
41
41
|
while line[0, 1] == ';'
|
data/lib/rsokoban/level_set.rb
CHANGED
@@ -3,22 +3,28 @@ module RSokoban
|
|
3
3
|
# I am a set of sokoban levels.
|
4
4
|
# Level set are found in .xsb files.
|
5
5
|
#
|
6
|
-
# =xsb file format
|
6
|
+
# = xsb file format
|
7
7
|
#
|
8
8
|
# Info lines begins with semi-colon (;)
|
9
|
-
# Map lines begins with a # (that's a wall !)
|
9
|
+
# Map lines begins with a # (that's a wall !) preceded by 0, 1 or more spaces.
|
10
10
|
#
|
11
|
-
# 1
|
12
|
-
# 2
|
13
|
-
# 3
|
14
|
-
# 4
|
15
|
-
# 5
|
16
|
-
# 6
|
17
|
-
# 7
|
11
|
+
# 1. First info is title of the set
|
12
|
+
# 2. Blank line
|
13
|
+
# 3. List of info lines : description
|
14
|
+
# 4. Blank line
|
15
|
+
# 5. Level map
|
16
|
+
# 6. info title of this level
|
17
|
+
# 7. List of info lines : blabla about this level
|
18
18
|
#
|
19
19
|
# From 4 to 7 again for each supplementary level
|
20
20
|
class LevelSet
|
21
|
-
|
21
|
+
|
22
|
+
# @param [String] title get/set the title of this level set.
|
23
|
+
attr_accessor :title
|
24
|
+
# @param [String] description get/set the description of this level set.
|
25
|
+
attr_accessor :description
|
26
|
+
# @param [Array<RawLevel>] rawLevels get/set the raw levels of this set
|
27
|
+
attr_accessor :rawLevels
|
22
28
|
|
23
29
|
def initialize
|
24
30
|
@title = 'Unknown set title'
|
@@ -26,6 +32,10 @@ module RSokoban
|
|
26
32
|
@rawLevels = []
|
27
33
|
end
|
28
34
|
|
35
|
+
def size
|
36
|
+
@rawLevels.size
|
37
|
+
end
|
38
|
+
|
29
39
|
end
|
30
40
|
|
31
41
|
end
|
data/lib/rsokoban/map.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module RSokoban
|
2
|
+
|
3
|
+
# I am the map of a level.
|
4
|
+
# @since 0.73
|
5
|
+
class Map
|
6
|
+
# @param [Array<String>]
|
7
|
+
attr_reader :rows
|
8
|
+
|
9
|
+
# Construct a map from an array of strings.
|
10
|
+
# @example very simple maps
|
11
|
+
# map1 = Map.new['###', '#@#', '###']
|
12
|
+
#
|
13
|
+
# map2 = Map.new
|
14
|
+
# map2.rows = ['###', '#@#', '###']
|
15
|
+
#
|
16
|
+
# @param [Array<String>]
|
17
|
+
def initialize rows = []
|
18
|
+
@rows= rows
|
19
|
+
@width = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def rows=(rows)
|
23
|
+
@rows = rows
|
24
|
+
@width = 0
|
25
|
+
@rows.each {|row| @width = row.size if row.size > @width }
|
26
|
+
end
|
27
|
+
|
28
|
+
def height
|
29
|
+
@rows.size
|
30
|
+
end
|
31
|
+
|
32
|
+
def width
|
33
|
+
@width
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](num)
|
37
|
+
@rows[num]
|
38
|
+
end
|
39
|
+
|
40
|
+
def each(&block)
|
41
|
+
@rows.each(&block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def ==(obj)
|
45
|
+
return false unless obj.kind_of?(Map)
|
46
|
+
@rows == obj.rows
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|