Jason Tang 2002-09-25: Here is another implementation of the cellular automata game of
Life.
Use the mouse and left drag to add life, right drag to remove. Hit the enter key to resume simulating. As cells "age" they change colors. Hope you like...If you want to try a
Starkit version download it here [
1].
An optimized version of the program may be downloaded from
http://jtang.org/tcl/life/ Screenshot edit
package require Tk
set CELL_SIZE 15 ;# size of each cell, in pixels
set NUM_ROWS 20 ;# number of rows in the grid
set NUM_COLS 20 ;# number of columns in the grid
set REFRESH 10 ;# how often to calculate next iteration, in milliseconds
set RESPAWN 120 ;# number of iterations before spawning new life
set DENSITY 0.6 ;# how crowded to make world when generating life
set NUM_ROWS 15 ;# number of rows in the grid
set NUM_COLS 15 ;# number of columns in the grid
proc life {start_col end_col start_row end_row} {
global life
for {set row $start_row} {$row <= $end_row} {incr row} {
for {set col $start_col} {$col <= $end_col} {incr col} {
set count($col,$row) 0
if {$life($col,$row) == 1} {
set count($col,$row) -1
}
for {set x [expr $col - 1]} {$x <= [expr $col + 1]} {incr x} {
for {set y [expr $row - 1]} {$y <= [expr $row + 1]} {incr y} {
if {$x < $start_col} {
set x_col $end_col
} elseif {$x > $end_col} {
set x_col $start_col
} else {
set x_col $x
}
if {$y < $start_row} {
set y_row $end_row
} elseif {$y > $end_row} {
set y_row $start_row
} else {
set y_row $y
}
incr count($col,$row) $life($x_col,$y_row)
}
}
}
}
for {set row $start_row} {$row <= $end_row} {incr row} {
for {set col $start_col} {$col <= $end_col} {incr col} {
if {$life($col,$row) == 0 && $count($col,$row) == 3} {
make_life $col $row
} elseif {$life($col,$row) != 1 || \
[expr {$count($col,$row) != 2} && \
{$count($col,$row) != 3}]} {
kill_life $col $row
} else {
incr life($col,$row:age)
if {$life($col,$row:age) >= 10} {
.c itemconfigure $life($col,$row:id) -fill red
} elseif {$life($col,$row:age) >= 7} {
.c itemconfigure $life($col,$row:id) -fill orange
} elseif {$life($col,$row:age) >= 5} {
.c itemconfigure $life($col,$row:id) -fill yellow
} elseif {$life($col,$row:age) >= 4} {
.c itemconfigure $life($col,$row:id) -fill greenyellow
} elseif {$life($col,$row:age) >= 3} {
.c itemconfigure $life($col,$row:id) -fill green
} elseif {$life($col,$row:age) >= 2} {
.c itemconfigure $life($col,$row:id) -fill turquoise
}
}
}
}
}
proc make_life {col row} {
global life CELL_SIZE
set life($col,$row:id) [.c create rectangle \
[expr $col * $CELL_SIZE + 1] \
[expr $row * $CELL_SIZE + 1] \
[expr [expr $col + 1] * $CELL_SIZE - 1] \
[expr [expr $row + 1] * $CELL_SIZE - 1] \
-fill blue -width 0]
set life($col,$row:age) 1
set life($col,$row) 1
}
proc kill_life {col row} {
global life
set life($col,$row) 0
if {[info exists life($col,$row:id)]} {
.c delete $life($col,$row:id)
unset life($col,$row:id)
unset life($col,$row:age)
}
}
proc generate_life {start_col end_col start_row end_row ratio} {
global life
for {set row $start_row} {$row <= $end_row} {incr row} {
for {set col $start_col} {$col <= $end_col} {incr col} {
if {[expr rand() < $ratio]} {
make_life $col $row
}
}
}
}
proc init_life {start_col end_col start_row end_row} {
global life
for {set row $start_row} {$row <= $end_row} {incr row} {
for {set col $start_col} {$col <= $end_col} {incr col} {
kill_life $col $row
}
}
}
proc button_down {x y new_life} {
global CELL_SIZE
set col [expr int($x / $CELL_SIZE)]
set row [expr int($y / $CELL_SIZE)]
kill_life $col $row
if {$new_life} {
make_life $col $row
}
}
proc life_timer {} {
global age NUM_ROWS NUM_COLS REFRESH RESPAWN DENSITY
incr age -1
if {$age < 0} {
set age $RESPAWN
init_life 0 [expr $NUM_COLS - 1] 0 [expr $NUM_ROWS - 1]
generate_life 0 [expr $NUM_COLS - 1] 0 [expr $NUM_ROWS - 1] $DENSITY
} else {
life 0 [expr $NUM_COLS - 1] 0 [expr $NUM_ROWS - 1]
}
update idletasks
after $REFRESH life_timer
}
canvas .c -bg black -relief flat \
-scrollregion [list 0 0 [expr $NUM_COLS * $CELL_SIZE] \
[expr $NUM_ROWS * $CELL_SIZE]] \
-width [expr $NUM_COLS * $CELL_SIZE] \
-height [expr $NUM_ROWS * $CELL_SIZE]
pack .c
wm protocol . WM_DELETE_WINDOW { exit }
wm title . "Life"
bind . <ButtonPress-1> {after cancel life_timer; button_down %x %y 1}
bind . <ButtonPress-3> {after cancel life_timer; button_down %x %y 0}
bind . <B1-Motion> {button_down %x %y 1}
bind . <B3-Motion> {button_down %x %y 0}
bind . <Key-Return> {after cancel life_timer; set age $RESPAWN; life_timer}
bind . <Key-space> {after cancel life_timer}
update idletasks
set age 0
life_timer