Remote agent/Agent interface: Difference between revisions
Content added Content deleted
(Agent interface task part.) |
(→Tcl: Added implementation + demonstration of how to use the interface) |
||
Line 1: | Line 1: | ||
{{draft task}}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. |
{{draft task}}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. |
||
=={{header|Tcl}}== |
|||
{{works with|Tcl|8.6}} |
|||
<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 "" |
|||
} |
|||
} |
|||
} |
|||
if {rand() < 0.5} { |
|||
my TurnLeft |
|||
} else { |
|||
my TurnRight |
|||
} |
|||
} trap bumpedWall {} {} |
|||
} |
|||
set ::wonGame ok |
|||
} |
|||
} |
|||
Agent new |
|||
vwait wonGame</lang> |