/***************************************************************************
 *                                                                         *
 * 'Game of Life - Duo' - a "Game of Life" for 2 kinds of cells            *
 *                                                                         *
 ***************************************************************************
 *                                                                         *
 * Created by                                                              *
 *              Andreas Neubacher                                          *
 *                                                                         *
 *              Hausleitnerweg 26                                          *
 *              4020 Linz                                                  *
 *              Austria                                                    *
 *                                                                         *
 *              E-mail: aneubach@risc.uni-linz.ac.at (Internet)            *
 *                       k318577@alijku11            (Bitnet)              *
 *                                                                         *
 ***************************************************************************
 *                                                                         *
 *  Copyright notice:                                                      *
 *  I don't claim any copyright and put this code into the public domain.  *
 *                                                                         *
 ***************************************************************************
 *                                                                         *
 * 92-01-23  AN : Finished release version                                 *
 *                                                                         *
 ***************************************************************************/

#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include "functions.h"

#ifdef _DCC
void wbmain() { main(); }   /* DICE needs this for Workbench startup. */
#endif


extern void SetCell(struct BitMap*,short,short,short),
            ScanAgar(char*,char*,char*,short,short,short),
            ChangeAgar(struct BitMap*,char*,char*,short,short,short,short),
            DisplayAgar(struct BitMap*,char*,short,short,short,short),
            CountCells(char*,short*,short*,unsigned short);


#define XSIZE   162   /* No. of columns + 2 */
#define YSIZE   62    /* No. of rows + 2    */

#define AGARLE  0     /* Coordinates of the left upper corner */
#define AGARTE  25    /* of the 'Agar' output area.           */

#define MAXCELL 3     /* No. of cell types (dead, red, blue) */
#define MAXVAL  16    /* 8 * (MAXCELL-1)                     */

#define NULLCODE  1   /* Code for dead cells               */
#define P1CODE    0   /* Code for Strain 1 cells           */
#define P2CODE    2   /* Code for Strain 2 cells           */
#define NOCODE    3   /* Code for 'cell remains unchanges' */

#define NULLPEN   0             /* Color for dead cells     */
#define P1PEN     2             /* Color for Strain 1 cells */
#define P2PEN     1             /* Color for Strain 2 cells */
#define GRIDPEN   MAXCELL       /* Color for grid           */
#define FGPEN     MAXCELL + 1L  /* Textcolor                */


/***************************************************************************
 *
 * MENUS
 *
 ***************************************************************************/

#define EDITITEMWID 100 + COMMWIDTH
#define PITEMWID    60

struct IntuiText ClearText = {
  0,0,JAM1,
  8,1,
  NULL,
  "Clear Agar",
  NULL
};

struct MenuItem ClearItem = {
  NULL,
  0,10,EDITITEMWID,10,
  ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ,
  0,
  (APTR)&ClearText,
  NULL,
  'c',
  NULL,0
};

struct IntuiText UndoText = {
  0,0,JAM1,
  8,1,
  NULL,
  "Undo",
  NULL
};

struct MenuItem UndoItem = {
  &ClearItem,
  0,0,EDITITEMWID,10,
  ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ,
  0,
  (APTR)&UndoText,
  NULL,
  'u',
  NULL,0
};

struct Menu EditMenu = {
  NULL,
  75,0,40,0,
  MENUENABLED,
  "Edit",
  &UndoItem
};

struct IntuiText QuitText = {
  0,0,JAM1,
  8,1,
  NULL,
  "Quit",
  NULL
};

struct MenuItem QuitItem = {
  NULL,
  0,36,PITEMWID,10,
  ITEMTEXT | ITEMENABLED | HIGHCOMP,
  0,
  (APTR)&QuitText,
  NULL,0,NULL,0
};

struct IntuiText ResetText = {
  0,0,JAM1,
  8,1,
  NULL,
  "Reset",
  NULL
};

struct MenuItem ResetItem = {
  &QuitItem,
  0,24,PITEMWID,10,
  ITEMTEXT | ITEMENABLED | HIGHCOMP,
  0,
  (APTR)&ResetText,
  NULL,0,NULL,0
};

struct IntuiText RulesText = {
  0,0,JAM1,
  8,1,
  NULL,
  "Rules",
  NULL
};

struct MenuItem RulesItem = {
  &ResetItem,
  0,12,PITEMWID,10,
  ITEMTEXT | ITEMENABLED | HIGHCOMP,
  0,
  (APTR)&RulesText,
  NULL,0,NULL,0
};

struct IntuiText AboutText = {
  0,0,JAM1,
  8,1,
  NULL,
  "About",
  NULL
};

struct MenuItem AboutItem = {
  &RulesItem,
  0,0,PITEMWID,10,
  ITEMTEXT | ITEMENABLED | HIGHCOMP,
  0,
  (APTR)&AboutText,
  NULL,0,NULL,0
};

