一个非常具体的康威生命游戏(Ruby初学者)

时间:2014-01-26 17:46:46

标签: ruby logic conways-game-of-life

寻找有关此明显逻辑错误的反馈,而不是优化。我一直在最终游戏消息上得到奇怪的滴答计数(例如:1滴答成11个滴答)

我在运行代码时发现的最大错误是在第二个刻度线上,出现了大量的活细胞。我太新了,不明白为什么,但似乎@alive_cells在每次检查后都没有重置为0。

这是我的整个代码,它很大,但它应该是任何有经验的人的孩子的游戏。

class CellGame

def initialize

  puts "How big do you want this game?"
  @size = gets.chomp.to_i

  @cell_grid = Array.new(@size) { Array.new(@size) }
  @grid_storage = Array.new(@size) { Array.new(@size) }

  @tick_count = 0

  fill_grid_with_random_cells
end

def fill_grid_with_random_cells
  @cell_grid.each do |row|
    row.map! do |cell|
      roll = rand(10)
        if roll > 9
          "•"
        else
          " "
        end
    end
  end

  check_cells_for_future_state
end

def check_for_any_alive_cells

  @cell_grid.each do |row|
    if row.include?("•")
      check_cells_for_future_state
    break
    else
      end_game_print_result
    end
  end
end


def check_cells_for_future_state

  @cell_grid.each_with_index do |row, row_index|
    row.each_with_index do |cell, cell_index|
      @live_neighbors = 0

      add_row_shift = (row_index + 1)
      if add_row_shift == @size
        add_row_shift = 0
      end

      add_cell_shift = (cell_index + 1)
      if add_cell_shift == @size
        add_cell_shift = 0
      end

      def does_this_include_alive(cell)
        if cell.include?("•")
          @live_neighbors +=1
        end
      end

      top_left_cell = @cell_grid[(row_index - 1)][(cell_index - 1)] 
        does_this_include_alive(top_left_cell)

      top_cell = @cell_grid[(row_index - 1)][(cell_index)]
        does_this_include_alive(top_cell)

      top_right_cell = @cell_grid[(row_index - 1)][(add_cell_shift)]
         does_this_include_alive(top_right_cell)

      right_cell = @cell_grid[(row_index)][(add_cell_shift)]
        does_this_include_alive(right_cell)

      bottom_right_cell = @cell_grid[(add_row_shift)][(add_cell_shift)]
        does_this_include_alive(bottom_right_cell)

      bottom_cell = @cell_grid[(add_row_shift)][(cell_index)]
        does_this_include_alive(bottom_cell)

      bottom_left_cell = @cell_grid[(add_row_shift)][(cell_index - 1)] 
        does_this_include_alive(bottom_left_cell)

      left_cell = @cell_grid[(row_index)][(cell_index - 1)] 
        does_this_include_alive(left_cell)


      if @live_neighbors == 2 || @live_neighbors == 3
        @grid_storage[row_index][cell_index] = "•"
      else
        @grid_storage[row_index][cell_index] = " "
      end

    end
  end

  update_cell_grid
end

def update_cell_grid
  @cell_grid = @grid_storage

  print_cell_grid_and_counter
end


def print_cell_grid_and_counter

  system"clear"
  @cell_grid.each do |row|
    row.each do |cell|
      print cell + " "
    end
    print "\n"  
  end

  @tick_count += 1
  print "\n"
  print "Days passed: #{@tick_count}"
  sleep(0.25)
  check_for_any_alive_cells
end


def end_game_print_result
  print "#{@tick_count} ticks were played, end of game."
  exit
end


end

2 个答案:

答案 0 :(得分:2)

我无法看到代码出错的地方。它确实有一个递归调用,很容易导致奇怪的行为。以下是我提出的建议:

