[comp.sys.handhelds] Black Box game for the HP-48SX

peril@extro.ucc.su.OZ.AU (Peter Lisle) (03/30/91)

Black Box Game for the HP-48SX
==============================

This program is copyright (C) 1991 Chris Tham.
Permission is given to copy, use or modify this program, as long as the
original author is given credit.

Game Description
----------------

The Black Box is a large square containing grid of squares.  There are 8
by 8 or 64 squares within the Black Box.  Four of these squares contain
atoms.  You do not know where the four atoms are located within the
black box.  Your task, should you decide to accept it, is to find the
locations of the atoms within the Black Box.

Each square within the Black Box can be labelled by its coordinate
position, as in Figure 1.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       5| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       6| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       7| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       8| | | | | | | | |
        +-+-+-+-+-+-+-+-+

       Figure 1: Coordinate labelling of squares in the black box

Hence the square on the upper left hand corner of the black box is
labelled (1,1), the square on the upper right hand corner of the black
box is labelled (1,8) and so on.

You can determine the location of atoms in the black box by firing rays
into the black box from its sides.  Rays always enter the black box
perpendicular to the side from which it is fired from.  Rays normally
travel in a straight line whilst in the black box.  Depending on the
location of atoms within the black box, the following may happen to a
ray:
1. The ray may pass straight through on the other side of the box.
   This usually (but not always!) means that the ray did not encounter
   any atoms within or adjacent to its path.

               Ray leaves box here
         1 2 3 ^ 5 6 7 8
        +-+-+-+^+-+-+-+-+
       1| | | |^| | | | |            Figure 2: Ray passing straight through
        +-+-+-+^+-+-+-+-+
       2| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       3| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       4| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       5| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters from here

2. The ray may be absorbed.
   A ray is absorbed if it collides with an atom in its path.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |            Figure 3: Ray absorption
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | | |X| | | | |
        +-+-+-+^+-+-+-+-+
       5| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters from here

3. The ray may be reflected back to the side it came from.
   A ray is reflected if it encounters two atoms in front of it, one to
   the left and the other to the right.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |            Figure 4: Ray reflection
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | |X| |X| | | |
        +-+-+-+^+-+-+-+-+
       5| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters and leaves here

4. The ray may be refracted and emerge on a different side of the black
   box.
   A ray is refracted when it encounters an atom in front of it, either
   to the left or to the right.  Note that refractions are reflexive,
   i.e. if you fire a ray and it gets refracted, you can fire a ray into
   the location where the original ray emerged and it will emerge where
   you had fired the original ray.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |            Figure 5: Ray refraction
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | |X| | | | | |
        +-+-+-+-+-+-+-+-+
       5| | | |>>>>>>>>>> Ray emerges here
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters from here

Of course, a ray may get refracted, then absorbed, and so on.  By firing
rays at strategic locations into the black box, you should be able to
deduce the correct locations of the four atoms.

At the start of the game, the program will draw the black box on the
screen and choose the locations of the four atoms randomly.  The object
of the game is to be able to guess correctly the location of all four
atoms within the black box by firing less than or equal to eight rays.
Each time you fire a ray, the program traces its progress in the black
box and informs you what happened to the ray (where it emerges or has
been absorbed).  You may submit a guess of an atom's location at any
time in the game.  If your guess is correct, the program will plot the
atom on the black box.  If your guess is wrong, you lose immediately. 
You win if you manage to guess all four atom locations correctly.  You
lose if you run out of rays.

How to download the game into the HP-48SX
-----------------------------------------

Download the file at the end of this document as 'BBOX'.  Enter into the
directory, press the VAR key to get a list of variables.  Press the soft
key corresponding 'INIT' to execute the initialization routine.
[Warning: this will take a while].  After executing INIT, the game will
be properly set up on the HP-48SX.

How to play the game
--------------------

