Created
April 30, 2010 00:06
-
-
Save iain/384487 to your computer and use it in GitHub Desktop.
Conway's Game of Life, in one line of Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2010, Iain Hecker. All Rights Reserved | |
# Conway's Game of Life, in one line of Ruby. | |
# http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life | |
# Tested and found working on Ruby 1.8.7 and 1.9.2 | |
# The grid is spherical or "wrap around", so the left is connected to the right and top to bottom. | |
# | |
# Generate a random grid, 30 cells wide and 10 cells high | |
# | |
# grid = "30x10".to_grid | |
# | |
# Use a predefined grid. The lower case letter 'o' is used as an alive cell, any other character is dead. | |
# Newlines (\n) delimit rows in the grid. | |
# | |
# grid = some_grid_string.to_grid | |
# | |
# Get the next generation, with a grid you just created | |
# | |
# grid.next | |
# | |
# Get the string representation back: | |
# | |
# grid.to_s | |
String.class_eval { define_method(:to_grid) { (self =~ /\A(\d+)x(\d+)\z/ ? (0...split('x').last.to_i).map { |_| (0...split('x').first.to_i).map { |_| rand > 0.5 } } : split("\n").map { |row| row.split(//).map { |cell_string| cell_string == "o" } }).tap { |grid| grid.class.class_eval { define_method(:next) { each { |row| row.each { |cell| cell.class.class_eval { define_method(:next?) { |neighbours| (self && (2..3).include?(neighbours.select { |me| me }.size)) || (!self && neighbours.select { |me| me }.size == 3) } } } } && enum_for(:each_with_index).map { |row, row_num| row.enum_for(:each_with_index).map { |cell, col_num| cell.next?([ at(row_num - 1).at(col_num - 1), at(row_num - 1).at(col_num), at(row_num - 1).at((col_num + 1) % row.size), row.at((col_num + 1) % row.size), row.at(col_num - 1), at((row_num + 1) % size).at(col_num - 1), at((row_num + 1) % size).at(col_num), at((row_num + 1) % size).at((col_num + 1) % row.size) ]) } } } && define_method(:to_s) { map { |row| row.map { |cell| cell ? "o" : "." }.join }.join("\n") } } } } } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2010, Iain Hecker. All Rights Reserved | |
require 'game_of_life' | |
require 'test/unit' | |
class GameOfLifeTest < Test::Unit::TestCase | |
def test_block_example | |
before = <<GRID.strip | |
.... | |
.oo. | |
.oo. | |
.... | |
GRID | |
expected = before | |
assert_equal expected, before.to_grid.next.to_s | |
assert_equal before, expected.to_grid.next.to_s | |
end | |
def test_toad_example | |
before = <<GRID.strip | |
...... | |
...... | |
..ooo. | |
.ooo.. | |
...... | |
...... | |
GRID | |
expected = <<GRID.strip | |
...... | |
...o.. | |
.o..o. | |
.o..o. | |
..o... | |
...... | |
GRID | |
assert_equal expected, before.to_grid.next.to_s | |
assert_equal before, expected.to_grid.next.to_s | |
end | |
def test_beacon_example | |
before = <<GRID.strip | |
...... | |
.oo... | |
.oo... | |
...oo. | |
...oo. | |
...... | |
GRID | |
expected = <<GRID.strip | |
...... | |
.oo... | |
.o.... | |
....o. | |
...oo. | |
...... | |
GRID | |
assert_equal expected, before.to_grid.next.to_s | |
assert_equal before, expected.to_grid.next.to_s | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2010, Iain Hecker. All Rights Reserved | |
# The oneliner, but now with newlines, indenting and documentation | |
# Within the following closure the class String is "self" | |
String.class_eval { | |
# define an instance method | |
define_method(:to_grid) { | |
# check if this string contains dimensions | |
(self =~ /\A(\d+)x(\d+)\z/ ? | |
# Generate a range for the last part of the dimensions and change each item. | |
(0...split('x').last.to_i).map { |_| | |
# And also for the first part of the dimensions, thus making a 2-dimensional array (aka "the grid") | |
(0...split('x').first.to_i).map { |_| | |
# random true or false. | |
rand > 0.5 | |
} | |
} : | |
# if it's not dimensions, assume it's a completly drawn grid, which must be splitted in rows | |
split("\n").map { |row| | |
# and every row splitted on every character | |
row.split(//).map { |cell_string| | |
# it's true (alive) when it's a lower case 'o' | |
cell_string == "o" | |
} | |
} | |
# modify (but always return the original object) with the result (which is the grid) | |
).tap { |grid| | |
# the class of the grid becomes "self" | |
grid.class.class_eval { | |
# and define the instance method "next" on it | |
define_method(:next) { | |
# for each row | |
each { |row| | |
# and each cell in each row | |
row.each { |cell| | |
# become the class of the content (TrueClass or FalseClass) | |
cell.class.class_eval { | |
# and define the instance method "next?", which accepts neighbours to know if it should die or live | |
define_method(:next?) { |neighbours| | |
# self is true or false (instances of resp. TrueClass and FalseClass), so this expression just returns a new boolean | |
(self && (2..3).include?(neighbours.select { |me| me }.size)) || (!self && neighbours.select { |me| me }.size == 3) | |
} | |
} | |
} | |
# map with index, is shorter in Ruby 1.9, but this strange construction works also in Ruby 1.8 | |
} && enum_for(:each_with_index).map { |row, row_num| | |
# same, we're dealing a 2-dimensional array, remember? | |
row.enum_for(:each_with_index).map { |cell, col_num| | |
# call the next?-method we defined about 10 lines earlier and find all its neighbours | |
cell.next?([ | |
at(row_num - 1).at(col_num - 1), | |
at(row_num - 1).at(col_num), | |
at(row_num - 1).at((col_num + 1) % row.size), | |
row.at((col_num + 1) % row.size), | |
row.at(col_num - 1), | |
at((row_num + 1) % size).at(col_num - 1), | |
at((row_num + 1) % size).at(col_num), | |
at((row_num + 1) % size).at((col_num + 1) % row.size) | |
]) | |
} | |
} | |
# and define the method to_s | |
} && define_method(:to_s) { | |
# which loops | |
map { |row| | |
# the 2-dimensional array | |
row.map { |cell| | |
# and replaces booleans with strings | |
cell ? "o" : "." | |
# and join a row together | |
}.join | |
# and join the lines together | |
}.join("\n") | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
An example of a 100x30 grid: | |
...........................o...o...........................o................ooo....o..oo.....ooo.... | |
......oo...........oo..........o.......o..............o..o..................o..o...ooo.......oo..o.. | |
.......o.o........o..o.................oo....o..o.....oo..............oo.....o.o....o.oo....ooo.o... | |
...........oo......oo....oo..o..o.....o..o.oo...ooo...oo......ooo....o.o......o.....o.oo.....ooo.... | |
........o...o..........o....o.ooo.................o....o......ooo....ooo............o.........o..... | |
.........ooo................o.........oo.o...oo...o....o....oo.oo....o...............o.............o | |
............................o........o.oo.....o..o......o...oo....................................oo | |
.......................oo.oo........ooo..o.....oo...oo..oo.o...............oo.....................o. | |
.........................o........o...o............oo.oo................oo.oo....................... | |
.....................................oo............oo.oo...oo...........oo..o....................... | |
...............................o.....oo............o..oo...oo.oo...o..o..oo.......oo...........oo..o | |
...............................o..oooo..............oo......o.oo...ooo....ooo....o..o.............o. | |
.o..............................o...............................o..........o......oo...........o.... | |
o.o.............................o..o...........................o................................oo.. | |
...o.............................o.o.................................o.............................o | |
o..o..oo.........................ooo................................ooo............................. | |
.ooo..o.o.........................o.o..............................ooo.o................ooo......... | |
...o....o..........................o..............................oo...o............................ | |
....o.o...o.......................................................o.ooo...............o............. | |
...oo.o...o........................................................ooo................o.........ooo. | |
...o....oo...............................................................oo...........o.........o.o. | |
....o..o..................................................................o.o.oo................ooo. | |
....oo..o....oo..........................................o................o.....o.......ooo......... | |
......ooo......................................oo.......oooo.............oo......o.................. | |
.......oo.....................................oo.......oo................oooo....o.................. | |
.......................o.......................o..o...oo.oo.oo................o..o...oo...........oo | |
......................o.o.................oo.......oo.ooooo.oo................o.o....oo........oo.oo | |
o.....................o.o.............ooooooo.....oo...o.o....................................o..... | |
.......................o....oo.......o......oo.........oo..oo........................o........o...oo | |
...........................o..o.......oooo..............o...o.......................oooo.....o.....o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2010, Iain Hecker. All Rights Reserved | |
require 'game_of_life' | |
# generate pattern glider, which continues forever | |
grid = <<GRID.to_grid | |
.............. | |
..o........... | |
...o.......... | |
.ooo.......... | |
.............. | |
.............. | |
.............. | |
.............. | |
GRID | |
interrupted = false | |
generations = 0 | |
trap("INT") { puts "\rExiting after #{generations} generations..."; interrupted = true } | |
until interrupted do | |
system "clear" | |
puts grid.to_s | |
grid = grid.next | |
generations += 1 | |
sleep 0.1 | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2010, Iain Hecker. All Rights Reserved | |
require 'game_of_life' | |
# generate a random grid | |
grid = "100x30".to_grid | |
interrupted = false | |
generations = 0 | |
trap("INT") { puts "\rExiting after #{generations} generations..."; interrupted = true } | |
until interrupted do | |
system "clear" | |
puts grid.to_s | |
grid = grid.next | |
generations += 1 | |
sleep 0.1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is perfect. 10/10.