struct Menu ProjectMenu = {
  &EditMenu,
  0,0,PITEMWID,0,
  MENUENABLED,
  "Project",
  &AboutItem
};


/***************************************************************************
 *
 * GADGETS
 *
 ***************************************************************************/

#define P1ID    1
#define P2ID    2
#define GROWID  3
#define AGARID  4
#define CS1ID   8
#define CS2ID   9
#define GS1ID   10
#define GS2ID   11
#define ROKID   12
#define RCANID  13
#define UOKID   14
#define UCANID  15
#define AOKID   16
#define UX0ID   32

short PnXY[] = { 0,0, 67,0, 67,11, 0,11, 0,0 };

struct Border PnBorder = {
  -1,-1,
  0,0,JAM1,
  5,PnXY,
  NULL
};

struct Border P2Border = {
  -1,-1,
  P2PEN,0,JAM1,
  5,PnXY,
  NULL
};

struct IntuiText P2Text = {
  P2PEN,0,JAM2,
  1,1,
  NULL,
  "Strain 2",
  NULL
};

struct Gadget P2Gadget = {
  NULL,
  520,13,66,10,
  GADGHIMAGE,
  TOGGLESELECT | RELVERIFY,
  BOOLGADGET,
  (APTR)&PnBorder,
  (APTR)&P2Border,
  &P2Text,
  0,NULL,
  P2ID,
  NULL
};

struct Border P1Border = {
  -1,-1,
  P1PEN,0,JAM1,
  5,PnXY,
  NULL
};

struct IntuiText P1Text = {
  P1PEN,0,JAM2,
  1,1,
  NULL,
  "Strain 1",
  NULL
};

struct Gadget P1Gadget = {
  &P2Gadget,
  55,13,66,10,
  GADGHIMAGE | SELECTED,
  TOGGLESELECT | RELVERIFY,
  BOOLGADGET,
  (APTR)&PnBorder,
  (APTR)&P1Border,
  &P1Text,
  0,NULL,
  P1ID,
  NULL
};

short GrowXY[] =
 { 0,0, 35,0, 35,11, 0,11, 0,0 };

struct Border GrowBorder = {
  -1,-1,
  FGPEN,0,JAM1,
  5,GrowXY,
  NULL
};

struct IntuiText GrowText = {
  FGPEN,0,JAM2,
  1,1,
  NULL,
  "Grow",
  NULL
};

struct Gadget GrowGadget = {
  &P1Gadget,
  302,30,34,10,
  GADGHCOMP,
  GADGIMMEDIATE | RELVERIFY,
  BOOLGADGET,
  (APTR)&GrowBorder,
  NULL,
  &GrowText,
  0,NULL,
  GROWID,
  NULL
};

struct Gadget AgarGadget = {
  &GrowGadget,
  4 * AGARLE,3 * AGARTE,
  4 * XSIZE - 9,3 * YSIZE - 7,
  GADGHNONE,
  GADGIMMEDIATE | RELVERIFY,
  BOOLGADGET,
  NULL,NULL,NULL,0,NULL,
  AGARID,
  NULL
};

SHORT RReqGXY[] = { 0,0, 51,0, 51,11, 0,11, 0,0 };

struct Border RReqGBorder = {
  -2,-2,
  GRIDPEN,0,JAM1,
  5,RReqGXY,
  NULL
};

char CS1Buffer[7] = { '0',0 };

struct StringInfo CS1SInfo = {
  CS1Buffer,
  NULL,
  0,7,0,
  0,0,0,0,0,NULL,0L,NULL
};

struct Gadget CS1Gadget = {
  NULL,
  146,30,50,10,
  GADGHCOMP,
  RELVERIFY | LONGINT,
  STRGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,NULL,0L,
  (APTR)&CS1SInfo,
  CS1ID,
  NULL
};

char CS2Buffer[7] = { '0',0 };

struct StringInfo CS2SInfo = {
  CS2Buffer,
  NULL,
  0,7,0,
  0,0,0,0,0,NULL,0L,NULL
};

struct Gadget CS2Gadget = {
  &CS1Gadget,
  218,30,50,10,
  GADGHCOMP,
  RELVERIFY | LONGINT,
  STRGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,NULL,0L,
  (APTR)&CS2SInfo,
  CS2ID,
  NULL
};

char GS1Buffer[7] = { '0',0 };

struct StringInfo GS1SInfo = {
  GS1Buffer,
  NULL,
  0,7,0,
  0,0,0,0,0,NULL,0L,NULL
};

struct Gadget GS1Gadget = {
  &CS2Gadget,
  146,44,50,10,
  GADGHCOMP,
  RELVERIFY | LONGINT,
  STRGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,NULL,0L,
  (APTR)&GS1SInfo,
  GS1ID,
  NULL
};

char GS2Buffer[7] = { '0',0 };