Press the soft key corresponding to 'PLAY'.  The HP-48SX screen will
show a blank black box together with game credits.  The numeral `4' at
the bottom side of the black box should be inversed.  This is where the
cursor defaults to at the start of the game.  You can move the cursor by
using the arrow keys on the keyboard.  The cursor always inverses
whatever is lying underneath it.  You can move the cursor into the box
as well as any location on the side of the box except at the corners. 

Pressing the ENTER key when the cursor is at the side of the box will
fire a ray into the box from the location of the cursor.  Rays are
labelled from A to H.  The program will trace the movement of the ray
and label the location where the ray emerges with the same letter.

Pressing the ENTER key when the cursor is within the box will cause the
program to check whether the square underneath the cursor contains an
atom.  If it does, the program informs you, draws a cross at the
location of the atom and lets the game continue. 
If it doesn't, you lose immediately.

Pressing the <- key (the one next to the DEL key) causes the game to
exit immediately.

Program description
-------------------

The program uses two global variables which you can change to customise
the game.  'NATOMS' contains the number of atoms that the program puts
into the black box and consequently the number of atoms that you have
to guess.  The default value of 'NATOMS' is 4.  'MAXRAYS' contains the
maximum number of rays that you can fire into the black box.  The
default value of 'MAXRAYS' is 8.  The INIT program generates the cursor
mask grob (using the program GENMASK) and the cross grob (using the
program GENCROS) as well as generating the PICT for the playing screen 
(using the program GENBOX).  It then creates some global variables
needed by the program and then reorders the variables in the directory.
Finally, it deletes the initialization programs that are no longer
needed during game play, including itself, in order to conserve memory.

The main program PLAY calls GENATOMS to generate NATOMS worth of atoms
on the screen.  The atom locations are stored as complex numbers in a
list called ATOMS.  GENATOMS does make sure that each atom occupies a
position in the black box not occupied by any other atom.

PLAY then calls DBOX to put the playing screen PICT (stored in SCREEN) up. 
Finally, PLAY calls MAIN which actually plays the game.

MAIN initializes two variables called NRAYS (containing the number of
rays that the user has fired) and NGUESS (containing the number of
guesses that the user has entered which are correct).  MAIN also does
keyboard processing.  If the user presses the ENTER key, MAIN calls
PROCESS which calls TRACERAY if the ENTER key was pressed whilst the
cursor was on a side rather than within the black box.  If the user
loses or quits the game, DRAWATOMS draws the locations of all atoms on
the screen by marking them with crosses.

Helper routines are NRAND, WRITE1, WRITE2, PUTSTR, PUTCROS, GETPOS and
PUTMASK.

Bugs
----

If you managed to make one correct atom guess, you can win the game by
repeatedly guessing at the same location until NATOMS reaches MAXATOMS.
The program does not check that you have already guessed at that location.
However, if you did that, you are merely cheating yourself.

Author
------
Chris Tham
30th of March 1991

Author's comments
-----------------
BBOX is my first non trivial programming project on the HP-48SX, so
please excuse me for any stupid coding styles or inefficiencies.  The
program is extensively commented and should be reasonably easy to read.

--- cut here ---
%%HP: T(3)A(D)F(.);
DIR @ Black box program
    @ Copyright (C) 1991 Chris Tham
    @ BYTES: 4398 # 16459d
  PLAY @ Main routine for playing game
    \<<
      RCLF 'FLGS' STO             @ Store system and user flags
      STD DEC                     @ Set standard display mode, decimal base
      GENATOMS DBOX MAIN          @ Play game
      FLGS STOF                   @ Restore system and user flags
      3 FREEZE                    @ Freeze display until key pressed
    \>>
  NATOMS 4 @ number of atoms to be generated
  MAXRAYS 8 @ maximum number of rays user can fire
  INIT @ Generate things needed for game
    \<<
      RCLF 'FLGS' STO             @ Store system and user flags
      STD DEC                     @ Set standard display mode, decimal base
      GENMASK                     @ Generate mask for cursor
      GENCROS                     @ Generate cross for displaying atom
      GENBOX                      @ Generate playing screen
      { } 'ATOMS' STO
      0 'NGUESS' STO
      0 'NRAYS' STO               @ Initialize variables
      { PLAY NATOMS MAXRAYS GENATOMS DBOX MAIN PROCESS TRACERAY
        DRAWATOMS NRAND WRITE1 WRITE2 PUTSTR PUTCROS GETPOS PUTMASK
        CROS MASK SCREEN ATOMS NGUESS NRAYS FLGS }
      ORDER                       @ We want the important vars in front }
      'INIT' PURGE
      'GENMASK' PURGE
      'GENCROS' PURGE
      'GENBOX' PURGE              @ Throw aways things we don't need
      FLGS STOF                   @ Restore system and user flags
    \>>
  GENATOMS @ Generate list of atoms
    \<<
      { } 'ATOMS' STO             @ Initialise ATOMS to an empty list
      0 \-> i                     @ zero number of atoms created
      \<<
        DO
          1 8 NRAND
          1 8 NRAND
          R\->C                   @ generate random atom position
          IF ATOMS OVER POS NOT   @ make sure atom not already in list
          THEN
            'ATOMS' STO+          @ add atom to list
            'i' INCR DROP         @ increment number of atoms generated
          END
        UNTIL i NATOMS ==         @ stop when we have generated NATOMS atoms
        END
      \>>
    \>>
  DBOX
    \<<
      SCREEN PICT STO
      { # 0d # 0d } PVIEW
    \>>
  MAIN @ main playing loop
    \<<
      (4,9) \-> p                 @ Default starting cursor position
      \<<
        0 'NRAYS' STO             @ Initialise number of rays fired
        0 'NGUESS' STO            @ Initialise number of correct atom guesses
        DO
          p PUTMASK               @ Put cursor
          0 WAIT                  @ Wait for keystroke
          p PUTMASK               @ Hide cursor
          CASE
          DUP 34.1 ==             @ Left arrow
          p RE 0 \=/ AND          @ Not at left edge
          p (1,0) \=/ AND         @ Not entering undefined region
          p (1,9) \=/ AND
            THEN (-1,0) END       @ Move one position left
          DUP 36.1 ==             @ Right arrow
          p RE 9 \=/ AND          @ Not at right edge
          p (8,0) \=/ AND         @ Not entering undefined region
          p (8,9) \=/ AND
            THEN (1,0) END        @ Move one position right
          DUP 25.1 ==             @ Up arrow
          p IM 0 \=/ AND          @ Not at top edge
          p (0,1) \=/ AND         @ Not entering undefined region
          p (9,1) \=/ AND
            THEN (0,-1) END       @ Move one position up
          DUP 35.1 ==             @ Down arrow
          p IM 9 \=/ AND          @ Not at bottom edge
          p (0,8) \=/ AND         @ Not entering undefined region
          p (9,8) \=/ AND
            THEN (0,1) END        @ Move one position down
          DUP 51.1 ==             @ ENTER key
            THEN
              p PROCESS           @ process ENTER key
              IF DUP (0,0) \=/    @ if not zero then store as new position
                THEN 'p' STO (0,0) END
            END
            (0,0)                 @ don't move from stored position
          END
          'p' STO+                @ changed current position
        UNTIL 55.1 ==             @ Quit key pressed
              NRAYS MAXRAYS > OR  @ maximum number of rays fired
              NGUESS NATOMS == OR @ correct number of atoms guessed
        END
        PICT { # 70d # 40d }      @ Position in which to write message
        CASE
        NGUESS NATOMS ==          @ User won
          THEN "You Win" END
        NRAYS MAXRAYS >           @ User lost
          THEN
            DRAWATOMS
            "You Lose"
          END
          DRAWATOMS               @ User quitted
          "You Quit"
        END
        2 \->GROB REPL            @ Write message
      \>>
    \>>
  PROCESS @ Handle processing when ENTER key is pressed
    \<<
      PICT { # 70d # 48d } # 60d # 16d BLANK REPL
      IF DUP RE 0 ==              @ if we are at edge of grid
         OVER RE 9 == OR
         OVER IM 0 == OR
         OVER IM 9 == OR
      THEN                        @ fire off ray and check results
        NRAYS 65 + CHR            @ convert ray number into alphabetic character
        DUP2 SWAP PUTSTR          @ replace current character at edge
        DUP2 "Ray " SWAP + SWAP \->STR + WRITE1 @ Write Ray N(x,y)
        SWAP DUP TRACERAY         @ Trace movement of ray
        CASE
        DUP (0,0) ==
          THEN
            "Absorbed" WRITE2
            DROP SWAP DROP        @ throw away Ray character and start position
          END
        DUP2 ==
          THEN
            "Reflected" WRITE2
            SWAP DROP SWAP DROP   @ throw away Ray character and start position
          END
          SWAP DROP "To " OVER \->STR + WRITE2
          SWAP OVER PUTSTR        @ put Ray character at end of ray
        END
        'NRAYS' INCR DROP         @ Increment number of rays fired
      ELSE
        "Atom " OVER \->STR + WRITE1 @ write Atom (x,y)
        IF ATOMS OVER POS         @ is guess correct
        THEN
          DUP PUTCROS             @ yes, put a cross at location
          'NGUESS' INCR DROP      @ increment number of correct guesses
          "Correct"               @ print this string
        ELSE
          MAXRAYS 1 + 'NRAYS' STO           @ finish off the user
          "Wrong"
        END WRITE2
      END
    \>>
  TRACERAY @ trace path of ray fired
    \<<
      (0,0) \-> r m               @ local variables
      \<<
        CASE
        r RE 0 ==                 @ determine which direction ray goes
          THEN (1,0) END
        r RE 9 ==
          THEN (-1,0) END
        r IM 0 ==
          THEN (0,1) END
        r IM 9 ==
          THEN (0,-1) END
          "Illegal ray" DOERR
        END
        DO
          DUP RE (0,1) (1,0) IFTE @ find direction perpendicular to ray
          'm' STO
          DUP r + DUP DUP
          6                       @ Flag 6 is set if ray is deflected
          IF SWAP m + ATOMS SWAP POS
          THEN SF
          ELSE CF
          END
          7                       @ Flag 7 is set if ray is deflected
          IF SWAP m - ATOMS SWAP POS
          THEN SF
          ELSE CF
          END
          CASE
          ATOMS SWAP POS          @ if atom in front of ray then absorb ray
            THEN DROP r NEG END
          6 FS? 7 FS? AND         @ if both flags set then reflect ray
            THEN NEG END
          7 FS?                   @ otherwise deflect ray depending on flag
            THEN DROP m END
          6 FS?
            THEN DROP m NEG END
          END
          DUP 'r' STO+            @ get new ray position
        UNTIL r RE 0 ==           @ until we reach an edge
              r RE 9 == OR
              r IM 0 == OR
              r IM 9 == OR
        END
        DROP r                    @ return final position
      \>>
    \>>
  DRAWATOMS @ Draw current location of all atoms
    \<<
      1 NATOMS
      FOR i
        ATOMS i GET PUTCROS       @ put a cross for every atom
      NEXT
    \>>
  NRAND @ Generate random number from m to n
    \<<
      \-> M N
      \<<
        N M - 1 + RAND * IP M +
      \>>
    \>>
  WRITE1 @ Write string in Area 1 of display
    \<<
      PICT { # 70d # 48d } ROT 2 \->GROB REPL
    \>>
  WRITE2 @ Write string in Area 2 of display
    \<<
      PICT { # 70d # 56d } ROT 2 \->GROB REPL
    \>>
  PUTSTR @ Put string in level 2 at cursor position in level 1
    \<<
      GETPOS                      @ get coordinates of cursor position
      ROT 1 \->GROB               @ convert string to graphics object
      # 5d # 5d BLANK             @ create a blank clip window
      { # 1d # 0d } ROT REPL      @ clip string in window
      REPL                        @ draw string
    \>>
  PUTCROS @ Put a cross mark at cursor position
    \<<
      GETPOS CROS REPL
    \>>
  GETPOS @ get PICT position given cursor position
    \<<
      C\->R
      6 * 1 + R\->B
      SWAP 6 * 1 + R\->B
      SWAP 2 \->LIST
      PICT SWAP
    \>>
  PUTMASK @ XOR mask onto PICT
    \<<
      GETPOS MASK GXOR
    \>>
  GENMASK @ Generate cursor mask
    \<<
      # 5d # 5d BLANK PICT STO    @ create 5x5 blank grob
      0 4
      FOR i                       @ fill each pixel
        0 4 FOR j
          i R\->B
          j R\->B
          2 \->LIST PIXON
        NEXT
      NEXT
      PICT { # 0d # 0d } { # 4d # 4d } SUB
      'MASK' STO                  @ store in variable name
    \>>
  GENCROS @ Generate cross mark
    \<<
      # 5d # 5d BLANK PICT STO    @ create 5x5 blank grob
      0 4                         @ draw the cross pixel at a time
      FOR i
        i R\->B DUP DUP DUP
        2 \->LIST PIXON
        4 SWAP - 2 \->LIST PIXON
      NEXT
      PICT { # 0d # 0d } { # 4d # 4d } SUB
      'CROS' STO                  @ store in variable name
    \>>
  GENBOX @ create main playing screen
    \<<
      # 131d # 64d BLANK PICT STO
      1 8
      FOR i
        6 i * 1 + R\->B           @ calculate # 6 * i + 1
        i 1 \->GROB               @ create grob from i in smallest font
        PICT # 2d 4 PICK 2 \->LIST 3 PICK REPL
        PICT # 56d 4 PICK 2 \->LIST 3 PICK REPL
        SWAP 1 + SWAP
        PICT 3 PICK # 1d 2 \->LIST 3 PICK REPL
        PICT SWAP ROT # 55d 2 \->LIST SWAP REPL
      NEXT
      6 56                        @ Draw lines of grid
      FOR i
        i R\->B
        # 6d OVER 2 \->LIST
        # 54d 3 PICK 2 \->LIST LINE
       DUP # 6d 2 \->LIST
       SWAP # 54d 2 \->LIST LINE
      6 STEP
      PICT { # 76d # 0d } "Black Box" 3 \->GROB REPL
      PICT { # 100d # 10d } "(C) 1990" 1 \->GROB REPL
      PICT { # 90d # 16d } "Chris Tham" 1 \->GROB REPL
      PICT RCL 'SCREEN' STO       @ Store in SCREEN variable
    \>>
END
--- cut here ---

ngsl@hpsgm2.sgp.hp.com (Shuh Lit Ng) (04/05/91)

There is one other rule that you forgot to
mention:

     When an atom is at the edge of the box,
     a ray on either sides of the atom will
     be reflected.

This feature is not very well taken care of in the 48
program, because the ray does not get reflected back
to its original path, but instead goes to the adjacent
row/column.  Can this be corrected?

Also, it'll be super if the option of number of atoms
and rays are given to the user, just like the original.
I know the size of the box is limited to the 48 screen,
but maybe the width can span wider than 8 columns?

All in all, a great effort!  Thanx.

peril@extro.ucc.su.oz.au (Peter Lisle) (04/07/91)

In article <210003@hpsgm2.sgp.hp.com> ngsl@hpsgm2.sgp.hp.com (Shuh Lit Ng) writes:
>There is one other rule that you forgot to
>mention:
>
>     When an atom is at the edge of the box,
>     a ray on either sides of the atom will
>     be reflected.
>
>This feature is not very well taken care of in the 48
>program, because the ray does not get reflected back
>to its original path, but instead goes to the adjacent
>row/column.  Can this be corrected?

I realized about this problem and was going to mention it in the Bugs
section but then forgot about it.

What the program does is that it notices the atom on the edge and prepares
to do a refraction.  After performing the refraction it then notices the ray
is already on the edge so it thinks the ray has already emerged.

A fix should be possible, just edit TRACERAY to handle this special case.

By the way, I was not aware of this rule, and I thought the current
implementation was acceptable.  Now that you have mentioned it, I might
include it in Version 1.1.

You also mentioned something about the "original" version of the game.
Do you have the "official" instructions for the original version?  I
have never seen the original version.  My implementation was from
memory of a Commodore PET version that I have seen published in a
magazine some ten years ago.

>Also, it'll be super if the option of number of atoms
>and rays are given to the user, just like the original.
>I know the size of the box is limited to the 48 screen,
>but maybe the width can span wider than 8 columns?

The number of atoms that the program generates is in the variable NATOMS
and the maximum number of rays the user can fire is in the variable MAXRAYS.
This is mentioned in the "Program Description" section of the documentation
and you are allowed to change the contents of the variables.

It should be possible to generalize the program so that the user can select
the box dimensions.  I'll think about it for Version 1.1.

>All in all, a great effort!  Thanx.
Thank you.

By the way, I noticed that you are from HP Singapore.  How are things there?
I am planning a visit in early May.  Any interesting 48SX goodies for me
to purchase? (not that my wife will let me, but never mind)

Chris Tham, masquerading as Peter

rep@genrad.com (Pete Peterson) (04/27/91)

In article <1991Apr7.055320.9091@metro.ucc.su.OZ.AU> peril@extro.ucc.su.oz.au (Peter Lisle) writes:
 >In article <210003@hpsgm2.sgp.hp.com> ngsl@hpsgm2.sgp.hp.com (Shuh Lit Ng) writes:
 >>There is one other rule that you forgot to
 >>mention:
 >>     When an atom is at the edge of the box,
 >>     a ray on either sides of the atom will
 >>     be reflected.
 >>This feature is not very well taken care of in the 48
 >>program, because the ray does not get reflected back
 >>to its original path, but instead goes to the adjacent
 >>row/column.  Can this be corrected?
 >
 >You also mentioned something about the "original" version of the game.
 >Do you have the "official" instructions for the original version?  I
 >have never seen the original version.  My implementation was from
 >memory of a Commodore PET version that I have seen published in a
 >magazine some ten years ago.
 
I have not seen the hp48 game, but if this is the same as the Parker
Brothers board game, there is a 1978 rule book which has about 8 pages
explaining the rules, one practice game ( 1 page) and 14 pages of solitaire
play lookup tables and game solutions.

ngsl@hpsgm2.sgp.hp.com (Shuh Lit Ng) (05/08/91)

Here's the posting of the rules for Black Box game.


Black Box Game for the HP-48SX
==============================

This program is copyright (C) 1991 Chris Tham.
Permission is given to copy, use or modify this program, as long as the
original author is given credit.

Game Description
----------------

The Black Box is a large square containing grid of squares.  There are 8
by 8 or 64 squares within the Black Box.  Four of these squares contain
atoms.  You do not know where the four atoms are located within the
black box.  Your task, should you decide to accept it, is to find the
locations of the atoms within the Black Box.

Each square within the Black Box can be labelled by its coordinate
position, as in Figure 1.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       5| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       6| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       7| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       8| | | | | | | | |
        +-+-+-+-+-+-+-+-+

       Figure 1: Coordinate labelling of squares in the black box

Hence the square on the upper left hand corner of the black box is
labelled (1,1), the square on the upper right hand corner of the black
box is labelled (1,8) and so on.

You can determine the location of atoms in the black box by firing rays
into the black box from its sides.  Rays always enter the black box
perpendicular to the side from which it is fired from.  Rays normally
travel in a straight line whilst in the black box.  Depending on the
location of atoms within the black box, the following may happen to a
ray:
1. The ray may pass straight through on the other side of the box.
   This usually (but not always!) means that the ray did not encounter
   any atoms within or adjacent to its path.

               Ray leaves box here
         1 2 3 ^ 5 6 7 8
        +-+-+-+^+-+-+-+-+
       1| | | |^| | | | |            Figure 2: Ray passing straight through
        +-+-+-+^+-+-+-+-+
       2| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       3| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       4| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       5| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters from here

2. The ray may be absorbed.
   A ray is absorbed if it collides with an atom in its path.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |            Figure 3: Ray absorption
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | | |X| | | | |
        +-+-+-+^+-+-+-+-+
       5| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters from here

3. The ray may be reflected back to the side it came from.
   A ray is reflected if it encounters two atoms in front of it, one to
   the left and the other to the right.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |            Figure 4: Ray reflection
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | |X| |X| | | |
        +-+-+-+^+-+-+-+-+
       5| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters and leaves here

4. The ray may be refracted and emerge on a different side of the black
   box.
   A ray is refracted when it encounters an atom in front of it, either
   to the left or to the right.  Note that refractions are reflexive,
   i.e. if you fire a ray and it gets refracted, you can fire a ray into
   the location where the original ray emerged and it will emerge where
   you had fired the original ray.

         1 2 3 4 5 6 7 8
        +-+-+-+-+-+-+-+-+
       1| | | | | | | | |            Figure 5: Ray refraction
        +-+-+-+-+-+-+-+-+
       2| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       3| | | | | | | | |
        +-+-+-+-+-+-+-+-+
       4| | |X| | | | | |
        +-+-+-+-+-+-+-+-+
       5| | | |>>>>>>>>>> Ray emerges here
        +-+-+-+^+-+-+-+-+
       6| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       7| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
       8| | | |^| | | | |
        +-+-+-+^+-+-+-+-+
               ^
               Ray enters from here

Of course, a ray may get refracted, then absorbed, and so on.  By firing
rays at strategic locations into the black box, you should be able to
deduce the correct locations of the four atoms.

At the start of the game, the program will draw the black box on the
screen and choose the locations of the four atoms randomly.  The object
of the game is to be able to guess correctly the location of all four
atoms within the black box by firing less than or equal to eight rays.
Each time you fire a ray, the program traces its progress in the black
box and informs you what happened to the ray (where it emerges or has
been absorbed).  You may submit a guess of an atom's location at any
time in the game.  If your guess is correct, the program will plot the
atom on the black box.  If your guess is wrong, you lose immediately. 
You win if you manage to guess all four atom locations correctly.  You
lose if you run out of rays.

How to download the game into the HP-48SX
-----------------------------------------

Download the file at the end of this document as 'BBOX'.  Enter into the
directory, press the VAR key to get a list of variables.  Press the soft
key corresponding 'INIT' to execute the initialization routine.
[Warning: this will take a while].  After executing INIT, the game will
be properly set up on the HP-48SX.

How to play the game
--------------------

Press the soft key corresponding to 'PLAY'.  The HP-48SX screen will
show a blank black box together with game credits.  The numeral `4' at
the bottom side of the black box should be inversed.  This is where the
cursor defaults to at the start of the game.  You can move the cursor by
using the arrow keys on the keyboard.  The cursor always inverses
whatever is lying underneath it.  You can move the cursor into the box
as well as any location on the side of the box except at the corners. 

