RSokoban 0.74 → 0.76

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.
Files changed (111) hide show
  1. data/NEWS +33 -1
  2. data/README.rdoc +21 -14
  3. data/TODO +6 -12
  4. data/VERSION +1 -1
  5. data/bin/rsokoban +40 -3
  6. data/data/big.xsb +35 -0
  7. data/lib/rsokoban/config.rb +34 -0
  8. data/lib/rsokoban/crate.rb +4 -5
  9. data/lib/rsokoban/exception.rb +2 -0
  10. data/lib/rsokoban/game/game.rb +153 -0
  11. data/lib/rsokoban/game/game_curses.rb +11 -0
  12. data/lib/rsokoban/game/game_factory.rb +23 -0
  13. data/lib/rsokoban/game/game_gui.rb +27 -0
  14. data/lib/rsokoban/game/game_portable.rb +10 -0
  15. data/lib/rsokoban/game/game_tk.rb +11 -0
  16. data/lib/rsokoban/game/game_ui.rb +81 -0
  17. data/lib/rsokoban/game.rb +7 -238
  18. data/lib/rsokoban/install.rb +22 -0
  19. data/lib/rsokoban/layered_map.rb +139 -0
  20. data/lib/rsokoban/level.rb +112 -166
  21. data/lib/rsokoban/level_set.rb +4 -4
  22. data/lib/rsokoban/man.rb +3 -4
  23. data/lib/rsokoban/map.rb +91 -15
  24. data/lib/rsokoban/move_result.rb +2 -2
  25. data/lib/rsokoban/option.rb +4 -4
  26. data/lib/rsokoban/position.rb +1 -1
  27. data/lib/rsokoban/raw_level.rb +11 -2
  28. data/lib/rsokoban/record.rb +51 -0
  29. data/lib/rsokoban/set_loader.rb +108 -0
  30. data/lib/rsokoban/ui/base_ui.rb +1 -0
  31. data/lib/rsokoban/ui/console.rb +56 -25
  32. data/lib/rsokoban/ui/curses_console.rb +59 -33
  33. data/lib/rsokoban/ui/player_action.rb +13 -7
  34. data/lib/rsokoban/ui/skin.rb +105 -0
  35. data/lib/rsokoban/ui/tk_dialogs.rb +104 -18
  36. data/lib/rsokoban/ui/tk_ui.rb +410 -233
  37. data/lib/rsokoban/ui.rb +1 -0
  38. data/lib/rsokoban.rb +14 -2
  39. data/skins/AntiqueDesk/crate.bmp +0 -0
  40. data/skins/AntiqueDesk/crate_store.bmp +0 -0
  41. data/skins/AntiqueDesk/floor.bmp +0 -0
  42. data/skins/AntiqueDesk/man_down.bmp +0 -0
  43. data/skins/AntiqueDesk/man_left.bmp +0 -0
  44. data/skins/AntiqueDesk/man_right.bmp +0 -0
  45. data/skins/AntiqueDesk/man_store_down.bmp +0 -0
  46. data/skins/AntiqueDesk/man_store_left.bmp +0 -0
  47. data/skins/AntiqueDesk/man_store_right.bmp +0 -0
  48. data/skins/AntiqueDesk/man_store_up.bmp +0 -0
  49. data/skins/AntiqueDesk/man_up.bmp +0 -0
  50. data/skins/AntiqueDesk/outside.bmp +0 -0
  51. data/skins/AntiqueDesk/skin.conf +3 -0
  52. data/skins/AntiqueDesk/store.bmp +0 -0
  53. data/skins/AntiqueDesk/wall.bmp +0 -0
  54. data/skins/BlueGranite/outside.bmp +0 -0
  55. data/skins/BlueGranite/skin.conf +3 -0
  56. data/skins/HeavyMetal/crate.bmp +0 -0
  57. data/skins/HeavyMetal/crate_store.bmp +0 -0
  58. data/skins/HeavyMetal/floor.bmp +0 -0
  59. data/skins/HeavyMetal/man.bmp +0 -0
  60. data/skins/HeavyMetal/man_store.bmp +0 -0
  61. data/skins/HeavyMetal/outside.bmp +0 -0
  62. data/skins/HeavyMetal/skin.conf +3 -0
  63. data/skins/HeavyMetal/store.bmp +0 -0
  64. data/skins/HeavyMetal/wall.bmp +0 -0
  65. data/skins/HeavyMetal/wall_d.bmp +0 -0
  66. data/skins/HeavyMetal/wall_dl.bmp +0 -0
  67. data/skins/HeavyMetal/wall_dlr.bmp +0 -0
  68. data/skins/HeavyMetal/wall_dr.bmp +0 -0
  69. data/skins/HeavyMetal/wall_l.bmp +0 -0
  70. data/skins/HeavyMetal/wall_lr.bmp +0 -0
  71. data/skins/HeavyMetal/wall_r.bmp +0 -0
  72. data/skins/HeavyMetal/wall_u.bmp +0 -0
  73. data/skins/HeavyMetal/wall_ud.bmp +0 -0
  74. data/skins/HeavyMetal/wall_udl.bmp +0 -0
  75. data/skins/HeavyMetal/wall_udlr.bmp +0 -0
  76. data/skins/HeavyMetal/wall_udr.bmp +0 -0
  77. data/skins/HeavyMetal/wall_ul.bmp +0 -0
  78. data/skins/HeavyMetal/wall_ulr.bmp +0 -0
  79. data/skins/HeavyMetal/wall_ur.bmp +0 -0
  80. data/test/record/original.yaml +2 -0
  81. data/test/tc_game.rb +24 -15
  82. data/test/tc_game_factory.rb +40 -0
  83. data/test/tc_game_gui.rb +26 -0
  84. data/test/tc_game_ui.rb +153 -0
  85. data/test/tc_install.rb +12 -0
  86. data/test/tc_layered_map.rb +105 -0
  87. data/test/tc_level.rb +109 -107
  88. data/test/tc_level_set.rb +4 -4
  89. data/test/tc_map.rb +46 -10
  90. data/test/tc_record.rb +100 -0
  91. data/test/{tc_level_loader.rb → tc_set_loader.rb} +25 -24
  92. data/test/test.rb +9 -2
  93. data/test/ui/tc_skin.rb +71 -0
  94. metadata +89 -26
  95. data/lib/rsokoban/level_loader.rb +0 -81
  96. data/lib/rsokoban/ui/tk_box.rb +0 -21
  97. data/skins/default/outside.bmp +0 -0
  98. data/skins/default/readme +0 -1
  99. /data/skins/{default → BlueGranite}/crate.bmp +0 -0
  100. /data/skins/{default → BlueGranite}/crate_store.bmp +0 -0
  101. /data/skins/{default → BlueGranite}/floor.bmp +0 -0
  102. /data/skins/{default → BlueGranite}/man_down.bmp +0 -0
  103. /data/skins/{default → BlueGranite}/man_left.bmp +0 -0
  104. /data/skins/{default → BlueGranite}/man_right.bmp +0 -0
  105. /data/skins/{default → BlueGranite}/man_store_down.bmp +0 -0
  106. /data/skins/{default → BlueGranite}/man_store_left.bmp +0 -0
  107. /data/skins/{default → BlueGranite}/man_store_right.bmp +0 -0
  108. /data/skins/{default → BlueGranite}/man_store_up.bmp +0 -0
  109. /data/skins/{default → BlueGranite}/man_up.bmp +0 -0
  110. /data/skins/{default → BlueGranite}/store.bmp +0 -0
  111. /data/skins/{default → BlueGranite}/wall.bmp +0 -0