struct StringInfo GS2SInfo = {
  GS2Buffer,
  NULL,
  0,7,0,
  0,0,0,0,0,NULL,0L,NULL
};

struct Gadget GS2Gadget = {
  &GS1Gadget,
  218,44,50,10,
  GADGHCOMP,
  RELVERIFY | LONGINT,
  STRGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,NULL,0L,
  (APTR)&GS2SInfo,
  GS2ID,
  NULL
};

struct IntuiText ROKText = {
  GRIDPEN,0,JAM1,
  0,0,
  NULL,
  "  OK  ",
  NULL
};

struct Gadget ROKGadget = {
  &GS2Gadget,
  45,65,48,8,
  GADGHCOMP,
  RELVERIFY | ENDGADGET,
  BOOLGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,
  &ROKText,
  0L,NULL,
  ROKID,
  NULL
};

struct IntuiText RCanText = {
  GRIDPEN,0,JAM1,
  0,0,
  NULL,
  "CANCEL",
  NULL
};

struct Gadget RCanGadget = {
  &ROKGadget,
  185,65,48,8,
  GADGHCOMP,
  RELVERIFY | ENDGADGET,
  BOOLGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,
  &RCanText,
  0L,NULL,
  RCANID,
  NULL
};

SHORT UXXY[] = { 0,0, 15,8, 15,0, 0,8, 0,0 };

struct Border BlackXBorder = {
  0,0,
  0,0,JAM1,
  5,UXXY,
  NULL
};

struct Border P1XBorder = {
  0,0,
  P1PEN,0,JAM1,
  5,UXXY,
  NULL
};

struct Border P2XBorder = {
  0,0,
  P2PEN,0,JAM1,
  5,UXXY,
  NULL
};

struct Gadget UXGadget[MAXCELL][MAXVAL+1] = { {
  NULL,
  6,26,15,9,
  GADGHCOMP,
  RELVERIFY,
  BOOLGADGET | REQGADGET,
  (APTR)&BlackXBorder,
  NULL,NULL,0L,NULL,
  UX0ID,
  NULL
} };

struct Gadget UOKGadget = {
  UXGadget,
  45,65,48,8,
  GADGHCOMP,
  RELVERIFY | ENDGADGET,
  BOOLGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,
  &ROKText,
  0L,NULL,
  UOKID,
  NULL
};

struct Gadget UCanGadget = {
  &UOKGadget,
  185,65,48,8,
  GADGHCOMP,
  RELVERIFY | ENDGADGET,
  BOOLGADGET | REQGADGET,
  (APTR)&RReqGBorder,
  NULL,
  &RCanText,
  0L,NULL,
  UCANID,
  NULL
};

SHORT AOKGXY[] = { 0,0, 51,0, 51,11, 0,11, 0,0 };

struct Border AOKGBorder = {
  -2,-2,
  P2PEN,0,JAM1,
  5,AOKGXY,
  NULL
};

struct IntuiText AOKText = {
  P1PEN,0,JAM1,
  0,0,
  NULL,
  "Great!",
  NULL
};

struct Gadget AOKGadget = {
  NULL,
  100,76,48,8,
  GADGHCOMP,
  RELVERIFY | ENDGADGET,
  BOOLGADGET | REQGADGET,
  (APTR)&AOKGBorder,
  NULL,
  &AOKText,
  0L,NULL,
  AOKID,
  NULL
};

/***************************************************************************
 *
 * REQUESTERS
 *
 ***************************************************************************/

#define RREQWID 280
#define RREQHEI 78

SHORT RReqXY[] = { 1,13, RREQWID-2,13, RREQWID-2,RREQHEI-2, 1,RREQHEI-2,
  1,1, RREQWID-2,1, RREQWID-2,13 };

struct Border RReqBorder = {
  0,0,
  0,0,JAM1,
  7,RReqXY,
  NULL
};

struct IntuiText RReqText1 = {
  P1PEN,0,JAM1,
  138,18,
  NULL,
  "Strain 1",
  NULL
};

struct IntuiText RReqText2 = {
  P2PEN,0,JAM1,
  210,18,
  NULL,
  "Strain 2",
  &RReqText1
};

struct IntuiText RReqText3 = {
  GRIDPEN,0,JAM1,
  6,4,
  NULL,
  "Reset initial values",
  &RReqText2
};

struct IntuiText RReqText4 = {
  GRIDPEN,0,JAM1,
  10,30,
  NULL,
  "Cells in Freezer",
  &RReqText3
};

struct IntuiText RReqText5 = {
  GRIDPEN,0,JAM1,
  6,44,
  NULL,
  "Growth in Freezer",
  &RReqText4
};

struct Requester ResetReq = {
  NULL,
  180,121,RREQWID,RREQHEI,
  0,0,
  &RCanGadget,
  &RReqBorder,
  &RReqText5,
  0,FGPEN,
  NULL,{0},NULL,NULL,{0}
};

