Remote agent/Agent interface: Difference between revisions

m
→‎{{header|Wren}}: Changed to Wren S/H
(Added Wren)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(3 intermediate revisions by 2 users not shown)
Line 12:
 
=={{header|Go}}==
<langsyntaxhighlight lang="go">package ifc
 
// Streamer interface type defines how agent and world talk to each other.
Line 49:
EvNoBallInSector = 's'
EvNoBallInAgent = 'a'
)</langsyntaxhighlight>
Above is the interface package defining the abstract interface. Below is an example of a driver program that supplies a concrete implementation of the interface. This implementation uses Go channels to run a simulation in a single process. A different implementation might produce two executables that would run on different processes or different machines. It would use the same interface however.
<langsyntaxhighlight lang="go">package main
 
import (
Line 99:
// point. The program terminates immediately when World returns.
world.World(chanStreamer{"world", cmd, ev})
}</langsyntaxhighlight>
A separate file, but still part of the driver, contains human readable text for commands and events. Alternatively, this file could be included with the interface package for maintainability, but technically it is not a required part of the abstract interface used by the agent and world simulator.
<langsyntaxhighlight lang="go">package main
 
import "ra/ifc"
Line 127:
ifc.EvNoBallInSector: "event no ball in sector",
ifc.EvNoBallInAgent: "event no ball in agent",
}</langsyntaxhighlight>
 
 
Line 136:
=== abstract interface ===
Modify this to include one of the two concrete implementations below (but not both!)
<!--<langsyntaxhighlight Phixlang="phix">(notonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\Remote_Agent_Interface.exw
Line 227:
-- in a suitable format for use as per fn() in show() above.
--</span>
<!--</langsyntaxhighlight>-->
 
=== Interface_Direct ===
This implementation of the above interface is a single standalone program
<!--<langsyntaxhighlight Phixlang="phix">(notonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\Remote_Agent_Interface_Direct.e
Line 281:
<span style="color: #008080;">if</span> <span style="color: #000000;">accept_command</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #000000;">main_event_loop</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<!--</langsyntaxhighlight>-->
 
=== Interface_IPC ===
This implementation of the above interface lets two separate programs communicate using ipc
<!--<langsyntaxhighlight Phixlang="phix">(notonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\Remote_Agent_Interface_IPC.e
Line 396:
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<!--</langsyntaxhighlight>-->
 
=={{header|PicoLisp}}==
Line 403:
=={{header|Tcl}}==
{{works with|Tcl|8.6}}
<langsyntaxhighlight lang="tcl">package require Tcl 8.6
 
oo::class create AgentAPI {
Line 526:
 
# Export as package
package provide RC::RemoteAgent 1</langsyntaxhighlight>
 
=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-strfmt}}
Unlike the Go entry, I've placed the 'human readable text' code inside the 'ifc' module and have also placed a basic logging facility in there too as its called by both the 'world' and 'agent' modules. It always prints to stdout.
<lang ecmascript>/* agent.wren */
 
I've changed the 'handshake' symbol from "A" to "H" to distinguish it from 'evAgentFull'.
import "random" for Random
import "./ifc" for Ifc, Log
import "./str" for Char
 
The 'driver code' has been moved to the [[Remote_agent/Simulation]] task.
// The agent's awareness is quite limited. It has no representation of the
<syntaxhighlight lang="wren">/* ifc.wren */
// maze, which direction it is facing, or what it did last. It notices and
// remembers just three things: The color of the sector just entered, the
// presence and color of any ball there, and the presence and color of any
// ball it is holding.
var sectorColor = " "
var sectorBall = " "
var agentBall = " "
var stream = null
var noColor = "-"
 
import "./fmt" for Fmt
var rand = Random.new()
 
// The Streamer abstract class defines how agent and world talk to each other.
// Move moves one sector in a random direction.
// If implemented in Wren, send and rec will be synchronous blocking operations.
// It retries on bumps and doesn't return until a forward command succeeds.
class Streamer {
// It expects a color event on a successful move and terminates if it doesn't
send(ch) {}
// get one.
var move = Fn.new rec() {}
while (true) {
// Randomness: 50/50 chance of turning or attempting move.
// For turns, equal chance of turning right or left.
var t = rand.int(4)
if (t == 0) {
stream.send(Ifc.cmdLeft)
Fiber.yield()
while (stream.rec() != Ifc.evStop) { Fiber.yield() }
continue
} else if (t == 1) {
stream.send(Ifc.cmdRight)
Fiber.yield()
while (stream.rec() != Ifc.evStop) { Fiber.yield() }
continue
}
stream.send(Ifc.cmdForward)
var bump = false
sectorColor = noColor
sectorBall = noColor
while (true) {
Fiber.yield()
var ev = stream.rec()
if (ev == Ifc.evBump) {
bump = true
} else if ([Ifc.evColorRed, Ifc.evColorGreen, Ifc.evColorYellow, Ifc.evColorBlue].contains(ev)) {
sectorColor = ev
} else if ([Ifc.evBallRed, Ifc.evBallGreen, Ifc.evBallYellow, Ifc.evBallBlue].contains(ev)) {
sectorBall = ev
} else if (ev == Ifc.evStop) {
break
}
}
if (bump) continue
if (sectorColor == noColor) Log.fatal("agent: expected color event after move")
return
}
}
 
// The agent and world send and receive single characters, out of the set of
// Get is only called when get is possible.
// constants defined here.
var get = Fn.new {
class Ifc {
stream.send(Ifc.cmdGet)
static handshake { "H" }
while (true) {
static cmdForward Fiber.yield() { "^" }
static cmdRight var ch = stream.rec() { ">" }
static cmdLeft if (ch == Ifc.evStop) { "<" }
static cmdGet // agent notes ball color,{ and"@" that sector is now empty}
static cmdDrop agentBall = sectorBall { "!" }
static evGameOver sectorBall{ ="+" noColor}
static evStop return { "." }
static evColorRed { "R" }
} else if (ch == Ifc.evNoBallInSector || ch == Ifc.evAgentFull) {
static evColorGreen { "G" }
Log.fatal("agent:expected get to succeed")
static evColorYellow { "Y" }
static evColorBlue { "B" }
static evBallRed { "r" }
static evBallGreen { "g" }
static evBallYellow { "y" }
static evBallBlue { "b" }
static evBump { "|" }
static evSectorFull { "S" }
static evAgentFull { "A" }
static evNoBallInSector { "s" }
static evNoBallInAgent { "a" }
 
// Human readable text for the above commands and events.
static init_() {
__hrText = {
handshake : "handshake",
cmdForward : "command forward",
cmdRight : "command turn right",
cmdLeft : "command turn left",
cmdGet : "command get",
cmdDrop : "command drop",
evGameOver : "event game over",
evStop : "event stop",
evColorRed : "event color red",
evColorGreen : "event color green",
evColorYellow : "event color yellow",
evColorBlue : "event color blue",
evBallRed : "event ball red",
evBallGreen : "event ball green",
evBallYellow : "event ball yellow",
evBallBlue : "event ball blue",
evBump : "event bump",
evSectorFull : "event sector full",
evAgentFull : "event agent full",
evNoBallInSector : "event no ball in sector",
evNoBallInAgent : "event no ball in agent"
}
}
}
 
static text { __hrText }
// FindMatching is only called when agent has a ball.
// FindMatching finds a sector where the color matches the ball the agent
// is holding and which does not already contain a matching ball.
// It does not necessarily find an empty matching sector.
var findMatching = Fn.new {
while (Char.lower(sectorColor) != agentBall || agentBall == sectorBall) move.call()
}
 
class Log {
// FindMisplaced wanders the maze looking for a ball on the wrong sector.
static prefix=(p) { __prefix = Fmt.swrite("$06d: ", p) }
var findMisplaced = Fn.new {
while (true) {
move.call()
 
static print(s) { System.print(__prefix + s) }
// get ball from current sector if meaningful
if ([Ifc.evBallRed, Ifc.evBallGreen, Ifc.evBallYellow, Ifc.evBallBlue].contains(sectorBall)) {
if (sectorBall != Char.lower(sectorColor)) return
}
}
}
 
static fatal(s) { Fiber.abort(s) }
// Drop is only called when the agent has a ball. Unlike get() however,
// drop() can be called whether the sector is empty or not. drop() means
// drop as soon as possible, so if the sector is full, drop() will wander
// at random looking for an empty sector.
var drop = Fn.new {
var gameOver = false
while (sectorBall != noColor) move.call()
 
// expected to work
stream.send(Ifc.cmdDrop)
while(true) {
Fiber.yield()
var ch = stream.rec()
if (ch == Ifc.evGameOver) {
gameOver = true
} else if (ch == Ifc.evStop) {
break
} else if (ch == Ifc.evNoBallInAgent || ch == Ifc.evSectorFull) {
Log.fatal("expected drop to succeed")
}
}
sectorBall = agentBall
agentBall = noColor
return gameOver
}
 
Ifc.init_()</syntaxhighlight>
var Agent = Fn.new { |s|
stream = s
 
// handshake
var hs = stream.rec()
if (hs != Ifc.handshake) Log.fatal("agent: that's no handshake")
stream.send(Ifc.handshake)
Fiber.yield()
 
// agent behavior main loop
var gameOver = false
while (!gameOver) {
findMisplaced.call()
get.call()
findMatching.call()
gameOver = drop.call()
}
}</lang>
9,488

edits