从节点对象中提取父节点

时间:2020-05-26 19:10:16

标签: ruby

我正在尝试使用红宝石中的深度优先搜索来生成简单的迷宫求解器,该算法可以正常工作并找到解决方案,我正在努力解决的问题是打印解决方案,更具体地说是从父对象属性访问父节点

我得到的路径输出是:

S  

  G
state: #<MazeLocation:0x000055999847fe50>, parent: [#<Node:0x000055999847ff68 @state=#<MazeLocation:0x0000559998490138 @row=1, @column=2>, @parent=[#<Node:0x00005599984902a0 @state=#<MazeLocation:0x00005599984904a8 @row=0, @column=2>, @parent=[#<Node:0x0000559998490610 @state=#<MazeLocation:0x0000559998490778 @row=0, @column=1>, @parent=[#<Node:0x0000559998490980 @state=#<MazeLocation:0x0000559998493ef0 @row=0, @column=0>, @parent=[]>]>]>]>]
Solution found

我要提取的是父级中的每个MazeLocation以追溯解决方案路径。

要重现的代码如下(抱歉,很长): 迷宫相关课程

require_relative 'search.rb'

module Cells
  EMPTY = :" "
  BLOCKED = :X
  START = :S
  GOAL = :G
  PATH = :*
end

class MazeLocation
  attr_reader :row
  attr_reader :column
  def initialize(row, column)
    @row = row
    @column = column
  end

  def ==(x)
    @row == x.row && @column == x.column
  end

  def eql?(x)
    @row == x.row && @column == x.column
  end

  # needed for set include? method
  def hash
    @row.hash
  end

end

class Maze
  attr_reader :start
  def initialize(start, goal, rows = 10, columns = 10, sparseness = 0.1)
    @rows = rows
    @columns = columns
    @start = start
    @goal = goal
    @grid = []

    @rows.times do |row|
      @grid << Array.new(@columns)
    end

    random_fill(sparseness)

    @grid[start.row][start.column] = Cells::START
    @grid[goal.row][goal.column] = Cells::GOAL
  end

  def to_s
    output = ""
    @grid.each do |row|
      output += "" + row.join + "\n"
    end
    output
  end

  def mark(path)
    path.each do |maze_location|
      @grid[maze_location.row][maze_location.column] = Cells::PATH
    end
    @grid[@start.row][@start.column] = Cells::START
    @grid[@goal.row][@goal.column] = Cells::GOAL
  end

  def goal_test(current_location)
    current_location == @goal
  end

  def successors(current_location)
    possible_moves = []
    if current_location.row + 1 < @rows &&
      @grid[current_location.row + 1][current_location.column] != Cells::BLOCKED
      possible_moves << MazeLocation.new(current_location.row + 1, current_location.column)
    end
    if current_location.row - 1 >= 0 &&
      @grid[current_location.row - 1][current_location.column] != Cells::BLOCKED
      possible_moves << MazeLocation.new(current_location.row - 1, current_location.column)
    end
    if current_location.column + 1 < @columns &&
      @grid[current_location.row][current_location.column + 1] != Cells::BLOCKED
      possible_moves << MazeLocation.new(current_location.row, current_location.column + 1)
    end
    if current_location.column - 1 >= 0 &&
      @grid[current_location.row][current_location.column - 1] != Cells::BLOCKED
      possible_moves << MazeLocation.new(current_location.row, current_location.column - 1)
    end
    possible_moves
  end

  private

  def random_fill(sparseness)
    @grid.each_with_index do |column, row|
      column.each_with_index do |cell, element|
        if rand() < sparseness
          @grid[row][element] = cell = Cells::BLOCKED
        else
          @grid[row][element] = cell = Cells::EMPTY
        end
      end
    end
  end
end

start = MazeLocation.new(0,0)
goal = MazeLocation.new(2,2)

maze = Maze.new(start, goal,3,3)
puts maze

solution = DSF.new(maze, start)
unless solution.solution_found
  puts "No solution found using depth-first search!"
else
  puts "Solution found"
  maze.mark(solution.solution_path)
  puts maze
end

搜索相关类别:

require 'set'

class Node
    attr_reader :state
    attr_reader :parent
    attr_reader :cost
    attr_reader :heuristic
    def initialize(state, *parent)
        @state = state
        @parent = parent
    end

    def to_s
        "state: #{@state}, parent: #{@parent}"
    end
end

class DSF
    attr_reader :solution_found
    attr_reader :solution_path
    def initialize(maze, inital)
        # the object being searched
        @maze = maze
        # frontier to where we've yet to go
        @frontier = []
        @frontier.push(Node.new(inital))
        @solution_found = false
        @solution_path = []
        # explored is where we have been
        @explored = Set.new
        search
    end

    def node_to_path(node)
        puts node
        path = []
        path << node.state
        path << node.parent
        while node.parent.empty?
            node = node.parent
            path.append(node.state)
        end
        path.reverse
    end

    private

    def search
        while not @frontier.empty?
            current_node = @frontier.pop
            current_state = current_node.state
            #goal check
            if @maze.goal_test(current_state)
                @solution_found = true
                @solution_path = node_to_path(current_node)
            end
            @maze.successors(current_state).each do |child|
                if @explored.include?(child) 
                    next
                end
                @explored << child
                @frontier.push(Node.new(child, current_node))
            end
        end
    end
end

利益的功能就是这样:

DFS#node_to_path

    def node_to_path(node)
        puts node
        path = []
        path << node.state
        path << node.parent
        while node.parent.empty?
            node = node.parent
            path.append(node.state)
        end
        path.reverse
    end

我确实考虑过将父节点作为object_id传递,但这似乎不正确。

1 个答案:

答案 0 :(得分:0)

我弄清楚了并想与我分享-问题是我认为方法参数中的*运算符意味着它是可选的(我想这是某种方式),但我没有意识到它会变成将参数捕获到数组中。

我进行了以下修改:

到班级节点:

class Node
    attr_reader :state
    attr_reader :parent
    def initialize(state, parent = nil)
        @state = state
        @parent = parent
    end

    def to_s
        "state: #{@state}, parent: #{@parent}"
    end
end

到DFS#node_to_path

    def node_to_path(node)
        path = []
        path << node.state
        while not node.parent.nil?
            node = node.parent
            path.append(node.state)
        end
        path.reverse
    end

现在可以正常工作,并产生一个简单的深度优先搜索迷宫求解算法。