Class: ConwayDeathmatch
- Inherits:
-
Object
- Object
- ConwayDeathmatch
- Defined in:
- lib/conway_deathmatch.rb
Overview
Provides a 2d array for the grid Implements standard and deathmatch evaluation rules Boundaries are toroidal: they wrap in each direction
Defined Under Namespace
Modules: Shapes 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).
-
#initialize(width, height, deathmatch = nil) ⇒ ConwayDeathmatch
constructor
A new instance of ConwayDeathmatch.
-
#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.
-
#population ⇒ Object
full grid 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 grid table.
- #value(x, y) ⇒ Object
Constructor Details
#initialize(width, height, deathmatch = nil) ⇒ ConwayDeathmatch
Returns a new instance of ConwayDeathmatch.
25 26 27 28 29 30 31 |
# File 'lib/conway_deathmatch.rb', line 25 def initialize(width, height, deathmatch = nil) @width = width @height = height @grid = self.class.new_grid(width, height) @deathmatch = deathmatch #@lager = self.class.lager end |
Instance Attribute Details
#deathmatch ⇒ Object
nil for traditional, otherwise :aggressive, :defensive, or :friendly
23 24 25 |
# File 'lib/conway_deathmatch.rb', line 23 def deathmatch @deathmatch end |
Class Method Details
.new_grid(width, height) ⇒ Object
16 17 18 19 20 |
# File 'lib/conway_deathmatch.rb', line 16 def self.new_grid(width, height) grid = [] width.times { grid << Array.new(height, DEAD) } grid end |
Instance Method Details
#add_points(points, x_off = 0, y_off = 0, val = ALIVE) ⇒ Object
set several points (2d array)
128 129 130 131 |
# File 'lib/conway_deathmatch.rb', line 128 def add_points(points, x_off = 0, y_off = 0, val = ALIVE) points.each { |point| populate(point[0] + x_off, point[1] + y_off, val) } self end |
#neighbor_population(x, y) ⇒ Object
population of every neighboring entity, including DEAD
98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/conway_deathmatch.rb', line 98 def neighbor_population(x, y) x = x % @width y = y % @height neighbors = Hash.new(0) (x-1).upto(x+1) { |xn| (y-1).upto(y+1) { |yn| xn = xn % @width yn = yn % @height neighbors[@grid[xn][yn]] += 1 unless (xn == x and yn == y) } } neighbors end |
#neighbor_stats(x, y) ⇒ Object
total (alive) neighbor count and birthright
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 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/conway_deathmatch.rb', line 49 def neighbor_stats(x, y) x = x % @width y = y % @height 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 = (@grid[x][y] == DEAD or @deathmatch == :aggressive) total = 0 largest = 0 birthrights = [] npop.each { |sym, cnt| total += cnt return [0, DEAD] if total > 3 # [optimization] if determine_majority if cnt > largest largest = cnt birthrights = [sym] elsif cnt == largest birthrights << sym end end } [total, determine_majority ? (birthrights.sample || DEAD) : @grid[x][y]] when :friendly # [optimization] 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 @grid[x][y] == DEAD npop.reduce([]) { |memo, (sym,cnt)| cnt == 3 ? memo + [sym] : memo }.sample || DEAD else @grid[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
34 35 36 37 38 39 40 41 42 |
# File 'lib/conway_deathmatch.rb', line 34 def next_value(x, y) # don't bother toroidaling, only called by #tick n, birthright = neighbor_stats(x, y) if @grid[x][y] != DEAD (n == 2 or n == 3) ? birthright : DEAD else (n == 3) ? birthright : DEAD end end |
#populate(x, y, val = ALIVE) ⇒ Object
set a single point
123 124 125 |
# File 'lib/conway_deathmatch.rb', line 123 def populate(x, y, val = ALIVE) @grid[x % @width][y % @height] = val end |
#population ⇒ Object
full grid scan
140 141 142 143 144 |
# File 'lib/conway_deathmatch.rb', line 140 def population population = Hash.new(0) @grid.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)
134 135 136 |
# File 'lib/conway_deathmatch.rb', line 134 def render_text @grid.transpose.map { |row| row.join }.join("\n") end |
#tick ⇒ Object
generate the next grid table
113 114 115 116 117 118 119 120 |
# File 'lib/conway_deathmatch.rb', line 113 def tick new_grid = self.class.new_grid(@width, @height) @width.times { |x| @height.times { |y| new_grid[x][y] = next_value(x, y) } } @grid = new_grid self end |
#value(x, y) ⇒ Object
44 45 46 |
# File 'lib/conway_deathmatch.rb', line 44 def value(x, y) @grid[x % @width][y % @height] end |