RSokoban 0.73 → 0.74

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