/*      Connect4.C - A simulation
*
*       Mitchell/Ware           Version 2.05            12/6/87
*
*       Graphics included:
*               rc4c_Image      -       Red Chip
*               bc4c_Image      -       Blue Chip
*               bc4b_Image      -       Background part of 'Board'
*               bc4f_Image      -       ForeGround part of 'Board'
*               Play_Image      -       The Play Gadget
*               Level_Image     -       The Level Gadget
*               Info_Image      -       Information about game
*               Back_Image      -       Icon to put this screen to back
*               Quit_Image      -       End Game
*
*
*       NOTE: UserData on gadgets will point to routines to be called
*               in case of gadget activation. They will return something.
*/

#include "exec/types.h"
#include "exec/memory.h"
#include "exec/tasks.h"
#include "intuition/intuition.h"
#include "graphics/gfx.h"
#include "graphics/gfxmacros.h"
#include "graphics/display.h"
#include "graphics/rastport.h"
#include "graphics/gels.h"
#include "clib/macros.h"
#include <stdio.h>
#include <setjmp.h>
#include <ascii.h>
#include <math.h>
#include <Iff.h>
#include <Tools.h>
#include <connect4.h>   

#define MAGIC_LOCKOUT   666

#define STACK   6000   /* size to allocate stacks of tasks */

#define CHIP    (MEMF_CHIP | MEMF_CLEAR)
#define PUB     (MEMF_PUBLIC | MEMF_CLEAR)

#define NBOBS   3       /* number of BOBS in system     */
#define RED     0       /* RED chip  (HUMAN)            */
#define BLUE    1       /* BLUE chip (COMPUTER)         */
#define BOARD   2       /* BOARD bob                    */

#define NGADS   5       /* Number of control gadgets    */
#define PLAY    0       /* PLAY gadget */
#define LEVEL   1
#define BACK    2
#define INFO    3
#define QUIT    4

#define RED_CHIP_IMAGE        rc4c_Image
#define BLUE_CHIP_IMAGE       bc4c_Image
#define BACK_BOARD_IMAGE      bc4b_Image
#define FORE_BOARD_IMAGE      bc4f_Image

#define GADR            (-Play_Image->Width)
#define GADT(_x)        (20 + (_x) * Play_Image->Height)

#define BTOP    30      /* distance of board from top   */
#define BLEFT   -10     /* distance of board from left */
#define BS_TOP  24      /* board starts relative to BTOP */
#define BS_LEFT 28      /* board starts relative to BLEFT */
#define BWIDTH  27      /* board width of one chip      */
#define BHEIGHT 23      /* board height of one chip     */

#define IDCMP_SET       (REQCLEAR | MOUSEBUTTONS | GADGETUP | INTUITICKS | MOUSEMOVE)

extern struct  Screen  *OpenScreen();
extern struct  Window  *OpenWindow();

struct Image *rc4c_Image;
struct Image *bc4c_Image;
struct Image *bc4b_Image;
struct Image *bc4f_Image;

struct Image *Play_Image;
struct Image *Level_Image;
struct Image *Back_Image;        
struct Image *Info_Image;
struct Image *Quit_Image;

RTGLIST info_list[] =
{
        { 26, 76, 25, 17, 0, GADGHCOMP, ENDGADGET  },
        { 0  }
};

RTGLIST level_list[] =
{
        /* list of the 16 levels comes first, followed by OK-WHO-CANCEL
                and the the (useless) TREE and LINEAR. */
        { 26, 97, 22, 14, 1, GADGHCOMP, TOGGLESELECT },         /* 1 */
        { 50, 97, 22, 14, 2, GADGHCOMP, TOGGLESELECT },         /* 2 */
        { 74, 97, 22, 14, 3, GADGHCOMP, TOGGLESELECT },         /* 3 */
        { 98, 97, 22, 14, 4, GADGHCOMP, TOGGLESELECT },         /* 4 */
        { 122, 97, 22, 14, 5, GADGHCOMP, TOGGLESELECT },        /* 5 */
        { 146, 97, 22, 14, 6, GADGHCOMP, TOGGLESELECT },        /* 6 */
        { 170, 97, 22, 14, 7, GADGHCOMP, TOGGLESELECT },        /* 7 */
        { 194, 97, 22, 14, 8, GADGHCOMP, TOGGLESELECT },        /* 8 */

        { 26, 113, 22, 14, 9, GADGHCOMP, TOGGLESELECT },        /* 9 */
        { 50, 113, 22, 14, 10, GADGHCOMP, TOGGLESELECT },        /* 10 */
        { 74, 113, 22, 14, 11, GADGHCOMP, TOGGLESELECT },        /* 11 */
        { 98, 113, 22, 14, 12, GADGHCOMP, TOGGLESELECT },        /* 12 */
        { 122, 113, 22, 14, 13, GADGHCOMP, TOGGLESELECT },       /* 13 */
        { 146, 113, 22, 14, 14, GADGHCOMP, TOGGLESELECT },       /* 14 */
        { 170, 113, 22, 14, 15, GADGHCOMP, TOGGLESELECT },       /* 15 */
        { 194, 113, 22, 14, 16, GADGHCOMP, TOGGLESELECT },       /* 16 */
        
        { 32, 138, 32, 6, 0, GADGHCOMP, ENDGADGET | TOGGLESELECT }, /* OK */
        { 103, 138, 32, 6, 0, GADGHCOMP, GADGIMMEDIATE }, /* WHO */
        { 176, 138, 32, 6, 0, GADGHCOMP, ENDGADGET | TOGGLESELECT }, /* CANCEL */
        
        { 22, 44, 42, 15, 0, GADGHCOMP | SELECTED, TOGGLESELECT, NULL, TRUE }, /* TREE */
        { 168, 44, 51, 15, 0, GADGHCOMP | GADGDISABLED, TOGGLESELECT }, /* LINEAR */
        
        { 0 }        
};