#define UREQWID 284
#define UREQHEI 78

SHORT UReqXY2[] = { 0,0, 273,0, 273,10, 0,10, 0,0 };

struct Border UReqBorder4 = {
  5,49,
  P2PEN,0,JAM1,
  5,UReqXY2,
  NULL
};

struct Border UReqBorder3 = {
  5,37,
  0,0,JAM1,
  5,UReqXY2,
  &UReqBorder4
};

struct Border UReqBorder2 = {
  5,25,
  P1PEN,0,JAM1,
  5,UReqXY2,
  &UReqBorder3
};

SHORT UReqXY1[] = { 1,13, UREQWID-2,13, UREQWID-2,UREQHEI-2, 1,UREQHEI-2,
  1,1, UREQWID-2,1, UREQWID-2,13 };

struct Border UReqBorder1 = {
  0,0,
  0,0,JAM1,
  7,UReqXY1,
  &UReqBorder2
};

struct IntuiText UReqText4 = {
  P2PEN,0,JAM1,
  150,16,
  NULL,
  "+1+2+3+4+5+6+7+8",
  NULL
};

struct IntuiText UReqText3 = {
  GRIDPEN,0,JAM1,
  134,16,
  NULL,
  "±0",
  &UReqText4
};

struct IntuiText UReqText2 = {
  P1PEN,0,JAM1,
  6,16,
  NULL,
  "-8-7-6-5-4-3-2-1",
  &UReqText3
};

struct IntuiText UReqText1 = {
  GRIDPEN,0,JAM1,
  6,4,
  NULL,
  "Define rules",
  &UReqText2
};

struct Requester RulesReq = {
  NULL,
  180,121,UREQWID,UREQHEI,
  0,0,
  &UCanGadget,
  &UReqBorder1,
  &UReqText1,
  0,FGPEN,
  NULL,{0},NULL,NULL,{0}
};

#define AREQWID   158L
#define AREQHEI   90L

SHORT AReqXY[] = { 0,0, AREQWID-5,0, AREQWID-5,AREQHEI-3, 0,AREQHEI-3, 0,0 };

struct Border AReqBorder = {
  2,1,
  P2PEN,0,JAM1,
  5,AReqXY,
  NULL
};

struct IntuiText AReqText1 = {
  P1PEN,0,JAM1,
  6,4,
  NULL,
  "GAME OF LIFE - DUO",
  NULL
};

struct IntuiText AReqText2 = {
  P1PEN,0,JAM1,
  6,20,
  NULL,
  "(C)reated 1992 by",
  &AReqText1
};

struct IntuiText AReqText3 = {
  P1PEN,0,JAM1,
  6,34,
  NULL,
  "Andreas Neubacher",
  &AReqText2
};

struct IntuiText AReqText4 = {
  P1PEN,0,JAM1,
  6,48,
  NULL,
  "Hausleitnerweg 26",
  &AReqText3
};

struct IntuiText AReqText5 = {
  P1PEN,0,JAM1,
  6,58,
  NULL,
  "4020 Linz",
  &AReqText4
};

struct IntuiText AReqText6 = {
  P1PEN,0,JAM1,
  6,68,
  NULL,
  "Austria",
  &AReqText5
};

struct Requester AboutReq = {
  NULL,
  244,81,AREQWID,AREQHEI,
  0,0,
  &AOKGadget,
  &AReqBorder,
  &AReqText6,
  0,FGPEN,
  NULL,{0},NULL,NULL,{0}
};

/***************************************************************************
 *
 * POINTER
 *
 ***************************************************************************/

#define PTRWIDTH    9L
#define PTRHEIGHT   8L
#define PTRXOFFSET  -5L
#define PTRYOFFSET  -4L

USHORT PointerData[] = {
  0x0000,0x0000,
  0x8080,0x0000,
  0x4100,0x0000,
  0x2200,0x0000,
  0x0000,0x0000,
  0x0000,0x0000,
  0x2200,0x0000,
  0x4100,0x0000,
  0x8080,0x0000,
  0x0000,0x0000
};

/***************************************************************************
 *
 * SCREEN UND WINDOW
 *
 ***************************************************************************/

#define SCRWID  642L
#define SCRHEI  254L

struct NewScreen ns = {
  0,0,
  SCRWID,SCRHEI,
  3,
  0,FGPEN,
  HIRES,CUSTOMSCREEN,
  0,0,0,0
};

struct NewWindow nw = {
  0,0,
  SCRWID,SCRHEI,
  0,FGPEN,
  GADGETDOWN | GADGETUP | MENUPICK | MENUVERIFY | MOUSEBUTTONS,
  SMART_REFRESH | NOCAREREFRESH | BACKDROP | BORDERLESS | ACTIVATE,
  &AgarGadget,
  0,0,0,0,0,0,SCRWID,SCRHEI,
  CUSTOMSCREEN
};

