Version 0 of Life

Updated 2002-09-25 15:23:15

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

Use the mouse and right click on the canvas to add cells. Hope you like...


 ### Life by Jason Tang
 ### configurable parameters below

 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 . <Key-F2> {console show}
 bind . <ButtonPress-1> {after cancel life_timer}
 bind . <ButtonPress-3> {after cancel life_timer}
 bind . <B1-Motion> {button_down %x %y 1}
 bind . <B3-Motion> {button_down %x %y 0}
 bind . <ButtonRelease-1> {life_timer}
 bind . <ButtonRelease-3> {life_timer}

 update idletasks

 set age 0
 life_timer