RTGLIST who_list[] =
{
        { 19, 47, 56, 14, 0, GADGHCOMP, TOGGLESELECT }, /* left HUMAN */
        { 19, 72, 56, 14, 0, GADGHCOMP, TOGGLESELECT }, /* left COMPUTER */
        { 131, 47, 56, 14, 0, GADGHCOMP, TOGGLESELECT }, /* right HUMAN */
        { 131, 72, 56, 14, 0, GADGHCOMP, TOGGLESELECT }, /* right COMPUTER */
        
        { 65, 97, 32, 6, 0, GADGHCOMP, TOGGLESELECT | ENDGADGET }, /* OK */
        { 113, 97, 32, 6, 0, GADGHCOMP, TOGGLESELECT | ENDGADGET }, /* CANCEL */
        { 0 }        
};

struct NewScreen       NewHS =
{
        0, 0, 320, 200, 5,      /* left, right, width, height, depth */
        1, 30,                  /* Detail pen & Block pen       */
        NULL,              /* View modes   */
        CUSTOMSCREEN,           /* Type         */
        NULL,                   /* Font         */
        "Connect-4 (c) 1987-88 Mitchell/Ware",  /* Title Bar */
        NULL,                   /* Gadgets - not used at this time      */
        NULL                    /* Custom BitMap        */      
};

struct NewWindow       NewWin  =
{
        0, 0,                   /* Leftedge, Topedge    */
        320, 200,               /* Width, Height        */
        -1, -1,                 /* DetailPen, BlockPen - default to customscr */
        IDCMP_SET,              /* IDCMPflags   */
        BACKDROP | BORDERLESS | ACTIVATE | RMBTRAP | SMART_REFRESH 
                 | REPORTMOUSE, /* flags */
        NULL,                   /* First Gadget */
        NULL,                   /* Check Mark   */
        NULL,                   /* Title        */
        NULL,                   /* Screen - to be inited during runtime */
        NULL,                   /* BitMap       */
        0, 0, 0, 0,             /* MinWidth & Height, MaxWidth & Height */
        CUSTOMSCREEN            /* Type */
};

static struct  Screen  *screen = NULL;
static struct  Window  *window = NULL;
struct TmpRas  tmpras;
struct AreaInfo areainfo;
WORD   areabuffer[250];
struct Remember *key = 0;

int    game_level = 1;
int    debug_state = 0;
LONG   IntuitionBase = 0;
LONG   GfxBase = 0;

static jmp_buf save;

static struct GelsInfo GInfo;  
static struct Bob *bob[NBOBS];
static struct VSprite *vsp[NBOBS];
static struct DBufPacket db[NBOBS];
static struct Image **im[NBOBS] =
        { &RED_CHIP_IMAGE, &BLUE_CHIP_IMAGE, &FORE_BOARD_IMAGE };
        
static struct Gadget *gad[NGADS];

static struct Image **ig[NGADS] = { 
        &Play_Image, &Level_Image, &Back_Image, &Info_Image, &Quit_Image };

int     PlayCommand(), LevelCommand(), BackCommand(), 
        InfoCommand(), QuitCommand(), 
        FallTask(), PlayTask(), LeviTask();
        
static int (*command[NGADS])() = {
        PlayCommand, LevelCommand, BackCommand, InfoCommand, QuitCommand   };

/*      MainTask Globals        ******************************
*/
WORD    MTsig;
struct  Task *ptrMainTask = NULL;
C4BOARD C4Template;     /* blank template with borders set */
char    dstring[100];        /* debug string */
BOOL    dprint = FALSE;         /* true if printing */

/*      PlayTask Globals        ******************************
*/
struct Task *ptrPlayTask = NULL;        /* NULL if not active */
WORD    PTsig;                          /* signal number */
C4BOARD MasterBoard;    /* board of current game - diff. configuration */
int winner[4][2];     /* winning location of chips 0=c,1=r  */

/*      FallTask Globals        ******************************
*/
struct  Task *ptrFallTask = NULL;        /* NULL if not active */
WORD    FTsig;                  /* signal number */
int     FallSlot = -1;      /* becomes 0-5 when decided, or -2 if error */
BOOL    DoneFalling = FALSE; /* set to TRUE when done */

/*      LeviTask Globals    ******************************
*/
struct  Task    *ptrLeviTask = NULL;
int     LTsig;
int     LTwhere = -1;   /* where to put the chip. Levitate if -1 */

#
/*     Generic Main Graphics Initer
*/