/***************************************************************************
 *
 * Main routine
 *
 ***************************************************************************/

char AgarMem[2][YSIZE][XSIZE];  /* Agar, Undo Agar */
char (*Agar)[XSIZE];            /* Current Agar    */
short AgarNum;                  /* Index of current Agar (0,1) */
char AuxAgar[YSIZE][XSIZE];     /* Auxiliary Agar */

char NewCT[MAXCELL][MAXVAL+1];        /* Backup for 'CellTrans'          */
char CellTrans[MAXCELL][MAXVAL+1] = { /* Table for transformation rules  */
  1,1,1,1,1,3,3,1,1,1,1,1,1,1,1,1,1,
  3,3,3,3,3,0,3,3,3,3,3,2,3,3,3,3,3,
  1,1,1,1,1,1,1,1,1,1,3,3,1,1,1,1,1
};

struct StrainData {
  short LCells;       /* No. of cells of this Strain on the Agar */
  short SCells;       /* No. of cells in freezer                 */
  short GRate;        /* Growth of cells in freezer              */
  short ox,oy;        /* Left upper corner of output area        */
  short CellCode;     /* Code of this Strain                     */
  short CellPen;      /* Color of this Strain                    */
} pd[2];

struct StrainData *actp;  /* Pointer to active Strain */

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;

struct Screen *sc;
struct Window *wd;
struct RastPort *rp;
struct BitMap *bm;
USHORT *PointerCMem;

main()

{
  void ProcessMenu(),ProcessGadgetUp(),ProcessGadgetDown(),ProcessRMB();
  void ClearAgar(),SumOut(),CleanUp();
  APTR XBorder();

  struct IntuiMessage *Msg;
  USHORT MClass,MCode;
  APTR   MAddress;
  struct ViewPort *vp;
  short  i,j,c;


/***
 *
 * OPEN LIBRARIES, SCREEN AND WINDOW; INITIALISE GRAPHICS
 *
 ***/

  IntuitionBase = GfxBase = sc = wd = PointerCMem = NULL;

  if (! (GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0L)))
    CleanUp(NULL);
  if (! (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0L)))
    CleanUp(NULL);

  if (!(sc = OpenScreen(&ns))) CleanUp("GoLD: Couldn't open screen!");
  nw.Screen = sc;
  if (!(wd = OpenWindow(&nw))) CleanUp("GoLD: Couldn't open window!");
  ShowTitle(sc,FALSE);

  vp = &(sc->ViewPort);
  rp = wd->RPort;
  bm = rp->BitMap;

  SetRGB4(vp,0L,0L,0L,0L);
  SetRGB4(vp,1L,15L,0L,0L);
  SetRGB4(vp,2L,0L,7L,15L);
  SetRGB4(vp,(long)MAXCELL,3L,3L,3L);
  SetRGB4(vp,(long)FGPEN,12L,12L,12L);
  SetRGB4(vp,5L,0L,0L,15L);
  SetRGB4(vp,6L,8L,0L,0L);
  SetRGB4(vp,7L,15L,15L,15L);
  SetRGB4(vp,16L,0L,15L,0L);

  if (!(PointerCMem = (USHORT *)AllocMem((long)sizeof(PointerData),MEMF_CHIP)))
    CleanUp("GoLD: No ChipMem for pointer!\n");
  for (i=0;i<2*PTRHEIGHT+4;i++)
    PointerCMem[i] = PointerData[i];
  SetPointer(wd,PointerCMem,PTRHEIGHT,PTRWIDTH,PTRXOFFSET,PTRYOFFSET);

  SetMenuStrip(wd,&ProjectMenu);

  SetAPen(rp,(long)GRIDPEN);
  RectFill(rp,0L,74L,SCRWID-1L,SCRHEI-1L);
  SetAPen(rp,(long)FGPEN);

/***
 *
 * INITIALISE GADGETS IN 'UXGadget'
 *
 ***/

  c = UX0ID;
  for (i=0;i<MAXCELL;i++)
    for (j=0;j<=MAXVAL;j++) {
      UXGadget[i][j] = UXGadget[0][0];
      UXGadget[i][j].GadgetID = c;
      c++;
      UXGadget[i][j].LeftEdge += (j * 16);
      UXGadget[i][j].TopEdge += (i * 12);
      UXGadget[i][j].GadgetRender = XBorder(CellTrans,i,j);
      if (j == MAXVAL)
        if (i == MAXCELL-1) UXGadget[i][j].NextGadget = NULL;
        else                UXGadget[i][j].NextGadget = &(UXGadget[i+1][0]);
      else
        UXGadget[i][j].NextGadget = &(UXGadget[i][j+1]);
    }

