--
--	student-bots.occ -- bots (student implementations)
--	Copyright (C) 2006-2008 Fred Barnes <frmb@kent.ac.uk>
--      Portions Copyright (C) 2007 Jon Simpson
--
--	This program is free software; you can redistribute it and/or modify
--	it under the terms of the GNU General Public License as published by
--	the Free Software Foundation; either version 2 of the License, or
--	(at your option) any later version.
--
--	This program is distributed in the hope that it will be useful,
--	but WITHOUT ANY WARRANTY; without even the implied warranty of
--	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--	GNU General Public License for more details.
--
--	You should have received a copy of the GNU General Public License
--	along with this program; if not, write to the Free Software
--	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
--

#INCLUDE "types.inc"
#INCLUDE "course.module"
#INCLUDE "string.module"
#USE "game-utils"
#USE "game-objects"


--{{{  PROC bot.conga (VAL []BYTE name, to.follow, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
--
--  this is a bot which forms a 10 bot conga line, then goes around the world
--  randomly. sometimes members get left behind if the bot they are following
--  leaves the room quickly, but they join back up when the bot comes back.
--  this leads to interesting effects, and the conga line will often enter a
--  room it is still leaving.. interesting exploration.
--
PROC bot.conga (VAL []BYTE name, to.follow, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! take.damage; mb1; mb2; ib1
                4
                  response ! leaving.room; mb1; ib1
              case := -1
             --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            take.damage; mb1; mb2; ib1
              case := 3
            leaving.room; mb1; ib1
              case := 4
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
            leaving.room; mb1; ib1
              out ! leaving.room; mb1; ib1
      --}}} 
  :
  --}}}  

  VAL BOOL is.immortal IS TRUE:
  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  MOBILE []BYTE msg:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

     --{{{  register with game
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}} 
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
     --}}} 
    --{{{  attach to game by entering room
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}} 

    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          --{{{  get.name -- game requesting our name (passed as a parameter)
          get.name
            link[s.out] ! name; name
          --}}}
          --{{{  genmsg -- general message
          genmsg; msg
            SKIP
          --}}}
          --{{{  have.object -- inquiry to see if we have a particular object
          have.object; msg
            link[s.out] ! no
          --}}}
          --{{{  enter.room -- message to indicate someone entering a room
          enter.room; msg
            SKIP
          --}}}
          --{{{  leaving.room -- message to  indicate someone leaving a room, nesw indicates direction
          INT nesw:
          leaving.room; msg; nesw
            for.local ! leaving.room; msg; nesw
          --}}}
          --{{{  self.enter.room -- message to indicate that we're entering a room
          INT roomno:
          self.enter.room; roomno
            SKIP
          --}}}
          --{{{  drop.object -- message to indicate something dropping something in a room
          MOBILE []BYTE oname:
          drop.object; msg; oname
            for.local ! drop.object; msg; oname
          --}}} 
          --{{{  sa y.room -- something saying something in a room
          MOBILE []BYTE pname:
          say.room; pname; msg
            for.local ! say.room; pname; msg
          --}}}
          --{{{  action.room -- something performing an action in a room
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          --}}}
          --{{{  take.damage -- damage being inflicted, pass onto local handling
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          --}}}
          --{{{  status -- requesting our status
          status
            -- returns health, vitality and a description
            link[s.out] ! status; 100; 100; "a wandering AI unit"
          --}}}
          --{{{ dropall -- dropping all items
          SHARED CT.ROOMLINK! drop.room:
          dropall; drop.room
            SKIP
          --}}}
          --{{{ resurrect -- bring the bot back to life.
          resurrect
            SKIP
          --}}}
      --}}}
      --{{{  main bot-code
      INT t, d:
      INITIAL BOOL conga.on IS FALSE:
      INITIAL BOOL follow.on IS FALSE:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        tim ? t
        d, seed := random (6, seed)
        t := t PLUS (2000000 + (d TIMES 500000))

        INITIAL BOOL running IS TRUE:
        WHILE running
          PRI ALT
            local.in ? CASE
              --{{{  take.damage -- damage being inflicted on us
              MOBILE []BYTE who, oname:
              INT damage:
              take.damage; who; oname; damage
                IF
                  is.immortal
                    link[out] ! say; "I*'m immortal :)"
                  TRUE
                    link[out] ! action; "clinks and clutters"
              --}}}
              --{{{ say.room
              MOBILE []BYTE pname, msg:
              say.room; pname; msg
                SEQ
                  IF
                    msg = "CONGA!"
                      IF
                        name = to.follow
                          -- If this bot is the leader, enable the conga.
                          -- The timeout move then happens.
                          conga.on := TRUE
                        TRUE
                          -- This bot is not the leader.
                          -- Start the conga (put this bot in follow mode)
                          follow.on := TRUE  
                    TRUE
                      SKIP
              --}}}
              --{{{ leaving.room -- follow the leader, if we're doing the conga..
              MOBILE []BYTE msg:
              INT nesw:
              leaving.room; msg; nesw
                IF
                  (follow.on) AND (msg = to.follow)
                    INT delay:
                    SEQ
                      tim ? t
                      -- Delay can be anything between 1 and 2 seconds.
                      delay, seed := random (1000000, seed)
                      delay := delay + 1000000
                      tim ? AFTER t PLUS delay
                      IF
                        nesw  < 0
                          SKIP -- nowhere
                        nesw = 0
                          link[out] ! north
                        nesw = 1
                          link[out] ! east
                        nesw = 2
                          link[out] ! south
                        nesw = 3
                          link[out] ! west
                        TRUE
                          SKIP -- also nowhere, but for safety.
                  TRUE
                    SKIP
              --}}}
            tim ? AFTER t
              --{{{  timeout, time to move if we're the leader.
              SEQ
                IF
                  conga.on
                    [4]BOOL exit:
                    INT location, selected.exit:
                    SEQ
                      link[out] ! lookexits
                      link[in] ? CASE exits; location; exit
                      selected.exit := -1
                      -- Select an exit.
                      INITIAL INT retries IS 100:
                      INT random.pick:
                      -- Crude method for room select from original code.
                      WHILE (selected.exit < 0) AND (retries > 0)
                        SEQ
                          random.pick, seed := random(4, seed)
                          IF
                            exit[random.pick] -- this exit exists.
                              selected.exit := random.pick
                            TRUE
                              retries := retries - 1
                      IF
                        selected.exit = 0
                          link[out] ! north
                        selected.exit = 1
                          link[out] ! east
                        selected.exit = 2
                          link[out] ! south
                        selected.exit = 3
                          link[out] ! west
                        TRUE
                          SKIP -- Can't go anywhere with an invalid direction
                  TRUE
                    SKIP
                INT range:
                SEQ
                  -- reset the timeout.
                  range, seed := random (6, seed)
                  t := t PLUS (3000000 + (range * 500000))
              --}}}
      --}}}