data/lib/rsokoban/game.rb CHANGED
@@ -1,238 +1,7 @@
1
- module RSokoban
2
-
3
- # I manage the game.
4
- class Game
5
- # @return [Fixnum]
6
- # @since 0.74
7
- attr_reader :level_number
8
-
9
- # Construct a new game that you can later run.
10
- # @param [:curses|:portable|:tk] ui_as_symbol the user interface for the game
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
15
- @level_number = 1
16
- case ui_as_symbol
17
- when :curses
18
- require "rsokoban/ui/curses_console"
19
- @ui = UI::CursesConsole.new
20
- when :portable
21
- require "rsokoban/ui/console"
22
- @ui = UI::Console.new
23
- when :tk
24
- require "rsokoban/ui/tk_ui"
25
- require "rsokoban/ui/tk_box"
26
- require "rsokoban/ui/tk_dialogs"
27
- @gui = UI::TkUI.new self
28
- end
29
- end
30
-
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.
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
47
- player_action = start_level
48
- loop do
49
- if player_action.level_number?
50
- player_action = load_level player_action.action
51
- next
52
- elsif player_action.set_name?
53
- player_action = load_a_new_set player_action.action
54
- next
55
- elsif player_action.quit?
56
- break
57
- elsif player_action.next?
58
- player_action = next_level
59
- next
60
- elsif player_action.retry?
61
- player_action = try_again
62
- next
63
- elsif player_action.move?
64
- result = @level.move(player_action.action)
65
- elsif player_action.undo?
66
- result = @level.undo
67
- end
68
-
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])
73
- else
74
- player_action = @ui.get_action(:type=>:display, :map=>@level.map, :error=>result[:message])
75
- end
76
- end
77
- true
78
- end
79
-
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
183
-
184
- # Load and start the next level of the set
185
- # @return [PlayerAction|nil] the user's action for console window
186
- # interface, or nil for GUI.
187
- def next_level
188
- @level_number += 1
189
- start_level
190
- end
191
-
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
206
- start_level
207
- end
208
-
209
- # Load a new set of levels and start its first level.
210
- # @param [String] setname the name of the set (with .xsb extension)
211
- # @return [PlayerAction|nil] the user's action for console window
212
- # interface, or nil for GUI.
213
- def load_a_new_set setname
214
- begin
215
- @level_loader = LevelLoader.new setname
216
- @level_number = 1
217
- @level = @level_loader.level(@level_number)
218
- title = @level.title
219
- rescue NoFileError
220
- error = "Error, no such file : #{setname}"
221
- end
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
226
- end
227
-
228
- private
229
-
230
- # Restart the current level
231
- # @return [Object] the user's {action}[Console#get_action]
232
- def try_again
233
- start_level
234
- end
235
-
236
- end
237
-
238
- end
1
+ require "rsokoban/game/game"
2
+ require "rsokoban/game/game_ui"
3
+ require "rsokoban/game/game_gui"
4
+ require "rsokoban/game/game_factory"
5
+ require "rsokoban/game/game_portable"
6
+ require "rsokoban/game/game_curses"
7
+ require "rsokoban/game/game_tk"
@@ -0,0 +1,22 @@
1
+ require "fileutils"
2
+ require "yaml"
3
+
4
+ module RSokoban
5
+
6
+ class Install
7
+
8
+ def self.create_folder path
9
+ FileUtils.makedirs path
10
+ end
11
+
12
+ def self.create_config path
13
+ self.create_folder path
14
+ config = {'skin' => 'BlueGranite', 'set' => 'microban.xsb', 'level' => 1}
15
+ f = File.new(File.join(path, 'config'), "w")
16
+ f.write config.to_yaml
17
+ f.close
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,139 @@
1
+ module RSokoban
2
+
3
+ # I separate elements of the map from the floor/wall.
4
+ # @since 0.74.1
5
+ class LayeredMap
6
+ # @return [Array<String>] map with only walls and floor
7
+ attr_reader :floor
8
+
9
+ # @return [Man]
10
+ attr_reader :man
11
+
12
+ # @return [Array<Crate>]
13
+ attr_reader :crates
14
+
15
+ # @return [Array<Storage>]
16
+ attr_reader :storages
17
+
18
+ # @param [Map|Array<String>] map
19
+ def initialize map
20
+ raise ArgumentError unless [Map, Array].include?(map.class)
21
+ @map = map
22
+ @floor = nil
23
+ @map_array = []
24
+ @man = nil
25
+ @crates = []
26
+ @storages = []
27
+ init_floor
28
+ init_man
29
+ init_crates_and_storages
30
+ end
31
+
32
+ # Get an instant map of the game.
33
+ # @return [Array<String>] the map, after X turns of game.
34
+ def map_as_array
35
+ @map_array = init_floor
36
+ draw_crates
37
+ draw_storages
38
+ draw_man
39
+ @map_array
40
+ end
41
+
42
+ # Get the content of box x_coord, y_coord
43
+ # @param [Fixnum] x_coord x coordinate in the map
44
+ # @param [Fixnum] y_coord y coordinate in the map
45
+ # @return [' ' | '#' | '.' | 'o' | '*']
46
+ # @todo I think we can optimize this algo
47
+ # @todo should accept too only one argument: a Position (or an object inherited from Position)
48
+ def what_is_on x_coord, y_coord
49
+ return WALL if (@floor[y_coord][x_coord]).chr == WALL
50
+
51
+ position = Position.new(x_coord, y_coord)
52
+ if @storages.include?(position)
53
+ return STORAGE unless @crates.include?(position)
54
+ CRATE_ON_STORAGE
55
+ elsif @crates.include?(position)
56
+ CRATE
57
+ else
58
+ FLOOR
59
+ end
60
+
61
+ end
62
+
63
+ private
64
+
65
+ # Draw the crates for map output
66
+ def draw_crates
67
+ @crates.each {|crate| @map_array[crate.y][crate.x] = what_is_on(crate.x, crate.y) }
68
+ end
69
+
70
+ # Draw the storages location for map output
71
+ def draw_storages
72
+ @storages.each {|st| @map_array[st.y][st.x] = what_is_on(st.x, st.y) }
73
+ end
74
+
75
+ # Draw the man for map output
76
+ def draw_man
77
+ box = what_is_on @man.x, @man.y
78
+ put_man_in_map if box == FLOOR
79
+ put_man_on_storage_in_map if box == STORAGE
80
+ end
81
+
82
+ def put_man_in_map
83
+ @map_array[@man.y][@man.x] = MAN
84
+ end
85
+
86
+ def put_man_on_storage_in_map
87
+ @map_array[@man.y][@man.x] = MAN_ON_STORAGE
88
+ end
89
+
90
+ # Removes all storages locations, all crates and the man, leaving only walls and floor.
91
+ #
92
+ # @return [Array<String>] map with only walls and floor
93
+ def init_floor
94
+ @floor = []
95
+ @map.each {|row|
96
+ @floor.push row.tr("#{STORAGE}#{CRATE}#{MAN}#{CRATE_ON_STORAGE}", FLOOR)
97
+ }
98
+ @floor
99
+ end
100
+
101
+ # Find the man's position, at the begining of the level.
102
+ def init_man
103
+ x_coord = y_coord = 0
104
+ @map.each {|row|
105
+ if row.include?(MAN)
106
+ x_coord = row.index(MAN)
107
+ break
108
+ elsif row.include?(MAN_ON_STORAGE)
109
+ x_coord = row.index(MAN_ON_STORAGE)
110
+ break
111
+ end
112
+ y_coord += 1
113
+ }
114
+ @man = Man.new x_coord, y_coord
115
+ end
116
+
117
+ # Find position of crates and storages, at the begining of the level.
118
+ def init_crates_and_storages
119
+ y_coord = 0
120
+ @map.each do |line|
121
+ count = 0
122
+ line.each_char do |char|
123
+ if char == CRATE
124
+ @crates.push Crate.new(count, y_coord)
125
+ elsif char == STORAGE or char == MAN_ON_STORAGE
126
+ @storages.push Storage.new(count, y_coord)
127
+ elsif char == CRATE_ON_STORAGE
128
+ @crates.push Crate.new(count, y_coord)
129
+ @storages.push Storage.new(count, y_coord)
130
+ end
131
+ count += 1
132
+ end
133
+ y_coord += 1
134
+ end
135
+ end
136
+
137
+ end
138
+
139
+ end