Remote agent/Agent interface
In Remote agent, a component is described that marshals commands and events between a stream and a program that issues commands and processes the resulting events. Using the protocol definition described there, build this component in a fashion idiomatic and natural to your language.
Tcl
<lang tcl>package require Tcl 8.6
oo::class create AgentAPI {
variable sock events sectorColor ballColor constructor {host port} {
set sock [socket $host $port] fconfigure $sock -buffering none -translation binary -encoding ascii \ -blocking 0 # Temporary hack interp alias {} yieldto {} ::tcl::unsupported::yieldTo coroutine ReaderCoroutine my ReadLoop
} destructor {
if {[llength [info command ReaderCoroutine]]} { rename ReaderCoroutine {} } if {[llength [info command AgentCoroutine]]} { rename AgentCoroutine {} } if {$sock ne ""} { catch {close $sock} }
} method Log message { }
# Commands method ForwardStep {} {
my Log "action: forward" puts -nonewline $sock "^" my ProcessEvents [yield]
} method TurnRight {} {
my Log "action: turn right" puts -nonewline $sock ">" my ProcessEvents [yield]
} method TurnLeft {} {
my Log "action: turn left" puts -nonewline $sock "<" my ProcessEvents [yield]
} method GetBall {} {
my Log "action: get ball" puts -nonewline $sock "@" my ProcessEvents [yield]
} method DropBall {} {
my Log "action: drop ball" puts -nonewline $sock "!" my ProcessEvents [yield]
} method ProcessEvents {events} {
set sectorColor {} set ballColor {} set err {} set done 0 foreach e $events { my Log "event: $e" switch [lindex $e 0] { sector {set sectorColor [lindex $e 1]} ball {set ballColor [lindex $e 1]} error {set err [lindex $e 1]} gameOver {set done 1} } } if {$err ne ""} {throw $err "can't do that: $err"} return $done
}
# Event demux method ReadLoop {} {
puts -nonewline $sock "A" fileevent $sock readable [info coroutine] while 1 { yield if {[read $sock 1] eq "A"} break } try { coroutine AgentCoroutine my Behavior while 1 { yield set ch [read $sock 1] switch $ch { "." { # Stop - end of events from move set e $events set events {} yieldto AgentCoroutine $e if {"gameOver" in $e} break } "+" {lappend events gameOver} "R" {lappend events {sector red}} "G" {lappend events {sector green}} "Y" {lappend events {sector yellow}} "B" {lappend events {sector blue}} "r" {lappend events {ball red}} "g" {lappend events {ball green}} "y" {lappend events {ball yellow}} "b" {lappend events {ball blue}} "|" {lappend events {error bumpedWall}} "S" {lappend events {error sectorFull}} "A" {lappend events {error agentFull}} "s" {lappend events {error sectorEmpty}} "a" {lappend events {error agentEmpty}} } } } finally { close $sock set sock "" }
}
method Behavior {} {
error "method not implemented"
}
}</lang> Sample agent (not a good player of the game; just to show how to program to the interface): <lang tcl>oo::class create Agent {
superclass AgentAPI variable sectorColor ballColor method Behavior {} {
set ball "" while 1 { try { while {rand() < 0.5} { my ForwardStep if { $ball eq "" && $ballColor ne "" && $ballColor ne $sectorColor } then { set ball [set ballTarget $ballColor] my GetBall } elseif {$ball ne "" && $ballTarget eq $sectorColor} { try { if {[my DropBall]} { break } set ball "" } trap sectorFull {} { # Target square full; drop this ball anywhere set ballTarget "" } } } } trap bumpedWall {} {} if {rand() < 0.5} { my TurnLeft } else { my TurnRight } } set ::wonGame ok
}
}
Agent new "localhost" 12345 vwait wonGame</lang>