Skip to content
Browse files

added some tests for the cell and for the conways class also, put eve…

…rything in namespace etc
  • Loading branch information...
1 parent 6caaf9b commit 995030adf3d65e1c43e3bb557c304bc3d2a46714 @skorks committed
Showing with 281 additions and 169 deletions.
  1. +10 −9 bin/conways
  2. +57 −8 lib/conways.rb
  3. +29 −24 lib/conways/cell.rb
  4. +130 −128 lib/conways/grid.rb
  5. +28 −0 spec/cell_test.rb
  6. +1 −0 spec/helpers.rb
  7. +26 −0 spec/life_test.rb
View
19 bin/conways
@@ -23,13 +23,14 @@ dead cells, i.e. all the edges of the grid should be made
up of zeros (dead cells)
EOS
end
+Conways::Life.new(ARGV.join("")).live
-begin
- input = eval(ARGV[0])
- life = Conways::Life.new(input)
- life.live
-rescue StandardError => e
- puts "Your input was invalid somehow: #{e}"
-rescue SyntaxError => e
- puts "Your input didn't have correct syntax: #{e}"
-end
+#begin
+ #input = eval(ARGV[0])
+ #life = Conways::Life.new(input)
+ #life.live
+#rescue StandardError => e
+ #puts "Your input was invalid somehow: #{e}"
+#rescue SyntaxError => e
+ #puts "Your input didn't have correct syntax: #{e}"
+#end
View
65 lib/conways.rb
@@ -1,17 +1,72 @@
+require 'conways/cell'
require 'conways/grid'
module Conways
class Life
+#some possible inputs
+#[[0,0,0,0,0,0], [0,0,1,1,1,0], [0,1,1,1,0,0], [0,0,0,0,0,0]]
+#[[0,0,0,0,0], [0,0,1,0,0], [0,0,0,1,0], [0,1,1,1,0], [0,0,0,0,0]]
+#[[0,0,0,0,0,0,0], [0,1,0,0,1,0,0],[0,0,0,0,0,1,0],[0,1,0,0,0,1,0],[0,0,1,1,1,1,0],[0,0,0,0,0,0,0]]
+ CROSS = [[0,0,0,0,0], [0,1,1,1,0], [0,0,0,0,0]]
+ ACORN = [[0,0,0,0,0,0,0,0,0], [0,0,1,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0],[0,1,1,0,0,1,1,1,0],[0,0,0,0,0,0,0,0,0]]
+
def initialize(input)
- @input = input
+ input = input.gsub(/\s+/,"")
+ begin
+ @input = (input.class == String ? eval(input) : input)
+ rescue StandardError => e
+ raise "Your input was invalid somehow: #{e}"
+ rescue SyntaxError => e
+ raise "Your input didn't have correct syntax: #{e}"
+ end
+ raise "Input must be a two dimensional array" unless two_dimensional? @input
+ raise "Input must only contain 0 or 1 as values" unless cell_values_correct? @input
+ raise "Input rows must all be of the same size" unless even_rows? @input
+
@exit = false
trap("SIGINT") do
@exit = true
end
end
+ def two_dimensional?(input)
+ input.each do |row|
+ return false unless row.class == Array
+ end
+ true
+ end
+
+ def cell_values_correct?(input)
+ input.each do |row|
+ row.each do |cell|
+ return false unless Conways::Cell::LIVE || Conways::Cell::DEAD
+ end
+ end
+ true
+ end
+
+ def even_rows?(input)
+ current_row_size = nil
+ input.each do |row|
+ return false if current_row_size && current_row_size != row.size
+ current_row_size = row.size
+ end
+ true
+ end
+
+ #def live
+ #grid = InputToGrid.convert @input
+ #loop do
+ #@output_writer.write grid
+ #grid.tick
+ #sleep(1)
+ #exit if @exit
+ #end
+ #@output_writer.write grid
+ #end
+
def live
- grid = CellGrid.build_from(@input)
+ grid = Grid.from_input @input
loop do
puts grid.to_s
puts
@@ -25,9 +80,3 @@ def live
end
end
-#some possible inputs
-#[[0,0,0,0,0], [0,1,1,1,0], [0,0,0,0,0]]
-#[[0,0,0,0,0,0], [0,0,1,1,1,0], [0,1,1,1,0,0], [0,0,0,0,0,0]]
-#[[0,0,0,0,0], [0,0,1,0,0], [0,0,0,1,0], [0,1,1,1,0], [0,0,0,0,0]]
-#[[0,0,0,0,0,0,0], [0,1,0,0,1,0,0],[0,0,0,0,0,1,0],[0,1,0,0,0,1,0],[0,0,1,1,1,1,0],[0,0,0,0,0,0,0]]
-#[[0,0,0,0,0,0,0,0,0], [0,0,1,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0],[0,1,1,0,0,1,1,1,0],[0,0,0,0,0,0,0,0,0]] #acorn
View
53 lib/conways/cell.rb
@@ -1,30 +1,35 @@
-class Cell
- class << self
- def live
- 1
- end
+module Conways
+ class Cell
+ LIVE = 1
+ DEAD = 0
+ class << self
+ def live_one
+ Cell.new Cell::LIVE
+ end
- def dead
- 0
- end
- end
+ def dead_one
+ Cell.new Cell::DEAD
+ end
+ end
- attr_accessor :neighbours, :state, :next_state, :previous_state
- def initialize(state)
- @state = state
- @next_state = nil
- end
+ attr_accessor :state, :next_state
+ def initialize(state)
+ raise "Cell must be live or dead" unless state == Cell::LIVE || state == Cell::DEAD
+ @state = state
+ @next_state = nil
+ end
- def live?
- @state == Cell.live
- end
+ def live?
+ @state == Cell::LIVE
+ end
- def dead?
- @state == Cell.dead
- end
+ def dead?
+ @state == Cell::DEAD
+ end
- def update_to_next_generation_state
- @state = @next_state
- @next_state = nil
- end
+ def move_to_next_state
+ @state = @next_state
+ @next_state = nil
+ end
+ end
end
View
258 lib/conways/grid.rb
@@ -1,142 +1,144 @@
require 'conways/cell'
-class CellGrid
- class << self
- def build_from(bit_grid)
- cell_grid = []
- bit_grid.each do |row|
- current_row = []
- row.each do |column|
- current_row << Cell.new(column)
- end
- cell_grid << current_row
- end
- CellGrid.new(cell_grid)
- end
- end
+module Conways
+ class Grid
+ class << self
+ def from_input(bit_grid)
+ cell_grid = []
+ bit_grid.each do |row|
+ current_row = []
+ row.each do |column|
+ current_row << Cell.new(column)
+ end
+ cell_grid << current_row
+ end
+ Grid.new(cell_grid)
+ end
+ end
- def initialize(cell_grid)
- @cell_grid = cell_grid
- end
+ def initialize(cell_grid)
+ @cell_grid = cell_grid
+ end
- def find_cell_at(row, column)
- return nil if row < 0 || row > @cell_grid.size - 1
- return nil if column < 0 || column > @cell_grid[row].size - 1
- @cell_grid[row][column]
- end
+ def find_cell_at(row, column)
+ return nil if row < 0 || row > @cell_grid.size - 1
+ return nil if column < 0 || column > @cell_grid[row].size - 1
+ @cell_grid[row][column]
+ end
- def find_neighbours_of_cell_at(row,column)
- neighbours = {}
- neighbours["N"] = find_cell_at(row-1, column)
- neighbours["NE"] = find_cell_at(row-1, column+1)
- neighbours["E"] = find_cell_at(row, column+1)
- neighbours["SE"] = find_cell_at(row+1, column+1)
- neighbours["S"] = find_cell_at(row+1, column)
- neighbours["SW"] = find_cell_at(row+1, column-1)
- neighbours["W"] = find_cell_at(row, column-1)
- neighbours["NW"] = find_cell_at(row-1, column-1)
- neighbours
- end
+ def find_neighbours_of_cell_at(row,column)
+ neighbours = {}
+ neighbours["N"] = find_cell_at(row-1, column)
+ neighbours["NE"] = find_cell_at(row-1, column+1)
+ neighbours["E"] = find_cell_at(row, column+1)
+ neighbours["SE"] = find_cell_at(row+1, column+1)
+ neighbours["S"] = find_cell_at(row+1, column)
+ neighbours["SW"] = find_cell_at(row+1, column-1)
+ neighbours["W"] = find_cell_at(row, column-1)
+ neighbours["NW"] = find_cell_at(row-1, column-1)
+ neighbours
+ end
- def to_s
- grid_string = ""
- @cell_grid.each_with_index do |row, row_index|
- row_string = ""
- row.each_with_index do |cell, column_index|
- if cell.live?
- row_string += "X "
- else
- row_string += ". "
- end
- end
- grid_string += "#{row_string}\n"
- end
- grid_string
- end
+ def to_s
+ grid_string = ""
+ @cell_grid.each_with_index do |row, row_index|
+ row_string = ""
+ row.each_with_index do |cell, column_index|
+ if cell.live?
+ row_string += "X "
+ else
+ row_string += ". "
+ end
+ end
+ grid_string += "#{row_string}\n"
+ end
+ grid_string
+ end
- def tick
- calculate_next_generation_state
- next_generation
- remove_redundant_rows(@cell_grid)
- add_new_rows(@cell_grid)
- transposed_cell_grid = @cell_grid.transpose
- remove_redundant_rows(transposed_cell_grid)
- add_new_rows(transposed_cell_grid)
- @cell_grid = transposed_cell_grid.transpose
- end
+ def tick
+ calculate_next_generation_state
+ next_generation
+ remove_redundant_rows(@cell_grid)
+ add_new_rows(@cell_grid)
+ transposed_cell_grid = @cell_grid.transpose
+ remove_redundant_rows(transposed_cell_grid)
+ add_new_rows(transposed_cell_grid)
+ @cell_grid = transposed_cell_grid.transpose
+ end
- def remove_redundant_rows(cell_grid)
- candidate_row_indexes = []
- cell_grid.each_with_index do |row, row_index|
- if row.select{|cell| cell.live?}.size == 0
- candidate_row_indexes << row_index
- else
- candidate_row_indexes.pop
- break
- end
- end
- (cell_grid.size - 1).downto(0) do |row_index|
- row = cell_grid[row_index]
- if row.select{|cell| cell.live?}.size == 0
- candidate_row_indexes << row_index
- else
- candidate_row_indexes.pop
- break
- end
- end
- if candidate_row_indexes.size > 10
- candidate_row_indexes = candidate_row_indexes.select{|row_index|
-row_index >= 0}
- candidate_row_indexes.each_with_index {|row, index|
-cell_grid.delete_at(row - index)}
- end
- end
+ def remove_redundant_rows(cell_grid)
+ candidate_row_indexes = []
+ cell_grid.each_with_index do |row, row_index|
+ if row.select{|cell| cell.live?}.size == 0
+ candidate_row_indexes << row_index
+ else
+ candidate_row_indexes.pop
+ break
+ end
+ end
+ (cell_grid.size - 1).downto(0) do |row_index|
+ row = cell_grid[row_index]
+ if row.select{|cell| cell.live?}.size == 0
+ candidate_row_indexes << row_index
+ else
+ candidate_row_indexes.pop
+ break
+ end
+ end
+ if candidate_row_indexes.size > 10
+ candidate_row_indexes = candidate_row_indexes.select{|row_index|
+ row_index >= 0}
+ candidate_row_indexes.each_with_index {|row, index|
+ cell_grid.delete_at(row - index)}
+ end
+ end
- def add_new_rows(cell_grid)
- #if a row has a live cell and doesn't have another row following it, we should add another row
- live_cells_in_last_row = cell_grid.last.select{|cell| cell.live?}
- cell_grid << create_row_of_dead_cells_from_template(cell_grid.last) if live_cells_in_last_row.size > 0
- live_cells_in_first_row = cell_grid.first.select{|cell| cell.live?}
- cell_grid.insert(0, create_row_of_dead_cells_from_template(cell_grid.first)) if live_cells_in_first_row.size > 0
- end
+ def add_new_rows(cell_grid)
+ #if a row has a live cell and doesn't have another row following it, we should add another row
+ live_cells_in_last_row = cell_grid.last.select{|cell| cell.live?}
+ cell_grid << create_row_of_dead_cells_from_template(cell_grid.last) if live_cells_in_last_row.size > 0
+ live_cells_in_first_row = cell_grid.first.select{|cell| cell.live?}
+ cell_grid.insert(0, create_row_of_dead_cells_from_template(cell_grid.first)) if live_cells_in_first_row.size > 0
+ end
- def create_row_of_dead_cells_from_template(row_of_cells)
- new_row_of_cells = []
- row_of_cells.each do |cell|
- new_row_of_cells << Cell.new(0)
- end
- new_row_of_cells
- end
+ def create_row_of_dead_cells_from_template(row_of_cells)
+ new_row_of_cells = []
+ row_of_cells.each do |cell|
+ new_row_of_cells << Cell.new(0)
+ end
+ new_row_of_cells
+ end
- def calculate_next_generation_state
- @cell_grid.each_with_index do |row, row_index|
- row.each_with_index do |cell, column_index|
- live_neighbours = live_neighbours_of_cell_at(row_index, column_index)
- if cell.live? && live_neighbours < 2
- cell.next_state = Cell.dead
- elsif cell.live? && live_neighbours > 1 && live_neighbours < 4
- cell.next_state = Cell.live
- elsif cell.live? && live_neighbours > 3
- cell.next_state = Cell.dead
- elsif cell.dead? && live_neighbours == 3
- cell.next_state = Cell.live
- else
- cell.next_state = cell.state
- end
- end
- end
- end
+ def calculate_next_generation_state
+ @cell_grid.each_with_index do |row, row_index|
+ row.each_with_index do |cell, column_index|
+ live_neighbours = live_neighbours_of_cell_at(row_index, column_index)
+ if cell.live? && live_neighbours < 2
+ cell.next_state = Cell::DEAD
+ elsif cell.live? && live_neighbours > 1 && live_neighbours < 4
+ cell.next_state = Cell::LIVE
+ elsif cell.live? && live_neighbours > 3
+ cell.next_state = Cell::DEAD
+ elsif cell.dead? && live_neighbours == 3
+ cell.next_state = Cell::LIVE
+ else
+ cell.next_state = cell.state
+ end
+ end
+ end
+ end
- def live_neighbours_of_cell_at(row, column)
- live_neighbours_count = find_neighbours_of_cell_at(row, column).values.compact.inject(0){|sum, element| sum + element.state}
- live_neighbours_count
- end
+ def live_neighbours_of_cell_at(row, column)
+ live_neighbours_count = find_neighbours_of_cell_at(row, column).values.compact.inject(0){|sum, element| sum + element.state}
+ live_neighbours_count
+ end
- def next_generation
- @cell_grid.each_with_index do |row, row_index|
- row.each_with_index do |cell, column_index|
- cell.update_to_next_generation_state
- end
- end
- end
+ def next_generation
+ @cell_grid.each_with_index do |row, row_index|
+ row.each_with_index do |cell, column_index|
+ cell.move_to_next_state
+ end
+ end
+ end
+ end
end
View
28 spec/cell_test.rb
@@ -0,0 +1,28 @@
+require File.expand_path(File.join(File.dirname(__FILE__), 'helpers'))
+require 'conways/cell'
+
+this_tests Conways::Cell do
+ test("creating a live cell should set its state to a value associated with live") do
+ cell = Conways::Cell.live_one
+ should_equal(Conways::Cell::LIVE, cell.state)
+ end
+ test("creating a dead cell should set its state to a value associated with dead") do
+ cell = Conways::Cell.dead_one
+ should_equal(Conways::Cell::DEAD, cell.state)
+ end
+ test("a cell instantiated as live should report live and should not report dead") do
+ cell = Conways::Cell.live_one
+ should_be_true cell.live?
+ should_be_false cell.dead?
+ end
+ test("state of cell should equal to next state and next state should be nil after moving to next state") do
+ cell = Conways::Cell.live_one
+ cell.next_state = Conways::Cell::DEAD
+ cell.move_to_next_state
+ should_equal Conways::Cell::DEAD, cell.state
+ should_equal nil, cell.next_state
+ end
+ test("should get an exception if instantiating cell as neither live nor dead") do
+ should_raise { Conways::Cell.new 5 }
+ end
+end
View
1 spec/helpers.rb
@@ -0,0 +1 @@
+$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../lib/'))) unless $:.include?(File.expand_path(File.join(File.dirname(__FILE__), '../lib')))
View
26 spec/life_test.rb
@@ -0,0 +1,26 @@
+require File.expand_path(File.join(File.dirname(__FILE__), 'helpers'))
+require 'conways'
+
+this_tests Conways::Life do
+ test("a an array that is not two dimensional should be rejected as input") do
+ input = "[1,1,1]"
+ should_raise {Conways::Life.new input}
+ end
+ test ("a two dimensional array that has uneven rows should be rejected as input") do
+ input = "[[0,0,0],[0,1]]"
+ should_raise {Conways::Life.new input}
+ end
+ test("an array that contains more than just zeros and ones should be rejected as input") do
+ input = "[[0,0,0],[2,1]]"
+ should_raise {Conways::Life.new input}
+ end
+ test("a string that had spaces in it but is otherwise valid should NOT be rejected as input") do
+ input = "[[0,0, 0], [0,1, 0], [0,0,0]]"
+ should_not_raise {Conways::Life.new input}
+ end
+
+ test("a string that is invalid input should be rejected") do
+ input = "["
+ should_raise {Conways::Life.new input}
+ end
+end

0 comments on commit 995030a

Please sign in to comment.
Something went wrong with that request. Please try again.