RSokoban 0.71 → 0.73
Sign up to get free protection for your applications and to get access to all the features.
- 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
|