Search code examples
rubylogicconways-game-of-life

A very specific Conway's Game of Life (Ruby beginner)


Looking for feedback on obvious logic errors on this, not optimizing. I keep getting weird tick counts on the end game message (ex: 1 tick turns into 11 ticks)

The largest error I can spot while running the code is on the 2nd tick, a very large amount of alive cells appear. I am too new to this to understand why, but it seems like the @alive_cells is not resetting back to 0 after each check.

Here is my entire code, its large but it should be child's play to anyone with experience.

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

Solution

  • I couldn't see where your code went wrong. It does have a recursive call which can easily cause strange behavior. Here is what I came up with:

    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
    

    The tick count stops but the program keeps running through the oscillator at the end.