Mercurial > maze-src
diff grid.rb @ 0:1eef88068f9f tip
initial commit of maze game source
| author | ferencd |
|---|---|
| date | Sun, 15 Sep 2019 11:46:47 +0200 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/grid.rb Sun Sep 15 11:46:47 2019 +0200 @@ -0,0 +1,338 @@ +#--- +# Excerpted from "Mazes for Programmers", +# published by The Pragmatic Bookshelf. +# Copyrights apply to this code. It may not be used to create training material, +# courses, books, articles, and the like. Contact us if you are in doubt. +# We make no guarantees that this code is fit for any purpose. +# Visit http://www.pragmaticprogrammer.com/titles/jbmaze for more book information. +#--- +require 'cell' + +# Corner constants, to know where to put a torch +TOP_LEFT = 1 +TOP_RIGHT = 2 +BOTTOM_LEFT = 4 +BOTTOM_RIGHT = 8 + +class Room + attr_reader :x1, :y1, :x2, :y2 + + def initialize(px1,py1,px2,py2) + @x1 = px1 + @x2 = px2 + @y1 = py1 + @y2 = py2 + end + + def in(x, y) + return x > @x1-1 && x < @x2+1 && y > @y1-1 && y < @y2+1 + end +end + + +class Grid + attr_reader :rows, :columns, :dead_ends + + def initialize(rows, columns) + @rows = rows + @columns = columns + @grid = prepare_grid + @rooms = [] + + # Where food will go + @dead_ends = {} + @dead_ends[1] = [] + @dead_ends[2] = [] + @dead_ends[4] = [] + @dead_ends[8] = [] + + configure_cells + + end + + def prepare_grid + Array.new(rows) do |row| + Array.new(columns) do |column| + Cell.new(row, column) + end + end + end + + def configure_cells + each_cell do |cell| + row, col = cell.row, cell.column + + cell.north = self[row - 1, col] + cell.south = self[row + 1, col] + cell.west = self[row, col - 1] + cell.east = self[row, col + 1] + end + end + + # + # Creates a room in the grid + # + def create_room(x1,y1, x2,y2) + # Firstly: empty the room + puts "x1:#{x1}, y1, x2, y2" + (x1..x2).each do |col| + (y1..y2).each do |row| + @grid[row][col].link(@grid[row][col+1]) # -> Next to right + @grid[row][col].link(@grid[row+1][col]) # -> Next to down + end + end + # then create the surrounding walls + (x1 + 1..x2-1).each do |col| + @grid[y1+1][col].unlink(@grid[y1][col]) + @grid[y2-1][col].unlink(@grid[y2][col]) + end + + (y1+1..y2-1).each do |row| + @grid[row][x1+1].unlink(@grid[row][x1]) + @grid[row][x2-1].unlink(@grid[row][x2]) + end + + r = Room.new(x1,y1,x2,y2) + @rooms << r + + end + + def [](row, column) + return nil unless row.between?(0, @rows - 1) + return nil unless column.between?(0, @grid[row].count - 1) + @grid[row][column] + end + + def random_cell + row = rand(@rows) + column = rand(@grid[row].count) + + self[row, column] + end + + def size + @rows * @columns + end + + def each_row + @grid.each do |row| + yield row + end + end + + def each_cell + each_row do |row| + row.each do |cell| + yield cell if cell + end + end + end + + # By default, we'll simply display a cell as a blank space. We'll use this + # to add other ways of displaying cells. + def contents_of(cell) + ' ' + end +# + def background_color_for(cell) + nil + end + + def to_s + maze_js_array + end + + # + # Returns the number for the given cell + # + def number_for_cell(cell) + number = 0 + number |= EAST if cell.linked?(cell.east) + number |= NORTH if cell.linked?(cell.north) + number |= SOUTH if cell.linked?(cell.south) + number |= WEST if cell.linked?(cell.west) + number + end + + # + # Returns the maze as a javascript array. This also will initialize all the dead ends + # where we will put food or jevelry items + # + def maze_js_array + output = "var maze = [\n" + dist_strings = [] + + each_row do |row| + # num array, the number for the row + numarray = [] + row_string = '' + + row.each do |cell| + cell = Cell.new(-1, -1) unless cell + cell_nr = number_for_cell(cell) + numarray << cell_nr + + # Add this cell as a dead end destination + @dead_ends[cell_nr] << cell if [1,2,4,8].include? cell_nr + + end + + row_string << '[' << numarray.join(', ') << ']' + dist_strings << row_string + + end + output << dist_strings.join( ",\n" ) + output << "];\n" + output + end + + def direction(d) + return 'N' if d == NORTH + return 'W' if d == WEST + return 'E' if d == EAST + return 'S' if d == SOUTH + return '?' + end + + # + # Returns the distances as a js array + # + def distances_js_array_path + output = '' + dist_strings = [] + + each_row do |row| + row.each do |cell| + cell = Cell.new(-1, -1) unless cell + conts = contents_of(cell).to_s + if conts.strip.length > 0 + dist_strings << {:idx => conts.to_i, :row => cell.row, :col => cell.column} + end + end + end + sorted = dist_strings.sort_by {|el| el[:idx]} + + # The directions this will go + walk_string = '' + # Now create a direction array from the elements + current_cell = self[sorted[0][:row], sorted[0][:col] ] + sorted.each_with_index do |cell, index| + if index > 0 + # Find the direction from curent_cell to cell + the_cell = self[cell[:row], cell[:col] ] + dest = direction(current_cell.direction(the_cell)) + walk_string << dest + current_cell = the_cell + end + end + output << "#{walk_string}" + output + end + + # + # Returns the distances as a js array + # + def distances_js_array + output = "var distances = [\n" + dist_strings = [] + + each_row do |row| + + row_string = '' + numarray = [] + + row.each do |cell| + cell = Cell.new(-1, -1) unless cell + conts = contents_of(cell).to_s + if conts.strip.length > 0 + numarray << conts + else + numarray << '-1' + end + end + row_string << '[' << numarray.join(', ') << ']' + dist_strings << row_string + end + output << dist_strings.join(",\n") + output << "];\n" + output + end + + def maze_serialized_text + output = [] + each_row do |row| + numarray = [] + row.each do |cell| + cell = Cell.new(-1, -1) unless cell + numarray << number_for_cell(cell) + end + output << numarray.join(',') + end + output.join(':') + end + + def deadends + list = [] + + each_cell do |cell| + list << cell if cell.links.count == 1 + end + + list + end + + def braid(p=1.0) + deadends.shuffle.each do |cell| + next if cell.links.count != 1 || rand > p + + neighbors = cell.neighbors.reject { |n| cell.linked?(n) } + best = neighbors.select { |n| n.links.count == 1 } + best = neighbors if best.empty? + + neighbor = best.sample + cell.link(neighbor) + end + end + + + # + # Tells us if we can make a room at the given coordinates + # + def can_make_room(x1, y1, x2, y2) + + @rooms.each do |room| + return FALSE if room.in(x1, y1) + return FALSE if room.in(x2, y2) + return FALSE if room.in(x1, y2) + return FALSE if room.in(x2, y1) + end + + TRUE + end + + # + # Creates a list of locations where torches can be found, mostly in corners at unfinshed walls + # + def identify_torch_locations + numarray = [] + j = 0 + each_row do |row| + i = 0 + row.each do |cell| + number = 0 + 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) + 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) + 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) + 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) + if number != 0 + torch_data = { :i => i, :j => j, :number => number} + numarray << torch_data + end + i += 1 + end + j += 1 + end + numarray + end + +end