main(ac, av)
int ac;
char **av;
{
        char *s;
        int i, j;
        
        if (ac == 2)
                debug_state = 1;
        
        if (!(IntuitionBase = OpenLibrary("intuition.library", 0)))
        {
                printf("Cant open intuition library");
                MainShutdown(10);
        }

        if (!(GfxBase = OpenLibrary("graphics.library", 0)))
        {
                printf("Cant open graphics library");
                MainShutdown(20);
        }

        if (!(NewWin.Screen = screen = OpenScreen(&NewHS)))
        {
                printf("Cant open Screen");
                MainShutdown(30);
        }

        if (!(window = OpenWindow(&NewWin)))
        {
                printf("Cant open backdrop window");
                MainShutdown(40);
        }

        InitArea(&areainfo, areabuffer, 100);
        window->RPort->AreaInfo = &areainfo;
        window->RPort->TmpRas = (struct TmpRas *) 
                InitTmpRas(&tmpras, AllocRaster(320, 400), RASSIZE(320, 400));
        if (!tmpras.RasPtr)
        {
                printf("Out of Memory (45)");
                MainShutdown(45);
        }

/*        if (!Icircle(512))
        {
                printf("Out of memory (50)");
                MainShutdown(50);
        }       */

        setmem(&C4Template, sizeof(C4BOARD), NULL);
        setmem(C4Template.border1, sizeof(C4Template.border1), -2);
        setmem(C4Template.border2, sizeof(C4Template.border2), -2);
        for (i = 0; i < 7; ++i)
                for (j = 6; j < 9; ++j)
                        C4Template.b[i][j] = -2;
        
        if (!(s = setjmp(save)))
        {
                Startup();
                Connect4();
                s = "return from Connect4()";
        }
        
        printf("Exit reason: <%s>\n", s);
        MainShutdown(0);
}

Startup ()          /* window graphics routine */
{
        struct  RastPort *rp = window->RPort;
        struct  ViewPort *vp = &screen->ViewPort;
        struct  Gadget *g;
        int i;
        USHORT *cm, *MakeColorMap();
        int ncolors;
        IFF *iff;                /* display bit2 */
        struct Image *title;
        
        ShowTitle(screen, FALSE);
        ScreenToBack(screen);
        InitDoubleBuffering();
        
        /* Read in title page and display it */
        if ((iff = IffRead("Title.Page")) && (title = MakeImage(&key, iff, NULL))) 
        {
                if (cm = MakeColorMap(key, iff, &ncolors))  
                        LoadRGB4(vp, cm, ncolors);
                else 
                        longjmp(save, "Title.Page ColorMap problem");
                SwapBitMaps();
                DrawImage(rp, title, 0, 0);
                SwapBitMaps();
                DrawImage(rp, title, 0, 0);
                ScreenToFront(screen);
        }
        
        
        /* Read in images, and create the IMAGE */
        ForgetAllOfIt();
        if (!(iff = IffRead("Blue.c4.Chip"))) longjmp(save, "bc4c not found");
        if (!(bc4c_Image = MakeImage(&key, iff, NULL))) longjmp(save, "bc4c - not ILBM");

        ForgetAllOfIt();
        if (!(iff = IffRead("Board.c4.Back"))) longjmp(save, "bc4b.pic not found");
        if (!(bc4b_Image = MakeImage(&key, iff, NULL))) longjmp(save, "bc4b - not ILBM");

        ForgetAllOfIt();
        if (!(iff = IffRead("Board.c4.Front"))) longjmp(save, "bc4f.pic not found");
        if (!(bc4f_Image = MakeImage(&key, iff, NULL))) longjmp(save, "bc4f - not ILBM");

        ForgetAllOfIt();
        if (!(iff = IffRead("Level.Gadget"))) longjmp(save, "Level.Gadget not found");
        if (!(Level_Image = MakeImage(&key, iff, NULL))) longjmp(save, "Level - not ILBM");

        ForgetAllOfIt();
        if (!(iff = IffRead("Back.Gadget"))) longjmp(save, "Back.Gadget not found");
        if (!(Back_Image = MakeImage(&key, iff, NULL))) longjmp(save, "Back - not ILBM");

        ForgetAllOfIt();
        if (!(iff = IffRead("Info.Gadget"))) longjmp(save, "Info.Gadget not found");
        if (!(Info_Image = MakeImage(&key, iff, NULL))) longjmp(save, "Info - not ILBM");

        ForgetAllOfIt();
        if (!(iff = IffRead("Quit.Gadget"))) longjmp(save, "Quit.Gadget not found");
        if (!(Quit_Image = MakeImage(&key, iff, NULL))) longjmp(save, "Quit - not ILBM");
        
        ForgetAllOfIt();
        if (!(iff = IffRead("Red.c4.Chip"))) longjmp(save, "rc4c.pic not found");
        if (!(rc4c_Image = MakeImage(&key, iff, NULL))) longjmp(save, "rc4c - not ILBM");
        
        ForgetAllOfIt();
        if (!(iff = IffRead("Play.Gadget"))) longjmp(save, "Play.Gadget not found");
        if (!(Play_Image = MakeImage(&key, iff, NULL))) longjmp(save, "Play - not ILBM");

        if (cm = MakeColorMap(key, iff, &ncolors))
        {
                LoadRGB4(vp, cm, ncolors);
        }
        else longjmp(save, "ColorMap problem");
        
        ForgetAllOfIt();
        
        /* Set background */
        SetRast(rp, 0);
        DrawImage(rp, bc4b_Image, BLEFT, BTOP);
        SwapBitMaps();
        SetRast(rp, 0);
        DrawImage(rp, bc4b_Image, BLEFT, BTOP);

        /* Init Gels System */
        if (ReadyGels(&key, &GInfo, rp))
                longjmp(save, "ReadyGels failed");
                
        /* Init Bobs & AninObs */
        for (i = 0; i < NBOBS; ++i)
        {
                if (!(bob[i] = MakeBob(&key, *im[i], SAVEBACK | OVERLAY)))
                        longjmp(save, "MakeBob Failed");
                DoubleBob(&key, bob[i]);
                AddBob(bob[i], rp);
                vsp[i] = bob[i]->BobVSprite;
        }
        
        vsp[BOARD]->X = BLEFT;
        vsp[BOARD]->Y = BTOP;
        vsp[RED]->X = 230;
        vsp[RED]->Y = 20;
        vsp[BLUE]->X = 230;
        vsp[BLUE]->Y = 100;
        bob[BOARD]->Flags |= SAVEBOB;   
        TimeAdvance(rp, vp);
        
        /* Install Gadgets */
        for (i = 0; i < 5; ++i)
        {
                gad[i] = g = MakeBGadget(&key, *ig[i], NULL, 
                                        GADR, GADT(i), GRELRIGHT, NULL);
                if (!g)
                        longjmp(save, "MakeBGadget failed");
                g->UserData = command[i];
                AddGadget(window, g, -1);
                gad[i] = g;
                SwapBitMaps();
                DrawImage(rp, *ig[i], GADR + 319, GADT(i));
                SwapBitMaps();
                DrawImage(rp, *ig[i], GADR + 319, GADT(i));
        }
        RefreshGadgets(gad[0], window, NULL);
        ScreenToFront(screen);
}

