RSokoban 0.73 → 0.74
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 +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
|