/***
 *
 * INITIALISE PLAYER/STRAIN DATA
 *
 ***/

  actp = &(pd[1]);
  actp->LCells = 0;
  actp->SCells = 100;
  actp->GRate = 0;
  actp->ox = 458;
  actp->oy = 40;
  actp->CellCode = P2CODE;
  actp->CellPen = P2PEN;

  Move(rp,(long)actp->ox,(long)actp->oy);
  Text(rp,"   Cells on Agar:",17L);
  Move(rp,(long)actp->ox,(long)(actp->oy+15));
  Text(rp,"Cells in Freezer:",17L);

  actp = &(pd[0]);
  actp->LCells = 0;
  actp->SCells = 100;
  actp->GRate = 0;
  actp->ox = 4;
  actp->oy = 40;
  actp->CellCode = P1CODE;
  actp->CellPen = P1PEN;

  Move(rp,(long)actp->ox,(long)actp->oy);
  Text(rp,"   Cells on Agar:",17L);
  Move(rp,(long)actp->ox,(long)(actp->oy+15));
  Text(rp,"Cells in Freezer:",17L);

  ClearAgar(AgarMem[0]);
  ClearAgar(AgarMem[1]);
  ClearAgar(AuxAgar);

  Agar = AgarMem[0];
  AgarNum = 0;

/***
 *
 * MAIN LOOP
 *
 ***/

  SumOut(&(pd[0]));
  SumOut(&(pd[1]));
  DisplayAgar(bm,Agar,XSIZE,YSIZE,AGARLE,AGARTE);

  Request(&AboutReq,wd);

  for (;;) {
    Wait(1L << wd->UserPort->mp_SigBit);
    while (Msg = (struct IntuiMessage *)GetMsg(wd->UserPort)) {
      MClass = Msg->Class;
      MCode = Msg->Code;
      MAddress = Msg->IAddress;
      if (MClass != MENUVERIFY) ReplyMsg(Msg);
      switch (MClass) {
        case MENUVERIFY:
          if (MCode == MENUHOT) ProcessRMB(Msg);
          else                  ReplyMsg(Msg);
          break;
        case MENUPICK:
          ProcessMenu(MCode);
          break;
        case GADGETUP:
          ProcessGadgetUp(MAddress);
          break;
        case GADGETDOWN:
          ProcessGadgetDown(MAddress);
          break;
        case MOUSEBUTTONS:
          break;
        default:
          CleanUp("GoLD: Unknown message!");
      }
    }
  }
}


/***************************************************************************
 *
 * SUBROUTINES
 *
 ***************************************************************************/

APTR XBorder(a,i,j)   /* Returns a pointer to the 'XBorder'-structure */
                      /* for 'a[i][j]'.                               */
  char a[MAXCELL][MAXVAL+1];
  short i,j;

{
  switch (a[i][j]) {
    case P1CODE:
      return(&P1XBorder);
    case NULLCODE:
      return(&BlackXBorder);
    case P2CODE:
      return(&P2XBorder);
    case NOCODE:
      if (i == P1CODE)        return(&P1XBorder);
      else if (i == NULLCODE) return(&BlackXBorder);
      else if (i == P2CODE)   return(&P2XBorder);
    default:
      return(NULL);
  }
}


APTR NextXBorder(a,i,j)   /* Toggles 'a[i][j]' and returns a pointer to the */
                          /* the 'XBorder'-structure for 'a[i][j]'.         */
  char a[MAXCELL][MAXVAL+1];
  short i,j;

{
  switch (a[i][j]) {
    case P1CODE:
      if (i == NULLCODE) a[i][j] = NOCODE;
      else               a[i][j] = NULLCODE;
      return(&BlackXBorder);
    case NULLCODE:
      if (i == P2CODE) a[i][j] = NOCODE;
      else             a[i][j] = P2CODE;
      return(&P2XBorder);
    case P2CODE:
      if (i == P1CODE) a[i][j] = NOCODE;
      else             a[i][j] = P1CODE;
      return(&P1XBorder);
    case NOCODE:
      switch (i) {
        case P1CODE:
          a[i][j] = NULLCODE;
          return(&BlackXBorder);
        case NULLCODE:
          a[i][j] = P2CODE;
          return(&P2XBorder);
        case P2CODE:
          a[i][j] = P1CODE;
          return(&P1XBorder);
      }
    default:
      return(NULL);
  }
}


char TxtBuf[6];

void SumOut(pptr)   /* Prints the values for "Cells on Agar" and "Cells */
                    /* in Freezer".                                     */
  register struct StrainData *pptr;

{
  SetDrMd(rp,JAM2);
  SetAPen(rp,(long)pptr->CellPen);
  Move(rp,(long)(pptr->ox+138),(long)pptr->oy);
  sprintf(TxtBuf,"%5d",pptr->LCells);
  Text(rp,TxtBuf,5L);
  Move(rp,(long)(pptr->ox+138),(long)(pptr->oy+15));
  sprintf(TxtBuf,"%5d",pptr->SCells);
  Text(rp,TxtBuf,5L);
  SetAPen(rp,(long)FGPEN);
}


