Class: ConwayDeathmatch::BoardState
- Inherits:
-
Object
- Object
- ConwayDeathmatch::BoardState
- Defined in:
- lib/conway_deathmatch/board_state.rb
Overview
data structure for the board - 2d array implements standard and deathmatch evaluation static boundaries are treated as dead
Defined Under Namespace
Classes: BoundsError
Constant Summary collapse
- DEAD =
'.'- ALIVE =
'0'
Instance Attribute Summary collapse
-
#deathmatch ⇒ Object
nil for traditional, otherwise :aggressive, :defensive, or :friendly.
Class Method Summary collapse
Instance Method Summary collapse
-
#add_points(points, x_off = 0, y_off = 0, val = ALIVE) ⇒ Object
set several points (2d array), ignore OOB.
-
#alive?(x, y) ⇒ Boolean
out of bounds considered dead.
- #in_bounds!(x, y) ⇒ Object
- #in_bounds?(x, y) ⇒ Boolean
-
#initialize(x_len, y_len, deathmatch = nil) ⇒ BoardState
constructor
A new instance of BoardState.
-
#neighbor_population(x, y) ⇒ Object
population of every neighboring entity, including DEAD.
-
#neighbor_stats(x, y) ⇒ Object
total (alive) neighbor count and birthright.
-
#next_value(x, y) ⇒ Object
Conway’s Game of Life transition rules.
-
#populate(x, y, val = ALIVE) ⇒ Object
set a single point, raise on OOB.
-
#population ⇒ Object
full board scan.
-
#render_text ⇒ Object
(also: #render)
for line-based text output, iterate over y-values first (i.e. per row).
-
#tick ⇒ Object
generate the next state table.
- #value(x, y) ⇒ Object
Constructor Details
#initialize(x_len, y_len, deathmatch = nil) ⇒ BoardState
Returns a new instance of BoardState.
22 23 24 25 26 27 |
# File 'lib/conway_deathmatch/board_state.rb', line 22 def initialize(x_len, y_len, deathmatch = nil) @x_len = x_len @y_len = y_len @state = self.class.new_state(x_len, y_len) @deathmatch = deathmatch end |
Instance Attribute Details
#deathmatch ⇒ Object
nil for traditional, otherwise :aggressive, :defensive, or :friendly
20 21 22 |
# File 'lib/conway_deathmatch/board_state.rb', line 20 def deathmatch @deathmatch end |
Class Method Details
.new_state(x_len, y_len) ⇒ Object
13 14 15 16 17 |
# File 'lib/conway_deathmatch/board_state.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
132 133 134 135 136 137 138 139 |
# File 'lib/conway_deathmatch/board_state.rb', line 132 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
100 101 102 |
# File 'lib/conway_deathmatch/board_state.rb', line 100 def alive?(x, y) @state[x][y] != DEAD rescue false end |
#in_bounds!(x, y) ⇒ Object
95 96 97 |
# File 'lib/conway_deathmatch/board_state.rb', line 95 def in_bounds!(x, y) raise(BoundsError, "(#{x}, #{y})") unless in_bounds?(x, y) end |
#in_bounds?(x, y) ⇒ Boolean
91 92 93 |
# File 'lib/conway_deathmatch/board_state.rb', line 91 def in_bounds?(x, y) x.between?(0, @x_len - 1) and y.between?(0, @y_len - 1) end |
#neighbor_population(x, y) ⇒ Object
population of every neighboring entity, including DEAD
105 106 107 108 109 110 111 112 113 |
# File 'lib/conway_deathmatch/board_state.rb', line 105 def neighbor_population(x, y) neighbors = Hash.new(0) (x-1 > 0 ? x-1 : 0).upto(x+1 < @x_len ? x+1 : @x_len - 1) { |xn| (y-1 > 0 ? y-1 : 0).upto(y+1 < @y_len ? y+1 : @y_len - 1) { |yn| neighbors[@state[xn][yn]] += 1 unless (xn == x and yn == y) } } neighbors end |
#neighbor_stats(x, y) ⇒ Object
total (alive) neighbor count and birthright
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/conway_deathmatch/board_state.rb', line 40 def neighbor_stats(x, y) npop = neighbor_population(x, y).tap { |h| h.delete(DEAD) } case @deathmatch when nil [npop.values.reduce(0, :+), ALIVE] when :aggressive, :defensive # dead: determine majority (always 3, no need to sample for tie) # alive: agg: determine majority (may tie at 2); def: cell_val determine_majority = (@state[x][y] == DEAD or @deathmatch == :aggressive) total = 0 largest = 0 birthrights = [] npop.each { |sym, cnt| total += cnt return [0, DEAD] if total >= 4 # [optimization] if determine_majority if cnt > largest largest = cnt birthrights = [sym] elsif cnt == largest birthrights << sym end end } [total, determine_majority ? (birthrights.sample || DEAD) : @state[x][y]] when :friendly # [optimized] with knowledge of conway rules # if DEAD, need 3 friendlies to qualify for birth sampling # if ALIVE, npop simply has the friendly count cell_val = if @state[x][y] == DEAD npop.reduce([]) { |memo, (sym,cnt)| cnt == 3 ? memo + [sym] : memo }.sample || DEAD else @state[x][y] end # return [0, DEAD] if no one qualifies [npop[cell_val] || 0, cell_val] else raise "unknown: #{@deathmatch.inspect}" 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/board_state.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
126 127 128 129 |
# File 'lib/conway_deathmatch/board_state.rb', line 126 def populate(x, y, val = ALIVE) in_bounds!(x, y) @state[x][y] = val end |
#population ⇒ Object
full board scan
148 149 150 151 152 |
# File 'lib/conway_deathmatch/board_state.rb', line 148 def population population = Hash.new(0) @state.each { |col| col.each { |val| population[val] += 1 } } population end |
#render_text ⇒ Object Also known as: render
for line-based text output, iterate over y-values first (i.e. per row)
142 143 144 |
# File 'lib/conway_deathmatch/board_state.rb', line 142 def render_text @state.transpose.map { |row| row.join }.join("\n") end |
#tick ⇒ Object
generate the next state table
116 117 118 119 120 121 122 123 |
# File 'lib/conway_deathmatch/board_state.rb', line 116 def tick new_state = self.class.new_state(@x_len, @y_len) @x_len.times { |x| @y_len.times { |y| new_state[x][y] = next_value(x, y) } } @state = new_state self end |
#value(x, y) ⇒ Object
86 87 88 89 |
# File 'lib/conway_deathmatch/board_state.rb', line 86 def value(x, y) in_bounds!(x,y) @state[x][y].dup end |