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 CHANGED
@@ -1,4 +1,19 @@
1
- version 0.73 2011-??-??
1
+ version 0.74 2011-01-31
2
+
3
+ * New: redo feature (only available with Tk GUI)
4
+
5
+ * Tk GUI
6
+ + add a 'next level' feature
7
+ + undo shortcut change to Ctrl+Z
8
+ + view description of a level set in the 'load set' dialog box
9
+ + minor improvements in dialog boxes
10
+
11
+ * Fix
12
+ + bug#1
13
+ + bug#2
14
+
15
+
16
+ version 0.73 2011-01-27
2
17
 
3
18
  * New: undo
4
19
 
@@ -4,32 +4,35 @@ RSokoban is a clone of the famous {Sokoban}[http://en.wikipedia.org/wiki/Sokoban
4
4
  I wrote this program just to improve my skills in Ruby.
5
5
 
6
6
  *Features*
7
- * User interface comes in 3 flavors (tk, curses, straight text mode)
8
- * Installing via RubyGem
9
- * Undo
7
+ * Graphical user interface with Tk
8
+ * Two console user interfaces (curses and straight text mode)
9
+ * 200+ levels
10
+ * Undo/redo
10
11
  * Use xsb file format to load sets of levels
11
12
 
12
13
  Enjoy the game.
13
14
 
15
+
14
16
  == Documentation
17
+
15
18
  Players can look at {the wiki}[https://github.com/lkdjiin/RSokoban/wiki].
16
19
 
17
20
  Developpers can look at Level, Game and TkUI.
18
21
 
19
22
 
20
23
  == Dependancies
24
+
21
25
  Ruby >= 1.8.7
22
26
 
23
- == Installing RSokoban
24
- Installing via RubyGem is easy doing in one command:
25
27
 
26
- gem install RSokoban
28
+ == Installing RSokoban
27
29
 
28
- Or you can install RSokoban from the source. To do this, go to the RSokoban folder and build the gem :
30
+ *First method*
31
+ To install RSokoban from the source, go to the RSokoban folder and build the gem:
29
32
 
30
33
  gem build rsokoban.gemspec
31
34
 
32
- Then install it :
35
+ Then install it (maybe you have to be root):
33
36
 
34
37
  gem install RSokoban-x-xx.gem
35
38
 
@@ -37,11 +40,25 @@ Now start playing :
37
40
 
38
41
  rsokoban
39
42
 
43
+ If you prefer using rake to build and install:
44
+
45
+ rake build
46
+ rake install
47
+
48
+ *Second method*
49
+ Or install via RubyGem in one command (you need ruby 1.9 or higher).
50
+ Note that I don't certify you will get the latest release:
51
+
52
+ gem install RSokoban
53
+
54
+ *Third method*
40
55
  Or you can play without installing it. Go to the RSokoban/bin folder and type:
41
56
 
42
57
  ./rsokoban
43
58
 
59
+
44
60
  == Questions and/or Comments
61
+
45
62
  Feel free to email {Xavier Nayrac}[mailto:xavier.nayrac@gmail.com]
46
63
  with any questions.
47
64
 
@@ -0,0 +1,51 @@
1
+ desc 'Unit tests'
2
+ task :default => :unit
3
+
4
+ # Running RSokoban with the different (G)UIs
5
+
6
+ desc 'Run RSokoban with Tk GUI'
7
+ task :tk do
8
+ exec "./bin/rsokoban --tk"
9
+ end
10
+
11
+ desc 'Run RSokoban with curses UI'
12
+ task :curses do
13
+ exec "./bin/rsokoban --curses"
14
+ end
15
+ # Aliases to rake curses
16
+ task :curse => :curses
17
+ task :curs => :curses
18
+ task :cur => :curses
19
+
20
+ desc 'Run RSokoban in straight text mode'
21
+ task :portable do
22
+ exec "./bin/rsokoban --portable"
23
+ end
24
+
25
+ # Build & Install RSokoban
26
+
27
+ desc 'Build the gem'
28
+ task :build do
29
+ exec "gem build rsokoban.gemspec"
30
+ end
31
+
32
+ desc 'Install the gem (maybe you have to be root)'
33
+ task :install do
34
+ require 'rake'
35
+ f = FileList['RSokoban*gem'].to_a
36
+ exec "gem install #{f.first}"
37
+ end
38
+
39
+ # Testing RSokoban
40
+
41
+ desc 'Unit tests'
42
+ task :unit do
43
+ Dir.chdir 'test'
44
+ exec "./test.rb"
45
+ end
46
+
47
+ # Documentation
48
+ desc 'Generate yard documentation'
49
+ task :doc do
50
+ exec 'yardoc --title "RSokoban Documentation" - NEWS COPYING VERSION'
51
+ end
data/TODO CHANGED
@@ -1,41 +1,19 @@
1
-
2
- doing gem and upload it
3
-
4
- --- next ---
5
- tk: view description of level set in 'load set' dialog
6
-
7
- tk: add a scrollbar to the 'load set' dialog.
8
-
9
- tk: adjust window size to map size
10
-
11
- tk: level preview
12
-
13
- redo
14
-
15
- --gnome
16
-
17
- skining (several size of tile, resizing ?)
18
-
19
- Console UI should provide a way to know names of sets
20
-
21
- save & reload
22
-
23
- multi player
24
-
25
- save score
26
-
27
- don't restrict map size
28
-
29
- solver (plugin ?)
30
-
31
- format fichier niveau xml (.slc)
32
-
33
- --windows ?
34
-
35
- --kde ?
36
-
37
- --fxruby ?
38
-
39
- mouse support
40
-
41
- i18n
1
+ * tk: use one image for rendering, not an array (if possible ?)
2
+ * don't restrict map size
3
+ * gnome GUI (gtk only to be windows compatible ?)
4
+ * give a try to rubygame ?
5
+ * skining
6
+ + load skin
7
+ + several size of tiles or resizing ?)
8
+ * Console UI should provide a way to know names of sets
9
+ * save a game
10
+ * reload a saved game
11
+ * multi player support
12
+ * save score
13
+ * scoring moves and pushes
14
+ * level preview
15
+ * solver (plugin ?)
16
+ * format fichier niveau xml (.slc)
17
+ * other GUI(s) ?
18
+ * mouse support
19
+ * i18n
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.73
1
+ 0.74
@@ -69,6 +69,4 @@ if option[:ui] == :curses
69
69
  end
70
70
 
71
71
  game = RSokoban::Game.new option[:ui]
72
-
73
- # It seems I have somme troubles to unify GUI and non-GUI.
74
- game.run if [:curses, :portable].include?(option[:ui])
72
+ game.run
@@ -1,3 +1,4 @@
1
+ require "rsokoban/extensions"
1
2
  require "rsokoban/level"
2
3
  require "rsokoban/level_loader"
3
4
  require "rsokoban/exception"
@@ -12,6 +13,7 @@ require "rsokoban/raw_level"
12
13
  require "rsokoban/ui"
13
14
  require "rsokoban/move_recorder"
14
15
  require "rsokoban/map"
16
+ require "rsokoban/move_result"
15
17
 
16
18
  # I am the main module of the game.
17
19
  module RSokoban
@@ -8,4 +8,8 @@ module RSokoban
8
8
  # Used by {MoveRecorder}. Raise me if one try to undo when there is no move left.
9
9
  class EmptyMoveQueueError < StandardError
10
10
  end
11
+
12
+ # Used by {MoveRecorder}. Raise me if one try to redo when there is nothing to redo.
13
+ class EmptyRedoError < StandardError
14
+ end
11
15
  end
@@ -0,0 +1,26 @@
1
+
2
+ class String
3
+ # In xsb files, an information line start with a semi-colon.
4
+ # I remove it, remove leading and trailing spaces and inject a newline to the end.
5
+ # @example
6
+ # > a = "; hello world ! \n"
7
+ # > a.get_xsb_info_line
8
+ # => "hello world !\n"
9
+ # @since 0.74
10
+ # @see #get_xsb_info_line_chomp
11
+ def get_xsb_info_line
12
+ self.get_xsb_info_line_chomp + "\n"
13
+ end
14
+
15
+ # In xsb files, an information line start with a semi-colon.
16
+ # I remove it and also remove leading and trailing spaces.
17
+ # @example
18
+ # > a = "; hello world ! \n"
19
+ # > a.get_xsb_info_line_chomp
20
+ # => "hello world !"
21
+ # @since 0.74
22
+ # @see #get_xsb_info_line
23
+ def get_xsb_info_line_chomp
24
+ self.sub(/;/, '').strip
25
+ end
26
+ end
@@ -1,12 +1,17 @@
1
1
  module RSokoban
2
2
 
3
- # I am mostly the game loop.
3
+ # I manage the game.
4
4
  class Game
5
+ # @return [Fixnum]
6
+ # @since 0.74
7
+ attr_reader :level_number
5
8
 
6
9
  # Construct a new game that you can later run.
7
10
  # @param [:curses|:portable|:tk] ui_as_symbol the user interface for the game
8
- def initialize ui_as_symbol
9
- @level_loader = LevelLoader.new "original.xsb"
11
+ # @param [String] setname only used during testing
12
+ def initialize ui_as_symbol, setname = 'microban.xsb'
13
+ @ui_as_symbol = ui_as_symbol
14
+ @level_loader = LevelLoader.new setname
10
15
  @level_number = 1
11
16
  case ui_as_symbol
12
17
  when :curses
@@ -17,12 +22,28 @@ module RSokoban
17
22
  @ui = UI::Console.new
18
23
  when :tk
19
24
  require "rsokoban/ui/tk_ui"
20
- @ui = UI::TkUI.new
25
+ require "rsokoban/ui/tk_box"
26
+ require "rsokoban/ui/tk_dialogs"
27
+ @gui = UI::TkUI.new self
21
28
  end
22
29
  end
23
30
 
24
- # I am the game loop.
31
+ # Start the game loop.
32
+ #
33
+ # For GUI game (like Tk), the tool kit provide its own event-loop. In that case, I simply
34
+ # call this event-loop via the run() method (for example see {RSokoban::UI::TkUI#run}.
35
+ #
36
+ # For UI game (like Curses) which are text based, Game provide the loop.
25
37
  def run
38
+ if [:curses, :portable].include?(@ui_as_symbol)
39
+ ui_mainloop
40
+ else
41
+ @gui.run
42
+ end
43
+ end
44
+
45
+ # I am the game loop for UIs.
46
+ def ui_mainloop
26
47
  player_action = start_level
27
48
  loop do
28
49
  if player_action.level_number?
@@ -45,40 +66,151 @@ module RSokoban
45
66
  result = @level.undo
46
67
  end
47
68
 
48
- move_index = result =~ /\d+/
49
- if result.start_with?('WIN')
50
- player_action = @ui.get_action(:type=>:win, :map=>@level.map, :move=>result[move_index..-1])
69
+ if result[:status] == :win
70
+ player_action = @ui.get_action(:type=>:win, :map=>@level.map, :move=>result[:move_number])
71
+ elsif result[:status] == :ok
72
+ player_action = @ui.get_action(:type=>:display, :map=>@level.map, :move=>result[:move_number])
51
73
  else
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
74
+ player_action = @ui.get_action(:type=>:display, :map=>@level.map, :error=>result[:message])
57
75
  end
58
76
  end
77
+ true
59
78
  end
60
79
 
61
- private
80
+ # @return [Fixnum]
81
+ # @since 0.74
82
+ # @see Level#width
83
+ def level_width
84
+ @level.width
85
+ end
86
+
87
+ # @return [Fixnum]
88
+ # @since 0.74
89
+ # @see Level#height
90
+ def level_height
91
+ @level.height
92
+ end
93
+
94
+ # @return [String]
95
+ # @since 0.74
96
+ # @see Level#title
97
+ def level_title
98
+ @level.title
99
+ end
100
+
101
+ # Get current map of the game
102
+ # @return [Map]
103
+ # @since 0.74
104
+ def map
105
+ @level.map
106
+ end
107
+
108
+ # Get x coordinate of the man
109
+ # @return [Fixnum]
110
+ # @since 0.74
111
+ def man_x
112
+ @level.man.x
113
+ end
114
+
115
+ # Get y coordinate of the man
116
+ # @return [Fixnum]
117
+ # @since 0.74
118
+ def man_y
119
+ @level.man.y
120
+ end
121
+
122
+ # Get result of the move
123
+ # @param [:up|:down|:right|:left] direction
124
+ # @return [Object]
125
+ # @since 0.74
126
+ def move direction
127
+ @level.move direction
128
+ end
129
+
130
+ # Get current move number
131
+ # @return [Fixnum]
132
+ # @since 0.74
133
+ def move_number
134
+ @level.move_number
135
+ end
136
+
137
+ # Get result of undo last move
138
+ # @return [MoveResult]
139
+ # @since 0.74
140
+ def undo
141
+ @level.undo
142
+ end
143
+
144
+ # Get result of redo last undone move
145
+ # @return [MoveResult]
146
+ # @since 0.74
147
+ def redo
148
+ @level.redo
149
+ end
150
+
151
+ # Get title of the current set
152
+ # @return [String]
153
+ # @since 0.74
154
+ def set_title
155
+ @level_loader.title
156
+ end
157
+
158
+ # Get numbers of level from the current set
159
+ # @return [Fixnum]
160
+ # @since 0.74
161
+ def set_size
162
+ @level_loader.size
163
+ end
164
+
165
+ # Start a level, according to some instance members.
166
+ # @return [PlayerAction|nil] the user's action for console window
167
+ # interface, or nil for GUI.
168
+ def start_level
169
+ begin
170
+ @level = @level_loader.level(@level_number)
171
+ if @ui
172
+ @ui.get_action(:type=>:start, :map=>@level.map, :title=>@level.title, :set=>@level_loader.title,
173
+ :number=>@level_number, :total=>@level_loader.size)
174
+ end
175
+ rescue LevelNumberTooHighError
176
+ if @ui
177
+ @ui.get_action(:type=>:end_of_set, :map=>Map.new)
178
+ else
179
+ raise LevelNumberTooHighError
180
+ end
181
+ end
182
+ end
62
183
 
63
184
  # Load and start the next level of the set
64
- # @return [Object] the user's {action}[Console#get_action]
185
+ # @return [PlayerAction|nil] the user's action for console window
186
+ # interface, or nil for GUI.
65
187
  def next_level
66
188
  @level_number += 1
67
189
  start_level
68
190
  end
69
191
 
70
- # Restart the current level
71
- # @return [Object] the user's {action}[Console#get_action]
72
- def try_again
192
+ # Load a level from the current set.
193
+ # @param [Fixnum] num the number of the set (base 1)
194
+ # @return [PlayerAction|nil] the user's action for console window
195
+ # interface, or nil for GUI.
196
+ def load_level num
197
+ @level_number = num
198
+ start_level
199
+ end
200
+
201
+ # Restart from level 1.
202
+ # @return [PlayerAction|nil] the user's action for console window
203
+ # interface, or nil for GUI.
204
+ def restart_set
205
+ @level_number = 1
73
206
  start_level
74
207
  end
75
208
 
76
209
  # Load a new set of levels and start its first level.
77
210
  # @param [String] setname the name of the set (with .xsb extension)
78
- # @return [Object] the user's {action}[Console#get_action]
211
+ # @return [PlayerAction|nil] the user's action for console window
212
+ # interface, or nil for GUI.
79
213
  def load_a_new_set setname
80
- title = nil
81
- error = nil
82
214
  begin
83
215
  @level_loader = LevelLoader.new setname
84
216
  @level_number = 1
@@ -87,32 +219,20 @@ module RSokoban
87
219
  rescue NoFileError
88
220
  error = "Error, no such file : #{setname}"
89
221
  end
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)
222
+ if @ui
223
+ @ui.get_action(:type=>:start, :map=>@level.map, :title=>title, :error=>error, :set=>@level_loader.title,
224
+ :number=>@level_number, :total=>@level_loader.size)
225
+ end
92
226
  end
93
227
 
94
- # Load a level from the current set.
95
- # @param [Fixnum] num the number of the set (base 1)
96
- # @return [Object] the user's {action}[Console#get_action]
97
- def load_level num
98
- @level_number = num
99
- start_level
100
- end
228
+ private
101
229
 
102
- # Start a level, according to some instance members.
230
+ # Restart the current level
103
231
  # @return [Object] the user's {action}[Console#get_action]
104
- def start_level
105
- begin
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)
109
- rescue LevelNumberTooHighError
110
- @ui.get_action(:type=>:end_of_set, :map=>Map.new)
111
- end
232
+ def try_again
233
+ start_level
112
234
  end
113
235
 
114
236
  end
115
237
 
116
-
117
-
118
238
  end