- Make the eyes follow the purple dog by combining the two examples
- Make a polka dot that follows the cursor and changes colour from time to time
- Make the rabbits hunt the dog
- Emulate the classic game of "pong"
- Emulate the classic game of "pacman"
- Emulate turtle graphics (including drawing trees)
- A simple drawing program
Zarutian 14. april 2005: Hmm... NetLogo[2] is probably similar what you are thinking about.
# arena.tcl --
# Run a small arena with various agents (animals if you like) that
# hunt or hinder each other
# Use:
# wish arena.tcl agents.def
# (agents.def defines what agents)
#
# Problems:
# - Errors in the user-defined script can make the application
# enter an endless loop of error messages. (catch these in
# the "act" procedure?)
# - No checking of unique names yet - may lead to error messages
# - If you write to the (Windows) console in an action callback, the
# application may get stuck.
#
package require Tk
package require BWidget
#
# Create the main window
#
proc MainWindow {_width _height} {
global canvas
global ctree
global width
global height
set width $_width
set height $_height
set canvas [canvas .cnv -width $width -height $height -background white]
set ctree [Tree .ctree]
grid $ctree $canvas -sticky news
}
#
# Basic shapes
#
proc oval {color xmin ymin xmax ymax} {
global canvas
$canvas create oval $xmin $ymin $xmax $ymax -fill $color
}
proc rectangle {color xmin ymin xmax ymax} {
global canvas
$canvas create rectangle $xmin $ymin $xmax $ymax -fill $color
}
#
# Agents
#
proc type-agent {type data} {
global ctree
global agent_type
set agent_type($type) $data
$ctree insert end root type_$type -text $type
}
proc agent {type name data} {
global agent_type
global agent
global ctree
set agent($name,type) $type
$ctree insert end type_$type agent_$name -text $name
ConstructAgent $name initial color black
ConstructAgent $name initial form dot
ConstructAgent $name initial size 10
set agent($name,oldattr,position) {0 0}
ConstructAgent $name initial position {0 0}
set cmd {}
foreach line [split $agent_type($type) \n] {
if { [string trim $line] == "" } {
continue
}
append cmd $line
if { [info complete $cmd] } {
eval ConstructAgent [list $name] $cmd
set cmd {}
} else {
append cmd \n
}
}
foreach {key value} $data {
ConstructAgent $name change $key $value
}
DrawAgent $name 1
}
proc ConstructAgent {name command args} {
global ctree
global agent
switch -- $command {
"initial" {
foreach {key value} $args break
set agent($name,attr,$key) $value
if { ! [$ctree exists agent_${name}_$key] } {
$ctree insert end agent_$name \
agent_${name}_$key -text "$key = $value"
} else {
$ctree itemconfigure \
agent_${name}_$key -text "$key = $value"
}
}
"change" {
foreach {key value} $args break
set agent($name,attr,$key) $value
$ctree itemconfigure agent_${name}_$key -text "$key = $value"
}
"action" {
foreach {key cmds} $args break
set agent($name,cmds,$key) $cmds
}
"start" {
act $name $args
}
}
}
proc DrawAgent {name create} {
global canvas
global agent
#
# For now: simply a dot
#
if { $create } {
set agent($name,id) \
[$canvas create oval 0 0 \
$agent($name,attr,size) $agent($name,attr,size) \
-fill $agent($name,attr,color)]
}
$canvas coords $agent($name,id) 0 0 \
$agent($name,attr,size) $agent($name,attr,size)
foreach {dx dy} $agent($name,attr,position) {
set dx [expr {$dx-0.5*$agent($name,attr,size)}]
set dy [expr {$dy-0.5*$agent($name,attr,size)}]
break
}
$canvas move $agent($name,id) $dx $dy
$canvas itemconfigure $agent($name,id) -fill $agent($name,attr,color)
}
proc act {name action} {
global self
if { $name == "Self" } {
set name $self
}
after 50 [list ActAgent $name $action]
}
proc ActAgent {name action} {
global self
global agent
set self $name
eval $agent($name,cmds,$action)
DrawAgent $name 0
}
#
# Things an agent can do
#
proc add {op1 op2} {
expr {$op1+$op2}
}
proc mult {op1 op2} {
expr {$op1*$op2}
}
proc random {op1} {
expr {rand()*$op1}
}
proc direction {from to} {
global self
global agent
#
# For the moment: only positions
#
foreach {x1 y1} $from {break}
foreach {x2 y2} $to {break}
expr {atan2(($y2-$y1),($x2-$x1))*180.0/3.1415926}
}
proc distance {from to} {
global self
global agent
#
# For the moment: only positions
#
foreach {x1 y1} $from {break}
foreach {x2 y2} $to {break}
expr {hypot(($y2-$y1),($x2-$x1))}
}
proc delay {delay} {
set ::continue 0
after [expr {int(1000*$delay)}] {set ::continue 1}
vwait ::continue
}
proc newpos {start dist dir} {
global self
global agent
global width
global height
#
# For the moment: only positions
#
foreach {xold yold} $start {break}
set xnew [expr {$xold+$dist*cos($dir/180.0*3.1415926)}]
set ynew [expr {$yold+$dist*sin($dir/180.0*3.1415926)}]
if { $xnew < 0.0 } {
set xnew [expr {$xnew+$width}]
}
if { $ynew < 0.0 } {
set ynew [expr {$ynew+$height}]
}
if { $xnew > $width } {
set xnew [expr {$xnew-$width}]
}
if { $ynew > $height } {
set ynew [expr {$ynew-$height}]
}
list $xnew $ynew
}
proc change-attr {name attr value} {
global self
global agent
if { $name == "Self" } {
set name $self
}
if { $attr == "position" } {
set agent($name,oldattr,$attr) $agent($name,attr,$attr)
}
ConstructAgent $name change $attr $value
DrawAgent $name 0
}
proc get-attr {name attr} {
global agent
global self
if { $name == "Self" } {
set name $self
}
return $agent($name,attr,$attr)
}
#
# Bring up the main window
#
MainWindow 300 300
#
# Define the mouse agent
#
type-agent Mouse {}
agent Mouse Mouse {position {-100 -100}}
bind $canvas <Motion> {ConstructAgent Mouse initial position {%x %y}}
#
# Test: xeyes-like agents
#
if { 0 } {
oval black 100 100 160 180
oval white 105 105 155 175
oval black 200 100 260 180
oval white 205 105 255 175
type-agent Eye {
initial color green
initial position {0 0}
initial centre {0 0}
action where {
set centre [get-attr Self centre]
set dir [direction $centre [get-attr Mouse position]]
set pos [newpos $centre 20 $dir]
change-attr Self position $pos
#
# Change color and size if the mouse is very close
# to the left (!) eye
# Note:
# You get somewhat unexpected effects if you do not
# restrict this to one eye!
#
if { $name == "left" } {
set dist [distance $pos [get-attr Mouse position]]
if { $dist < 40 } {
change-attr left color blue
change-attr right size 30
} else {
change-attr left color green
change-attr right size 10
}
}
act Self where
}
start where ;# We need to kick the agent into action
}
set count 0
agent Eye left {position {145 140} centre {130 140}}
agent Eye right {position {245 140} centre {230 140}}
}
if { 1 } {
#
# Test: dog hunting rabbits
# Note:
# Getting the distances right takes some experimenting.
# With the settings that are given, the movements of the dog
# are rather smooth. The rabbits "jitter" a bit.
#
type-agent Rabbit {
initial color brown
initial position {0 0}
action fleedog {
set selfpos [get-attr Self position]
set dogpos [get-attr dog position]
if { [distance $selfpos $dogpos] < 50 } {
set dir [direction $selfpos $dogpos]
set rnd [random 360]
set pos [newpos $selfpos 20 [add $dir $rnd]]
change-attr Self position $pos
change-attr Self color yellow
} else {
change-attr Self color brown
}
act Self fleedog
}
start fleedog ;# We need to kick the agent into action
}
type-agent Dog {
initial color magenta
initial size 30
initial position {0 0}
action chaserabbit {
set mindist 1000000000.0
set dir ""
foreach r {rabbit1 rabbit2 rabbit3} {
set selfpos [get-attr Self position]
set rabbitpos [get-attr $r position]
if { [distance $selfpos $rabbitpos] < $mindist } {
set mindist [distance $selfpos $rabbitpos]
set dir [direction $selfpos $rabbitpos]
}
}
if { $dir != "" } {
set pos [newpos $selfpos [mult $mindist 0.1] $dir]
change-attr Self position $pos
}
act Self chaserabbit
}
start chaserabbit ;# We need to kick the agent into action
}
agent Rabbit rabbit1 {position {40 290}}
agent Rabbit rabbit2 {position {140 90}}
agent Rabbit rabbit3 {position {290 90}}
agent Dog dog {position {245 140}}
}
#
# Traffic lights
#
if { 0 } {
rectangle black 70 70 130 250
type-agent TrafficLight {
initial color darkgrey
initial on-color purple
initial position {0 0}
initial size 40
initial next ?
initial delay 1
action changecolor {
change-attr Self color [get-attr Self on-color]
delay [get-attr Self delay]
change-attr Self color darkgrey
act [get-attr Self next] changecolor
}
}
agent TrafficLight green { position {100 220} on-color green next orange}
agent TrafficLight orange { position {100 160} on-color orange next red}
agent TrafficLight red { position {100 100} on-color red next green}
act green changecolor
}

