Remote agent/Simulation: Difference between revisions

m
m (Wrong command character)
m (→‎{{header|Wren}}: Minor tidy)
 
(18 intermediate revisions by 11 users not shown)
Line 1:
{{draft task}}As described in [[Remote agent]], generate a map, accept and respond to commands from an agent using an unbuffered stream.
 
=={{header|PicoLispC}}==
See [[Remote agent/Simulation/C]]
Here is my first attempt on the server part. It works. After starting
(gameServer), you can connect with 'telnet', type the commands, and see the
responses.
<lang PicoLisp>(load "@lib/simul.l")
 
=={{header|Go}}==
# Global variables:
<syntaxhighlight lang="go">package world
 
import (
"bytes"
"fmt"
"log"
 
"ra/ifc"
)
 
// Maze sectors are 3x3 bytes, with these quirks:
// String starts with a newline.
// Space at end of each line except the last.
// No space, newline, or blank lines following last W.
//
// In each sector, W or sector Color is mandantory. If sector has ball,
// it is in the byte to the right. If agent is in a sector, it is in
// the byte below and shown by its direction symbol. If agent has a ball,
// it is in the byte to the right.
//
// The variable maze is not just input, but is the primary representation
// of the world. Over the course of execution, walls and sector colors
// are constant; agent and balls can move.
 
/* Minimal layout looks like this:
var maze = []byte(`
W W W W W W
 
 
W Rb W Rg B W
 
 
W G G B G W
 
 
W Br G W R W
^
 
W W W W W W`)
*/
 
// Following is equivalent, but with walls a little easier to see:
var maze = []byte(`
WWWWWWWWWWWWWWWW
W W W
W W W
W Rb W Rg B W
W W
W W
W G G B G W
W W
W W
W Br G W R W
W W ^ W
W W W
WWWWWWWWWWWWWWWW`)
 
// Maze and the following variables are defined at package level
// just to simplify function calls.
var (
stream ifc.Streamer
rowLen int
agentPos int
)
 
// Directions are not part of the interface, but just something the
// world uses to keep track of the orientation of the agent.
// A little quirk of bytes.IndexAny is that it takes a string.
// Otherwise, the []byte version is more useful for bytes.IndexByte.
const dirString = "^>v<"
 
var directions = []byte(dirString)
 
func rightOf(dir byte) byte {
return directions[(bytes.IndexByte(directions, dir)+1)%4]
}
 
func leftOf(dir byte) byte {
return directions[(bytes.IndexByte(directions, dir)+3)%4]
}
 
func World(s ifc.Streamer) {
stream = s
rowLen = bytes.Index(maze[1:], []byte{'\n'}) + 1
// A couple of validations for things I thought might be easy to
// mess up when editing the maze or defining a new one.
// Additional maze validation could be added.
cols := rowLen / 3
rows := ((len(maze)+1)/rowLen + 2) / 3
if len(maze) != (rows*3-2)*cols*3-1 {
log.Fatal("mis-shaped maze")
}
agentPos = bytes.IndexAny(maze, dirString)
if agentPos < 0 {
log.Fatal("agent not in maze")
}
// initialize quantized time as specified
time := 0
logTime(time)
// handshake as specified
stream.Send(ifc.Handshake)
hs := stream.Rec()
if hs != ifc.Handshake {
log.Fatal("world: thats no handshake.")
}
// log initial configuration of maze
log.Print(string(maze))
// top level world simulation loop
gameOver := false
timeConsumed := 0
for !gameOver {
gameOver, timeConsumed = process(stream.Rec())
time += timeConsumed
logTime(time)
log.Print(string(maze))
}
}
 
// logTime sets the log prefix to the current quantized time value.
// It does not actually log anything.
func logTime(t int) {
log.SetPrefix(fmt.Sprintf("%06d: ", t))
}
 
// Process a single command.
func process(cmd byte) (gameOver bool, timeConsumed int) {
// timeConsumed is 1, unless the forward function says otherwise.
timeConsumed = 1
switch cmd {
case ifc.CmdForward:
timeConsumed = forward()
case ifc.CmdRight:
right()
case ifc.CmdLeft:
left()
case ifc.CmdGet:
get()
case ifc.CmdDrop:
// game over only detected by drop command
gameOver = drop()
}
// for all commands, send stop event after all other processing is complete
stream.Send(ifc.EvStop)
return
}
func forward() (timeConsumed int) {
sectorOrigin := agentPos - rowLen
switch maze[agentPos] {
case '^':
sectorOrigin -= 3 * rowLen
case 'v':
sectorOrigin += 3 * rowLen
case '<':
sectorOrigin -= 3
case '>':
sectorOrigin += 3
}
if maze[sectorOrigin] == 'W' {
stream.Send(ifc.EvBump)
// bump event consumes no time
return 0
}
// move agent, plus any ball it has.
newPos := sectorOrigin + rowLen
maze[newPos] = maze[agentPos]
maze[newPos+1] = maze[agentPos+1]
maze[agentPos] = ' '
maze[agentPos+1] = ' '
agentPos = newPos
// send color event for new sector
stream.Send(maze[sectorOrigin])
if ball := maze[sectorOrigin+1]; ball != ' ' {
// send ball event
stream.Send(maze[sectorOrigin+1])
}
// for all events except bump, time consumed is 1.
return 1
}
 
func right() {
maze[agentPos] = rightOf(maze[agentPos])
}
 
func left() {
maze[agentPos] = leftOf(maze[agentPos])
}
 
func get() {
agentBall := agentPos + 1
sectorBall := agentBall - rowLen
can := true
if maze[sectorBall] == ' ' {
stream.Send(ifc.EvNoBallInSector)
can = false
}
if maze[agentBall] != ' ' {
stream.Send(ifc.EvAgentFull)
can = false
}
if can {
maze[agentBall] = maze[sectorBall]
maze[sectorBall] = ' '
}
}
 
func drop() (gameOver bool) {
agentBall := agentPos + 1
sectorBall := agentBall - rowLen
can := true
if maze[agentBall] == ' ' {
stream.Send(ifc.EvNoBallInAgent)
can = false
}
if maze[sectorBall] != ' ' {
stream.Send(ifc.EvSectorFull)
can = false
}
if can {
maze[sectorBall] = maze[agentBall]
maze[agentBall] = ' '
}
if win() {
stream.Send(ifc.EvGameOver)
return true
}
return false
}
// Win tests for a win state indicating game over. A more efficient technique
// might be to track the number of balls out of place and recognize immediately
// when the last ball was dropped on a matching sector, but this technique
// is simple and robust.
func win() bool {
ballPos := 2
for ballPos < len(maze) {
switch ballColor := maze[ballPos]; ballColor {
case ifc.EvBallRed, ifc.EvBallGreen, ifc.EvBallYellow, ifc.EvBallBlue:
if ballColor != maze[ballPos-1]+32 {
return false
}
}
ballPos += 3
if ballPos%rowLen == 1 {
ballPos += 2 * rowLen
}
}
return true
}</syntaxhighlight>
{{out}}
<pre>
...
000277: world recieves command forward
000277: world sends event color red
000277: world sends event stop
000277: agent recieves event color red
000277: agent recieves event stop
000278: agent sends command drop
000278:
WWWWWWWWWWWWWWWW
W W W
W W W
W R W R B W
W ^r W
W W
W G G Bb Gg W
W W
W W
W B G W R W
W W W
W W W
WWWWWWWWWWWWWWWW
000278: world recieves command drop
000278: world sends event game over
000278: world sends event stop
000278: agent recieves event game over
000278: agent recieves event stop
000279:
WWWWWWWWWWWWWWWW
W W W
W W W
W Rr W R B W
W ^ W
W W
W G G Bb Gg W
W W
W W
W B G W R W
W W W
W W W
WWWWWWWWWWWWWWWW
</pre>
 
=={{header|Julia}}==
See [[Remote agent/Simulation/Julia]]
 
=={{header|Perl}}==
This is the server. It runs in one of three modes. If it is started with a port number as the argument, it
becomes a TCP server listening on that port, and the agent can talk to it over tcp.
If it is started on a terminal (perl's -t is true) it sets the terminal to cbreak mode and can be talked to directly.
Otherwise it can be run under xinetd.
<syntaxhighlight lang="perl">#!/usr/bin/perl
 
use strict; # https://rosettacode.org/wiki/Remote_agent
use warnings;
use List::Util qw( shuffle );
use Term::ReadKey;
 
# server
 
my $port = shift;
 
my ($wide, $high) = ( 30 ) x 2;
my $world = '-' x ($wide + 2) . "\n" .
('-' . ' ' x $wide . "-\n") x $high .
'-' x ($wide + 2) . "\n";
my $balls = $world;
for my $try (1 .. 1e3) # try again if no mismatch
{
s/\w/ /g for $world, $balls;
$world =~ s/ / qw(R G Y B)[rand 4] /ge; # fill in color sectors
my @balls = shuffle map lc, $world =~ /\w/g;
@balls[ @balls >> 1 .. $#balls] = (0) x @balls;
@balls = shuffle @balls;
$balls =~ s/ / shift @balls || 0 /ge; # add balls
findmismatch() and last;
}
 
#sub show
# {
# my @two = split /\n/, $balls;
# warn "$_ ", shift @two, "\n" for $world =~ /.+/g;
# }
#show();
 
my $gap = $wide + 3;
my @cells;
push @cells, $-[0] while $world =~ /\w/g;
my $agent = $cells[rand @cells]; # pick random starting cell
my $dirs = 'NESW' x 2;
my ($holds, $dir) = ( 0, substr $dirs, rand 4, 1 ); # random direction
my ($color, $ball) = map {substr $_, $agent, 1 } $world, $balls;
my %gap = ( N => -$gap, E => 1, S => $gap, W => -1 );
my %commands = (
'^' => \&forward,
'>' => sub { $dirs =~ /$dir(.)/ and $dir = $1 }, # turn right
'<' => sub { $dirs =~ /(.)$dir/ and $dir = $1 }, # turn left
'@' => \&get,
'!' => \&drop,
"\e" => sub {die "\nEnded by ESC\n" },
);
 
sub drop
{
print 'a' x !$holds, 'S' x !!$ball; # errors
if( $holds && !$ball )
{
substr $balls, $agent, 1, $holds;
($ball, $holds) = ($holds, 0);
findmismatch() or print '+';
}
}
 
sub get
{
$ball =~ /[rgby]/ or print 's';
$holds and print 'A';
if( $ball and not $holds )
{
$holds = $ball;
substr $balls, $agent, 1, 0;
}
}
 
sub forward
{
my $new = $agent + $gap{$dir};
if( substr($world, $new, 1) =~ /\w/ ) # not wall
{
$agent = $new;
($color, $ball) = map {substr $_, $agent, 1 } $world, $balls;
print $color, $ball || ''; # 0 means no ball
}
else { print '|'; }
}
 
sub findmismatch
{
my $mask = $balls =~ tr/rgby/\0/cr =~ tr/rgby/\xff/r;
lc($world & $mask) ne ($balls & $mask);
}
 
my $terminal = 0;
 
if( $port ) # then we are tcp server
{
use IO::Socket;
my $listen = IO::Socket::INET->new( LocalPort => $port,
Listen => 10, Reuse => 1 ) or die $@;
# warn "waiting for connect\n";
my $socket = $listen->accept;
close STDIN; # redir STDIN and STDOUT to socket
open STDIN, '<&', $socket or die "$! on input dup";
close STDOUT;
open STDOUT, '>&', $socket or die "$! on output dup";
}
elsif( -t ) # running on a tty
{
$terminal = 1;
# warn "running on terminal\n";
}
else # suitable for xinetd
{
# warn "running as subprocess\n";
}
 
eval # here so die when on tty can reset tty back to normal
{
local $/ = \1; # all commands are one byte
local $| = 1; # autoflush
$terminal and ReadMode 'cbreak';
print 'A'; # handshake
<> eq 'A' or die "handshake failed";
# warn "got handshake reply\n";
while( <> ) # command read loop
{
( $commands{$_} // sub {die "invalid command <$_>"} )->();
print '.'; # eol
}
1 } or warn $@;
$terminal and ReadMode 'restore';
#warn "final\n";
#show;
</syntaxhighlight>
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(notonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\Remote_Agent_Simulator.exw
-- =======================================
--</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">Remote_Agent_Interface</span><span style="color: #0000FF;">.</span><span style="color: #000000;">exw</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- eg {"+-+-+","|R G|","+-+-+"}, up to 8(h)x11(w)</span>
<span style="color: #000000;">balls</span><span style="color: #0000FF;">,</span> <span style="color: #000080;font-style:italic;">-- eg {".....",".g...","....."}, same size as board </span>
<span style="color: #000000;">drop</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> <span style="color: #000080;font-style:italic;">-- the allowed set of balls (sum(g)&lt;=sum(G) etc)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">h</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ball</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'.'</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">North</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">amaze</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"RGYB"</span><span style="color: #0000FF;">[</span><span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">4</span><span style="color: #0000FF;">)]</span>
<span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">c</span> <span style="color: #000080;font-style:italic;">-- give cell a colour / mark visited</span>
<span style="color: #000000;">drop</span> <span style="color: #0000FF;">&=</span> <span style="color: #7060A8;">lower</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">p</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">shuffle</span><span style="color: #0000FF;">({{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">-</span><span style="color: #000000;">2</span><span style="color: #0000FF;">}})</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">p</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">p</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">nx</span><span style="color: #0000FF;">></span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">nx</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">w</span>
<span style="color: #008080;">and</span> <span style="color: #000000;">ny</span><span style="color: #0000FF;">></span><span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ny</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">2</span><span style="color: #0000FF;">*</span><span style="color: #000000;">h</span>
<span style="color: #008080;">and</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">'?'</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">mx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span>
<span style="color: #000000;">board</span><span style="color: #0000FF;">[(</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">2</span><span style="color: #0000FF;">][</span><span style="color: #000000;">mx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">' '</span> <span style="color: #000080;font-style:italic;">-- knock down wall</span>
<span style="color: #000000;">amaze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">drop</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">shuffle</span><span style="color: #0000FF;">(</span><span style="color: #000000;">drop</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">drop</span><span style="color: #0000FF;">[$]</span> <span style="color: #000080;font-style:italic;">-- (all bar start cell)</span>
<span style="color: #000000;">drop</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">drop</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</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;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">create_world</span><span style="color: #0000FF;">()</span>
<span style="color: #000080;font-style:italic;">-- (The distro version has several other tests/examples)</span>
<span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">11</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">h</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">8</span><span style="color: #0000FF;">)+(</span><span style="color: #000000;">w</span><span style="color: #0000FF;">==</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- (prohibit 1x1 formations, simply because
-- the gameover check won't trigger right.)</span>
<span style="color: #000000;">x</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">w</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">2</span>
<span style="color: #000000;">y</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">rand</span><span style="color: #0000FF;">(</span><span style="color: #000000;">h</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">2</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">wall</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"+"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">w</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span><span style="color: #008000;">"-"</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">cell</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"|"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">w</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span><span style="color: #008000;">"?"</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"\n"</span>
<span style="color: #000000;">board</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">wall</span><span style="color: #0000FF;">,</span><span style="color: #000000;">h</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span><span style="color: #000000;">cell</span><span style="color: #0000FF;">),</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">balls</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'.'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">w</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">),</span><span style="color: #000000;">h</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">amaze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">gameover</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">y</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">by</span> <span style="color: #000000;">2</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">x</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">by</span> <span style="color: #000000;">2</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">byx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">byx</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'.'</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">byx</span><span style="color: #0000FF;">!=</span><span style="color: #7060A8;">lower</span><span style="color: #0000FF;">(</span><span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">false</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">true</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"A"</span> <span style="color: #000080;font-style:italic;">-- (the initial handshake) [this is a private field]</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">accept_command</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">command</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">switch</span> <span style="color: #000000;">command</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'^'</span><span style="color: #0000FF;">:</span> <span style="color: #000080;font-style:italic;">-- forward</span>
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dxy</span><span style="color: #0000FF;">[</span><span style="color: #000000;">d</span><span style="color: #0000FF;">],</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">+</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">+</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">][</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">]!=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'!'</span> <span style="color: #000080;font-style:italic;">-- bump</span>
<span style="color: #008080;">else</span>
<span style="color: #0000FF;">{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">nx</span><span style="color: #0000FF;">+</span><span style="color: #000000;">dx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ny</span><span style="color: #0000FF;">+</span><span style="color: #000000;">dy</span><span style="color: #0000FF;">}</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">board</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span> <span style="color: #000080;font-style:italic;">-- colour</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">bxy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">bxy</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'.'</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">bxy</span> <span style="color: #000080;font-style:italic;">-- ball</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'&lt;'</span><span style="color: #0000FF;">:</span> <span style="color: #000080;font-style:italic;">-- turn left -- NESW(ie 1..4) ==&gt; first four for '&lt;', </span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'&gt;'</span><span style="color: #0000FF;">:</span> <span style="color: #000080;font-style:italic;">-- turn right last four for '&gt;':</span>
<span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">}[</span><span style="color: #000000;">d</span><span style="color: #0000FF;">+</span><span style="color: #000000;">command</span><span style="color: #0000FF;">-</span><span style="color: #008000;">'&lt;'</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'@'</span><span style="color: #0000FF;">:</span> <span style="color: #000080;font-style:italic;">-- get</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">bxy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">bxy</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'.'</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'s'</span> <span style="color: #000080;font-style:italic;">-- no ball</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">ball</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'.'</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'A'</span> <span style="color: #000080;font-style:italic;">-- agent full</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'.'</span>
<span style="color: #000000;">ball</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">bxy</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">case</span> <span style="color: #008000;">'!'</span><span style="color: #0000FF;">:</span> <span style="color: #000080;font-style:italic;">-- drop</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">bxy</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ball</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'.'</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'a'</span> <span style="color: #000080;font-style:italic;">-- no ball in agent</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">bxy</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'.'</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'S'</span> <span style="color: #000080;font-style:italic;">-- sector full</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">balls</span><span style="color: #0000FF;">[</span><span style="color: #000000;">y</span><span style="color: #0000FF;">][</span><span style="color: #000000;">x</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ball</span>
<span style="color: #000000;">ball</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">'.'</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">gameover</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'+'</span> <span style="color: #000080;font-style:italic;">-- game over</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">default</span><span style="color: #0000FF;">:</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"uh?"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- unknown command</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">switch</span>
<span style="color: #000000;">event_queue</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">'.'</span> <span style="color: #000080;font-style:italic;">-- stop</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">get_event</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">event_queue</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #0000FF;">{</span><span style="color: #004080;">integer</span> <span style="color: #000000;">event</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">event_queue</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">event_queue</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">event_queue</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..$]}</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">event</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">get_world</span><span style="color: #0000FF;">()</span>
<span style="color: #000080;font-style:italic;">-- (for display only)</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{{</span><span style="color: #000000;">board</span><span style="color: #0000FF;">,</span><span style="color: #000000;">balls</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">x</span><span style="color: #0000FF;">,</span><span style="color: #000000;">y</span><span style="color: #0000FF;">,</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ball</span><span style="color: #0000FF;">}}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #000000;">create_world</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">register_world</span><span style="color: #0000FF;">(</span><span style="color: #000000;">accept_command</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">get_event</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">get_world</span><span style="color: #0000FF;">)</span>
<!--</syntaxhighlight>-->
{{out}}
Of course this is half on one screen and half on another for non-direct (ie the ipc) version.<br>
Note the code that actually displays this is in the [[Remote_agent/Agent_interface#Phix|Agent Interface]], rather than in the above.
<pre>
world -- x: 2, y: 6, ball:., d:3 server handshake recieved
+-+-+-+-+-+-+-+-+ ................. agent handshake recieved
|Y B|Y B|G R|B B| .y.b.y.b.g.r.b.b.
+ + + + + + + +-+ .................
|B|R|B|Y Y|G|B|G| .b.r.b.y.y.g.b.g. game over
+ + + +-+-+ + + + .................
|B|Y R G B|G G Y| .b.y.r...b.g.g.y.
+-+-+-+-+-+-+-+-+ .v...............
 
(after . accepted by agent)
 
agent -- x: 2, y: 6, ball:., d:3
+-+-+-+-+-+-+-+-+ .................
|Y B|Y B|G R|B B| .y.b.y.b.g.r.b.b.
+ + + + + + + +-+ .................
|B|R|B|Y Y|G|B|G| .b.r.b.y.y.g.b.g.
+ + + +-+-+ + + + .................
|B|Y R G B|G G Y| .b.y.r...b.g.g.y.
+-+-+-+-+-+-+-+-+ .v...............
</pre>
<small>(The little v indicates it was heading down, as does d=3, in lower left corner, as do x and y)</small>
 
=={{header|PicoLisp}}==
This is the server. For the client, see [[Remote agent/Agent logic#PicoLisp]].
After starting (gameServer), you might for testing purposes also connect with 'telnet', type the commands, and see the responses.
<syntaxhighlight lang="picolisp"># Global variables:
# '*Port' is the port where the server is listening
# '*Sock' is the TCP socket after a client connected
Line 14 ⟶ 603:
# '*Ball' is the ball the agent is holding
# '*Dir' is a circular list of directions (north east south west .)
 
(load "@lib/simul.l")
 
# The server port
Line 85 ⟶ 676:
 
# Start the game server
(de gameServer (DX DY Balls Walls)
(loop
(setq *Sock (listen *Port))
Line 93 ⟶ 684:
(in *Sock
(out *Sock (prin "A")) # Greeting
(when (= "A" (char (rd 1)))
(newGame 8DX 8DY 20Balls 40Walls)
(whileand *Dbg (charshowWorld))
(while (rd 1)
(out *Sock
(case (char @) # Command character
("\^" # Forward
(ifn ((car *Dir) *Agent) # Hit wall?
(prin "|") # Yes: Bump event
Line 113 ⟶ 705:
((not (: ball)) (prin "s")) # No ball in sector
(*Ball (prin "A")) # Agent full
(T (setq *Ball (: ball))) ) ) )
(setq *Ball (: ball))
(=: ball) ) ) ) )
("!" # Drop ball
(with *Agent
Line 119 ⟶ 713:
((not *Ball) (prin "a")) # No ball in agent
((: ball) (prin "S")) # Sector full
((ending?)T (prin=: "+"ball *Ball)) # Game over
(T (=: ball *Ball) (off *Ball)) ) ) ) )
(and (ending?) (prin "+")) ) ) ) ) ) # Game over
(prin ".") ) ) ) ) # Stop event
(bye) )
Line 131 ⟶ 726:
(if (== *Agent This) "*" " ")
(: field)
(if (: ball) (lowc @) " ") ) ) ) )</langsyntaxhighlight>
 
For testing, you can start also it interactively:
For local tests, you can start also it interactively:
<pre>: (newGame 8 8 20 40) (showWorld)
<pre>: (newGame 8 8 20 40) (showWorld)
+---+---+---+---+---+---+---+---+
8 | R Y | B | R R Br| Rb Br|
Line 152 ⟶ 748:
+---+---+---+---+---+---+---+---+
a b c d e f g h</pre>
This displays the field colors in upper case letters, the balls in lower case letters, and the position of the agent with an asterisk.
 
letters, and the position of the agent with an asterisk.
=={{header|Tcl}}==
{{libheader|TclOO}}
<syntaxhighlight lang="tcl">package require TclOO
 
# Utility: pick random item of list
proc pick list {
lindex $list [expr {int([llength $list] * rand())}]
}
# Utility: generate callback of method
proc callback {method args} {
list [uplevel 1 {namespace current}]::my $method {*}$args
}
# Utility: print errors in events to standard error
proc bgerror args {puts stderr $args}
 
# The main class that implements the server
oo::class create BallMaze {
variable grid balls x y dir carry turns identity chan timeout
 
# Install this class as a server
self method server {port width height args} {
set srv [socket -server [callback new $width $height] {*}$args $port]
if {$::debug} {
lassign [fconfigure $srv -sockname] addr host port
puts "server ready on ${addr}:${port}"
}
}
 
# Initialize the per-player structure
constructor {width height channel clientHost clientPort} {
set identity "${clientHost}:${clientPort}"
if {$::debug} {puts "$identity initializing..."}
global width height
set chan $channel
fconfigure $chan -blocking 0 -encoding ascii
set dir n
set carry ""
set turns 0
 
# Build the grid
set grid [set balls [lrepeat $width [lrepeat $height ""]]]
 
# Make a layout of random colors
for {set x 1} {$x < $width-1} {incr x} {
for {set y 1} {$y < $height-1} {incr y} {
lset grid $x $y [pick {R G B Y}]
}
}
 
# Sprinkle some walls in
for {set i 0} {$i < $width*$height/3} {incr i} {
while 1 {
set x [expr {int(1+($width-2)*rand())}]
set y [expr {int(1+($height-2)*rand())}]
if {[lindex $grid $x $y] eq ""} continue
if {[my WillCloseCell [expr {$x+1}] $y]} continue
if {[my WillCloseCell [expr {$x-1}] $y]} continue
if {[my WillCloseCell $x [expr {$y+1}]]} continue
if {[my WillCloseCell $x [expr {$y-1}]]} continue
break
}
lset grid $x $y ""
}
 
# Sprinkle some balls in
for {set i 0} {$i < $width*$height/5} {incr i} {
while 1 {
set x [expr {int(1+($width-2)*rand())}]
set y [expr {int(1+($height-2)*rand())}]
if {[lindex $grid $x $y] ne ""} break
}
lset balls $x $y [pick {R G B Y}]
}
 
# Select a starting location
while 1 {
set x [expr {int(1+($width-2)*rand())}]
set y [expr {int(1+($height-2)*rand())}]
if {[lindex $grid $x $y] ne ""} break
}
set dir [pick {n s e w}]
 
# OK, we're ready; wait for the client to be ready
puts -nonewline $chan "A"
fileevent $chan readable [callback PostInit]
my SetTimeout
}
 
# Close things down (particularly the channel and the timeout; other state
# is automatically killed with the object)
destructor {
if {$::debug} {puts "$identity closing down..."}
catch {close $chan}
catch {after cancel $timeout}
}
 
# How to (re)set the timeout
method SetTimeout {} {
catch {after cancel $timeout}
set timeout [after 60000 [callback destroy]]
}
 
# Callback used to wait for the client to acknowledge readiness
method PostInit {} {
if {[read $chan 1] ne "A"} {
my destroy
} else {
if {$::debug} {my print}
fileevent $chan readable [callback DispatchAction]
my SetTimeout
}
}
 
# Utility: test if a cell will be closed by putting a wall next to it
method WillCloseCell {i j} {
set num 0
incr num [expr {[lindex $grid [expr {$i+1}] $j] ne ""}]
incr num [expr {[lindex $grid [expr {$i-1}] $j] ne ""}]
incr num [expr {[lindex $grid $i [expr {$j+1}]] ne ""}]
incr num [expr {[lindex $grid $i [expr {$j-1}]] ne ""}]
return [expr {$num == 1}]
}
# Utility: is the game finished; all balls match, none in hand
method IsGameOver {} {
foreach gc $grid bc $balls {
foreach g $gc b $bc {
if {$b ne "" && $b ne $g} {
return 0
}
}
}
return [expr {$carry eq ""}]
}
 
# Main event handler; reads user action, dispatches, manages timeouts
method DispatchAction {} {
switch [read $chan 1] {
"^" {set events [my forward]}
"<" {set events [my left]}
">" {set events [my right]}
"@" {set events [my get]}
"!" {set events [my drop]}
default {
# EOF will come here too (read returns empty string)
my destroy
return
}
}
# Add the "stop" and send message to client
append events "."
puts -nonewline $chan $events
my SetTimeout
}
 
# Implementations of particular actions; doesn't include communication
method forward {} {
switch $dir {
n {set dx 0; set dy -1}
s {set dx 0; set dy 1}
e {set dx 1; set dy 0}
w {set dx -1; set dy 0}
}
if {[lindex $grid [expr {$x+$dx}] [expr {$y+$dy}]] eq ""} {
set response "|"
} else {
set response ""
incr turns
incr x $dx
incr y $dy
}
append response [lindex $grid $x $y]
append response [string tolower [lindex $balls $x $y]]
return $response
}
method left {} {
set dir [string map {n w w s s e e n} $dir]
incr turns
return
}
method right {} {
set dir [string map {n e e s s w w n} $dir]
incr turns
return
}
method get {} {
incr turns
set response ""
if {[lindex $balls $x $y] eq ""} {append response "s"}
if {$carry ne ""} {append response "A"}
if {$response eq ""} {
set carry [lindex $balls $x $y]
lset balls $x $y ""
}
return $reponse
}
method drop {} {
incr turns
set response ""
if {$carry eq ""} {append response "a"}
if {[lindex $balls $x $y] ne ""} {append response "S"}
if {$response eq ""} {
lset balls $x $y $carry
set carry ""
if {[my IsGameOver]} {
if {$::debug} {my print}
append response "+"
}
}
return $response
}
 
# Utility: prints the state of the service instance
method print {} {
set width [llength $grid]
set height [llength [lindex $grid 0]]
puts "$identity : [expr {[my IsGameOver] ? {finished} : {running}}]"
for {set i 0} {$i < $height} {incr i} {
for {set j 0} {$j < $width} {incr j} {
puts -nonewline [format "%1s%1s%1s" \
[expr {$j==$x&&$i==$y ? "*" : ""}] \
[lindex $grid $j $i] \
[string tolower [lindex $balls $j $i]]]
}
puts ""
}
}
}
 
# Parse command line arguments and test if we're in debug mode
lassign $argv port width height
set debug [info exists env(DEBUG_AGENT_WORLD)]
 
# Make the server and run the event loop
BallMaze server $port $width $height {*}$argv
vwait forever</syntaxhighlight>
Example call (with the server restricted to only serving on 127.0.0.1):
<pre>tclsh8.5 agent.world.tcl 54545 8 8 -myaddr localhost</pre>
 
=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-str}}
{{libheader|Wren-fmt}}
{{libheader|Wren-seq}}
<syntaxhighlight lang="wren">/* world.wren */
 
import "./ifc" for Ifc, Log
import "./str" for Char
import "./fmt" for Fmt
import "./seq" for Lst
 
// Maze sectors are 3x3 characters, with these quirks:
// String starts with a newline.
// Space at end of each line except the last.
// No space, newline, or blank lines following last W.
//
// In each sector, W or sector Color is mandatory. If sector has ball,
// it is in the character to the right. If agent is in a sector, it is in
// the character below and shown by its direction symbol. If agent has a ball,
// it is in the character to the right.
//
// The variable maze is not just input, but is the primary representation
// of the world. Over the course of execution, walls and sector colors
// are constant; agent and balls can move.
var maze = """
 
WWWWWWWWWWWWWWWW
W W W
W W W
W Rb W Rg B W
W W
W W
W G G B G W
W W
W W
W Br G W R W
W W ^ W
W W W
WWWWWWWWWWWWWWWW
""".toList
 
var stream = null
var rowLen = 0
var agentPos = -1
 
var dirString = "^>v<"
var directions = dirString.toList
 
var rightOf = Fn.new { |dir| directions[(dirString.indexOf(dir) + 1) % 4] }
var leftOf = Fn.new { |dir| directions[(dirString.indexOf(dir) + 3) % 4] }
 
var right = Fn.new { maze[agentPos] = rightOf.call(maze[agentPos]) }
var left = Fn.new { maze[agentPos] = leftOf.call(maze[agentPos]) }
 
var get = Fn.new {
var agentBall = agentPos + 1
var sectorBall = agentBall - rowLen
var can = true
if (maze[sectorBall] == " ") {
stream.send(Ifc.evNoBallInSector)
can = false
Fiber.yield()
}
if (maze[agentBall] != " ") {
stream.send(Ifc.evAgentFull)
can = false
Fiber.yield()
}
if (can) {
maze[agentBall] = maze[sectorBall]
maze[sectorBall] = " "
}
}
 
// Win tests for a win state indicating game over.
var win = Fn.new {
var ballPos = 2
while (ballPos < maze.count) {
var ballColor = maze[ballPos]
if ([Ifc.evBallRed, Ifc.evBallGreen, Ifc.evBallYellow, Ifc.evBallBlue].contains(ballColor)) {
if (ballColor != Char.lower(maze[ballPos-1])) return false
}
ballPos = ballPos + 3
if (ballPos % rowLen == 1) ballPos = ballPos + 2 * rowLen
}
return true
}
 
var drop = Fn.new {
var agentBall = agentPos + 1
var sectorBall = agentBall - rowLen
var can = true
if (maze[agentBall] == " ") {
stream.send(Ifc.evNoBallInAgent)
can = false
Fiber.yield()
}
if (maze[sectorBall] != " ") {
stream.send(Ifc.evSectorFull)
can = false
Fiber.yield()
}
if (can) {
maze[sectorBall] = maze[agentBall]
maze[agentBall] = " "
}
if (win.call()) {
stream.send(Ifc.evGameOver)
Fiber.yield()
return true
}
return false
}
 
var logTime = Fn.new { |t| Log.prefix = t }
 
var forward = Fn.new {
var sectorOrigin = agentPos - rowLen
var ch = maze[agentPos]
sectorOrigin = (ch == "^") ? sectorOrigin - 3 * rowLen :
(ch == "v") ? sectorOrigin + 3 * rowLen :
(ch == "<") ? sectorOrigin - 3 :
(ch == ">") ? sectorOrigin + 3 : sectorOrigin
if (maze[sectorOrigin] == "W") {
stream.send(Ifc.evBump)
Fiber.yield()
// bump event consumes no time
return 0
}
 
// move agent, plus any ball it has.
var newPos = sectorOrigin + rowLen
maze[newPos] = maze[agentPos]
maze[newPos+1] = maze[agentPos+1]
maze[agentPos] = " "
maze[agentPos+1] = " "
agentPos = newPos
 
// send color event for new sector
stream.send(maze[sectorOrigin])
Fiber.yield()
var ball = maze[sectorOrigin+1]
if (ball != " ") {
// send ball event
stream.send(maze[sectorOrigin+1])
Fiber.yield()
}
// for all events except bump, time consumed is 1.
return 1
}
 
// Process a single command.
var process = Fn.new { |cmd|
// timeConsumed is 1, unless the forward function says otherwise.
var gameOver = false
var timeConsumed = 1
if (cmd == Ifc.cmdForward) {
timeConsumed = forward.call()
} else if (cmd == Ifc.cmdRight) {
right.call()
} else if (cmd == Ifc.cmdLeft) {
left.call()
} else if (cmd == Ifc.cmdGet) {
get.call()
} else if (cmd == Ifc.cmdDrop) {
// game over only detected by drop command
gameOver = drop.call()
}
// for all commands, send stop event after all other processing is complete
stream.send(Ifc.evStop)
Fiber.yield()
return [gameOver, timeConsumed]
}
 
var World = Fn.new { |s|
stream = s
rowLen = maze[1..-1].indexOf("\n") + 1
var cols = (rowLen/3).floor
var rows = ((((maze.count + 1)/rowLen).floor + 2)/3).floor
if (maze.count != (rows*3 - 2) * cols * 3 - 1) Log.fatal("mis-shaped maze")
agentPos = Lst.indexOfAny(maze, directions)
if (agentPos == -1) Log.fatal("agent not in maze")
 
// initialize quantized time as specified
var time = 0
logTime.call(time)
 
// handshake as specified
stream.send(Ifc.handshake)
Fiber.yield()
var hs = stream.rec()
if (hs != Ifc.handshake) Log.fatal("world: that's no handshake.")
 
// log initial configuration of maze
Log.print(maze.join())
 
// top level world simulation loop
var gameOver = false
var timeConsumed = 0
while (!gameOver) {
Fiber.yield()
var res = process.call(stream.rec())
gameOver = res[0]
timeConsumed = res[1]
time = time + timeConsumed
logTime.call(time)
Log.print(maze.join())
}
}</syntaxhighlight>
<br>
Finally, we need a 'driver' script to run the various 'remote agent' modules (world, agent and ifc) as a whole assuming they've all been placed in the same directory.
 
The following supplies a concrete implementation of the Ifc abstract class. I've used queues and fibers in place of channels and goroutines respectively to run a simulation in a single process. The Agent and World functions run on separate fibers, yielding to each other at appropriate times, until the game is over.
 
To run a simulation between two processes (on the same or different machines) would require an embedded script though the interface should be the same.
 
{{libheader|Wren-queue}}
<syntaxhighlight lang="wren">/* Remote_agent_driver.wren */
 
import "./ifc" for Streamer, Ifc, Log
import "./agent" for Agent
import "./world" for World
import "./queue" for Queue
 
// Concrete implementation of Streamer abstract class.
class QueueStreamer is Streamer {
construct new(name, qin, qout) {
_name = name
_qin = qin
_qout = qout
}
 
send(ch) {
Log.print("%(_name) sends %(Ifc.text[ch])")
_qout.push(ch)
}
 
rec() {
var ch = _qin.pop()
if (ch) Log.print("%(_name) receives %(Ifc.text[ch])")
return ch
}
}
 
var cmd = Queue.new()
var ev = Queue.new()
 
var aqs = QueueStreamer.new("agent", ev, cmd)
var wqs = QueueStreamer.new("world", cmd, ev)
 
var af = Fiber.new(Agent)
var wf = Fiber.new(World)
 
wf.call(wqs)
 
while(!wf.isDone) {
af.call(aqs)
wf.call(wqs)
}</syntaxhighlight>
 
{{out}}
Sample game (abridged):
<pre>
...
000169: world receives command forward
000169: world sends event color red
000169: agent receives event color red
000169: world sends event stop
000169: agent receives event stop
000169: agent sends command drop
000170:
WWWWWWWWWWWWWWWW
W W W
W W W
W R W R B W
W ^r W
W W
W G G Bb Gg W
W W
W W
W B G W R W
W W W
W W W
WWWWWWWWWWWWWWWW
000170: world receives command drop
000170: world sends event game over
000170: agent receives event game over
000170: world sends event stop
000170: agent receives event stop
000171:
WWWWWWWWWWWWWWWW
W W W
W W W
W Rr W R B W
W ^ W
W W
W G G Bb Gg W
W W
W W
W B G W R W
W W W
W W W
WWWWWWWWWWWWWWWW
</pre>
9,476

edits