RSokoban 0.74 → 0.76

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