Here's an version of Conway's Game of Life I recently updated to use [Snit's Not Incr Tcl]; I'm posting it now as an example of application development using Snit. -- [WHD] ---- #----------------------------------------------------------------------- # TITLE: # life.tcl # # AUTHOR: # Will Duquette # # DESCRIPTION: # life.tcl implements John Conway's classic Game of Life, one of # the first experiments with what's now called artificial life. # # Think of a plane divided up into squares like a checker board. # Each square can hold a one-celled animal. You start the game # by placing cells in squares. Then you watch the cells breed # and die through successive generations. The game is to find # starting patterns that do interesting things. # # Each square on the board has 8 neighbor squares; cells breed # and die based on how crowded they are, i.e., the number of # neighbors they have. Each new generation is computed as # follows: # # For each square, count the number of neighbor cells. # If the square is empty, and it has exactly 3 neighbor cells, a # new cell will be born there. If the square has a cell in it, # and the cell has less than 2 or more than 3 neighbor cells, # the cell will die. All of the counting is done first, and # then the new cells are added and the dead cell are removed all # at once. # # This GUI implementation allows cells to be added and removed # by clicking on the board. A generation passes when the # "Generate" button is clicked, or when the player presses the # Return key. # # The implementation has two pieces: # # 1. The board, which contains cells that can be turned on # and off and knows how to compute a new generation. The board # includes its own GUI display code. # # 2. The rest of the GUI. # # If the code were written for reuse, the board would be split into # two pieces: a generic gameboard suitable for Life, Othello, and # similar games, and Life code that uses the board. package require snit #----------------------------------------------------------------------- # The Board # # The Board is implemented as a Tk canvas widget, broken up into # squares on an NxN grid. The background is white and the grid # lines are cyan. Each square holds a circle object which # can be set to any desired color, normally white (for dead cells) and # forestgreen (for living cells). Each circle has a tag "i,j" so that # it can be manipulated individually. snit::widget board { # By default, 20x20 cells option -cells 20 # By default, each cell is 20x20 pixels option -pixels 20 # Milliseconds between generations option -delay 200 # For each cell on the board, this array remembers # whether the cell is alive or dead, and the coordinates of its # neighbors; this allows the neighbors to be counted more quickly. variable data # Remembers the list of i,j indices, to save time while generating. variable indices {} # True if we're completely constructed, false otherwise. variable constructed 0 constructor {argv} { # FIRST, create the canvas. Then configure the options. component hull is [canvas $self -background white] $self configurelist $argv # NEXT, set up the board. $self SetupBoard # No longer constructing. set constructed 1 } onconfigure -cells {value} { set options(-cells) $value if {$constructed} { $self SetupBoard } } onconfigure -pixels {value} { set options(-pixels) $value if {$constructed} { $self SetupBoard } } # This is still a canvas; delegate other methods and options to it: delegate method * to hull delegate option * to hull method SetupBoard {} { # Destroy any previous definition $self delete all array unset data set cells $options(-cells) set pixels $options(-pixels) set size [expr {$cells * $pixels}] # FIRST, set the size of the canvas $self configure -width $size -height $size # NEXT, draw the grid lines. for {set i 1} {$i < $cells} {incr i 1} { set pos [expr {$i * $pixels}] # Draw a vertical line $i cells over $self create line 0 $pos $size $pos -fill cyan # Draw a horizontal line $i cells down $self create line $pos 0 $pos $size -fill cyan } # NEXT, compute the list of indices set indices {} for {set i 0} {$i < $cells} {incr i 1} { for {set j 0} {$j < $cells} {incr j 1} { lappend indices $i,$j } } # NEXT, add a circle object to each cell for {set i 0} {$i < $cells} {incr i 1} { for {set j 0} {$j < $cells} {incr j 1} { # Compute the upper left corner of the circle set p0 [expr {$i*$pixels + 1}] set q0 [expr {$j*$pixels + 1}] # Compute the lower left corner of the circle set p1 [expr {$p0 + $pixels - 2}] set q1 [expr {$q0 + $pixels - 2}] # Create the circle, tagging it $i,$j $self create oval $p0 $q0 $p1 $q1 \ -tag $i,$j -fill white -outline white # When the user clicks on it, it should toggle. $self bind $i,$j