Pressing the ENTER key when the cursor is at the side of the box will
fire a ray into the box from the location of the cursor.  Rays are
labelled from A to H.  The program will trace the movement of the ray
and label the location where the ray emerges with the same letter.

Pressing the ENTER key when the cursor is within the box will cause the
program to check whether the square underneath the cursor contains an
atom.  If it does, the program informs you, draws a cross at the
location of the atom and lets the game continue. 
If it doesn't, you lose immediately.

Pressing the <- key (the one next to the DEL key) causes the game to
exit immediately.

Program description
-------------------

The program uses two global variables which you can change to customise
the game.  'NATOMS' contains the number of atoms that the program puts
into the black box and consequently the number of atoms that you have
to guess.  The default value of 'NATOMS' is 4.  'MAXRAYS' contains the
maximum number of rays that you can fire into the black box.  The
default value of 'MAXRAYS' is 8.  The INIT program generates the cursor
mask grob (using the program GENMASK) and the cross grob (using the
program GENCROS) as well as generating the PICT for the playing screen 
(using the program GENBOX).  It then creates some global variables
needed by the program and then reorders the variables in the directory.
Finally, it deletes the initialization programs that are no longer
needed during game play, including itself, in order to conserve memory.

The main program PLAY calls GENATOMS to generate NATOMS worth of atoms
on the screen.  The atom locations are stored as complex numbers in a
list called ATOMS.  GENATOMS does make sure that each atom occupies a
position in the black box not occupied by any other atom.