static  BOOL   alternate = NULL;
static  struct BitMap   *bit1, *bit2;

InitDoubleBuffering() 
{
        struct  RastPort *rp = window->RPort;
        struct  ViewPort *vp = &screen->ViewPort;
        struct  RasInfo *ri = vp->RasInfo;
        struct  BitMap *bit;
        int i;
        
        /* 1. init a bitmap */
        bit = AllocRemember(&key, sizeof(*bit), CHIP);
        if (!bit)
                longjmp(save, "Out of memory");
        
        InitBitMap(bit, 5, 320, 200);
        for (i = 0; i < 5; ++i)
                bit->Planes[i] = AllocRemember(&key, RASSIZE(320, 200), CHIP);
        
        bit1 = rp->BitMap;
        bit2 = bit;
}

SwapBitMaps()
{
        struct  RastPort *rp = window->RPort;
        struct  ViewPort *vp = &screen->ViewPort;
        struct  RasInfo *ri = vp->RasInfo;
        struct  BitMap *bit;
        
        alternate ^= 1;
        
        if (alternate)
        {
                /* display bit2 */
                ri->BitMap = bit2;
                WaitTOF();
                MakeScreen(screen);
                RethinkDisplay();
                rp->BitMap = bit1;
        }
        else
        {
                /* display bit1 */
                ri->BitMap = bit1;
                WaitTOF();
                MakeScreen(screen);
                RethinkDisplay();
                rp->BitMap = bit2;
        }
}

ShutDownDB()    /* shut down double-buffering */
{
        /* WARNING: Do Not SwapBitMaps after this!!!! */
        
        if (!bit1 || !bit2)
                return; /* don't attempt to shutdowm if not up */
                
        if (!alternate) 
                SwapBitMaps();  /* make intuition kill its own bitmap */
        
}

MainShutdown(n)                /* shutdown procedure   */
int    n;
{
        switch (n)
        {
                case 0:
                        ForgetAllOfIt();
                        Dinit();
                        ShutDownDB();
                        
                case 50:
                        if (tmpras.RasPtr)
                                FreeRaster(tmpras.RasPtr, 320, 400);
                case 45:
                        if (window)
                                CloseWindow(window);
                case 40:
                        if (screen)
                                CloseScreen(screen);
                case 30:
                        if (GfxBase)
                                CloseLibrary(GfxBase);
                case 20:
                        if (IntuitionBase)
                                CloseLibrary(IntuitionBase);
                case 10:
                        FreeRemember(&key, TRUE);
                        break;
                default:
                        printf("??? Shutdown Error %d\n", n);
                        MainShutdown(0);
                        break;
        }
        exit(n);
}

#
/*****************************************************************************
        The Actual Connect-4 code
*****************************************************************************/

Connect4()       /* the connect 4 game */
{
        struct  IntuiMessage *m, cm;
        struct  RastPort *rp = window->RPort;
        struct  ViewPort *vp = &screen->ViewPort;
        int class, code;
        
        while (1)
        {
                WaitPort(window->UserPort);
                while (m = GetMsg(window->UserPort))
                {
                        cm = *m;
                        ReplyMsg(m);
                        
                        switch (cm.Class)
                        {
                        case GADGETUP:
                                ProcessGadget(&cm);
                                RefreshGadgets(gad[0], window, NULL);
                                break;
                        
                        case MOUSEMOVE:
                                ProcessMouse(&cm);
                                break;
                        
                        case MOUSEBUTTONS:
                                if (cm.Code == SELECTUP || cm.Code == MENUUP)
                                        ProcessRelease();
                                break;
                        
                        case INTUITICKS:
                                TimeAdvance(rp, vp);
                                ProcessTime();
                                break;
                        
                        default:
                                break;
                        }
                }
        }
}

/*      Processing
*/

TimeAdvance(rp, vp)     /* advance bobs to new posistion */
struct RastPort *rp;
struct ViewPort *vp;
{
        SortGList(rp);
        DrawGList(rp, vp);
        SwapBitMaps();
}

ProcessTime()   /* Signal children that a time event has been received */
{
        if (dprint)
        {
                printf(dstring);
                dprint = FALSE;
        }
        Forbid();
        if (ptrFallTask)
                Signal(ptrFallTask, 1<<FTsig);
        if (ptrPlayTask)
                Signal(ptrPlayTask, 1<<PTsig);
        if (ptrLeviTask)
                Signal(ptrLeviTask, 1<<LTsig);
        Permit();
}

ProcessGadget(m)
struct IntuiMessage *m;
{
        int (*funct)();
        struct Gadget *g;
        
        g = m->IAddress;
        funct = g->UserData;
        (*funct)(window->RPort, &screen->ViewPort);
}

