-
-
Save JEG2/dee55426374e780a9870 to your computer and use it in GitHub Desktop.
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
#!/usr/bin/env ruby | |
require "observer" | |
require "singleton" | |
class Clock | |
include Observable | |
include Singleton | |
def run( seconds_delay ) | |
start seconds_delay | |
@thread.join | |
end | |
def start( seconds_delay ) | |
@thread = Thread.new(self, seconds_delay) do |notifier, delay| | |
loop do | |
sleep delay | |
notifier.changed | |
notifier.notify_observers Time.now | |
end | |
end | |
end | |
def stop | |
unless @thread.nil? | |
@thread.kill | |
@thread = nil | |
end | |
end | |
end | |
if $0 == __FILE__ | |
unless ARGV.size == 1 and ARGV[0] =~ /^(?:\d+\.)?\d+$/ | |
puts "Usage: #$0 DELAY_IN_SECONDS" | |
exit | |
end | |
class TimePrinter | |
def update( time ) | |
puts time | |
end | |
end | |
clock = Clock.instance | |
clock.add_observer TimePrinter.new | |
puts "Starting clock... (waiting 10 seconds)" | |
clock.start ARGV.shift.to_f | |
sleep 10 | |
clock.stop | |
puts "Clock stopped... (waiting 10 seconds)" | |
sleep 10 | |
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
#!/usr/bin/env ruby | |
$: << "libs" | |
require "clock" | |
module Life | |
class Cell | |
def initialize( state = false ) | |
@state = @next_state = state | |
end | |
def alive?() @state end | |
def figure_next_state( *neighbor_cells ) | |
neighbors = neighbor_cells.inject(0) do |sum, v| | |
if v.alive? then sum + 1 else sum end | |
end | |
if @state and (neighbors < 2 or neighbors > 3) | |
@next_state = false | |
elsif not @state and neighbors == 3 | |
@next_state = true | |
end | |
not @state == @next_state | |
end | |
def transition | |
changed = (not @state == @next_state) | |
@state = @next_state | |
changed | |
end | |
end | |
class Universe | |
def initialize( max_turns, file, view_class = UniverseView ) | |
@max_turns = max_turns | |
@turn = 0 | |
cells = [ ] | |
File.open(file) do |f| | |
while line = f.gets | |
next unless line =~ /\S/ | |
cells << [ ] | |
line.delete(" \t\n").each_byte do |char| | |
if char == ?. | |
cells[-1] << Cell.new | |
else | |
cells[-1] << Cell.new(true) | |
end | |
end | |
end | |
end | |
@cells = cells.transpose | |
@neighbors = { } | |
@active = [ ] | |
(0...(@cells[0].size)).each do |y| | |
(0...(@cells.size)).each do |x| | |
@active.push(*find_neighbors(x, y)) if @cells[x][y].alive? | |
end | |
end | |
@view = create_view view_class | |
@view.render | |
end | |
def create_view( view_class ) | |
view = view_class.new(@cells.size, @cells[0].size, true) | |
(0...(@cells[0].size)).each do |y| | |
(0...(@cells.size)).each do |x| | |
view.update x, y, @cells[x][y].alive? | |
end | |
end | |
view | |
end | |
def update( time ) | |
@active = @active.find_all do |xy| | |
neighbors = find_neighbors(*xy).map { |e| @cells[e[0]][e[1]] } | |
@cells[xy[0]][xy[1]].figure_next_state(*neighbors) | |
end | |
next_active = [ ] | |
@active.find_all do |xy| | |
if @cells[xy[0]][xy[1]].transition | |
@view.update xy[0], xy[1], @cells[xy[0]][xy[1]].alive? | |
next_active << xy | |
next_active.push(*find_neighbors(*xy)) | |
end | |
end | |
@active = next_active.uniq | |
@view.render | |
@turn += 1 | |
exit if @max_turns == @turn or @active.size == 0 | |
end | |
private | |
def find_neighbors( x, y ) | |
where = x.to_s + "," + y.to_s | |
return @neighbors[where] if @neighbors.include? where | |
neighbors = [ ] | |
[-1, 0, 1].each { |n| neighbors.push( [x + n, y + -1], | |
[x + n, y + 0], | |
[x + n, y + 1] ) } | |
neighbors.delete_if { |xy| xy.include?(-1) or | |
xy[0] >= @cells.size or | |
xy[1] >= @cells[0].size or | |
(xy[0] == x && xy[1] == y) } | |
@neighbors[where] = neighbors | |
end | |
end | |
class UniverseView | |
def initialize( width, height, clear = false ) | |
@cells = Array.new(height) { Array.new(width) { " " } } | |
@clear = clear | |
end | |
def update( x, y, value ) | |
@cells[y][x] = if value then "X" else " " end | |
end | |
def render | |
system("clear") if @clear | |
@cells.each { |row| STDOUT << row.join("") + "\n" } | |
STDOUT << "\n" | |
end | |
end | |
end | |
if $0 == __FILE__ | |
unless ARGV.size == 2 and ARGV[0] =~ /^\d+$/ | |
puts "Usage: $0 MAX_TURNS PATTERN_FILE" | |
exit | |
end | |
Thread.abort_on_exception = true | |
clock = Clock.instance | |
clock.add_observer Life::Universe.new(ARGV.shift.to_i, ARGV.shift) | |
clock.run 0.3 | |
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
#!/usr/bin/env ruby | |
require "life" | |
class PPMGenerator < Life::UniverseView | |
@@filename = "life_" | |
def self.filename=( name ) @@filename = name end | |
def initialize( width, height, clear = false ) | |
@cells = Array.new(height) { Array.new(width) { false } } | |
@turn = 0 | |
end | |
def update( x, y, value ) @cells[y][x] = value end | |
def render | |
File.open("#@@filename#@turn.ppm", "w") do |f| | |
f.puts "P6" | |
f.puts "#{@cells[0].size * 7 + 1} #{@cells.size * 7 + 1} 255" | |
@cells.each do |row| | |
f.print "\x00\x00\xAA" * (@cells[0].size * 7 + 1) | |
f.print render_row(row) | |
end | |
end | |
@turn += 1 | |
end | |
private | |
def render_row( row ) | |
blank = "\x00\x00\xAA" + ("\0\0\0" * 6 + "\x00\x00\xAA") * row.size | |
divider = "\x00\x00\xAA" * (row.size * 7 + 1) | |
short = row.map do |cell| | |
if cell | |
"\0\0\0\xFF\x00\x00\xFF\x00\x00\0\0\0" | |
else | |
"\0\0\0" * 4 | |
end | |
end.join("\0\0\0\x00\x00\xAA\0\0\0") | |
short = "\x00\x00\xAA\0\0\0#{short}\0\0\0\x00\x00\xAA" | |
long = short.gsub( /\0\0\0\xFF\x00\x00\xFF\x00\x00\0\0\0/, | |
"\xFF\x00\x00\xFF\x00\x00\xFF\x00\x00\xFF\x00\x00" ) | |
blank + short + long * 2 + short + blank + divider | |
end | |
end | |
unless ARGV.size == 2 and ARGV[0] =~ /^\d+$/ | |
puts "Usage: $0 MAX_TURNS PATTERN_FILE" | |
exit | |
end | |
turns = ARGV.shift.to_i | |
file = ARGV.shift | |
PPMGenerator.filename = "images/" + File.basename(file, ".txt") + "_" | |
game = Life::Universe.new(turns, file, PPMGenerator ) | |
loop do | |
game.update "RUN" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment