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
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.
18 19 20 21 22 23 |
# File 'lib/conway_deathmatch.rb', line 18 def initialize(width, height, deathmatch = nil) @width = width @height = height @grid = self.class.new_grid(width, height) @deathmatch = deathmatch end |
Instance Attribute Details
#deathmatch ⇒ Object
nil for traditional, otherwise :aggressive, :defensive, or :friendly
16 17 18 |
# File 'lib/conway_deathmatch.rb', line 16 def deathmatch @deathmatch end |
Class Method Details
.new_grid(width, height) ⇒ Object
9 10 11 12 13 |
# File 'lib/conway_deathmatch.rb', line 9 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)
120 121 122 123 |
# File 'lib/conway_deathmatch.rb', line 120 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
90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/conway_deathmatch.rb', line 90 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
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 85 86 87 |
# File 'lib/conway_deathmatch.rb', line 41 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
26 27 28 29 30 31 32 33 34 |
# File 'lib/conway_deathmatch.rb', line 26 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
115 116 117 |
# File 'lib/conway_deathmatch.rb', line 115 def populate(x, y, val = ALIVE) @grid[x % @width][y % @height] = val end |
#population ⇒ Object
full grid scan
132 133 134 135 136 |
# File 'lib/conway_deathmatch.rb', line 132 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)
126 127 128 |
# File 'lib/conway_deathmatch.rb', line 126 def render_text @grid.transpose.map { |row| row.join }.join("\n") end |
#tick ⇒ Object
generate the next grid table
105 106 107 108 109 110 111 112 |
# File 'lib/conway_deathmatch.rb', line 105 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
36 37 38 |
# File 'lib/conway_deathmatch.rb', line 36 def value(x, y) @grid[x % @width][y % @height] end |