RSokoban 0.73 → 0.74
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +16 -1
- data/README.rdoc +25 -8
- data/Rakefile +51 -0
- data/TODO +19 -41
- data/VERSION +1 -1
- data/bin/rsokoban +1 -3
- data/lib/rsokoban.rb +2 -0
- data/lib/rsokoban/exception.rb +4 -0
- data/lib/rsokoban/extensions.rb +26 -0
- data/lib/rsokoban/game.rb +161 -41
- data/lib/rsokoban/level.rb +54 -37
- data/lib/rsokoban/level_loader.rb +34 -11
- data/lib/rsokoban/move_recorder.rb +14 -2
- data/lib/rsokoban/move_result.rb +37 -0
- data/lib/rsokoban/ui/base_ui.rb +0 -3
- data/lib/rsokoban/ui/console.rb +2 -2
- data/lib/rsokoban/ui/curses_console.rb +0 -2
- data/lib/rsokoban/ui/player_action.rb +2 -2
- data/lib/rsokoban/ui/tk_box.rb +21 -0
- data/lib/rsokoban/ui/tk_dialogs.rb +240 -0
- data/lib/rsokoban/ui/tk_ui.rb +170 -269
- data/test/tc_extensions.rb +26 -0
- data/test/tc_game.rb +28 -0
- data/test/tc_level.rb +187 -112
- data/test/tc_level_loader.rb +12 -17
- data/test/tc_move_recorder.rb +41 -7
- data/test/tc_move_result.rb +45 -0
- data/test/test.rb +3 -0
- data/test/ui/tc_console.rb +3 -1
- metadata +10 -3
- data/RSokoban-0.73.gem +0 -0
data/lib/rsokoban/level.rb
CHANGED
@@ -5,6 +5,14 @@ module RSokoban
|
|
5
5
|
class Level
|
6
6
|
attr_reader :floor, :man, :crates, :storages, :title
|
7
7
|
|
8
|
+
# Get map width of this level, in cells
|
9
|
+
# @return [Fixnum]
|
10
|
+
attr_reader :width
|
11
|
+
|
12
|
+
# Get map height of this level, in cells
|
13
|
+
# @return [Fixnum]
|
14
|
+
attr_reader :height
|
15
|
+
|
8
16
|
# I build the level from a RawLevel object.
|
9
17
|
# @example a RawLevel object
|
10
18
|
# A RawLevel object have got one title and one 'picture'. A 'picture' is an array of string.
|
@@ -21,6 +29,7 @@ module RSokoban
|
|
21
29
|
# @param [RawLevel] rawLevel
|
22
30
|
def initialize rawLevel
|
23
31
|
@title = rawLevel.title
|
32
|
+
init_dimension rawLevel.map
|
24
33
|
@floor = init_floor rawLevel.map
|
25
34
|
@man = init_man rawLevel.map
|
26
35
|
@crates = []
|
@@ -55,43 +64,14 @@ module RSokoban
|
|
55
64
|
@map
|
56
65
|
end
|
57
66
|
|
58
|
-
# Move the man one box up.
|
59
|
-
# @return [String] the move's result
|
60
|
-
# ["ERROR wall"] if the player is stopped by a wall
|
61
|
-
# ['ERROR wall behind crate'] si le joueur est stoppé par une caisse suivie d'un mur
|
62
|
-
# ['ERROR double crate'] if the player is stopped by a crate followed by a wall
|
63
|
-
# ['OK move ?'] if the move is accepted (? is replaced by the number of the move)
|
64
|
-
# ['WIN move ?'] if the level is completed (? is replaced by the number of the move)
|
65
|
-
def moveUp
|
66
|
-
move :up
|
67
|
-
end
|
68
|
-
|
69
|
-
# Move the man one box down.
|
70
|
-
# @see #moveUp for more explanation
|
71
|
-
def moveDown
|
72
|
-
move :down
|
73
|
-
end
|
74
|
-
|
75
|
-
# Move the man one box left.
|
76
|
-
# @see #moveUp for more explanation
|
77
|
-
def moveLeft
|
78
|
-
move :left
|
79
|
-
end
|
80
|
-
|
81
|
-
# Move the man one box right.
|
82
|
-
# @see #moveUp for more explanation
|
83
|
-
def moveRight
|
84
|
-
move :right
|
85
|
-
end
|
86
|
-
|
87
67
|
# Move the man one box +direction+.
|
88
|
-
# @
|
68
|
+
# @param [:up|:down|:right|:left] direction
|
69
|
+
# @return [MoveResult] the move's result
|
89
70
|
def move direction
|
90
|
-
return '
|
91
|
-
return '
|
92
|
-
return '
|
71
|
+
return MoveResult.new(:status => :error, :message => 'wall') if wall?(direction)
|
72
|
+
return MoveResult.new(:status => :error, :message => 'wall behind crate') if wall_behind_crate?(direction)
|
73
|
+
return MoveResult.new(:status => :error, :message => 'double crate') if double_crate?(direction)
|
93
74
|
@move += 1
|
94
|
-
|
95
75
|
@man.send(direction)
|
96
76
|
if @crates.include?(Crate.new(@man.x, @man.y))
|
97
77
|
i = @crates.index(Crate.new(@man.x, @man.y))
|
@@ -100,8 +80,31 @@ module RSokoban
|
|
100
80
|
else
|
101
81
|
@move_recorder.record direction
|
102
82
|
end
|
103
|
-
|
104
|
-
|
83
|
+
|
84
|
+
return MoveResult.new(:status => :win, :move_number => @move) if win?
|
85
|
+
MoveResult.new(:status => :ok, :move_number => @move)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Redo the last undo
|
89
|
+
# @since 0.74
|
90
|
+
def redo
|
91
|
+
begin
|
92
|
+
case @move_recorder.redo
|
93
|
+
when :up, :UP then direction = :up
|
94
|
+
when :down, :DOWN then direction = :down
|
95
|
+
when :left, :LEFT then direction = :left
|
96
|
+
when :right, :RIGHT then direction = :right
|
97
|
+
end
|
98
|
+
@man.send(direction)
|
99
|
+
@move += 1
|
100
|
+
if @crates.include?(Crate.new(@man.x, @man.y))
|
101
|
+
i = @crates.index(Crate.new(@man.x, @man.y))
|
102
|
+
@crates[i].send(direction)
|
103
|
+
end
|
104
|
+
rescue EmptyRedoError
|
105
|
+
# Nothing to do
|
106
|
+
end
|
107
|
+
MoveResult.new(:status => :ok, :move_number => @move)
|
105
108
|
end
|
106
109
|
|
107
110
|
# Undo last move
|
@@ -133,7 +136,14 @@ module RSokoban
|
|
133
136
|
rescue EmptyMoveQueueError
|
134
137
|
# Nothing to do
|
135
138
|
end
|
136
|
-
|
139
|
+
MoveResult.new(:status => :ok, :move_number => @move)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Get current move number
|
143
|
+
# @return [Fixnum]
|
144
|
+
# @since 0.74
|
145
|
+
def move_number
|
146
|
+
@move
|
137
147
|
end
|
138
148
|
|
139
149
|
private
|
@@ -265,6 +275,13 @@ module RSokoban
|
|
265
275
|
floor
|
266
276
|
end
|
267
277
|
|
278
|
+
# Initialize map width and map height of this level
|
279
|
+
def init_dimension map
|
280
|
+
@width = 0
|
281
|
+
map.each {|y| @width = y.size if y.size > @width }
|
282
|
+
@height = map.size
|
283
|
+
end
|
284
|
+
|
268
285
|
# Find the man's position, at the begining of the level.
|
269
286
|
#
|
270
287
|
# @param [Map] map
|
@@ -1,27 +1,29 @@
|
|
1
1
|
module RSokoban
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
3
|
+
# I load a file containing the levels of the game. On instanciation,
|
4
|
+
# you tell me a file (in xsb file format) containing one or more levels.
|
5
|
+
#
|
6
|
+
# You can then ask for a particular level (a Level object). You can use me too
|
7
|
+
# to find the description of a level.
|
8
|
+
# @todo give some examples
|
9
|
+
# @todo document and refactor
|
7
10
|
class LevelLoader
|
8
|
-
attr_reader :level, :set
|
9
11
|
|
10
|
-
# @param [String] filename
|
11
|
-
#
|
12
|
-
# @raise [RSokoban::NoFileError]
|
12
|
+
# @param [String] filename an xsb filename.
|
13
|
+
# This file is searched in thedata/ folder.
|
14
|
+
# @raise [RSokoban::NoFileError] if filename doesn't exist
|
13
15
|
# @see LevelSet overview of .xsb file format
|
14
16
|
def initialize filename
|
15
17
|
filename = "#{$RSOKOBAN_DATA_PATH}/" + filename
|
16
18
|
raise NoFileError unless File.exist?(filename)
|
17
19
|
@set = LevelSet.new
|
18
20
|
file = open(filename)
|
19
|
-
@set.title = file.readline.
|
21
|
+
@set.title = file.readline.get_xsb_info_line_chomp
|
20
22
|
file.readline # must be blank line
|
21
23
|
line = file.readline
|
22
24
|
desc = ''
|
23
25
|
while line[0] == ?; do
|
24
|
-
desc += line.
|
26
|
+
desc += line.get_xsb_info_line
|
25
27
|
line = file.readline
|
26
28
|
end
|
27
29
|
@set.description = desc
|
@@ -34,7 +36,7 @@ module RSokoban
|
|
34
36
|
raw.push line
|
35
37
|
line = file.readline.chomp
|
36
38
|
end
|
37
|
-
line = line.
|
39
|
+
line = line.get_xsb_info_line_chomp
|
38
40
|
@set.rawLevels.push RawLevel.new(line, raw) unless raw.empty?
|
39
41
|
|
40
42
|
line = file.readline
|
@@ -48,11 +50,32 @@ module RSokoban
|
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
53
|
+
# Get the level numbered +num+
|
54
|
+
# @param [Fixnum] num a level number in base 1
|
55
|
+
# @return [Level]
|
51
56
|
def level num
|
52
57
|
raise LevelNumberTooHighError if num > @set.rawLevels.size
|
53
58
|
Level.new @set.rawLevels[num-1]
|
54
59
|
end
|
55
60
|
|
61
|
+
# Get the description field of the loaded set of levels.
|
62
|
+
# @return [String] possibly multi-line
|
63
|
+
def file_description
|
64
|
+
@set.description
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get number of levels in this set.
|
68
|
+
# @return [Fixnum]
|
69
|
+
def size
|
70
|
+
@set.size
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get title of this set.
|
74
|
+
# @return [String]
|
75
|
+
def title
|
76
|
+
@set.title
|
77
|
+
end
|
78
|
+
|
56
79
|
end
|
57
80
|
|
58
81
|
end
|
@@ -7,14 +7,25 @@ module RSokoban
|
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
@queue = []
|
10
|
+
@redo = []
|
10
11
|
end
|
11
12
|
|
12
|
-
# @return [:up, :down, :left, :right]
|
13
|
+
# @return [:up, :down, :left, :right, :UP, :DOWN, :LEFT, :RIGHT]
|
13
14
|
# @raise EmptyMoveQueueError
|
14
15
|
# @since 0.73
|
15
16
|
def undo
|
16
17
|
raise EmptyMoveQueueError if @queue.empty?
|
17
|
-
@queue.pop
|
18
|
+
@redo << @queue.pop
|
19
|
+
@redo[-1]
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [:up, :down, :left, :right, :UP, :DOWN, :LEFT, :RIGHT]
|
23
|
+
# @raise EmptyRedoError
|
24
|
+
# @since 0.74
|
25
|
+
def redo
|
26
|
+
raise EmptyRedoError if @redo.empty?
|
27
|
+
@queue << @redo.pop
|
28
|
+
@queue[-1]
|
18
29
|
end
|
19
30
|
|
20
31
|
# Record the move +direction+
|
@@ -23,6 +34,7 @@ module RSokoban
|
|
23
34
|
# @since 0.73
|
24
35
|
def record direction, push = nil
|
25
36
|
raise ArgumentError unless [:up, :down, :left, :right].include?(direction)
|
37
|
+
@redo = []
|
26
38
|
if push
|
27
39
|
@queue.push :UP if direction == :up
|
28
40
|
@queue.push :DOWN if direction == :down
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RSokoban
|
2
|
+
|
3
|
+
# I am the result content of a move.
|
4
|
+
|
5
|
+
# @since 0.74
|
6
|
+
class MoveResult
|
7
|
+
|
8
|
+
# @param [Hash] hash the result
|
9
|
+
# @option hash [Symbol] :status Could be :ok, :win or :error
|
10
|
+
# @option hash [Fixnum] :move_number for a status of :ok or :win
|
11
|
+
# @option hash [String] :message for a status of :error
|
12
|
+
def initialize hash
|
13
|
+
@hash = hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](k)
|
17
|
+
@hash[k]
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return true if move is ok
|
21
|
+
def ok?
|
22
|
+
@hash[:status] == :ok
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return true if move result to winning the game
|
26
|
+
def win?
|
27
|
+
@hash[:status] == :win
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return true if move is an error
|
31
|
+
def error?
|
32
|
+
@hash[:status] == :error
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/rsokoban/ui/base_ui.rb
CHANGED
@@ -13,9 +13,6 @@ module RSokoban::UI
|
|
13
13
|
# my childs permit the user to do, they can only return an
|
14
14
|
# ActionPlayer object.
|
15
15
|
#
|
16
|
-
# @param [Hash] hash
|
17
|
-
# :type => type of message, :win or :start or :display or :end_of_set
|
18
|
-
# :map => current game map
|
19
16
|
# @param [Hash] hash the options passed to the UI.
|
20
17
|
# @option hash [:win|:start|:display|:end_of_set] :type The type of the message (always +requiered+)
|
21
18
|
# @option hash [Map] :map The current map of the game (always +requiered+)
|
data/lib/rsokoban/ui/console.rb
CHANGED
@@ -47,7 +47,7 @@ module RSokoban::UI
|
|
47
47
|
|
48
48
|
def ask_for_next_level
|
49
49
|
printf "Play next level ? "
|
50
|
-
line =
|
50
|
+
line = gets.chomp
|
51
51
|
if ['yes', 'ye', 'y', 'YES', 'YE', 'Y'].include?(line)
|
52
52
|
PlayerAction.new(:next)
|
53
53
|
else
|
@@ -57,7 +57,7 @@ module RSokoban::UI
|
|
57
57
|
|
58
58
|
def ask_player
|
59
59
|
printf "Your choice ? "
|
60
|
-
line =
|
60
|
+
line = gets.chomp
|
61
61
|
response = parse line
|
62
62
|
if response.nil?
|
63
63
|
puts "Error : #{line}"
|
@@ -18,7 +18,7 @@ module RSokoban::UI
|
|
18
18
|
|
19
19
|
@@Allowed_symbols = [ :up, :down, :left, :right, :quit, :next, :retry, :undo ]
|
20
20
|
|
21
|
-
# You can
|
21
|
+
# You can look to {PlayerAction} for an allowed list of value.
|
22
22
|
# @param [Object] value optional initial action
|
23
23
|
# @raise ArgumentError if value is not allowed
|
24
24
|
def initialize value = nil
|
@@ -30,8 +30,8 @@ module RSokoban::UI
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# Set the player action.
|
33
|
-
# You can the the {class description}[PlayerAction] for an allowed list of value.
|
34
33
|
# @param [Object] value the player action
|
34
|
+
# @raise ArgumentError if value is not allowed
|
35
35
|
def action=(value)
|
36
36
|
if value.instance_of?(Symbol)
|
37
37
|
raise ArgumentError unless @@Allowed_symbols.include?(value)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RSokoban::UI
|
2
|
+
|
3
|
+
# I am an image of the game who knows how to display itself on an array
|
4
|
+
# of TkLabel items.
|
5
|
+
# @since 0.73
|
6
|
+
class TkBox
|
7
|
+
|
8
|
+
# @param [String] file path of image file
|
9
|
+
# @param [Array<Array<TkLabel>>] output an array to display myself
|
10
|
+
def initialize file, output
|
11
|
+
@box = TkPhotoImage.new('file' => file, 'height' => 0, 'width' => 0)
|
12
|
+
@output = output
|
13
|
+
end
|
14
|
+
|
15
|
+
# Display myself at x,y coordinate
|
16
|
+
def display_at x, y
|
17
|
+
@output[y][x].configure('image' => @box)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
module RSokoban::UI
|
2
|
+
|
3
|
+
# As dialog box, I allow the user to choose a specific level number.
|
4
|
+
class TkLevelDialog < TkToplevel
|
5
|
+
|
6
|
+
# Create and show the dialog
|
7
|
+
# @param [TkRoot|TkToplevel] root the Tk widget I belong to
|
8
|
+
# @param [String] title my window title
|
9
|
+
def initialize(root, title)
|
10
|
+
super(root)
|
11
|
+
title(title)
|
12
|
+
minsize(200, 100)
|
13
|
+
@state = :cancel
|
14
|
+
grab
|
15
|
+
|
16
|
+
@frame_north = TkFrame.new(self) do
|
17
|
+
grid(:row => 0, :column => 0, :columnspan => 2, :sticky => :we)
|
18
|
+
padx 10
|
19
|
+
pady 10
|
20
|
+
end
|
21
|
+
|
22
|
+
$spinval = TkVariable.new
|
23
|
+
@spin = TkSpinbox.new(@frame_north) do
|
24
|
+
textvariable($spinval)
|
25
|
+
width 3
|
26
|
+
grid(:row => 0, :column => 0)
|
27
|
+
end
|
28
|
+
@spin.to(999)
|
29
|
+
@spin.from(1)
|
30
|
+
@spin.focus
|
31
|
+
@spin.bind 'Key-Return', proc{ ok_on_clic }
|
32
|
+
|
33
|
+
@ok = TkButton.new(self) do
|
34
|
+
text 'OK'
|
35
|
+
grid(:row => 1, :column => 0)
|
36
|
+
default :active
|
37
|
+
end
|
38
|
+
@ok.command { ok_on_clic }
|
39
|
+
|
40
|
+
@cancel = TkButton.new(self) do
|
41
|
+
text 'Cancel'
|
42
|
+
grid(:row => 1, :column => 1)
|
43
|
+
end
|
44
|
+
@cancel.command { cancel_on_clic }
|
45
|
+
|
46
|
+
wait_destroy
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return true if user clicked the OK button
|
50
|
+
def ok?
|
51
|
+
@state == :ok
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Fixnum] level number
|
55
|
+
def value
|
56
|
+
$spinval.to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def ok_on_clic
|
62
|
+
@state = :ok
|
63
|
+
destroy
|
64
|
+
end
|
65
|
+
|
66
|
+
def cancel_on_clic
|
67
|
+
@state = :cancel
|
68
|
+
destroy
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# As dialog box, I allow the user to choose a set name.
|
73
|
+
class SetDialog < TkToplevel
|
74
|
+
include RSokoban
|
75
|
+
# Create and show the dialog
|
76
|
+
# @param [TkRoot|TkToplevel] root the Tk widget I belong to
|
77
|
+
# @param [String] title my window title
|
78
|
+
def initialize(root, title)
|
79
|
+
super(root)
|
80
|
+
title(title)
|
81
|
+
width(300)
|
82
|
+
height(400)
|
83
|
+
@state = 'CANCEL'
|
84
|
+
grab
|
85
|
+
self['resizable'] = false, false
|
86
|
+
|
87
|
+
@xsb = get_xsb
|
88
|
+
$listval = TkVariable.new(@xsb)
|
89
|
+
@value = @xsb[0]
|
90
|
+
|
91
|
+
# A frame for the listbox
|
92
|
+
@frame_north = TkFrame.new(self) do
|
93
|
+
grid(:row => 0, :column => 0, :columnspan => 2, :sticky => :we)
|
94
|
+
padx 10
|
95
|
+
pady 10
|
96
|
+
end
|
97
|
+
|
98
|
+
@list = TkListbox.new(@frame_north) do
|
99
|
+
width 40
|
100
|
+
height 8
|
101
|
+
listvariable $listval
|
102
|
+
grid(:row => 0, :column => 0, :sticky => :we)
|
103
|
+
end
|
104
|
+
|
105
|
+
@list.bind '<ListboxSelect>', proc{ show_description }
|
106
|
+
@list.bind 'Double-1', proc{ ok_on_clic }
|
107
|
+
@list.bind 'Return', proc{ ok_on_clic }
|
108
|
+
|
109
|
+
|
110
|
+
scroll = TkScrollbar.new(@frame_north) do
|
111
|
+
orient 'vertical'
|
112
|
+
grid(:row => 0, :column => 1, :sticky => :ns)
|
113
|
+
end
|
114
|
+
|
115
|
+
@list.yscrollcommand(proc { |*args| scroll.set(*args) })
|
116
|
+
scroll.command(proc { |*args| @list.yview(*args) })
|
117
|
+
|
118
|
+
# A frame for the set description
|
119
|
+
@frame_desc = TkFrame.new(self) do
|
120
|
+
grid(:row => 1, :column => 0, :columnspan => 2, :sticky => :we)
|
121
|
+
padx 10
|
122
|
+
pady 10
|
123
|
+
end
|
124
|
+
|
125
|
+
@desc = TkText.new(@frame_desc) do
|
126
|
+
borderwidth 1
|
127
|
+
font TkFont.new('times 12')
|
128
|
+
width 40
|
129
|
+
height 10
|
130
|
+
wrap :word
|
131
|
+
grid(:row => 0, :column => 0)
|
132
|
+
end
|
133
|
+
|
134
|
+
scroll2 = TkScrollbar.new(@frame_desc) do
|
135
|
+
orient 'vertical'
|
136
|
+
grid(:row => 0, :column => 1, :sticky => :ns)
|
137
|
+
end
|
138
|
+
|
139
|
+
@desc.yscrollcommand(proc { |*args| scroll2.set(*args) })
|
140
|
+
scroll2.command(proc { |*args| @desc.yview(*args) })
|
141
|
+
|
142
|
+
# The buttons
|
143
|
+
@ok = TkButton.new(self) do
|
144
|
+
text 'OK'
|
145
|
+
grid(:row => 2, :column => 0)
|
146
|
+
default :active
|
147
|
+
end
|
148
|
+
@ok.command { ok_on_clic }
|
149
|
+
|
150
|
+
@cancel = TkButton.new(self) do
|
151
|
+
text 'Cancel'
|
152
|
+
grid(:row => 2, :column => 1)
|
153
|
+
end
|
154
|
+
@cancel.command { cancel_on_clic }
|
155
|
+
|
156
|
+
@list.focus
|
157
|
+
wait_destroy
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return true if user clicked the OK button
|
161
|
+
def ok?
|
162
|
+
@state == 'OK'
|
163
|
+
end
|
164
|
+
|
165
|
+
# @return [String] the name of the set
|
166
|
+
def value
|
167
|
+
@value
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def ok_on_clic
|
173
|
+
@state = 'OK'
|
174
|
+
idx = @list.curselection
|
175
|
+
unless idx.empty?
|
176
|
+
@value = @xsb[idx[0]]
|
177
|
+
end
|
178
|
+
destroy
|
179
|
+
end
|
180
|
+
|
181
|
+
def cancel_on_clic
|
182
|
+
@state = 'CANCEL'
|
183
|
+
destroy
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_xsb
|
187
|
+
current = Dir.pwd
|
188
|
+
Dir.chdir $RSOKOBAN_DATA_PATH
|
189
|
+
ret = Dir.glob '*.xsb'
|
190
|
+
Dir.chdir current
|
191
|
+
ret
|
192
|
+
end
|
193
|
+
|
194
|
+
def show_description
|
195
|
+
idx = @list.curselection
|
196
|
+
ll = LevelLoader.new @xsb[idx[0]]
|
197
|
+
@desc.delete '1.0', :end
|
198
|
+
@desc.insert :end, ll.file_description
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# As dialog box, I display some help.
|
203
|
+
class HelpDialog < TkToplevel
|
204
|
+
# Create and show the dialog
|
205
|
+
# @param [TkRoot|TkToplevel] root the Tk widget I belong to
|
206
|
+
# @param [String] title my window title
|
207
|
+
def initialize(root, title)
|
208
|
+
super(root)
|
209
|
+
title(title)
|
210
|
+
minsize(200, 100)
|
211
|
+
|
212
|
+
text = TkText.new(self) do
|
213
|
+
borderwidth 1
|
214
|
+
font TkFont.new('times 12 bold')
|
215
|
+
grid('row' => 0, 'column' => 0)
|
216
|
+
end
|
217
|
+
|
218
|
+
help=<<EOS
|
219
|
+
Welcome to RSokoban !
|
220
|
+
|
221
|
+
Goal of Sokoban game is to place each crate on a storage location.
|
222
|
+
Move the man using the arrow keys.
|
223
|
+
For a more comprehensive help, please visit the wiki at https://github.com/lkdjiin/RSokoban/wiki.
|
224
|
+
EOS
|
225
|
+
|
226
|
+
text.insert 'end', help
|
227
|
+
|
228
|
+
@ok = TkButton.new(self) do
|
229
|
+
text 'OK'
|
230
|
+
grid('row'=>1, 'column'=>0)
|
231
|
+
end
|
232
|
+
@ok.command { ok_on_clic }
|
233
|
+
end
|
234
|
+
|
235
|
+
def ok_on_clic
|
236
|
+
destroy
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|