Skip to content

Instantly share code, notes, and snippets.

@fpauser
Created July 2, 2010 09:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fpauser/461159 to your computer and use it in GitHub Desktop.
Save fpauser/461159 to your computer and use it in GitHub Desktop.
class GameOfLife
attr_reader :width, :height, :cells
def initialize(width = 5, height = 5, seed = 5)
raise ArgumentError.new "Seed must be < than width x height!" if seed >= width * height
@width, @height = width, height
@cells = Array.new(width * height, 0)
@cells[rand(cells.size)] = 1 while cells.select{|c| c == 1}.size < seed
end
def state
rows = []
cells.each_with_index do |cell, index|
rows << [] if index % width == 0
rows.last << cell
end
rows
end
def state=(rows)
raise ArgumentError.new "Rows must be an array of rows" unless rows.is_a?(Array) and rows.select{|r| r.is_a?(Array)}.size == rows.size
raise ArgumentError.new "All rows must be of equal size!" unless rows.map{|r| r.size}.uniq.size == 1
raise ArgumentError.new "Row-values must be 0 or 1" unless (rows.flatten.uniq - [0,1]).empty?
@cells, @width, @height = rows.flatten, rows.first.size, rows.size
end
def evolve
evolved = Array.new( cells.size, 0 )
cells.each_with_index do |cell, index|
nb = neighbours(index)
living_nb = nb.select{|p| cells[p] == 1}.size
case
when cell == 1 && living_nb < 2; evolved[index] = 0; nb.each{ |p| evolved[p] = 0 }
when cell == 1 && living_nb > 3; evolved[index] = 0; nb.each{ |p| evolved[p] = 0 }
when cell == 1 && living_nb == 2; evolved[index] = 1;
when cell == 1 && living_nb == 3; evolved[index] = 1;
when cell == 0 && living_nb == 3; evolved[index] = 1; nb.each{ |p| evolved[p] = 1 }
end
end
@cells = evolved
state
end
def neighbours(pos)
[north(pos), north_east(pos),
east(pos), south_east(pos),
south(pos), south_west(pos),
west(pos), north_west(pos)]
end
def north(pos)
p = pos - width
p < 0 ? p + cells.size : p
end
def south(pos)
p = pos + width
p >= cells.size ? p - cells.size : p
end
def east(pos)
p = pos + 1
p % width == 0 ? pos / width * width : p
end
def west(pos)
p = pos - 1
pos == pos / width * width ? pos + width - 1 : p
end
def north_east(pos); north east(pos); end
def south_east(pos); south east(pos); end
def north_west(pos); north west(pos); end
def south_west(pos); south west(pos); end
end
require "game_of_life"
describe GameOfLife do
before :each do
@width, @height, @seed = 10, 8, 5
@gol = GameOfLife.new(@width, @height, @seed)
end
it "should initialize correctly" do
@gol.width.should == @width
@gol.height.should == @height
@gol.cells.size.should == @width * @height
@gol.state.flatten.select{|c| c == 1}.size.should == @seed
end
it "should be possible to set a state" do
state = [[1,0,0,1],[0,0,1,1],[1,1,0,1]]
@gol.state = state
@gol.state.should == state
@gol.width.should == 4
@gol.height.should == 3
end
it "should determine north correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 6, 1 => 7, 2 => 8,
3 => 0, 4 => 1, 5 => 2,
6 => 3, 7 => 4, 8 => 5}
pos_check.keys.each { |p| @gol.north(p).should == pos_check[p] }
end
it "should determine east correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 1, 1 => 2, 2 => 0,
3 => 4, 4 => 5, 5 => 3,
6 => 7, 7 => 8, 8 => 6}
pos_check.keys.each { |p| @gol.east(p).should == pos_check[p] }
end
it "should determine south correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 3, 1 => 4, 2 => 5,
3 => 6, 4 => 7, 5 => 8,
6 => 0, 7 => 1, 8 => 2}
pos_check.keys.each { |p| @gol.south(p).should == pos_check[p] }
end
it "should determine west correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 2, 1 => 0, 2 => 1,
3 => 5, 4 => 3, 5 => 4,
6 => 8, 7 => 6, 8 => 7}
pos_check.keys.each { |p| @gol.west(p).should == pos_check[p] }
end
it "should determine north-east correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 7, 1 => 8, 2 => 6,
3 => 1, 4 => 2, 5 => 0,
6 => 4, 7 => 5, 8 => 3}
pos_check.keys.each { |p| @gol.north_east(p).should == pos_check[p] }
end
it "should determine south-east correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 4, 1 => 5, 2 => 3,
3 => 7, 4 => 8, 5 => 6,
6 => 1, 7 => 2, 8 => 0}
pos_check.keys.each { |p| @gol.south_east(p).should == pos_check[p] }
end
it "should determine north-west correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 8, 1 => 6, 2 => 7,
3 => 2, 4 => 0, 5 => 1,
6 => 5, 7 => 3, 8 => 4}
pos_check.keys.each { |p| @gol.north_west(p).should == pos_check[p] }
end
it "should determine south-west correctly" do
@gol.state = [[0,0,0],[0,0,0],[0,0,0]]
pos_check = {0 => 5, 1 => 3, 2 => 4,
3 => 8, 4 => 6, 5 => 7,
6 => 2, 7 => 0, 8 => 1}
pos_check.keys.each { |p| @gol.south_west(p).should == pos_check[p] }
end
it "should kill with just one neighbour" do
@gol.state = [[0,0,0],[1,0,0],[1,0,0]]
after = @gol.evolve
after.should == [[0,0,0],[0,0,0],[0,0,0]]
end
it "should kill with more than 3 neighbours" do
@gol.state = [[1,1,1],[1,1,1],[1,1,1]]
after = @gol.evolve
after.should == [[0,0,0],[0,0,0],[0,0,0]]
end
it "should give birth if 3 neighbours" do
@gol.state = [[1,0,0],[1,1,0],[0,0,0]]
after = @gol.evolve
after.should == [[1,1,1],[1,1,1],[1,1,1]]
end
end
#
# This is a class to allow you to visualize your game of life with ncurses.
#
# howto
# if your class is called GameOfLife
# LifeNcurses.new(GameOfLife.new(...))
# with ... the paramters that your initialize method takes
#
# this should show you the game of life evolving in your terminal
# there is a second optional parameter for the number of generations you want to see
# LifeNcurses.new(GameOfLife.new(...),5)
# if you want to see only 5 generations
require 'rubygems'
require 'ffi-ncurses'
class LifeNcurses
# spaces from the border of the terminal
MARGIN = 2
include FFI::NCurses
def initialize(game_of_life,iterations=100)
@stdscr = initscr
cbreak
(1..iterations).each do |generation|
clear
display_title(generation)
show game_of_life.evolve
end
ensure
endwin
end
def show(state)
state.each_with_index do |row,row_index|
row.each_with_index do |col, col_index|
mvwaddstr @stdscr, row_index+MARGIN, col_index+MARGIN, '#' if state[row_index][col_index] == 1
end
end
refresh
sleep 1
end
def display_title(generation)
mvwaddstr @stdscr, 0, 1, "Game of life: Generation #{generation}"
end
end
require File.join(File.dirname(__FILE__), 'game_of_life')
require File.join(File.dirname(__FILE__), 'life_ncurses')
LifeNcurses.new( GameOfLife.new(20, 20, 20) )
<!Doctype html>
<html>
<head>
<title>The Game Of Life</title>
<style>
* { margin:0; padding:0; font-family:sans-serif;}
html { background:#555; color:#fff; height:100%;}
body { height:100%; }
#progress { position:absolute; top:10px; left:10px; background:#00cc00; padding:10px; opacity:0.5; font-weight:bold; -moz-border-radius: 5px; -webkit-border-radius: 5px; text-shadow: #000 1px 1px 1px; }
#trigger { position:absolute; z-index:1000; width:100%; height:100%; background:transparent; cursor:pointer; }
#cells { height:100%;}
#cells table { width:100%; height:100%; }
.cell { background:#fff; color:#000; text-align:center; }
.cell.alive { background:#000; color:#fff; }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript">
var step = 1;
var state = <%= @state.to_json %>;
$(function() {
visualize();
trigger_evolve();
});
function trigger_evolve() {
window.setTimeout(evolve, 500);
}
function evolve() {
$.ajax({
url: "evolve",
type: "post",
dataType: "json",
data: {state: state},
success: function(new_state) {
state = new_state;
step++;
visualize();
},
complete: trigger_evolve
});
}
function visualize() {
var cell_style = "width:"+ 100/state[0].length+"%; height:"+100/state.length+"%;"
var tbl = $("<table cellpadding='0' cellspacing='0' border='0'>");
for (var y = 0; y < state.length; y++) {
tbl.append("<tr>");
for (var x = 0; x < state[y].length; x++) {
var cell_class = state[y][x] == 1 ? 'cell alive' : 'cell';
var cell_text = state[y][x] == 1 ? '&hearts;' : '&dagger;';
$("tr:last", tbl).append("<td class='"+cell_class+"' style='"+cell_style+"'>"+cell_text+"</td>");
}
}
$("#cells").empty().append(tbl);
$("#progress").text(step);
}
</script>
</head>
<body>
<div id="trigger"></div>
<div id="progress">1</div>
<div id="cells"></div>
</div>
</body>
</html>
require 'rubygems'
require 'sinatra'
require 'json'
require 'game_of_life'
set :views, Proc.new { File.join(root) }
get '/' do
@state = GameOfLife.new(30, 30, 30).evolve
erb :"life_sinatra.html"
end
post '/evolve' do
@gol = GameOfLife.new(3,3,0)
@gol.state = prepared_params(params[:state]) if params[:state]
@gol.evolve.to_json
end
def prepared_params(state_params)
prepared = Array.new(state_params.size)
state_params.each {|index,row| prepared[index.to_i] = row.map{|v| v.to_i} }
prepared
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment