Caerwyn Jones | 1 Apr 2007 14:59
Picon

Re: blackbox: a game for acme

thanks, this is neat!

i wouldn't mind seeing an acme client similar to this to gnu chess and gnu go.

On 3/31/07, Arvindh Rajesh Tamilmani < arvindht-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
A port of the blackbox game from Emacs.

http://mordor.tip9ug.jp/who/art/inferno/blackbox

Requires acmeevent.b, ported from plan9port:

http://mordor.tip9ug.jp/who/art/inferno/acmeevent.b

From Emacs help:

        Blackbox is a game of hide and seek played on an 8 by 8
        grid (the blackbox).  Your opponent has hidden several
        balls (usually 4) within this box.  By shooting rays
        into the box and observing where they emerge it is
        possible to deduce the positions of the hidden balls.
        The fewer rays you use to find the balls, the lower
        your score.

To shoot a ray, middle-click on the edges of the box.
To place or remove a ball, middle-click inside the box.
Once you've placed 4 balls, middle-click on Score to get the score.

The possible outcomes for each ray are explained in the
commentary (in the header) and doc comment
(under 'defun blackbox') here:

http://cvs.savannah.gnu.org/viewcvs/emacs/emacs/lisp/play/blackbox.el?view=markup

Comments welcome.
Arvindh

==== blackbox ====

#!/dis/sh

load std
load expr

fn msg{
        echo blackbox: $*
}

# rand(mod): a random number between 0 and mod-1
subfn rand{
        mod := $1
        r := `{read 1 </dev/random | sum /fd/0}
        result = ${expr 16r${hd $r} $mod %}
}

# a random point within the blackbox
subfn randpt{
        result = ${expr ${rand 8} 1 +} ${expr ${rand 8} 1 +}
}

# convert a character address to a point
subfn topt{
        addr := $1
        result = ${expr $addr 21 % 2 /} ${expr $addr 21 /}
}

# convert a point (x y) to a compound address of n characters
subfn toaddr{
        (x y n) := $*
        q0 := ${expr $y 21 x $x 2 x +}
        q1 := ${expr $q0 $n +}
        result = '#'$q0',#'$q1
}

# replace n characters at (x y) with s
fn mark{
        (x y s n) := $*
        echo -n ${toaddr $x $y $n} >$wdir/addr
        echo -n $s >$wdir/data
        echo clean >$wdir/ctl
}

# ismember(ax ay l): true if (ax ay) is a member of l
fn ismember{
        (ax ay l) := $*
        (x y) := ()
        stat := no
        while{ntest $#l}{
                (x y l) = $l
                if{and {~ $ax $x} {~ $ay $y}}{
                        stat = ()
                        raise break
                }
        }
        status $stat
}

# del(ax ay l): delete (ax ay) from l
subfn del{
        (ax ay l) := $*
        (x y) := ()
        while{ntest $#l}{
                (x y l) = $l
                if{and {~ $ax $x} {~ $ay $y}}{
                        result = ($result $l)
                        raise break
                }{
                        result = ($x $y $result)
                }
        }
}

# given l1 and l2, mark the points unique in l1 with c
subfn comm-2{
        (s1 s2 c) := $*
        l1 := ${split ' ' $s1}
        l2 := ${split ' ' $s2}
        (x y) := ()
        result = 0
        while{ntest $#l1}{
                (x y l1) = $l1
                if{! ismember $x $y $l2}{
                        mark $x $y $c 1
                        result = ${expr $result 1 +}
                }
        }
}

fn drawbox{
        echo -n , >$wdir/addr
        echo -n >$wdir/data

        echo '  · · · · · · · ·   ' >$wdir/body
        for i in b l a c k b o x {
                echo '· - - - - - - - - · ' >$wdir/body
        }
        echo '  · · · · · · · ·   ' >$wdir/body
        echo clean >$wdir/ctl
}

# hide 4 balls in distinct random points
fn hideballs{
        for i in b a l l {
                b := ${randpt}
                while {ismember $b $balls}{
                        b = ${randpt}
                }
                balls = ($b $balls)
        }
}

fn iscorner{
        (x y) := $*
        and (
                {or {~ $x 0}{~ $x 9}}
                {or {~ $y 0}{~ $y 9}}
        )
}

fn isout{
        (x y) := $*
        or {~ $x 0}{~ $x 9}{~ $y 0}{~ $y 9}
}

subfn delta{
        n := $1
        if{~ $n 0}{
                result = 1
        }{~ $n 9}{
                result = -1
        }{
                result = 0
        }
}

fn event{
        if{~ $1$2 MX}{
                handlex ${topt $3}
        }{~ $1$2 Mx}{
                if{~ $9 Quit}{
                        echo delete >$wdir/ctl
                        exit
                }{~ $9 NewGame}{
                        newgame
                }{~ $9 Score}{
                        score
                }{
                        echo $1$2$3 $4 >$wdir/event
                }
        }
}

fn handlex{
        pt := $*
        if{iscorner $pt}{
                # do nothing
        }{isout $pt}{
                traceray $pt
        }{
                if{ismember $pt $guess}{
                        mark $pt - 1
                        guess = ${del $pt $guess}
                }{
                        mark $pt O 1
                        guess = ($pt $guess)
                }
        }
}

fn traceray{
        (x y) := $*
        target := ${traceray0 true $x ${delta $x} $y ${delta $y}}
        if{~ $target hit}{
                mark $x $y H 1
                score = ${expr $score 1 +}
        }{and {~ ${hd $target} $x} {~ ${tl $target} $y}}{
                mark $x $y R 1
                score = ${expr $score 1 +}
        }{
                detour = ${expr $detour 1 +}
                if{~ $detour 100}{
                        msg you''''ve run out of rays.
                }{
                        n := 1
                        if{~ ${expr $detour 9 gt} 1}{
                                n = 2
                        }
                        mark $x $y $detour $n
                        mark $target $detour $n
                        score = ${expr $score 2 +}
                }
        }
}

subfn traceray0{
        (first x dx y dy) := $*

        if{and {~ $first false} {isout $x $y}}{
                result = $x $y
        }{ismember ${expr $x $dx +} ${expr $y $dy +} $balls}{
                result = hit
        }{ismember ${expr $x $dx + $dy +} ${expr $y $dy + $dx +} $balls}{
                result = ${traceray0 false $x ${expr $dy _} $y ${expr $dx _}}
        }{ismember ${expr $x $dx + $dy -} ${expr $y $dy + $dx -} $balls}{
                result = ${traceray0 false $x $dy $y $dx}
        }{
                result = ${traceray0 false ${expr $x $dx +} $dx ${expr $y $dy +} $dy}
        }
}

fn score{
        if{~ $#guess $#balls}{
                missed := ${comm-2 $"guess $"balls X}
                {} ${comm-2 $"balls $"guess <at> }
                if{ntest $missed}{
                        msg you missed $missed 'ball(s); '^
                                your score is ${expr $missed 5 x $score +}^.
                }{
                        msg congrats! your score is $score^.
           &nbs
Arvindh Rajesh Tamilmani | 1 Apr 2007 21:24
Picon

Re: blackbox: a game for acme

> i wouldn't mind seeing an acme client similar to this to gnu chess and gnu
> go.

an acme chess client is in my wish list too, mainly because the chess
pieces are available in unicode.

rog | 2 Apr 2007 18:30
Favicon

Re: blackbox: a game for acme

i enjoyed that. i think that's the first time i've seen
recursive functions used in the inferno shell...
my only (extremely minor) comment might
be on the limbo style - you can usually get away
without declaring all those variables - just use :=!

i suppose that if i was doing it myself, i might
not have copied the acmeevent design quite so rigidly - given
that it's possible to do unquoting directly within the shell.
for instance, you could do something like:
	acmeevent < $wdir/event | getlines {
		event ${unquote $line}
	}

this also gets around the difficulty with the fact that %q doesn't
quote shell metacharacters (for that, you could have used String's
unquotedc, but i'm not sure if that's exported yet), and makes the
control flow more evident.

the down side is that it wouldn't work if there were newline characters
inside the quote. i've sometimes considered writing a new builtin to
allow reading of quoted lines, where the words in the quoted
line might contain newlines. or just a shell syntax reader,
not too different from getlines, but that reads and parses
shell commands, without actually executing them.

e.g.

	getcmds {
		echo got command $cmd
	}

Russ Cox | 2 Apr 2007 19:33

Re: blackbox: a game for acme

> this also gets around the difficulty with the fact that %q doesn't
> quote shell metacharacters

???

that's the only reason %q
>
> the down side is that it wouldn't work if there were newline characters
> inside the quote. i've sometimes considered writing a new builtin to
> allow reading of quoted lines, where the words in the quoted
> line might contain newlines. or just a shell syntax reader,
> not too different from getlines, but that reads and parses
> shell commands, without actually executing them.
>
> e.g.
>
>         getcmds {
>                 echo got command $cmd
>         }
>
>

Russ Cox | 2 Apr 2007 19:33

Re: blackbox: a game for acme

On 4/2/07, Russ Cox <rsc@...> wrote:
> > this also gets around the difficulty with the fact that %q doesn't
> > quote shell metacharacters
>
> ???

that's the only reason %q exists.

[sorry about the duplicate.  no idea what i typed but
it sent the message mid-edit.]

russ

Charles Forsyth | 2 Apr 2007 20:16

Re: blackbox: a game for acme

in Plan 9, by default %q quotes space and single quote but does not quote
shell metacharacters unless like du.c and ls.c the program sets the global
doquote to needsrcquote.  more things use %q than set doquote, though perhaps
more should.

Limbo hasn't got the latter, and would provide it differently if it had,
to avoid the global.
From: Russ Cox <rsc@...>
Subject: Re: [inferno-list] blackbox: a game for acme
Date: 2007-04-02 17:33:49 GMT
On 4/2/07, Russ Cox <rsc@...> wrote:
> > this also gets around the difficulty with the fact that %q doesn't
> > quote shell metacharacters
>
> ???

that's the only reason %q exists.

[sorry about the duplicate.  no idea what i typed but
it sent the message mid-edit.]

russ
rog | 3 Apr 2007 17:09
Favicon

Re: blackbox: a game for acme

to add a data point to what charles said:

cpu% sleep '1000;a' 'b c' &
cpu% cd /proc/$apid
cpu% cat args
sleep 1000;hello 'b c'cpu% 
cpu% 

shell style quoting is useful for separating items in a list of strings,
even if the result won't necessarily pass correctly through the shell parser.

maybe the above behaviour might be considered a bug, since as far as i'm
aware, there's nothing at the plan 9 shell level that can straighforwardly
use the above format. the nearest i can think of is:

. <{echo 'args=('; cat args; echo ')'}

but maybe shell level access isn't important?

sorry for belabouring the point.

rog | 3 Apr 2007 19:56
Favicon

Re: blackbox: a game for acme

oops, i wrote:

> sleep 1000;hello 'b c'cpu% 

but i meant to write:

	sleep 1000;a 'b c'cpu% 

that'll teach me to edit examples rather than
re-running them...

while i'm about it, the echo should have had a "-n" too:

	. <{echo -n 'args=('; cat args; echo ')'}

Arvindh Rajesh Tamilmani | 3 Apr 2007 20:16
Picon

Re: blackbox: a game for acme

On 4/2/07, rog@...
<rog@...> wrote:

> for instance, you could do something like:
>         acmeevent < $wdir/event | getlines {
>                 event ${unquote $line}
>         }
>
> this also gets around the difficulty with the fact that %q doesn't
> quote shell metacharacters (for that, you could have used String's
> unquotedc, but i'm not sure if that's exported yet), and makes the
> control flow more evident.
>
> the down side is that it wouldn't work if there were newline characters
> inside the quote.

i did try, but didn't use getlines, when i encountered this.
i wanted to have a general wineventloop that
could be used in other acme clients also.

besides, i was impressed by the rc one-liner :-)

> i've sometimes considered writing a new builtin to
> allow reading of quoted lines, where the words in the quoted
> line might contain newlines.

i think in this particular case, where each chunk written by
acmeevent is complete and of small size, we can use read
instead of getlines:

acmeevent <$wdir/event | while{e="{read}} {
 	${unquote $e}
}

this would preserve newlines and shell metacharacters.

thanks for your comments.

arvindh

Arvindh Rajesh Tamilmani | 3 Apr 2007 20:30
Picon

Re: blackbox: a game for acme

> like du.c and ls.c the program sets the global
> doquote to needsrcquote.

this was one of the minor complaints i had when i started
using plan9.  du always quotes its output, and hence can't
be used directly in pipelines (in the rare cases where file
names may contain shell metacharacters).

ls atleast has -Q option.

arvindh


Gmane