void ClearAgar(a)     /* Sets all cells in the Agar to type 'dead'. */

  register char *a;

{
  register USHORT i;

  for (i=XSIZE*YSIZE;i!=0;i--,a++)
    *a = NULLCODE;
}


void SaveAgar()       /* Copies the current Agar to the Undo Agar. */

{
  register char *a,*aa;
  register USHORT i;

  a = Agar;
  AgarNum = 1 - AgarNum;
  Agar = AgarMem[AgarNum];
  aa = Agar;
  for (i=XSIZE*YSIZE;i!=0;i--,a++,aa++)
    *aa = *a;
}


void ProcessMenu(mnum)    /* Does exactly that. */

  USHORT mnum;

{
  register short i,j;

  if (mnum != MENUNULL) switch (MENUNUM(mnum)) {
    case 0:                                             /* 'Project'-Menu */
      switch (ITEMNUM(mnum)) {
        case 0:                                             /* About */
          if (!Request(&AboutReq,wd)) DisplayBeep(sc);
          break;
        case 1:                                             /* Rules */
          for (i=0;i<MAXCELL;i++)
            for (j=0;j<=MAXVAL;j++) {
              NewCT[i][j] = CellTrans[i][j];
              UXGadget[i][j].GadgetRender = XBorder(NewCT,i,j);
             }
          SetAPen(rp,(long)FGPEN);
          SetDrMd(rp,JAM1);
          if (!Request(&RulesReq,wd)) DisplayBeep(sc);
          break;
        case 2:                                             /* Reset */
          SetAPen(rp,(long)FGPEN);
          SetDrMd(rp,JAM1);
          if (!Request(&ResetReq,wd)) DisplayBeep(sc);
          break;
        case 3:                                             /* Quit */
          CleanUp(NULL);
          break;
      }
      break;
    case 1:                                             /* 'Edit'-Menu */
      switch (ITEMNUM(mnum)) {
        case 0:                                             /* Undo */
          AgarNum = 1 - AgarNum;
          Agar = AgarMem[AgarNum];
          CountCells(Agar,&(pd[0].LCells),&(pd[1].LCells),XSIZE*YSIZE-1);
          SumOut(&(pd[0]));
          SumOut(&(pd[1]));
          DisplayAgar(bm,Agar,XSIZE,YSIZE,AGARLE,AGARTE);
          break;
        case 1:                                             /* Clear Agar */
          pd[0].SCells += pd[0].LCells;
          pd[1].SCells += pd[1].LCells;
          pd[0].LCells = pd[1].LCells = 0;
          SumOut(&(pd[0]));
          SumOut(&(pd[1]));
          AgarNum = 1 - AgarNum;
          Agar = AgarMem[AgarNum];
          ClearAgar(Agar);
          DisplayAgar(bm,Agar,XSIZE,YSIZE,AGARLE,AGARTE);
          break;
      }
      break;
  }
}


void ProcessGadgetUp(gptr)    /* Gadget clicked */

  struct Gadget *gptr;

{
  short i,j,id;

  switch (gptr->GadgetID) {
    case P1ID:                            /* 'Strain 1'-Gadget */
      if (gptr->Flags & SELECTED) actp = &(pd[0]);
      else                        actp = &(pd[1]);
      P2Gadget.Flags ^= SELECTED;
      RefreshGadgets(&P2Gadget,wd,NULL);
      return;
    case P2ID:                            /* 'Strain 2'-Gadget */
      if (gptr->Flags & SELECTED) actp = &(pd[1]);
      else                        actp = &(pd[0]);
      P1Gadget.Flags ^= SELECTED;
      RefreshGadgets(&P1Gadget,wd,NULL);
      return;
    case ROKID:                           /* 'OK' in 'Reset'-Req */
      pd[0].LCells = pd[1].LCells = 0;
      pd[0].SCells = CS1SInfo.LongInt;
      pd[1].SCells = CS2SInfo.LongInt;
      pd[0].GRate = GS1SInfo.LongInt;
      pd[1].GRate = GS2SInfo.LongInt;
      SumOut(&(pd[0]));
      SumOut(&(pd[1]));
      ClearAgar(AgarMem[0]);
      ClearAgar(AgarMem[1]);
      DisplayAgar(bm,Agar,XSIZE,YSIZE,AGARLE,AGARTE);
      return;
    case RCANID:                          /* 'CANCEL' in 'Reset'-Requester */
      return;
    case UOKID:                           /* 'OK' in 'Rules'-Requester */
      for (i=0;i<MAXCELL;i++)
        for (j=0;j<=MAXVAL;j++)
          CellTrans[i][j] = NewCT[i][j];
      return;
    case UCANID:                          /* 'CANCEL' in 'Rules'-Requester */
      return;
    case AOKID:                           /* 'OK' in 'About'-Requester */
      return;
    default:                              /* an 'X' in 'Rules'-Requester */
      id = gptr->GadgetID - UX0ID;
      if (id >= 0) {
        i = id / (MAXVAL+1);
        j = id % (MAXVAL+1);
        UXGadget[i][j].GadgetRender = NextXBorder(NewCT,i,j);
       }
      RefreshGadgets(gptr,wd,&RulesReq);
      return;
  }
}