:
--}}}
--{{{  PROC bot.js219 (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
--
--  this is a template for a bot, use it as a basis for creating other bots
--
PROC bot.js219 (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! take.damage; mb1; mb2; ib1
                4
                  response ! leaving.room; mb1; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            take.damage; mb1; mb2; ib1
              case := 3
            leaving.room; mb1; ib1
              case := 4
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
            leaving.room; mb1; ib1
              out ! leaving.room; mb1; ib1
      --}}} 
  :
  --}}}

  VAL BOOL is.immortal IS TRUE:
  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  MOBILE []BYTE msg:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

     --{{{  register with game
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
     --}}}
    --{{{  attach to game by entering room
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
     --}}}

    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          --{{{  get.name -- game requesting our name (passed as a parameter)
          get.name
            link[s.out] ! name; name
          --}}}
          --{{{  genmsg -- general message
          genmsg; msg
            SKIP
          --}}}
          --{{{  have.object -- inquiry to see if we have a particular object
          have.object; msg
            link[s.out] ! no
          --}}}
          --{{{  enter.room -- message to indicate someone entering a room
          enter.room; msg
            SKIP
          --}}}
          --{{{  leaving.room -- message to  indicate someone leaving a room, nesw indicates direction
          INT nesw:
          leaving.room; msg; nesw
            for.local ! leaving.room; msg; nesw
          --}}}
          --{{{  self.enter.room -- message to indicate that we're entering a room
          INT roomno:
          self.enter.room; roomno
            SKIP
          --}}}
          --{{{  drop.object -- message to indicate something dropping something in a room
          MOBILE []BYTE oname:
          drop.object; msg; oname
            for.local ! drop.object; msg; oname
          --}}} 
          --{{{  sa y.room -- something saying something in a room
          MOBILE []BYTE pname:
          say.room; pname; msg
            for.local ! say.room; pname; msg
          --}}}
          --{{{  action.room -- something performing an action in a room
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          --}}}
          --{{{  take.damage -- damage being inflicted, pass onto local handling
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          --}}}
          --{{{  status -- requesting our status
          status
            -- returns health, vitality and a description
            link[s.out] ! status; 100; 100; "a wandering AI unit"
          --}}}
           --{{{ dropall -- dropping all items
          SHARED CT.ROOMLINK! drop.room:
          dropall; drop.room
            SKIP
          --}}}
          --{{{ resurrect -- bring the bot back to life.
          resurrect
            SKIP
          --  }}}
       --}}}
      --{{{  main bot-code
      INT t, d:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        tim ? t
        d, seed := random (6, seed)
        t := t PLUS (2000000 + (d TIMES 500000))

        INITIAL BOOL running IS TRUE:
        WHILE running
          PRI ALT
            local.in ? CASE
              --{{{  take.damage -- damage being inflicted on us
              MOBILE []BYTE who, oname:
              INT damage:
              take.damage; who; oname; damage
                IF
                  is.immortal
                    link[out] ! say; "I*'m immortal :)"
                  TRUE
                    link[out] ! action; "clinks and clutters"
              --}}}
              --{{{ say.room
              MOBILE []BYTE pname, msg:
              say.room; pname; msg
                SEQ
                  IF
                    msg = "Hi"
                      [256]BYTE str:
                      INITIAL INT slen IS 0:
                      SEQ
                        buffer.set.string( str, slen, "Hi there ")
                        buffer.set.string( str, slen, pname )
                        buffer.set.string( str, slen, "!")
                        --MOBILE []BYTE out:
                        SEQ
                          --out := [slen]BYTE:
                          --out := [str FOR slen]
                          link[out] ! say; [str FOR slen]
                    TRUE
                      SKIP
              --}}}
              --{{{ leaving.room -- follow a user called operator.
              MOBILE []BYTE msg:
              INT nesw:
              leaving.room; msg; nesw
                IF
                  msg = "operator"
                    INT delay:
                    SEQ
                      tim ? t
                      -- Delay can be anything between 1 and 2 seconds.
                      delay, seed := random (1000000, seed)
                      delay := delay + 1000000
                      tim ? AFTER t PLUS delay
                      IF
                        nesw  < 0
                          SKIP -- nowhere
                        nesw = 0
                          link[out] ! north
                        nesw = 1
                          link[out] ! east
                        nesw = 2
                          link[out] ! south
                        nesw = 3
                          link[out] ! west
                        TRUE
                          SKIP -- also nowhere.
                  TRUE
                    SKIP
              --}}}
              --ZR{ drop.object
              MOBILE []BYTE msg, oname:
              drop.object; msg; oname
                [256]BYTE str:
                INITIAL INT slen IS 0:
                SEQ
                  buffer.set.string (str, slen, "Hey, ")
                  buffer.set.string (str, slen, msg)
                  buffer.set.string (str, slen, " you dropped your ")
                  buffer.set.string (str, slen, oname)
                  buffer.set.string (str, slen, "!")
                  link[out] ! say; [str FOR slen] 
              --}}}
            tim ? AFTER t
               --{{{  local timeout, do something and reset timeout
              INT v:
              SEQ
                v, seed := random (5, seed)
                IF
                  v < 3
                    --{{{  try and move
                    INT locn:
                    [4]BOOL exit:
                    INT sexit:
                    SEQ
                      -- look for exits here
                      link[out] ! lookexits
                      link[in] ? CASE exits; locn; exit
                      sexit := -1

                      -- randomly select an exit (slightly crude method, but heyho..)
                      INITIAL INT retries IS 100:
                      WHILE (sexit < 0) AND (retries > 0)
                        SEQ
                          v, seed := random (4, seed)
                          IF
                            exit[v]
                              sexit := v
                            TRUE
                              retries := retries - 1

                      -- don't move randomly anymore.
                      IF
                        sexit < 0
                          SKIP    -- not going anywhere..!
                        sexit = 0
                          --link[out] ! north
                          SKIP
                        sexit = 1
                          --link[out] ! east
                          SKIP
                        sexit = 2
                          --link[out] ! south
                          SKIP
                        sexit = 3
                          --link[out] ! west
                          SKIP
                    --}}}
                  TRUE
                    --{{{  other, say or do something
                    SEQ
                      v, seed := random (7, seed)
                      IF
                        (v >= 4) AND (NOT is.immortal)
                          --{{{  if we're not immortal, look for edibles and consume
                          MOBILE []BYTE ename:
                          BOOL eat:
                          SEQ
                            eat := FALSE

                            link[out] ! objects
                            INITIAL BOOL done IS FALSE:
                            WHILE NOT done
                              link[in] ? CASE
                                MOBILE []BYTE oname:
                                BOOL leat:
                                string; oname
                                  SEQ
                                    IF
                                      oname = "bread"
                                        leat := TRUE
                                      oname = "cookies"
                                        leat := TRUE
                                      oname = "ribena"
                                        leat := TRUE
                                      TRUE
                                        leat := FALSE
                                    IF
                                      leat
                                        SEQ
                                          eat := TRUE
                                          ename := oname
                                      TRUE
                                        SKIP
                                end.of.list
                                  done := TRUE
                            link[out] ! get; ename
                            link[in] ? CASE
                              no
                                -- didn't get object (maybe someone else got there first!)
                                SKIP
                              CT.OBJECT! obj:
                              get.object; ename; obj
                                SEQ
                                  -- consume
                                  obj[in] ! die
                          --}}}
                        v >= 4
                          SKIP
                        -- otherwise say something random
                        v = 0
                          link[out] ! action; "peers around the room"
                        v = 1
                          link[out] ! action; "codes some occam-pi."
                        v = 2
                          link[out] ! say; "Im not a bot, really!"
                        v = 3
                          link[out] ! say; "Me, following you? I dont think so!"
                    --}}}
                -- reset timeout
                d, seed := random (6, seed)
                t := t PLUS (3000000 + (d TIMES 500000))
              --}}}
:
--}}}

