Class: ConwayGame::BoardState

Inherits:
Object
  • Object
show all
Defined in:
lib/conway_deathmatch.rb

Overview

data structure for the board - 2d array implements standard and multiplayer evaluation static boundaries are treated as dead

Defined Under Namespace

Classes: BoundsError

Constant Summary collapse

DEAD =
'.'
ALIVE =
'0'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(x_len, y_len) ⇒ BoardState



21
22
23
24
25
26
27
# File 'lib/conway_deathmatch.rb', line 21

def initialize(x_len, y_len)
  # ranges, yay! (exclude_end)
  @xr = (0...x_len)
  @yr = (0...y_len)
  @state = self.class.new_state(x_len, y_len)
  @multiplayer = false
end

Instance Attribute Details

#multiplayerObject

Returns the value of attribute multiplayer.



19
20
21
# File 'lib/conway_deathmatch.rb', line 19

def multiplayer
  @multiplayer
end

Class Method Details

.new_state(x_len, y_len) ⇒ Object



13
14
15
16
17
# File 'lib/conway_deathmatch.rb', line 13

def self.new_state(x_len, y_len)
  state = []
  x_len.times { state << Array.new(y_len, DEAD) }
  state
end

Instance Method Details

#add_points(points, x_off = 0, y_off = 0, val = ALIVE) ⇒ Object

set several points (2d array), ignore OOB



99
100
101
102
103
104
105
106
# File 'lib/conway_deathmatch.rb', line 99

def add_points(points, x_off = 0, y_off = 0, val = ALIVE)
  points.each { |point|
    x = point[0] + x_off
    y = point[1] + y_off
    @state[x][y] = val if self.in_bounds?(x, y)
  }
  self
end

#alive?(x, y) ⇒ Boolean

out of bounds considered dead



48
49
50
# File 'lib/conway_deathmatch.rb', line 48

def alive?(x, y)
  in_bounds?(x, y) and @state[x][y] != DEAD
end

#in_bounds!(x, y) ⇒ Object

Raises:



43
44
45
# File 'lib/conway_deathmatch.rb', line 43

def in_bounds!(x, y)
  raise(BoundsError, "(#{x}, #{y}) (#{@xr}, #{@yr})") unless in_bounds?(x, y)
end

#in_bounds?(x, y) ⇒ Boolean



39
40
41
# File 'lib/conway_deathmatch.rb', line 39

def in_bounds?(x, y)
  @xr.include?(x) and @yr.include?(y)
end

#neighbor_population(x, y) ⇒ Object

population of each neighbor



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/conway_deathmatch.rb', line 53

def neighbor_population(x, y)
  neighbors = Hash.new(0)
  (x-1..x+1).each { |xn|
    (y-1..y+1).each { |yn|
      if alive?(xn, yn) and !(xn == x and yn == y) # don't count self
        neighbors[@state[xn][yn]] += 1
      end
    }
  }
  neighbors
end

#neighbor_stats(x, y) ⇒ Object

multiplayer, neighbor count and birthright



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/conway_deathmatch.rb', line 66

def neighbor_stats(x, y)
  if @multiplayer
    total = 0
    largest = 0
    birthright = nil
    neighbor_population(x, y).each { |sym, cnt|
      total += cnt
      if cnt > largest
        largest = cnt
        birthright = sym
      end
    }
    [total, birthright]
  else
    [neighbor_population(x, y).values.reduce(:+), ALIVE]
  end
end

#next_value(x, y) ⇒ Object

Conway’s Game of Life transition rules



30
31
32
33
34
35
36
37
# File 'lib/conway_deathmatch.rb', line 30

def next_value(x, y)
  n, birthright = neighbor_stats(x, y)
  if alive?(x, y)
    (n == 2 or n == 3) ? birthright : DEAD
  else
    (n == 3) ? birthright : DEAD
  end
end

#populate(x, y, val = ALIVE) ⇒ Object

set a single point, raise on OOB



93
94
95
96
# File 'lib/conway_deathmatch.rb', line 93

def populate(x, y, val = ALIVE)
  in_bounds!(x, y)
  @state[x][y] = val
end

#populationObject

full board scan



115
116
117
118
119
# File 'lib/conway_deathmatch.rb', line 115

def population
  population = Hash.new(0)
  @state.each { |col| col.each { |val|  population[val] += 1  } }
  population
end

#render_textObject Also known as: render

for line-based text output, iterate over y-values first (i.e. per row)



109
110
111
# File 'lib/conway_deathmatch.rb', line 109

def render_text
  @state.transpose.map { |row| row.join }.join("\n")
end

#tickObject

generate the next state table



85
86
87
88
89
90
# File 'lib/conway_deathmatch.rb', line 85

def tick
  new_state = self.class.new_state(@xr.last, @yr.last)
  @xr.each { |x| @yr.each { |y|  new_state[x][y] = next_value(x, y)  } }
  @state = new_state
  self
end