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
@@ -0,0 +1,38 @@
|
|
1
|
+
module RSokoban
|
2
|
+
|
3
|
+
# I can record the moves of a game and restitute them one by one,
|
4
|
+
# started from the last one. I am very usefull to implement an
|
5
|
+
# undo feature.
|
6
|
+
class MoveRecorder
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@queue = []
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [:up, :down, :left, :right]
|
13
|
+
# @raise EmptyMoveQueueError
|
14
|
+
# @since 0.73
|
15
|
+
def undo
|
16
|
+
raise EmptyMoveQueueError if @queue.empty?
|
17
|
+
@queue.pop
|
18
|
+
end
|
19
|
+
|
20
|
+
# Record the move +direction+
|
21
|
+
# @param [:up, :down, :left, :right] direction
|
22
|
+
# @raise ArgumentError if +direction+ is not included in [:up, :down, :left, :right]
|
23
|
+
# @since 0.73
|
24
|
+
def record direction, push = nil
|
25
|
+
raise ArgumentError unless [:up, :down, :left, :right].include?(direction)
|
26
|
+
if push
|
27
|
+
@queue.push :UP if direction == :up
|
28
|
+
@queue.push :DOWN if direction == :down
|
29
|
+
@queue.push :LEFT if direction == :left
|
30
|
+
@queue.push :RIGHT if direction == :right
|
31
|
+
else
|
32
|
+
@queue.push direction
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/rsokoban/option.rb
CHANGED
@@ -5,41 +5,50 @@ class Option
|
|
5
5
|
|
6
6
|
# Here is a list of command line options :
|
7
7
|
# * --curses
|
8
|
-
# * --version
|
9
|
-
# * --license
|
10
8
|
# * --help
|
11
9
|
# * --help-output
|
10
|
+
# * --license
|
12
11
|
# * --portable
|
12
|
+
# * --tk
|
13
|
+
# * --version
|
13
14
|
# @todo refactoring
|
14
15
|
def initialize
|
15
|
-
|
16
|
+
# Default option(s)
|
17
|
+
@options = {:ui => :tk}
|
16
18
|
|
17
19
|
optparse = OptionParser.new do|opts|
|
18
20
|
opts.banner = "Usage: #{$0} [options]"
|
19
21
|
|
22
|
+
# Options that define the UI
|
20
23
|
opts.on( '-c', '--curses', 'Use curses console for user interface (default)' ) do
|
21
24
|
@options[:ui] = :curses
|
22
25
|
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
@options[:help_output] = true
|
27
|
+
opts.on( '-p', '--portable', 'Use standard console for user interface' ) do
|
28
|
+
@options[:ui] = :portable
|
27
29
|
end
|
28
30
|
|
31
|
+
opts.on( '-t', '--tk', 'Make use of Tk library for a graphical user interface' ) do
|
32
|
+
@options[:ui] = :tk
|
33
|
+
end
|
34
|
+
|
35
|
+
# Options that display a few information
|
29
36
|
@options[:license] = false
|
30
37
|
opts.on( '-l', '--license', 'Print program\'s license and exit' ) do
|
31
38
|
@options[:license] = true
|
32
39
|
end
|
33
40
|
|
34
|
-
opts.on( '-p', '--portable', 'Use standard console for user interface' ) do
|
35
|
-
@options[:ui] = :portable
|
36
|
-
end
|
37
|
-
|
38
41
|
@options[:version] = false
|
39
42
|
opts.on( '-v', '--version', 'Print version number and exit' ) do
|
40
43
|
@options[:version] = true
|
41
44
|
end
|
42
45
|
|
46
|
+
# Options that display some help
|
47
|
+
@options[:help_output] = false
|
48
|
+
opts.on( '-o', '--help-output', 'Print help on output options and exit' ) do
|
49
|
+
@options[:help_output] = true
|
50
|
+
end
|
51
|
+
|
43
52
|
opts.on( '-h', '--help', 'Display this screen' ) do
|
44
53
|
puts opts
|
45
54
|
exit
|
@@ -62,10 +71,14 @@ class Option
|
|
62
71
|
@options[k]
|
63
72
|
end
|
64
73
|
|
74
|
+
def interface=(k)
|
75
|
+
@options[:ui] = k
|
76
|
+
end
|
77
|
+
|
65
78
|
private
|
66
79
|
|
67
80
|
def print_version
|
68
|
-
puts
|
81
|
+
puts File.read($RSOKOBAN_PATH + '/VERSION').strip
|
69
82
|
exit
|
70
83
|
end
|
71
84
|
|
@@ -77,7 +90,7 @@ private
|
|
77
90
|
def print_help_output
|
78
91
|
help=<<EOS
|
79
92
|
|
80
|
-
RSokoban can use
|
93
|
+
RSokoban can use 3 user interfaces :
|
81
94
|
|
82
95
|
--curses
|
83
96
|
This is the default UI. It uses the curses library in a console window.
|
@@ -86,6 +99,10 @@ RSokoban can use 2 user interfaces :
|
|
86
99
|
--portable
|
87
100
|
It uses a plain console window. This UI is boring but should work
|
88
101
|
everywhere.
|
102
|
+
|
103
|
+
--tk
|
104
|
+
The only graphical UI for now. Make sure tk is installed on your computer
|
105
|
+
along with the libtk-img extension library.
|
89
106
|
EOS
|
90
107
|
puts help
|
91
108
|
exit
|
data/lib/rsokoban/raw_level.rb
CHANGED
@@ -1,14 +1,25 @@
|
|
1
1
|
module RSokoban
|
2
2
|
|
3
3
|
# I figure out a level in a very simple format.
|
4
|
-
# I have a title and
|
4
|
+
# I have a title and a map.
|
5
5
|
# @todo document and give some examples
|
6
6
|
class RawLevel
|
7
|
-
|
7
|
+
attr_reader :map
|
8
|
+
attr_accessor :title
|
8
9
|
|
9
|
-
def initialize title = 'Unknown level title',
|
10
|
+
def initialize title = 'Unknown level title', map = Map.new
|
10
11
|
@title = title
|
11
|
-
@
|
12
|
+
@map = map
|
13
|
+
end
|
14
|
+
|
15
|
+
def map=(val)
|
16
|
+
if val.kind_of?(Map)
|
17
|
+
@map = val
|
18
|
+
elsif val.kind_of?(Array)
|
19
|
+
@map = Map.new val
|
20
|
+
else
|
21
|
+
raise ArgumentError
|
22
|
+
end
|
12
23
|
end
|
13
24
|
|
14
25
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RSokoban::UI
|
2
|
+
|
3
|
+
# Every concrete UI should inherits from me.
|
4
|
+
# @abstract Subclass and override {#get_action} to implement a working UI.
|
5
|
+
class BaseUI
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@level_title = ''
|
9
|
+
end
|
10
|
+
|
11
|
+
# Based on things found in the +hash+ argument, I display the game
|
12
|
+
# to the user. Then he can tell what he wants to do. Whatever
|
13
|
+
# my childs permit the user to do, they can only return an
|
14
|
+
# ActionPlayer object.
|
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
|
+
# @param [Hash] hash the options passed to the UI.
|
20
|
+
# @option hash [:win|:start|:display|:end_of_set] :type The type of the message (always +requiered+)
|
21
|
+
# @option hash [Map] :map The current map of the game (always +requiered+)
|
22
|
+
# @option hash [String] :title The level's title (requiered if type==:start)
|
23
|
+
# @option hash [String] :set The set's title (requiered if type==:start)
|
24
|
+
# @option hash [Fixnum] :number The level's number (requiered if type==:start)
|
25
|
+
# @option hash [Fixnum] :total Number of level in this set (requiered if type==:start)
|
26
|
+
# @option hash [Fixnum] :move The move's number (requiered when type is :display or :win)
|
27
|
+
# @option hash [String] :error An error message. Could happen when type is :display or :start
|
28
|
+
# @return [PlayerAction] the user's action
|
29
|
+
# @since 0.73
|
30
|
+
# @todo write some examples
|
31
|
+
def get_action(hash)
|
32
|
+
raise "Please implement me !"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/rsokoban/ui/console.rb
CHANGED
@@ -11,51 +11,60 @@ module RSokoban::UI
|
|
11
11
|
super()
|
12
12
|
end
|
13
13
|
|
14
|
-
def get_action(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def get_action(hash)
|
15
|
+
if hash[:type] == :start
|
16
|
+
@level_title = hash[:title]
|
17
|
+
@set_title = hash[:set]
|
18
|
+
@level_number = hash[:number]
|
19
|
+
@set_total = hash[:total]
|
20
|
+
end
|
21
|
+
display hash
|
22
|
+
if [:display, :start].include?(hash[:type])
|
23
|
+
ask_player
|
20
24
|
else
|
21
25
|
# assuming type == 'WIN'
|
22
|
-
|
26
|
+
ask_for_next_level
|
23
27
|
end
|
24
28
|
end
|
25
29
|
|
26
30
|
private
|
27
31
|
|
28
|
-
def display
|
32
|
+
def display hash
|
29
33
|
blankConsole = "\n" * 24 # assuming a console window of 24 lines height
|
30
34
|
puts blankConsole
|
31
|
-
puts @
|
35
|
+
puts "Set: #{@set_title}"
|
36
|
+
puts "Level: #{@level_title} (#{@level_number}/#{@set_total})"
|
32
37
|
puts "--------------------"
|
33
|
-
|
38
|
+
unless hash[:move].nil?
|
39
|
+
puts "move #{hash[:move]}"
|
40
|
+
else
|
41
|
+
puts ''
|
42
|
+
end
|
34
43
|
puts ''
|
35
|
-
|
44
|
+
hash[:map].each {|line| puts line }
|
36
45
|
puts ''
|
37
46
|
end
|
38
47
|
|
39
|
-
def
|
48
|
+
def ask_for_next_level
|
40
49
|
printf "Play next level ? "
|
41
50
|
line = readline.chomp
|
42
51
|
if ['yes', 'ye', 'y', 'YES', 'YE', 'Y'].include?(line)
|
43
|
-
:next
|
52
|
+
PlayerAction.new(:next)
|
44
53
|
else
|
45
|
-
:quit
|
54
|
+
PlayerAction.new(:quit)
|
46
55
|
end
|
47
56
|
end
|
48
57
|
|
49
|
-
def
|
58
|
+
def ask_player
|
50
59
|
printf "Your choice ? "
|
51
60
|
line = readline.chomp
|
52
61
|
response = parse line
|
53
62
|
if response.nil?
|
54
63
|
puts "Error : #{line}"
|
55
|
-
|
56
|
-
elsif response == :help
|
57
|
-
|
58
|
-
|
64
|
+
ask_player
|
65
|
+
elsif response.instance_of?(Symbol) and response == :help
|
66
|
+
display_help
|
67
|
+
ask_player
|
59
68
|
else
|
60
69
|
response
|
61
70
|
end
|
@@ -63,24 +72,24 @@ module RSokoban::UI
|
|
63
72
|
|
64
73
|
def parse str
|
65
74
|
case str
|
66
|
-
when 'quit', 'up', 'down', 'right', 'left', 'retry', '
|
67
|
-
str.to_sym
|
68
|
-
when '
|
69
|
-
|
70
|
-
when 's'
|
71
|
-
|
72
|
-
when '
|
73
|
-
:left
|
74
|
-
when 'd'
|
75
|
-
:right
|
75
|
+
when 'quit', 'up', 'down', 'right', 'left', 'retry', 'undo'
|
76
|
+
PlayerAction.new(str.to_sym)
|
77
|
+
when 'help' then :help
|
78
|
+
when 'z' then PlayerAction.new(:up)
|
79
|
+
when 's' then PlayerAction.new(:down)
|
80
|
+
when 'q' then PlayerAction.new(:left)
|
81
|
+
when 'd' then PlayerAction.new(:right)
|
76
82
|
when '1'..'999'
|
77
|
-
str.to_i
|
78
|
-
|
79
|
-
|
83
|
+
if str.to_i > @set_total
|
84
|
+
ask_player
|
85
|
+
else
|
86
|
+
PlayerAction.new(str.to_i)
|
87
|
+
end
|
88
|
+
when /\.xsb$/ then PlayerAction.new(str)
|
80
89
|
end
|
81
90
|
end
|
82
91
|
|
83
|
-
def
|
92
|
+
def display_help
|
84
93
|
help=<<EOS
|
85
94
|
------------------------------
|
86
95
|
General commands :
|
@@ -88,6 +97,7 @@ General commands :
|
|
88
97
|
quit : Quit game
|
89
98
|
help : Display this screen
|
90
99
|
retry : Restart level
|
100
|
+
undo : Undo last move
|
91
101
|
1 to 999 : Play this level
|
92
102
|
file.xsb : Load this set of levels
|
93
103
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
|
3
3
|
module RSokoban::UI
|
4
4
|
|
@@ -6,10 +6,11 @@ module RSokoban::UI
|
|
6
6
|
# I assume 24 lines height.
|
7
7
|
class CursesConsole < BaseUI
|
8
8
|
|
9
|
-
@@
|
10
|
-
@@
|
11
|
-
@@
|
12
|
-
@@
|
9
|
+
@@SET_LINE = 0
|
10
|
+
@@TITLE_LINE = 1
|
11
|
+
@@MOVES_LINE = 2
|
12
|
+
@@STATUS_LINE = 3
|
13
|
+
@@PICTURE_LINE = 5
|
13
14
|
|
14
15
|
def initialize
|
15
16
|
super()
|
@@ -18,17 +19,19 @@ module RSokoban::UI
|
|
18
19
|
Curses.close_screen
|
19
20
|
end
|
20
21
|
|
21
|
-
def get_action(
|
22
|
-
if type ==
|
23
|
-
@level_title =
|
24
|
-
|
22
|
+
def get_action(hash)
|
23
|
+
if hash[:type] == :start
|
24
|
+
@level_title = hash[:title]
|
25
|
+
@set_title = hash[:set]
|
26
|
+
@level_number = hash[:number]
|
27
|
+
@set_total = hash[:total]
|
25
28
|
Curses.clear
|
26
29
|
end
|
27
|
-
if
|
28
|
-
ask_player
|
30
|
+
if [:display, :start].include?(hash[:type])
|
31
|
+
ask_player hash
|
29
32
|
else
|
30
|
-
# assuming
|
31
|
-
|
33
|
+
# assuming :win
|
34
|
+
ask_for_next_level hash
|
32
35
|
end
|
33
36
|
end
|
34
37
|
|
@@ -41,13 +44,13 @@ module RSokoban::UI
|
|
41
44
|
Curses.curs_set 0
|
42
45
|
end
|
43
46
|
|
44
|
-
def display
|
45
|
-
write @@
|
46
|
-
|
47
|
-
write @@MOVES_LINE, 0,
|
48
|
-
write @@STATUS_LINE, 0, 'arrows=move (q)uit (r)etry (l)oad level/set'
|
47
|
+
def display hash
|
48
|
+
write @@SET_LINE, 0, "Set: #{@set_title}"
|
49
|
+
write @@TITLE_LINE, 0, "Level: #{@level_title} (#{@level_number}/#{@set_total})"
|
50
|
+
write @@MOVES_LINE, 0, "moves : #{hash[:move].to_s} " unless hash[:move].nil?
|
51
|
+
write @@STATUS_LINE, 0, 'arrows=move (q)uit (r)etry (u)ndo (l)oad level/set'
|
49
52
|
line_num = @@PICTURE_LINE
|
50
|
-
|
53
|
+
hash[:map].each {|line|
|
51
54
|
write line_num, 0, line
|
52
55
|
line_num += 1
|
53
56
|
}
|
@@ -58,22 +61,22 @@ module RSokoban::UI
|
|
58
61
|
Curses.addstr(text);
|
59
62
|
end
|
60
63
|
|
61
|
-
def
|
62
|
-
display
|
64
|
+
def ask_for_next_level hash
|
65
|
+
display hash
|
63
66
|
write @@STATUS_LINE, 0, "LEVEL COMPLETED ! Play next level ? (yes, no) "
|
64
67
|
case Curses.getch
|
65
|
-
when ?n, ?N then :quit
|
66
|
-
when ?y, ?Y then :next
|
68
|
+
when ?n, ?N then PlayerAction.new(:quit)
|
69
|
+
when ?y, ?Y then PlayerAction.new(:next)
|
67
70
|
else
|
68
|
-
|
71
|
+
ask_for_next_level hash
|
69
72
|
end
|
70
73
|
end
|
71
74
|
|
72
|
-
def ask_player
|
73
|
-
display
|
75
|
+
def ask_player hash
|
76
|
+
display hash
|
74
77
|
response = get_player_input
|
75
78
|
if response.nil?
|
76
|
-
ask_player
|
79
|
+
ask_player hash
|
77
80
|
else
|
78
81
|
response
|
79
82
|
end
|
@@ -81,13 +84,14 @@ module RSokoban::UI
|
|
81
84
|
|
82
85
|
def get_player_input
|
83
86
|
case Curses.getch
|
84
|
-
when Curses::Key::UP then :up
|
85
|
-
when Curses::Key::DOWN then :down
|
86
|
-
when Curses::Key::LEFT then :left
|
87
|
-
when Curses::Key::RIGHT then :right
|
88
|
-
when ?q, ?Q then :quit
|
89
|
-
when ?r, ?R then :retry
|
87
|
+
when Curses::Key::UP then PlayerAction.new(:up)
|
88
|
+
when Curses::Key::DOWN then PlayerAction.new(:down)
|
89
|
+
when Curses::Key::LEFT then PlayerAction.new(:left)
|
90
|
+
when Curses::Key::RIGHT then PlayerAction.new(:right)
|
91
|
+
when ?q, ?Q then PlayerAction.new(:quit)
|
92
|
+
when ?r, ?R then PlayerAction.new(:retry)
|
90
93
|
when ?l, ?L then ask_level_or_set
|
94
|
+
when ?u, ?U then PlayerAction.new(:undo)
|
91
95
|
else
|
92
96
|
nil
|
93
97
|
end
|
@@ -111,10 +115,15 @@ EOS
|
|
111
115
|
Curses.curs_set 0
|
112
116
|
Curses.noecho
|
113
117
|
case str
|
114
|
-
when '1'..'999'
|
115
|
-
|
118
|
+
when '1'..'999'
|
119
|
+
if str.to_i > @set_total
|
120
|
+
ask_level_or_set
|
121
|
+
else
|
122
|
+
PlayerAction.new(str.to_i)
|
123
|
+
end
|
124
|
+
when /\.xsb$/ then PlayerAction.new(str)
|
116
125
|
else
|
117
|
-
:retry
|
126
|
+
PlayerAction.new(:retry)
|
118
127
|
end
|
119
128
|
end
|
120
129
|
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module RSokoban::UI
|
2
|
+
|
3
|
+
# I am an action from the player.
|
4
|
+
#
|
5
|
+
# List of possible actions :
|
6
|
+
# [:quit] to quit game
|
7
|
+
# [:next] to load and play next level
|
8
|
+
# [:retry] to restart the current level
|
9
|
+
# [:up, :down, :left, :right] to move the man
|
10
|
+
# [:undo] to undo a move
|
11
|
+
# [a number] to load this level number
|
12
|
+
# [an .xsb filename] to load the set with this name
|
13
|
+
#
|
14
|
+
# @since 0.73
|
15
|
+
class PlayerAction
|
16
|
+
# @param [Object] the action
|
17
|
+
attr_reader :action
|
18
|
+
|
19
|
+
@@Allowed_symbols = [ :up, :down, :left, :right, :quit, :next, :retry, :undo ]
|
20
|
+
|
21
|
+
# You can the the {class description}[PlayerAction] for an allowed list of value.
|
22
|
+
# @param [Object] value optional initial action
|
23
|
+
# @raise ArgumentError if value is not allowed
|
24
|
+
def initialize value = nil
|
25
|
+
self.action = value unless value.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(obj)
|
29
|
+
@action == obj.action
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set the player action.
|
33
|
+
# You can the the {class description}[PlayerAction] for an allowed list of value.
|
34
|
+
# @param [Object] value the player action
|
35
|
+
def action=(value)
|
36
|
+
if value.instance_of?(Symbol)
|
37
|
+
raise ArgumentError unless @@Allowed_symbols.include?(value)
|
38
|
+
end
|
39
|
+
if value.instance_of?(String)
|
40
|
+
raise ArgumentError unless value =~ /\.xsb$/
|
41
|
+
end
|
42
|
+
@action = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def level_number?
|
46
|
+
@action.instance_of?(Fixnum)
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_name?
|
50
|
+
@action.instance_of?(String)
|
51
|
+
end
|
52
|
+
|
53
|
+
def quit?
|
54
|
+
(not @action.nil?) and @action == :quit
|
55
|
+
end
|
56
|
+
|
57
|
+
def next?
|
58
|
+
(not @action.nil?) and @action == :next
|
59
|
+
end
|
60
|
+
|
61
|
+
def retry?
|
62
|
+
(not @action.nil?) and @action == :retry
|
63
|
+
end
|
64
|
+
|
65
|
+
def move?
|
66
|
+
(not @action.nil?) and [:down, :up, :left, :right].include?(@action)
|
67
|
+
end
|
68
|
+
|
69
|
+
def undo?
|
70
|
+
(not @action.nil?) and @action == :undo
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|