Updated 2012-10-28 05:22:05 by RLE

Jason Tang (September 25, 2002) - Here is another implementation of the cellular automata game of Life.

(The gratuitous screenshots, one for Windows and the other for Linux.)

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://tcl.jtang.org/life/life.tcl
 ### Life by Jason Tang
 ### configurable parameters below

 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

 ### end configuration

 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