/*      Mouse attached to peice
*/

BOOL    peicemove = FALSE;
int     peice;

ProcessMouse(m)         /* move the RED peice - human */
struct IntuiMessage *m;
{
        if (peicemove)
        {
                vsp[peice]->X += ((m->MouseX - BWIDTH) - vsp[peice]->X)/2;
                vsp[peice]->Y += ((m->MouseY - BHEIGHT) - vsp[peice]->Y)/2;
        }
}

ProcessRelease()        /* The Chip has been released, so turn off mouse movements */
{
        peicemove = FALSE;
        FallSlot = -1;
        DoneFalling = FALSE;
        if (!CreateTask( "fall.task", 0, FallTask, STACK ))
                longjmp(save, "fall.task creation failed");
}
#
/****************************************************************************
        Command Routines
****************************************************************************/

PlayCommand()   /* initiate play task */
{
        Forbid();
        if (!ptrPlayTask)
        {
                if (!CreateTask("play.task", 0, PlayTask, STACK))
                {        
                        Permit();
                        longjmp(save, "play.task creation failed");
                }
        }
        Permit();
}

LevelCommand()
{
        struct  RastPort *rp = window->RPort;
        struct  ViewPort *vp = &screen->ViewPort;
        IFF *iff, *bmhd;
        struct BitMap *bit;
        struct Requester *req;
        struct Remember *lkey = NULL;
        BitMapHeader *bh;
        RTGLIST *l;
        int i;
        
        if ((iff = IffRead("Level.Requester")) 
                && (bit = MakeBitMap(&lkey, iff, NULL)))
        {
                bmhd = IffFind(iff, BMHD, NULL, IF_FRONT);
                if (!bmhd)
                        longjmp(save, "Iff BMHD NOT FOUND??@@!!");
                bh = bmhd->info;
                screen->ViewPort.RasInfo->BitMap = screen->RastPort.BitMap;
                WaitTOF();      /* shut down double buffering */
                MakeScreen(screen);
                RethinkDisplay();
                
                
                if (DoCustomRequest(&key, bit, level_list, window, 
                                        10, 20, bh->w, bh->h))
                {
                        for (l = level_list, i=0; i < 16; ++l, ++i)
                        {
                                if (l->selected)
                                {
                                        game_level = l->userid;
                                        printf("changed to level %d\n", game_level);
                                        break;
                                }
                        }
                }
                else
                {
                        printf("/* Level Requester Failure */");
                }
                SwapBitMaps();
                FreeRemember(&lkey, TRUE);
        }
        ForgetAllOfIt();
}

BackCommand()
{
        ScreenToBack(screen);
}

InfoCommand()
{
        struct  RastPort *rp = window->RPort;
        struct  ViewPort *vp = &screen->ViewPort;
        IFF *iff, *bmhd;
        struct BitMap *bit;
        struct Requester *req;
        struct Remember *key = NULL;
        BitMapHeader *bh;

        if ((iff = IffRead("Info.Requester")) 
                && (bit = MakeBitMap(&key, iff, NULL)))
        {
                bmhd = IffFind(iff, BMHD, NULL, IF_FRONT);
                if (!bmhd)
                        longjmp(save, "Iff BMHD NOT FOUND??@@!!");
                bh = bmhd->info;
                screen->ViewPort.RasInfo->BitMap = screen->RastPort.BitMap;
                WaitTOF();      /* shut down double buffering */
                MakeScreen(screen);
                RethinkDisplay();
                if (!DoCustomRequest(NULL, bit, info_list, window, 
                                10, 20, bh->w, bh->h))
                        printf("/* Info Requester Failure */");
                SwapBitMaps();
                FreeRemember(&key, TRUE);
        }
        ForgetAllOfIt();
}

QuitCommand()
{
        longjmp(save, "user exit");
}
#
/****************************************************************************
        The Play Task
****************************************************************************/

PlayTask()
{
        struct RastPort *rp = window->RPort;
        struct ViewPort *vp = &screen->ViewPort;
        int time, i, j;
        C4BOARD b;
        
        PTsig = AllocSignal(-1);
        ptrPlayTask = FindTask();
        peicemove = FALSE;
        
        DrawImage(rp, BACK_BOARD_IMAGE, BLEFT, BTOP);
        Wait(1<<PTsig);
        DrawImage(rp, BACK_BOARD_IMAGE, BLEFT, BTOP);
        Wait(1<<PTsig);
        
        movmem(&C4Template, &MasterBoard, sizeof(C4BOARD));
        for (i = 0; i < 7; ++i)
                for (j = 0; j < 6; ++j)
                        MasterBoard.b[i][j] = -1;
        /* MasterBoard: -1 is unoccupied, RED is red, BLUE is blue */
        /* other boards: BLUE is 1 (computer), RED is -1 (human) */
        
        /* initialize bob positions */
        vsp[BOARD]->X = BLEFT;
        vsp[BOARD]->Y = BTOP;
        vsp[RED]->X = 230;
        vsp[RED]->Y = 20;
        vsp[BLUE]->X = 230;
        vsp[BLUE]->Y = 100;
        
        Wait(1<<PTsig);
        
        for (i = 0; i < 21; ++i)
        {
                LetHumanMove();
                if (WhoWon(&MasterBoard, -1) != -1)
                {
                        /* Human has won */
                        DisplayBeep(0);
                        break;
                }
                ConvertBoard(&b);
                MakeComputerMove(-1);   /* start the gyrations */
                MakeComputerMove(Analyze(&b, 1, game_level)); /* 1 for computer */
                if (WhoWon(&MasterBoard, -1) != -1)
                {
                        /* Computer has won */
                        DisplayBeep(0);
                        break;
                }
        }
        
        ptrPlayTask = NULL; /* before winking out */
}