--{{{  PROC bot.template (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
--
--  this is a template for a bot, use it as a basis for creating other bots
--
PROC bot.template (VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)

  --{{{  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
  PROC sci.buf (CHAN S.CLIENT.IN in?, out!)
    CHAN BOOL request:
    CHAN S.CLIENT.IN response:
    PAR
      --{{{  owb
      MOBILE []BYTE mb1, mb2:
      INT ib1:
      INITIAL INT case IS -1:
      WHILE TRUE
        PRI ALT
          BOOL any:
          (case >= 0) & request ? any
            --{{{  request
            SEQ
              CASE case
                0
                  response ! enter.room; mb1
                1
                  response ! say.room; mb1; mb2
                2
                  response ! drop.object; mb1; mb2
                3
                  response ! take.damage; mb1; mb2; ib1
              case := -1
            --}}}
          in ? CASE
            enter.room; mb1
              case := 0
            say.room; mb1; mb2
              case := 1
            drop.object; mb1; mb2
              case := 2
            take.damage; mb1; mb2; ib1
              case := 3
      --}}}
      --{{{  auto-prompt
      WHILE TRUE
        MOBILE []BYTE mb1, mb2:
        INT ib1:
        SEQ
          request ! TRUE
          response ? CASE
            enter.room; mb1
              out ! enter.room; mb1
            say.room; mb1; mb2
              out ! say.room; mb1; mb2
            drop.object; mb1; mb2
              out ! drop.object; mb1; mb2
            take.damage; mb1; mb2; ib1
              out ! take.damage; mb1; mb2; ib1
      --}}}
  :
  --}}}

  VAL BOOL is.immortal IS TRUE:
  CT.CLIENT? link:
  CT.CLIENT! gamelink:
  TIMER tim:
  INT seed:
  MOBILE []BYTE msg:
  SHARED CT.ROOMLINK! to.room:
  SEQ
    link, gamelink := MOBILE CT.CLIENT

    --{{{  register with game
    CLAIM to.game
      SEQ
        to.game[in] ! register.name; name
        to.game[out] ? CASE
          name.in.use
            STOP
          ok
            SKIP
    --}}}
    --{{{  get room
    BOOL ok:
    SEQ
      CHAN SHARED CT.ROOMLINK! rc:
      PAR
        util.getroom (2, ilocn, to.game, rc!, ok)
        rc ? to.room
      IF
        NOT ok
          STOP
        TRUE
          SKIP
    --}}}
    --{{{  attach to game by entering room
    CLAIM to.room
      to.room[in] ! new.client; name; gamelink
    --}}}

    CHAN S.CLIENT.IN for.local, local.in:
    PAR
      sci.buf (for.local?, local.in!)
      --{{{  absorb messages and junk
      MOBILE []BYTE msg:
      WHILE TRUE
        link[s.in] ? CASE
          --{{{  get.name -- game requesting our name (passed as a parameter)
          get.name
            link[s.out] ! name; name
          --}}}
          --{{{  genmsg -- general message
          genmsg; msg
            SKIP
          --}}}
          --{{{  have.object -- inquiry to see if we have a particular object
          have.object; msg
            link[s.out] ! no
          --}}}
          --{{{  enter.room -- message to indicate someone entering a room
          enter.room; msg
            SKIP
          --}}}
          --{{{  leaving.room -- message to indicate someone leaving a room, nesw indicates direction
          INT nesw:
          leaving.room; msg; nesw
            SKIP
          --}}}
          --{{{  self.enter.room -- message to indicate that we're entering a room
          INT roomno:
          self.enter.room; roomno
            SKIP
          --}}}
          --{{{  drop.object -- message to indicate something dropping something in a room
          MOBILE []BYTE oname:
          drop.object; msg; oname
            SKIP
          --}}}
          --{{{  say.room -- something saying something in a room
          MOBILE []BYTE pname:
          say.room; pname; msg
            SKIP
          --}}}
          --{{{  action.room -- something performing an action in a room
          MOBILE []BYTE pname:
          action.room; pname; msg
            SKIP
          --}}}
          --{{{  take.damage -- damage being inflicted, pass onto local handling
          MOBILE []BYTE who, oname:
          INT damage:
          take.damage; who; oname; damage
            SEQ
              for.local ! take.damage; who; oname; damage
              link[s.out] ! yes
          --}}}
          --{{{  status -- requesting our status
          status
            -- returns health, vitality and a description
            link[s.out] ! status; 100; 100; "a wandering AI unit"
          --}}}

      --}}}
      --{{{  main bot-code
      INT t, d:
      SEQ
        tim ? seed
        seed := (seed >> 2) + 1
        tim ? t
        d, seed := random (6, seed)
        t := t PLUS (2000000 + (d TIMES 500000))

        INITIAL BOOL running IS TRUE:
        WHILE running
          PRI ALT
            local.in ? CASE
              --{{{  take.damage -- damage being inflicted on us
              MOBILE []BYTE who, oname:
              INT damage:
              take.damage; who; oname; damage
                IF
                  is.immortal
                    link[out] ! say; "I*'m immortal :)"
                  TRUE
                    link[out] ! action; "clinks and clutters"
              --}}}
            tim ? AFTER t
              --{{{  local timeout, do something and reset timeout
              INT v:
              SEQ
                v, seed := random (5, seed)
                IF
                  v < 3
                    --{{{  try and move
                    INT locn:
                    [4]BOOL exit:
                    INT sexit:
                    SEQ
                      -- look for exits here
                      link[out] ! lookexits
                      link[in] ? CASE exits; locn; exit
                      sexit := -1

                      -- randomly select an exit (slightly crude method, but heyho..)
                      INITIAL INT retries IS 100:
                      WHILE (sexit < 0) AND (retries > 0)
                        SEQ
                          v, seed := random (4, seed)
                          IF
                            exit[v]
                              sexit := v
                            TRUE
                              retries := retries - 1

                      -- do actual move
                      IF
                        sexit < 0
                          SKIP    -- not going anywhere..!
                        sexit = 0
                          link[out] ! north
                        sexit = 1
                          link[out] ! east
                        sexit = 2
                          link[out] ! south
                        sexit = 3
                          link[out] ! west
                    --}}}
                  TRUE
                    --{{{  other, say or do something
                    SEQ
                      v, seed := random (7, seed)
                      IF
                        (v >= 4) AND (NOT is.immortal)
                          --{{{  if we're not immortal, look for edibles and consume
                          MOBILE []BYTE ename:
                          BOOL eat:
                          SEQ
                            eat := FALSE

                            link[out] ! objects
                            INITIAL BOOL done IS FALSE:
                            WHILE NOT done
                              link[in] ? CASE
                                MOBILE []BYTE oname:
                                BOOL leat:
                                string; oname
                                  SEQ
                                    IF
                                      oname = "bread"
                                        leat := TRUE
                                      oname = "cookies"
                                        leat := TRUE
                                      oname = "ribena"
                                        leat := TRUE
                                      TRUE
                                        leat := FALSE
                                    IF
                                      leat
                                        SEQ
                                          eat := TRUE
                                          ename := oname
                                      TRUE
                                        SKIP
                                end.of.list
                                  done := TRUE
                            link[out] ! get; ename
                            link[in] ? CASE
                              no
                                -- didn't get object (maybe someone else got there first!)
                                SKIP
                              CT.OBJECT! obj:
                              get.object; ename; obj
                                SEQ
                                  -- consume
                                  obj[in] ! die
                          --}}}
                        v >= 4
                          SKIP
                        -- otherwise say something random
                        v = 0
                          link[out] ! say; "hello, world!"
                        v = 1
                          link[out] ! action; "looks around"
                        v = 2
                          link[out] ! say; "what a nice place this is"
                        v = 3
                          link[out] ! say; "sometimes I feel so trapped in here"
                    --}}}
                -- reset timeout
                d, seed := random (6, seed)
                t := t PLUS (3000000 + (d TIMES 500000))
              --}}}

        SKIP
      --}}}
:
--}}}


--{{{  PROC spawn.student.bot (VAL []BYTE botid, VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
--
--  spawns one of the bots defined here -- tried after others in game-bots.occ
--
PROC spawn.student.bot (VAL []BYTE botid, VAL []BYTE name, VAL INT ilocn, SHARED CT.GAMECTRL! to.game)
  IF
    botid = "js219-bot"
      FORK bot.js219 (name, ilocn, to.game)
    botid = "template"
      FORK bot.template (name, ilocn, to.game)
    TRUE
      SKIP
:
--}}}