void ProcessGadgetDown(gptr)    /* Gadget pressed (and held?) */

  struct Gadget *gptr;

{
  register struct IntuiMessage *Msg;
  ULONG IDCMPFlags;
  register short i,j;

  SaveAgar();

  switch (gptr->GadgetID) {
    case GROWID:                                      /* 'Grow'-Gadget */
      for (;;) {
        ScanAgar(Agar,AuxAgar,CellTrans,XSIZE,YSIZE,MAXVAL+1);
        ChangeAgar(bm,Agar,AuxAgar,XSIZE,YSIZE,AGARLE,AGARTE);

        pd[0].SCells += pd[0].GRate;
        pd[1].SCells += pd[1].GRate;
        CountCells(Agar,&(pd[0].LCells),&(pd[1].LCells),XSIZE*YSIZE-1);
        SumOut(&(pd[0]));
        SumOut(&(pd[1]));

        if (Msg = (struct IntuiMessage *)GetMsg(wd->UserPort)) {
          i = Msg->Class;
          j = Msg->Code;
          ReplyMsg(Msg);
          if ((i == GADGETUP) || (j == SELECTUP)) break;
        }
      }
      break;
    case AGARID:                                      /* Agar */
      for (;;) {
        i = wd->MouseY/3 - AGARTE + 1;
        j = wd->MouseX/4 - AGARLE + 1;
        if ((i > 0) && (i < YSIZE-1) && (j > 0) && (j < XSIZE-1))
          if ((Agar[i][j] == 1) && (actp->SCells != 0)) {
            Agar[i][j] = actp->CellCode;
            actp->LCells ++;
            actp->SCells --;
            SetCell(bm,j+AGARLE-1,i+AGARTE-1,actp->CellPen);
            SumOut(actp);
          }

        if (Msg = (struct IntuiMessage *)GetMsg(wd->UserPort)) {
          i = Msg->Class;
          j = Msg->Code;
          ReplyMsg(Msg);
          if ((i == GADGETUP) || (j == SELECTUP)) break;
        }
      }
      break;
  }

  return;
}


void ProcessRMB(msg)        /* Right mouse button */

  struct IntuiMessage *msg;

{
  register struct IntuiMessage *Msg;
  ULONG IDCMPFlags;
  register short i,j;

  i = wd->MouseY/3 - AGARTE + 1;
  j = wd->MouseX/4 - AGARLE + 1;
  if ((i < 1) || (i > YSIZE-2) || (j < 1) || (j > XSIZE-2)) {
    ReplyMsg(msg);
    return;
  }

  SaveAgar();

  msg->Code = MENUCANCEL;
  ReplyMsg(msg);

  for (;;) {
    i = wd->MouseY/3 - AGARTE + 1;
    j = wd->MouseX/4 - AGARLE + 1;
    if ((i > 0) && (i < YSIZE-1) && (j > 0) && (j < XSIZE-1))
      if (Agar[i][j] == actp->CellCode) {
        Agar[i][j] = 1;
        actp->LCells --;
        actp->SCells ++;
        SetCell(bm,j+AGARLE-1,i+AGARTE-1,NULLPEN);
        SumOut(actp);
      }

    if (Msg = (struct IntuiMessage *)GetMsg(wd->UserPort)) {
      j = Msg->Code;
      ReplyMsg(Msg);
      if (j == MENUUP) break;
    }
  }
  return;
}


#define ERRLEN  70

char ErrorString[ERRLEN];

void CleanUp(txt)

  char *txt;

{
  short i;

  if (PointerCMem) {
    ClearPointer(wd);
    FreeMem(PointerCMem,(long)sizeof(PointerData));
  }
  if (wd) {
    ClearMenuStrip(wd);
    CloseWindow(wd);
  }
  if (sc) CloseScreen(sc);
  if (IntuitionBase) CloseLibrary(IntuitionBase);
  if (GfxBase) CloseLibrary(GfxBase);

  if (txt != NULL) {
    for(i=3;(i < ERRLEN-2) && (txt[i-3] != 0);i++)
      ErrorString[i] = txt[i-3];
    *((short *)ErrorString) = 308 - 4*i;
    ErrorString[2] = 15;
    ErrorString[i] = ErrorString[i+1] = 0;
    DisplayAlert(RECOVERY_ALERT,ErrorString,30L);
  }

  exit(0L);
}
