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