comparison grid.rb @ 0:1eef88068f9f tip

initial commit of maze game source
author ferencd
date Sun, 15 Sep 2019 11:46:47 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1eef88068f9f
1 #---
2 # Excerpted from "Mazes for Programmers",
3 # published by The Pragmatic Bookshelf.
4 # Copyrights apply to this code. It may not be used to create training material,
5 # courses, books, articles, and the like. Contact us if you are in doubt.
6 # We make no guarantees that this code is fit for any purpose.
7 # Visit http://www.pragmaticprogrammer.com/titles/jbmaze for more book information.
8 #---
9 require 'cell'
10
11 # Corner constants, to know where to put a torch
12 TOP_LEFT = 1
13 TOP_RIGHT = 2
14 BOTTOM_LEFT = 4
15 BOTTOM_RIGHT = 8
16
17 class Room
18 attr_reader :x1, :y1, :x2, :y2
19
20 def initialize(px1,py1,px2,py2)
21 @x1 = px1
22 @x2 = px2
23 @y1 = py1
24 @y2 = py2
25 end
26
27 def in(x, y)
28 return x > @x1-1 && x < @x2+1 && y > @y1-1 && y < @y2+1
29 end
30 end
31
32
33 class Grid
34 attr_reader :rows, :columns, :dead_ends
35
36 def initialize(rows, columns)
37 @rows = rows
38 @columns = columns
39 @grid = prepare_grid
40 @rooms = []
41
42 # Where food will go
43 @dead_ends = {}
44 @dead_ends[1] = []
45 @dead_ends[2] = []
46 @dead_ends[4] = []
47 @dead_ends[8] = []
48
49 configure_cells
50
51 end
52
53 def prepare_grid
54 Array.new(rows) do |row|
55 Array.new(columns) do |column|
56 Cell.new(row, column)
57 end
58 end
59 end
60
61 def configure_cells
62 each_cell do |cell|
63 row, col = cell.row, cell.column
64
65 cell.north = self[row - 1, col]
66 cell.south = self[row + 1, col]
67 cell.west = self[row, col - 1]
68 cell.east = self[row, col + 1]
69 end
70 end
71
72 #
73 # Creates a room in the grid
74 #
75 def create_room(x1,y1, x2,y2)
76 # Firstly: empty the room
77 puts "x1:#{x1}, y1, x2, y2"
78 (x1..x2).each do |col|
79 (y1..y2).each do |row|
80 @grid[row][col].link(@grid[row][col+1]) # -> Next to right
81 @grid[row][col].link(@grid[row+1][col]) # -> Next to down
82 end
83 end
84 # then create the surrounding walls
85 (x1 + 1..x2-1).each do |col|
86 @grid[y1+1][col].unlink(@grid[y1][col])
87 @grid[y2-1][col].unlink(@grid[y2][col])
88 end
89
90 (y1+1..y2-1).each do |row|
91 @grid[row][x1+1].unlink(@grid[row][x1])
92 @grid[row][x2-1].unlink(@grid[row][x2])
93 end
94
95 r = Room.new(x1,y1,x2,y2)
96 @rooms << r
97
98 end
99
100 def [](row, column)
101 return nil unless row.between?(0, @rows - 1)
102 return nil unless column.between?(0, @grid[row].count - 1)
103 @grid[row][column]
104 end
105
106 def random_cell
107 row = rand(@rows)
108 column = rand(@grid[row].count)
109
110 self[row, column]
111 end
112
113 def size
114 @rows * @columns
115 end
116
117 def each_row
118 @grid.each do |row|
119 yield row
120 end
121 end
122
123 def each_cell
124 each_row do |row|
125 row.each do |cell|
126 yield cell if cell
127 end
128 end
129 end
130
131 # By default, we'll simply display a cell as a blank space. We'll use this
132 # to add other ways of displaying cells.
133 def contents_of(cell)
134 ' '
135 end
136 #
137 def background_color_for(cell)
138 nil
139 end
140
141 def to_s
142 maze_js_array
143 end
144
145 #
146 # Returns the number for the given cell
147 #
148 def number_for_cell(cell)
149 number = 0
150 number |= EAST if cell.linked?(cell.east)
151 number |= NORTH if cell.linked?(cell.north)
152 number |= SOUTH if cell.linked?(cell.south)
153 number |= WEST if cell.linked?(cell.west)
154 number
155 end
156
157 #
158 # Returns the maze as a javascript array. This also will initialize all the dead ends
159 # where we will put food or jevelry items
160 #
161 def maze_js_array
162 output = "var maze = [\n"
163 dist_strings = []
164
165 each_row do |row|
166 # num array, the number for the row
167 numarray = []
168 row_string = ''
169
170 row.each do |cell|
171 cell = Cell.new(-1, -1) unless cell
172 cell_nr = number_for_cell(cell)
173 numarray << cell_nr
174
175 # Add this cell as a dead end destination
176 @dead_ends[cell_nr] << cell if [1,2,4,8].include? cell_nr
177
178 end
179
180 row_string << '[' << numarray.join(', ') << ']'
181 dist_strings << row_string
182
183 end
184 output << dist_strings.join( ",\n" )
185 output << "];\n"
186 output
187 end
188
189 def direction(d)
190 return 'N' if d == NORTH
191 return 'W' if d == WEST
192 return 'E' if d == EAST
193 return 'S' if d == SOUTH
194 return '?'
195 end
196
197 #
198 # Returns the distances as a js array
199 #
200 def distances_js_array_path
201 output = ''
202 dist_strings = []
203
204 each_row do |row|
205 row.each do |cell|
206 cell = Cell.new(-1, -1) unless cell
207 conts = contents_of(cell).to_s
208 if conts.strip.length > 0
209 dist_strings << {:idx => conts.to_i, :row => cell.row, :col => cell.column}
210 end
211 end
212 end
213 sorted = dist_strings.sort_by {|el| el[:idx]}
214
215 # The directions this will go
216 walk_string = ''
217 # Now create a direction array from the elements
218 current_cell = self[sorted[0][:row], sorted[0][:col] ]
219 sorted.each_with_index do |cell, index|
220 if index > 0
221 # Find the direction from curent_cell to cell
222 the_cell = self[cell[:row], cell[:col] ]
223 dest = direction(current_cell.direction(the_cell))
224 walk_string << dest
225 current_cell = the_cell
226 end
227 end
228 output << "#{walk_string}"
229 output
230 end
231
232 #
233 # Returns the distances as a js array
234 #
235 def distances_js_array
236 output = "var distances = [\n"
237 dist_strings = []
238
239 each_row do |row|
240
241 row_string = ''
242 numarray = []
243
244 row.each do |cell|
245 cell = Cell.new(-1, -1) unless cell
246 conts = contents_of(cell).to_s
247 if conts.strip.length > 0
248 numarray << conts
249 else
250 numarray << '-1'
251 end
252 end
253 row_string << '[' << numarray.join(', ') << ']'
254 dist_strings << row_string
255 end
256 output << dist_strings.join(",\n")
257 output << "];\n"
258 output
259 end
260
261 def maze_serialized_text
262 output = []
263 each_row do |row|
264 numarray = []
265 row.each do |cell|
266 cell = Cell.new(-1, -1) unless cell
267 numarray << number_for_cell(cell)
268 end
269 output << numarray.join(',')
270 end
271 output.join(':')
272 end
273
274 def deadends
275 list = []
276
277 each_cell do |cell|
278 list << cell if cell.links.count == 1
279 end
280
281 list
282 end
283
284 def braid(p=1.0)
285 deadends.shuffle.each do |cell|
286 next if cell.links.count != 1 || rand > p
287
288 neighbors = cell.neighbors.reject { |n| cell.linked?(n) }
289 best = neighbors.select { |n| n.links.count == 1 }
290 best = neighbors if best.empty?
291
292 neighbor = best.sample
293 cell.link(neighbor)
294 end
295 end
296
297
298 #
299 # Tells us if we can make a room at the given coordinates
300 #
301 def can_make_room(x1, y1, x2, y2)
302
303 @rooms.each do |room|
304 return FALSE if room.in(x1, y1)
305 return FALSE if room.in(x2, y2)
306 return FALSE if room.in(x1, y2)
307 return FALSE if room.in(x2, y1)
308 end
309
310 TRUE
311 end
312
313 #
314 # Creates a list of locations where torches can be found, mostly in corners at unfinshed walls
315 #
316 def identify_torch_locations
317 numarray = []
318 j = 0
319 each_row do |row|
320 i = 0
321 row.each do |cell|
322 number = 0
323 number |= TOP_RIGHT if cell.east && cell.north && cell.linked?(cell.east) && cell.linked?(cell.north) && !cell.east.linked?(cell.east.north) && !cell.north.linked?(cell.north.east)
324 number |= TOP_LEFT if cell.west && cell.north && cell.linked?(cell.west) && cell.linked?(cell.north) && !cell.west.linked?(cell.west.north) && !cell.north.linked?(cell.north.west)
325 number |= BOTTOM_RIGHT if cell.east && cell.south && cell.linked?(cell.east) && cell.linked?(cell.south) && !cell.east.linked?(cell.east.south) && !cell.south.linked?(cell.south.east)
326 number |= BOTTOM_LEFT if cell.west && cell.south && cell.linked?(cell.west) && cell.linked?(cell.south) && !cell.west.linked?(cell.west.south) && !cell.south.linked?(cell.south.west)
327 if number != 0
328 torch_data = { :i => i, :j => j, :number => number}
329 numarray << torch_data
330 end
331 i += 1
332 end
333 j += 1
334 end
335 numarray
336 end
337
338 end