WhoWon(b, space)        /* returns chip or space */
C4BOARD *b;
int space; /* value of unoccupied space */
{
        int c, r, chip, cc, rr, cnt, i;
        
        for (c = 0; c < 7; ++c)
                for (r = 0; r < 6; ++r)
                {
                        if ((chip = b->b[c][r]) != space)
                        {
                                for (cc = -1; cc <= 1; ++cc)
                                        for (rr = -1; rr <= 1; ++rr)
                                        {
                                                if (!cc && !rr)
                                                        break;
                                                for (i=0, cnt=0; i<4; ++i)
                                                {
                                                        if (b->b[c+cc*i][r+rr*i] == chip)
                                                        {
                                                                ++cnt;
                                                                winner[i][0] = c+cc*i;
                                                                winner[i][1] = r+rr*i;
                                                        }
                                                        else
                                                                break;
                                                }
                                                if (cnt == 4)
                                                        return chip;
                                                
                                        }
                                
                        }
                }
        return space;
}

LetHumanMove()  /* allow human to make his move */
{
        while (!DoneFalling)
                Wait(1<<PTsig);
                
        Forbid();
        FallSlot = -1;
        peice = RED;
        vsp[RED]->X = 230;
        vsp[RED]->Y = 20;
        DoneFalling = FALSE;
        Permit();
        
        do
        {
                FallSlot = -1;
                peicemove = TRUE;
                while (FallSlot == -1)
                        Wait(1<<PTsig);
        } while (FallSlot == -2);
}

MakeComputerMove(slot)  /* make computer's move. slot is -1 if undecided */
int slot;
{
        while (!DoneFalling)
                Wait(1<<PTsig);
                
        Forbid();
        LTwhere = slot;
        peice = BLUE;
        peicemove = FALSE;
        if (!ptrLeviTask)
        {
                vsp[BLUE]->X = 230;
                vsp[BLUE]->Y = 100;
                CreateTask("levi.task", 0, LeviTask, STACK);
        } 
        if (slot != -1)
                DoneFalling = FALSE;
        Permit();
        if (slot != -1)
                while (!DoneFalling)
                        Wait(1<<PTsig);
/*      bob[BLUE]->Flags &= ~SAVEBOB; */
}

ConvertBoard(b) /* converts MasterBoard to AnalBoard */
C4BOARD *b;
{
        C4BOARD *o = &MasterBoard;
        int c, r;
        
        movmem(&C4Template, b, sizeof(C4BOARD));
        movmem(o->pos, b->pos, sizeof(b->pos));
        for (c = 0; c < 7; ++c)
                for (r = 0; r < 6; ++r)
                        if (o->b[c][r] != -1)
                                b->b[c][r] = (o->b[c][r] == RED) ? -1 : 1;
        
}
#
/****************************************************************************
        The Fall Task
        
        Must determine location of [peice]
        Must calculate where it will fall - which slot
        Must know how far it will fall and where to stop it.
        Must return somewhere which slot (0-6) chip fell in.
****************************************************************************/

FallTask()
{
        int l, t, col, row, x, y, a, v; /* t has two uses */
        struct RastPort *rp = window->RPort;
        
        FallSlot = -1, DoneFalling = FALSE; /* to be sure */
        FTsig = AllocSignal(-1);
        ptrFallTask = FindTask(NULL);
        
        /* Which slot will it fall in? Is it high enough ? Over the board? */
        l = vsp[peice]->X - BLEFT + BWIDTH/2; /* middle top instead of left top */
        t = vsp[peice]->Y - BTOP;
        if ((t < BS_TOP) && (l > BS_LEFT) && (l < BS_LEFT + BWIDTH * 7))
        {
                /* calculate which column and adjust for alingment*/
                col = (l - BS_LEFT) / BWIDTH;
                x = vsp[peice]->X = xcoor(col);
                Wait(1<<FTsig);
                
                /* find row it must fall to */
                row = MasterBoard.pos[col];
                /* row is either 6 (full), or 0-5 */
                
                if (row >= 6)
                        FallSlot = -2;
                else /* there exist an empty slot. */ 
                {
                        DoMove(&MasterBoard, col, peice);
                        FallSlot = col;
                        peicemove = FALSE;
                        y = ycoor(row); /* where to fall to */
                        a = 600;
                        v = 0;
                        t = vsp[peice]->Y * 1000; 
                        while (t/1000 < y)
                        {
                                v += a;
                                t += v;
                                vsp[peice]->Y = t / 1000;
                                Wait(1<<FTsig);
                        }
                        vsp[peice]->X = 230;
                        vsp[peice]->Y = 100;
                        Wait(1<<FTsig); /* do it twice to clear double buffer */
                        DrawImage(rp, *im[peice], x, y);
                        Wait(1<<FTsig);
                        DrawImage(rp, *im[peice], x, y);
                        Wait(1<<FTsig); /* do it again for double buffering */
                        DrawImage(rp, *im[peice], x, y);
                }                
        }
        else /* out of range */
        {
                FallSlot = -2;
        }
        DoneFalling = TRUE;
        ptrFallTask = NULL;
}

xcoor(col)      /* compute the x coordiante from the given col slot */
int col;
{
        return BLEFT + BS_LEFT + col * BWIDTH;
}

ycoor(row)      /* compute the y coordinate from the given row slot */
int row;
{
        return (5-row) * BHEIGHT + BS_TOP + BTOP;
}

