Created
July 2, 2010 09:41
-
-
Save fpauser/461159 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
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 |
This file contains hidden or 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
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 file contains hidden or 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
# | |
# 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 |
This file contains hidden or 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
require File.join(File.dirname(__FILE__), 'game_of_life') | |
require File.join(File.dirname(__FILE__), 'life_ncurses') | |
LifeNcurses.new( GameOfLife.new(20, 20, 20) ) |
This file contains hidden or 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
<!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 ? '♥' : '†'; | |
$("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> |
This file contains hidden or 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
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