PLAY then calls DBOX to put the playing screen PICT (stored in SCREEN) up. 
Finally, PLAY calls MAIN which actually plays the game.

MAIN initializes two variables called NRAYS (containing the number of
rays that the user has fired) and NGUESS (containing the number of
guesses that the user has entered which are correct).  MAIN also does
keyboard processing.  If the user presses the ENTER key, MAIN calls
PROCESS which calls TRACERAY if the ENTER key was pressed whilst the
cursor was on a side rather than within the black box.  If the user
loses or quits the game, DRAWATOMS draws the locations of all atoms on
the screen by marking them with crosses.

Helper routines are NRAND, WRITE1, WRITE2, PUTSTR, PUTCROS, GETPOS and
PUTMASK.

Bugs
----

If you managed to make one correct atom guess, you can win the game by
repeatedly guessing at the same location until NATOMS reaches MAXATOMS.
The program does not check that you have already guessed at that location.
However, if you did that, you are merely cheating yourself.

Author
------
Chris Tham
30th of March 1991

Author's comments
-----------------
BBOX is my first non trivial programming project on the HP-48SX, so
please excuse me for any stupid coding styles or inefficiencies.  The
program is extensively commented and should be reasonably easy to read.

--- cut here ---
%%HP: T(3)A(D)F(.);
DIR @ Black box program
    @ Copyright (C) 1991 Chris Tham
    @ BYTES: 4398 # 16459d
  PLAY @ Main routine for playing game
    \<<
      RCLF 'FLGS' STO             @ Store system and user flags
      STD DEC                     @ Set standard display mode, decimal base
      GENATOMS DBOX MAIN          @ Play game
      FLGS STOF                   @ Restore system and user flags
      3 FREEZE                    @ Freeze display until key pressed
    \>>
  NATOMS 4 @ number of atoms to be generated
  MAXRAYS 8 @ maximum number of rays user can fire
  INIT @ Generate things needed for game
    \<<
      RCLF 'FLGS' STO             @ Store system and user flags
      STD DEC                     @ Set standard display mode, decimal base
      GENMASK                     @ Generate mask for cursor
      GENCROS                     @ Generate cross for displaying atom
      GENBOX                      @ Generate playing screen
      { } 'ATOMS' STO
      0 'NGUESS' STO
      0 'NRAYS' STO               @ Initialize variables
      { PLAY NATOMS MAXRAYS GENATOMS DBOX MAIN PROCESS TRACERAY
        DRAWATOMS NRAND WRITE1 WRITE2 PUTSTR PUTCROS GETPOS PUTMASK
        CROS MASK SCREEN ATOMS NGUESS NRAYS FLGS }
      ORDER                       @ We want the important vars in front }
      'INIT' PURGE
      'GENMASK' PURGE
      'GENCROS' PURGE
      'GENBOX' PURGE              @ Throw aways things we don't need
      FLGS STOF                   @ Restore system and user flags
    \>>
  GENATOMS @ Generate list of atoms
    \<<
      { } 'ATOMS' STO             @ Initialise ATOMS to an empty list
      0 \-> i                     @ zero number of atoms created
      \<<
        DO
          1 8 NRAND
          1 8 NRAND
          R\->C                   @ generate random atom position
          IF ATOMS OVER POS NOT   @ make sure atom not already in list
          THEN
            'ATOMS' STO+          @ add atom to list
            'i' INCR DROP         @ increment number of atoms generated
          END
        UNTIL i NATOMS ==         @ stop when we have generated NATOMS atoms
        END
      \>>
    \>>
  DBOX
    \<<
      SCREEN PICT STO
      { # 0d # 0d } PVIEW
    \>>
  MAIN @ main playing loop
    \<<
      (4,9) \-> p                 @ Default starting cursor position
      \<<
        0 'NRAYS' STO             @ Initialise number of rays fired
        0 'NGUESS' STO            @ Initialise number of correct atom guesses
        DO
          p PUTMASK               @ Put cursor
          0 WAIT                  @ Wait for keystroke
          p PUTMASK               @ Hide cursor
          CASE
          DUP 34.1 ==             @ Left arrow
          p RE 0 \=/ AND          @ Not at left edge
          p (1,0) \=/ AND         @ Not entering undefined region
          p (1,9) \=/ AND
            THEN (-1,0) END       @ Move one position left
          DUP 36.1 ==             @ Right arrow
          p RE 9 \=/ AND          @ Not at right edge
          p (8,0) \=/ AND         @ Not entering undefined region
          p (8,9) \=/ AND
            THEN (1,0) END        @ Move one position right
          DUP 25.1 ==             @ Up arrow
          p IM 0 \=/ AND          @ Not at top edge
          p (0,1) \=/ AND         @ Not entering undefined region
          p (9,1) \=/ AND
            THEN (0,-1) END       @ Move one position up
          DUP 35.1 ==             @ Down arrow
          p IM 9 \=/ AND          @ Not at bottom edge
          p (0,8) \=/ AND         @ Not entering undefined region
          p (9,8) \=/ AND
            THEN (0,1) END        @ Move one position down
          DUP 51.1 ==             @ ENTER key
            THEN
              p PROCESS           @ process ENTER key
              IF DUP (0,0) \=/    @ if not zero then store as new position
                THEN 'p' STO (0,0) END
            END
            (0,0)                 @ don't move from stored position
          END
          'p' STO+                @ changed current position
        UNTIL 55.1 ==             @ Quit key pressed
              NRAYS MAXRAYS > OR  @ maximum number of rays fired
              NGUESS NATOMS == OR @ correct number of atoms guessed
        END
        PICT { # 70d # 40d }      @ Position in which to write message
        CASE
        NGUESS NATOMS ==          @ User won
          THEN "You Win" END
        NRAYS MAXRAYS >           @ User lost
          THEN
            DRAWATOMS
            "You Lose"
          END
          DRAWATOMS               @ User quitted
          "You Quit"
        END
        2 \->GROB REPL            @ Write message
      \>>
    \>>
  PROCESS @ Handle processing when ENTER key is pressed
    \<<
      PICT { # 70d # 48d } # 60d # 16d BLANK REPL
      IF DUP RE 0 ==              @ if we are at edge of grid
         OVER RE 9 == OR
         OVER IM 0 == OR
         OVER IM 9 == OR
      THEN                        @ fire off ray and check results
        NRAYS 65 + CHR            @ convert ray number into alphabetic character
        DUP2 SWAP PUTSTR          @ replace current character at edge
        DUP2 "Ray " SWAP + SWAP \->STR + WRITE1 @ Write Ray N(x,y)
        SWAP DUP TRACERAY         @ Trace movement of ray
        CASE
        DUP (0,0) ==
          THEN
            "Absorbed" WRITE2
            DROP SWAP DROP        @ throw away Ray character and start position
          END
        DUP2 ==
          THEN
            "Reflected" WRITE2
            SWAP DROP SWAP DROP   @ throw away Ray character and start position
          END
          SWAP DROP "To " OVER \->STR + WRITE2
          SWAP OVER PUTSTR        @ put Ray character at end of ray
        END
        'NRAYS' INCR DROP         @ Increment number of rays fired
      ELSE
        "Atom " OVER \->STR + WRITE1 @ write Atom (x,y)
        IF ATOMS OVER POS         @ is guess correct
        THEN
          DUP PUTCROS             @ yes, put a cross at location
          'NGUESS' INCR DROP      @ increment number of correct guesses
          "Correct"               @ print this string
        ELSE
          MAXRAYS 1 + 'NRAYS' STO           @ finish off the user
          "Wrong"
        END WRITE2
      END
    \>>
  TRACERAY @ trace path of ray fired
    \<<
      (0,0) \-> r m               @ local variables
      \<<
        CASE
        r RE 0 ==                 @ determine which direction ray goes
          THEN (1,0) END
        r RE 9 ==
          THEN (-1,0) END
        r IM 0 ==
          THEN (0,1) END
        r IM 9 ==
          THEN (0,-1) END
          "Illegal ray" DOERR
        END
        DO
          DUP RE (0,1) (1,0) IFTE @ find direction perpendicular to ray
          'm' STO
          DUP r + DUP DUP
          6                       @ Flag 6 is set if ray is deflected
          IF SWAP m + ATOMS SWAP POS
          THEN SF
          ELSE CF
          END
          7                       @ Flag 7 is set if ray is deflected
          IF SWAP m - ATOMS SWAP POS
          THEN SF
          ELSE CF
          END
          CASE
          ATOMS SWAP POS          @ if atom in front of ray then absorb ray
            THEN DROP r NEG END
          6 FS? 7 FS? AND         @ if both flags set then reflect ray
            THEN NEG END
          7 FS?                   @ otherwise deflect ray depending on flag
            THEN DROP m END
          6 FS?
            THEN DROP m NEG END
          END
          DUP 'r' STO+            @ get new ray position
        UNTIL r RE 0 ==           @ until we reach an edge
              r RE 9 == OR
              r IM 0 == OR
              r IM 9 == OR
        END
        DROP r                    @ return final position
      \>>
    \>>
  DRAWATOMS @ Draw current location of all atoms
    \<<
      1 NATOMS
      FOR i
        ATOMS i GET PUTCROS       @ put a cross for every atom
      NEXT
    \>>
  NRAND @ Generate random number from m to n
    \<<
      \-> M N
      \<<
        N M - 1 + RAND * IP M +
      \>>
    \>>
  WRITE1 @ Write string in Area 1 of display
    \<<
      PICT { # 70d # 48d } ROT 2 \->GROB REPL
    \>>
  WRITE2 @ Write string in Area 2 of display
    \<<
      PICT { # 70d # 56d } ROT 2 \->GROB REPL
    \>>
  PUTSTR @ Put string in level 2 at cursor position in level 1
    \<<
      GETPOS                      @ get coordinates of cursor position
      ROT 1 \->GROB               @ convert string to graphics object
      # 5d # 5d BLANK             @ create a blank clip window
      { # 1d # 0d } ROT REPL      @ clip string in window
      REPL                        @ draw string
    \>>
  PUTCROS @ Put a cross mark at cursor position
    \<<
      GETPOS CROS REPL
    \>>
  GETPOS @ get PICT position given cursor position
    \<<
      C\->R
      6 * 1 + R\->B
      SWAP 6 * 1 + R\->B
      SWAP 2 \->LIST
      PICT SWAP
    \>>
  PUTMASK @ XOR mask onto PICT
    \<<
      GETPOS MASK GXOR
    \>>
  GENMASK @ Generate cursor mask
    \<<
      # 5d # 5d BLANK PICT STO    @ create 5x5 blank grob
      0 4
      FOR i                       @ fill each pixel
        0 4 FOR j
          i R\->B
          j R\->B
          2 \->LIST PIXON
        NEXT
      NEXT
      PICT { # 0d # 0d } { # 4d # 4d } SUB
      'MASK' STO                  @ store in variable name
    \>>
  GENCROS @ Generate cross mark
    \<<
      # 5d # 5d BLANK PICT STO    @ create 5x5 blank grob
      0 4                         @ draw the cross pixel at a time
      FOR i
        i R\->B DUP DUP DUP
        2 \->LIST PIXON
        4 SWAP - 2 \->LIST PIXON
      NEXT
      PICT { # 0d # 0d } { # 4d # 4d } SUB
      'CROS' STO                  @ store in variable name
    \>>
  GENBOX @ create main playing screen
    \<<
      # 131d # 64d BLANK PICT STO
      1 8
      FOR i
        6 i * 1 + R\->B           @ calculate # 6 * i + 1
        i 1 \->GROB               @ create grob from i in smallest font
        PICT # 2d 4 PICK 2 \->LIST 3 PICK REPL
        PICT # 56d 4 PICK 2 \->LIST 3 PICK REPL
        SWAP 1 + SWAP
        PICT 3 PICK # 1d 2 \->LIST 3 PICK REPL
        PICT SWAP ROT # 55d 2 \->LIST SWAP REPL
      NEXT
      6 56                        @ Draw lines of grid
      FOR i
        i R\->B
        # 6d OVER 2 \->LIST
        # 54d 3 PICK 2 \->LIST LINE
       DUP # 6d 2 \->LIST
       SWAP # 54d 2 \->LIST LINE
      6 STEP
      PICT { # 76d # 0d } "Black Box" 3 \->GROB REPL
      PICT { # 100d # 10d } "(C) 1990" 1 \->GROB REPL
      PICT { # 90d # 16d } "Chris Tham" 1 \->GROB REPL
      PICT RCL 'SCREEN' STO       @ Store in SCREEN variable
    \>>
END
--- cut here ---

jurjen@cwi.nl (Jurjen NE Bos) (05/14/91)

The people who like Black Box, will probably be interested in the hexagonal
version of the game: HEXATOM.  I posted the revised version August 28, 1990.
It is available on most archives.
Have fun!