class CellGame
  def initialize(size)
    @size = size; @archive = []
    @grid = Array.new(size) { Array.new(size) { rand(3).zero? } }
  end

  def lives_on?(row, col)
    neighborhood = (-1..1).map { |r| (-1..1).map { |c| @grid[row + r] && @grid[row + r][col + c] } }
    its_alive = neighborhood[1].delete_at(1)
    neighbors = neighborhood.flatten.count(true)
    neighbors == 3 || neighbors == 2 && its_alive
  end

  def next_gen
    (0...@size).map { |row| (0...@size).map { |col| lives_on?(row, col) } }
  end

  def play
    tick = 0; incr = 1
    loop do
      @archive.include?(@grid) ? incr = 0 : @archive << @grid
      sleep(0.5); system "clear"; @grid = next_gen
      puts "tick - #{tick += incr}"
      puts @grid.map { |row| row.map { |cell| cell ? '*' : ' ' }.inspect }
    end
  end
end

cg = CellGame.new 10
cg.play

滴答计数停止但程序在最后通过振荡器继续运行。

答案 1 :(得分:0)

我想重温这一点并自信地说我已经弄明白了!这是我的新解决方案 - 仍然是超级初学者专注。希望它可以帮助别人。

class Game

  # Uses constants for values that won't change
  LIVE = ""
  DEAD = " "
  WIDTH = 68
  HEIGHT = 34

  def initialize
    # Sets our grid to a new empty grid (set by method below)
    @grid = empty_grid

    # Randomly fills our grid with live cells
    @grid.each do |row|
      # Map will construct our new array, we use map! to edit the @grid
      row.map! do |cell|
        if rand(10) == 1
          LIVE # Place a live cell
        else
          DEAD # Place a dead cell
        end
      end
    end

    # Single line implimentation
    # @grid.each {|row|row.map! {|cell|rand(10) == 1 ? LIVE : DEAD}}

    loop_cells #start the cycle
  end

  def empty_grid
    Array.new(HEIGHT) do
      # Creates an array with HEIGHT number of empty arrays
      Array.new(WIDTH) do
        # Fills each array with a dead cell WIDTH number of times
        DEAD
      end
    end

    # Single line implimentation
    # Array.new(HEIGHT){ Array.new(WIDTH) { DEAD } }
  end

  def print_grid # Prints our grid to the terminal
    system "clear" # Clears the terminal window

    # Joins cells in each row with an empty space
    rows = @grid.map do |row|
      row.join(" ")
    end

    # Print rows joined by a new line
    print rows.join("\n")

    # Single line implimentation
    # print @grid.map{|row| row.join(" ")}.join("\n")
  end

  def loop_cells
    print_grid # Start by printing the current grid
    new_grid = empty_grid # Set an empty grid (this will be the next life cycle)

    # Loop through every cell in every row
    @grid.each_with_index do |row, row_index|
      row.each_with_index do |cell, cell_index|

        # Find the cells friends
        friends = find_friends(row_index, cell_index)

        # Apply life or death rules
        if cell == LIVE
          state = friends.size.between?(2,3)
        else
          state = friends.size == 3
        end

        # Set cell in new_grid for the next cycle
        new_grid[row_index][cell_index] = state ? LIVE : DEAD

      end
    end

    # Replace grid and start over
    @grid = new_grid
    start_over
  end

  def find_friends(row_index, cell_index)

    # Ruby can reach backwards through arrays and start over at the end - but it cannot reach forwards. If we're going off the grid, start over at 0
    row_fix = true if (row_index + 1) == HEIGHT
    cell_fix = true if (cell_index + 1) == WIDTH
    # You'll see below I will use 0 if one of these values is truthy when checking cells to the upper right, right, lower right, lower, and lower left.

    # Check each neighbor, use 0 if we're reaching too far
    friends = [
      @grid[(row_index - 1)][(cell_index - 1)],
      @grid[(row_index - 1)][(cell_index)],
      @grid[(row_index - 1)][(cell_fix ? 0 : cell_index + 1)],
      @grid[(row_index)][(cell_fix ? 0 : cell_index + 1)],
      @grid[(row_fix ? 0 : row_index + 1)][(cell_fix ? 0 : cell_index + 1)],
      @grid[(row_fix ? 0 : row_index + 1)][(cell_index)],
      @grid[(row_fix ? 0 : row_index + 1)][(cell_index - 1)],
      @grid[(row_index)][(cell_index - 1)]
    ]

    # Maps live neighbors into an array, removes nil values
    friends.map{|x| x if x == LIVE}.compact
  end

  def start_over
    sleep 0.1
    loop_cells
  end

end

# Start game when file is run
Game.new