#
/***************************************************************************

        The Levi Task
        
        Used by Computer to make its move.
        peice = peice to be moved
        LTwhere = where to move it (-1 if undecided)
        
        Conjurs up the FallTask to drop it in!

****************************************************************************/

LeviTask()
{
        int goalx, goaly, dx, dy, gyrate = 0;
        
        LTsig = AllocSignal(-1);
        ptrLeviTask = FindTask(NULL);
        
        /* move peice to top of screen, 2 chips above BS_TOP */
        goaly = BTOP + BS_TOP - 2*BHEIGHT;
        while (dy = (goaly - vsp[peice]->Y))
        {
                if (dy/5)
                        dy /= 5;
                vsp[peice]->Y += dy;
                Wait(1<<LTsig);
        }
        
        /* now that the peice is up there, either move it in place, or
           gyrate back and forth until the proper value comes in. */
        
        while (LTwhere == -1)
        {
                if (!gyrate)
                {
                        gyrate = BS_LEFT + BLEFT + rand()%(BWIDTH * 7);
                        gyrate -= vsp[peice]->X; /* displacement */
                }
                dx = gyrate / 5;
                if (!dx)
                        dx = gyrate;
                else if (ABS(dx) < 5)
                        dx = (dx * 5) / ABS(dx);
                        
                vsp[peice]->X += dx;
                gyrate -= dx;
                Wait(1<<LTsig);
        }
        /* LTwhere now contains a goal, so.... */
        goalx = LTwhere * BWIDTH + BS_LEFT + BLEFT;
        while (dx = goalx - vsp[peice]->X)
        {
                if (dx / 5)
                        dx /= 5;
                else
                        dx /= ABS(dx);
                vsp[peice]->X += dx;
                Wait(1<<LTsig);
        }
        
        /* Conjur up the FallTask */
        LTwhere = -1;
        FallTask();
        ptrLeviTask = NULL;
}
#
/*****************************************************************************
        The ANALYZE Brain (not a task unto itself, but called by PlayTask)
        
                The board is represented by +/- 1, 0 being unoccupied.
                The results are in nextmove array. For each recursion,
                a pro move and an anti move is analyzed.
*****************************************************************************/

int decide[4][4] = /* 1 - good move, 0 - bad move [him][me] */
{
        {       1,      1,      1,      1       },
        {       0,      1,      1,      1       },
        {       0,      0,      0,      1       },
        {       0,      0,      0,      0       }
};

Analyze (b, who, depth, pwin)    /* analyze board to depth, return the best move */
C4BOARD *b;
int   depth,  /* search depth */ 
        who,    /* who moves next */
        *pwin;  /* place to return the winning status (if non-zero) */
{
        int move, nextmove, counter, winner, i;
        int grade[7];
        
        Grade(b, who, grade);
        if (!depth)
        {
                tprintf("%d->", depth);
                for (i =0; i < 7; ++i)
                        tprintf("(%d)", grade[i]);
                move = PickAGrade(b, grade, who);
                tprintf(" pick=%d\n", move);
                if (pwin)
                        *pwin = WhoWon(b, 0);
                return move;
        }
        
        for (i = 0; i < 7; ++i)
        {
                move = PickAGrade(b, grade, who);
                if (!DoMove(b, move, who))
                {
                        grade[move] = MAGIC_LOCKOUT;
                        continue;
                }
        
                if (WhoWon(b, 0) == who)
                {
                        UndoMove(b, move);
                        if (pwin)
                                *pwin = who;
                        return move;
                }
                
                /* since this move is not a definite win, check it out */
                counter = Analyze(b, -who, depth-1, &winner);
                if (winner)
                {
                        if (pwin)
                                *pwin = winner;
                                
                        UndoMove(b, move); /* we're hopping somewhere else */
                        
                        if (winner == who)
                        {
                                return move;
                        }
                        else /* winner == -who, the other guy */
                        {
                                grade[move] = MAGIC_LOCKOUT;
                                continue;
                        }
                }
                UndoMove(b, move);
                break;
        }
        return move;
}

Highest (grade, who) /* pick highest entry and return it AS A POSITIVE */
int grade[];
int who;
{
        int i;
        int h;
        
        for (i = 0, h = -999; i < 7; ++i)
                if (grade[i] * who > h)
                        h = grade[i] * who;
        
        return (h > 0) ? h : 0;
}

Lowest (grade, who) /* pick lowest entry and return it AS A POSITIVE */
int grade[];
int who;
{
        int i;
        int h;
        
        for (i = 0, h = 999; i < 7; ++i)
                if (grade[i] * who < h)
                        h = grade[i] * who;
        
        return (h > 0) ? h : 0;
}

PickAGrade(b, grade, who)  /* pick a grade based on priority calculation */
C4BOARD *b;
int grade[];          /* ptr to 7-element array to choose from  */
int who;              /* in favor of */
{
        int ic, g, gic, pro;
        
        for (ic = 0, g=-1000; ic < 7; ++ic)
        {
                if (grade[ic] == MAGIC_LOCKOUT)
                        continue;
                        
                pro = (grade[ic]*who > 0) ? grade[ic] * who * 2 + 1 
                                          : -grade[ic] * who * 2;
                if ((pro > g) && (b->pos[ic] < 6))
                {
                        g = pro;
                        gic = ic;
                }
        }
        return gic;
}

DoMove(b, col, who)     /* move the peice there, return FALSE if failed */
C4BOARD *b;
int col;
int who;
{
        int pos;
        
        if ((pos = b->pos[col]) >= 6)
                return FALSE;
        ++b->pos[col];
        b->b[col][pos] = who;
        return TRUE;
}

UndoMove(b, col)  /* undo the previous move. Will not work with MASTERBOARD */
C4BOARD *b;
int col;
{
        int pos;
        
        pos = --b->pos[col];
        b->b[col][pos] = 0;
}

int sx[8] = {-1, 0, 1, -1, 1, -1, 0, 1};        /* x component directions */
int sy[8] = {-1, -1, -1, 0, 0, 1, 1, 1};        /* y component directions */
int xy[4][2] = {0, 7, 5, 2, 1, 6, 3, 4};        /* directional paring */
int gr[7] = {0, 1, 5, 100, 1000, 1000, 1000};

/*      Grading had been ambiguous before, but now it's done on the basis
        of who moves next. The next move is posulated for all 7 possibilities
        and a grade is computed for each position. 
*/
Grade(b, who, arr)
C4BOARD *b;
int who;        /* who moves next */
int *arr;       /* score of each indiv. column returned (if non-z) */
{
        int grade[7], i;
        
        _Grade(b, who, arr, 0);
        _Grade(b, who, grade, 1);
        
        for (i = 0; i < 7; ++i)
        {
                if (sgn(grade[i]) == -who)
                        if (sgn(arr[i]) == who)
                                if (ABS(grade[i]) > ABS(arr[i]))
                                        arr[i] = 0;
        }
}

_Grade(b, who, arr, phantom)        /* Grade the board, return the results */
C4BOARD *b;
int who;        /* who moves next */
int *arr;       /* score of each indiv. column returned (if non-z) */
int phantom;    /* posistion offset - either 0 or 1 */
{
        int c, g, i, j, d, pos, p, q, e, ppro, pant;
        int score[8]; /* ...for the 8 directions */
        int emp[8]; /* ...for the empty areas following the chip */
        int cso[7]; /* score for the seven columns */
        
        for (c = 0, ppro=pant=0; c < 7; ++c) /* for the seven columns */
        {
                pos = b->pos[c] + phantom;
                if (pos >= 6)
                {
                        cso[c] = 0;
                        continue;
                }
                        
                for (d = 0; d < 8; ++d) /* for the eight directions */
                {
                        for (i = 1, p = b->b[c+sx[d]][pos+sy[d]], 
                                score[d]=emp[d]=0; i < 4; ++i)
                        {
                                q = b->b[c+sx[d]*i][pos+sy[d]*i];
                                if (q == -2)
                                        break;
                                else if (q == p)
                                {
                                        if (p)
                                                score[d] += p;
                                        else
                                                ++emp[d];
                                }
                                else if (!q)
                                {
                                        if (d != 1 && d != 6)
                                                if (!b->b[c+sx[d]*i][pos+sy[d]*(i-1)])
                                                        break;
                                        
                                        p = 0;
                                        ++emp[d];
                                }
                                else
                                        break;
                        }
                }
                /* 8 directional score has been computed. */
                /* Now sift through it to find the most relavant score. */
                
                /* do a directional paring sweep... */
                for (i = 0; i < 4; ++i)
                {
                        p = score[xy[i][0]];
                        q = score[xy[i][1]];
                        if (!p && !q)   /* if both are empty */
                        {
                                /* skip */
                        }
                        else if (sgn(p) == sgn(q))   /* if chips match... */
                        {
                                score[xy[i][0]] += q;
                                score[xy[i][1]] = 0;
                                emp[xy[i][0]] += emp[xy[i][1]];
                                emp[xy[i][1]] = 0;
                        }
                        else if (!p)
                        {
                                emp[xy[i][1]] += emp[xy[i][0]];
                                emp[xy[i][0]] = 0;
                        }
                        else if (!q)
                        {
                                emp[xy[i][0]] += emp[xy[i][1]];
                                emp[xy[i][1]] = 0;
                        }
                        else /* p and q are opposite chips */
                        {
                                if (ABS(p) == ABS(q))
                                {
                                        if (who == p) /* if chip p goes next */
                                        {
                                                score[xy[i][1]] = 0;
                                                emp[xy[i][1]] = 0;
                                        }
                                        else /* q goes next */
                                        {
                                                score[xy[i][0]] = 0;
                                                emp[xy[i][0]] = 0;
                                        }
                                }
                        }
                } /* end of paring sweep. Now pick the most significant. */
                
                /* scan for most sig. EMP is not being made use of yet. */
                for (i = 0, j = 0; i < 8; ++i) /* j is used as place holder */
                {
                        if (ABS(j) < ABS(score[i]))
                        {
                                j = score[i];
                                e = emp[i];
                        }
                        else if (ABS(j) == ABS(score[i]))
                        {
                                if (who != sgn(j))      /* was != */
                                {
                                        j = score[i];
                                        e = emp[i];
                                }
                        }
                } /* meaningful score has been sifted. */
                cso[c] = (pos < 6) ? j : 0;
        }
        if (arr)
                movmem(cso, arr, sizeof(cso));
        /* Now cso contains the most meaningful score for the 7 cols */
        /* Now do the grading. */
        for (i = 0, g = 0; i < 7; ++i) /* g is used as place holder */
        {
                if (ABS(g) < ABS(cso[i]))
                        g = cso[i];
                else if (ABS(g) == ABS(cso[i]))
                {
                        if (who != sgn(g))
                               g = cso[i];
                } 
        }
        return g;
}

tprintf(s, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
char *s;
LONG p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
{
        if (!debug_state)
                return;
                
        while (dprint)
                Wait(-1);       /* wait for any signal */
        Forbid();
        sprintf(dstring, s, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
        dprint = TRUE;
        Permit();
}