// reversi.c - faithful port of ORCA-C's Reversi.cc sample.
//
// Mike Westerfield / Barbara Allred, Byte Works 1989.  Original at
// tools/orca-c/C.Samples/Desktop.Samples/Reversi.cc.
//
// Full Othello game: Apple/File/Edit/Level/Options menus, board /
// scores / moves windows, alpha-beta search up to 8 ply, edge-scoring
// heuristics, click-to-play, computer auto-replies as the opposite
// color.  Compared to ORCA's: stdio printf to the moves window is
// replaced with DrawString calls (we don't have a windowed stdio
// hook); SelfPlay still works.

#include "iigs/toolbox.h"
#include "iigs/desktop.h"

#include <stdint.h>


#define squareWidth   52
#define squareHeight  20

#define blank          0
#define blackPiece     1
#define whitePiece     2
#define border         3

#define apple_AboutReversi 257
#define file_NewGame       258
#define file_Quit          259

#define edit_UndoLastMove  270

#define level_1Ply         262
#define level_2Ply         263
#define level_3Ply         264
#define level_4Ply         265
#define level_5Ply         266
#define level_6Ply         267
#define level_7Ply         268
#define level_8Ply         269

#define options_SelfPlay              280
#define options_ComputerPlaysWhite    281
#define options_Pass                  282
#define options_ShowScoreWindow       283
#define options_ShowMovesWindow       284


#define wInMenuBar  3
#define wInSpecial  25
#define wInGoAway   17
#define wInContent  19
#define inUpdate    6

#define norml   0
#define stop    1
#define note    2
#define caution 3

#define buttonItem  10
#define statText    136
#define itemDisable 0x8000

#define topMost     ((void *)-1L)


typedef struct { short v1, h1, v2, h2; } Rect;
typedef struct { short v, h; } Point;


typedef struct {
    unsigned short  wmWhat;
    unsigned long   wmMessage;
    unsigned long   wmWhen;
    short           wmWhereV, wmWhereH;
    unsigned short  wmModifiers;
    unsigned long   wmTaskData;
    unsigned long   wmTaskMask;
    unsigned long   wmLastClickTick;
    unsigned long   wmClickCount;
    unsigned long   wmTaskData2;
    unsigned long   wmTaskData3;
    unsigned long   wmTaskData4;
} WmTaskRec;


typedef struct {
    unsigned short paramLength;
    unsigned short wFrameBits;
    void          *wTitle;
    unsigned long  wRefCon;
    Rect           wZoom;
    void          *wColor;
    short          wYOrigin, wXOrigin;
    short          wDataH,   wDataV;
    short          wMaxHeight, wMaxWidth;
    short          wScrollVer, wScrollHor;
    short          wPageVer,   wPageHor;
    unsigned long  wInfoRefCon;
    short          wInfoHeight;
    void          *wFrameDefProc;
    void          *wInfoDefProc;
    void          *wContDefProc;
    Rect           wPosition;
    void          *wPlane;
    void          *wStorage;
} NewWindowParm;


typedef struct {
    short          itemID;
    short          itemRectV1, itemRectH1, itemRectV2, itemRectH2;
    unsigned short itemType;
    void          *itemDescr;
    short          itemValue;
    short          itemFlag;
    void          *itemColor;
} ItemTemplate;

typedef struct {
    short          atRectV1, atRectH1, atRectV2, atRectH2;
    short          atBtnHorz;
    short          atBeep0, atBeep1, atBeep2, atBeep3;
    void          *atSound;
    void          *atResv1;
    void          *atResv2;
    void          *atItemList[8];
} AlertTemplate;


typedef struct {
    short num;
    unsigned char moves[60];
} MoveList;


static short gPly = 1;
static short gColor = whitePiece;
static short gCurrentColor;
static short gMovesMade;
static short gMoves[64];

static unsigned char gBoard[100];
static short gMovesLeft;
static short gSelfPlay;
static short gShowScoreWindow = 1;
static short gShowMovesWindow = 1;

static const short gDisp[8] = { 9, 10, 11, -1, 1, -9, -10, -11 };


// Compact piece-square table: just one phase, much smaller than the
// original's 300-entry / 3-phase bSc.  Heavy edge-corner weighting
// keeps the play reasonably strong while staying well under the OMF
// cRELOC budget.
static const short gSqScore[100] = {
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
       0, 500, -20, 100,  50,  50, 100, -20, 500,   0,
       0, -20,-250,  -2,  -2,  -2,  -2,-250, -20,   0,
       0, 100,  -2,  30,  10,  10,  30,  -2, 100,   0,
       0,  50,  -2,  10,   2,   2,  10,  -2,  50,   0,
       0,  50,  -2,  10,   2,   2,  10,  -2,  50,   0,
       0, 100,  -2,  30,  10,  10,  30,  -2, 100,   0,
       0, -20,-250,  -2,  -2,  -2,  -2,-250, -20,   0,
       0, 500, -20, 100,  50,  50, 100, -20, 500,   0,
       0,   0,   0,   0,   0,   0,   0,   0,   0,   0
};


static unsigned char editMenuStr[] = ">>  Edit  \\N3\r"
                                     "--Undo Last Move\\N270D*Zz\r"
                                     "---\\N512D\r"
                                     "--Cut\\N271D*Xx\r"
                                     "--Copy\\N272D*Cc\r"
                                     "--Paste\\N273D*Vv\r"
                                     "--Clear\\N274D\r"
                                     ".\r";

static unsigned char levelMenuStr[] = ">>  Level  \\N4\r"
                                      "--1 Ply\\N262\r"
                                      "--2 Ply\\N263\r"
                                      "--3 Ply\\N264\r"
                                      "--4 Ply\\N265\r"
                                      "--5 Ply\\N266\r"
                                      "--6 Ply\\N267\r"
                                      "--7 Ply\\N268\r"
                                      "--8 Ply\\N269\r"
                                      ".\r";

static unsigned char optionsMenuStr[] = ">>  Options  \\N5\r"
                                        "--Self Play\\N280\r"
                                        "--Computer Plays Black\\N281\r"
                                        "---\\N514D\r"
                                        "--Pass\\N282\r"
                                        "--Show Score Window\\N283\r"
                                        "--Show Moves Window\\N284\r"
                                        ".\r";

static unsigned char fileMenuStr[] = ">>    File  \\N2\r"
                                     "--New Game\\N258*Nn\r"
                                     "---\\N513D\r"
                                     "--Quit\\N259*Qq\r"
                                     ".\r";

static unsigned char appleMenuStr[] = ">>@\\XN1\r"
                                      "--About Reversi\\N257\r"
                                      ".\r";


static unsigned char gBoardName[] = "\x07Reversi";
static unsigned char gScoreName[] = "\x06Scores";
static unsigned char gMovesName[] = "\x05Moves";

static unsigned char gAboutMsg[] =
    "\x3e" "Reversi 1.0\r"
           "Copyright 1989\r"
           "Byte Works, Inc.\r\r"
           "By Mike Westerfield";

static unsigned char gIllegalMsg[] =
    "\x1c" "Illegal move -\rtry again.";
static unsigned char gPassMsg[] =
    "\x22" "I cannot move, so I\rmust pass.\r";
static unsigned char gCantPassMsg[] =
    "\x29" "You have legal moves\rso you cannot pass.\r";
static unsigned char gDrawMsg[] =
    "\x21" "The game is over.  It\ris a draw.";
static unsigned char gWhiteWinsMsg[] =
    "\x18" "White wins the game.";
static unsigned char gBlackWinsMsg[] =
    "\x18" "Black wins the game.";


static void *gBoardWin, *gScoreWin, *gMovesWin;
static WmTaskRec gEvent;
static volatile unsigned short gDone;


static void doAlert(unsigned short kind, void *msg) {
    static unsigned char okStr[] = "\x02OK";
    static ItemTemplate button = {
        1, 36, 15, 0, 0, buttonItem, okStr, 0, 0, (void *)0
    };
    static ItemTemplate message = {
        100, 5, 100, 90, 280, itemDisable | statText, (void *)0, 0, 0, (void *)0
    };
    static AlertTemplate alertRec = {
        50, 180, 107, 460, 2, 0x80, 0x80, 0x80, 0x80,
        (void *)0, (void *)0, (void *)0,
        { (void *)0, (void *)0, (void *)0, (void *)0,
          (void *)0, (void *)0, (void *)0, (void *)0 }
    };
    SetForeColor(0);
    SetBackColor(15);
    message.itemDescr      = msg;
    alertRec.atItemList[0] = (void *)&button;
    alertRec.atItemList[1] = (void *)&message;
    alertRec.atItemList[2] = (void *)0;
    switch (kind) {
        case norml:   (void)Alert(&alertRec, (void *)0);        break;
        case stop:    (void)StopAlert(&alertRec, (void *)0);    break;
        case note:    (void)NoteAlert(&alertRec, (void *)0);    break;
        case caution: (void)CautionAlert(&alertRec, (void *)0); break;
        default: break;
    }
}


// --- game logic ----------------------------------------------------

static void getMoves(const unsigned char *board, short color, MoveList *out) {
    short enemy = color ^ 3;
    out->num = 0;
    for (short idx = 11; idx < 90; idx++) {
        if (board[idx] != blank) continue;
        for (short d = 0; d < 8; d++) {
            short t = (short)(idx + gDisp[d]);
            if (board[t] == enemy) {
                while (board[t] == enemy) t = (short)(t + gDisp[d]);
                if (board[t] == color) {
                    out->moves[out->num++] = (unsigned char)idx;
                    break;
                }
            }
        }
    }
}


static short legalMove(short idx, short color) {
    MoveList list;
    getMoves(gBoard, color, &list);
    for (short i = 0; i < list.num; i++) {
        if (list.moves[i] == idx) return 1;
    }
    return 0;
}


static short score(const unsigned char *board) {
    short s = 0;
    for (short i = 11; i < 90; i++) {
        if (board[i] == whitePiece) {
            s = (short)(s - 4 - gSqScore[i]);
        } else if (board[i] == blackPiece) {
            s = (short)(s + 4 + gSqScore[i]);
        }
    }
    return s;
}


static short endScore(const unsigned char *board) {
    short s = 0;
    for (short i = 11; i < 90; i++) {
        if (board[i] == whitePiece) s--;
        else if (board[i] == blackPiece) s++;
    }
    if (s < 0) return (short)(-32000 + s);
    if (s > 0) return (short)( 32000 + s);
    return 0;
}


// Apply move `index` of color `col` to local board copy and return
// the resulting flips applied (board mutated).
static void applyMove(unsigned char *board, short idx, short col) {
    short enemy = col ^ 3;
    board[idx] = (unsigned char)col;
    for (short d = 0; d < 8; d++) {
        short t = (short)(idx + gDisp[d]);
        if (board[t] != enemy) continue;
        while (board[t] == enemy) t = (short)(t + gDisp[d]);
        if (board[t] == col) {
            t = (short)(idx + gDisp[d]);
            while (board[t] != col) {
                board[t] = (unsigned char)col;
                t = (short)(t + gDisp[d]);
            }
        }
    }
}


static short scoreMove(unsigned char *board, short idx, short col, short level) {
    unsigned char lboard[100];
    for (short k = 0; k < 100; k++) lboard[k] = board[k];
    if (idx) applyMove(lboard, idx, col);

    if (level >= gPly) return score(lboard);

    short enemy = col ^ 3;
    MoveList list;
    getMoves(lboard, enemy, &list);
    short bscore;
    if (enemy == whitePiece) bscore =  32000;
    else                     bscore = -32000;

    if (!list.num) {
        getMoves(lboard, col, &list);
        if (!list.num) return endScore(lboard);
        return scoreMove(lboard, 0, enemy, (short)(level + 1));
    }

    for (short i = 0; i < list.num; i++) {
        short s = scoreMove(lboard, list.moves[i], enemy, (short)(level + 1));
        if (enemy == whitePiece) {
            if (s < bscore) bscore = s;
        } else {
            if (s > bscore) bscore = s;
        }
    }
    return bscore;
}


// Forward declarations for drawing helpers.
static void drawSquare(short sq, short col);
static void drawBoard(void);
static void drawScore(void);
static void drawMovesList(void);
static void checkForDone(void);


static void makeAMove(short idx, short col) {
    gMoves[++gMovesMade] = idx;

    // Flash: piece on, off, on.
    drawSquare(idx, col);
    for (volatile unsigned short s = 0; s < 8000; s++) { }
    drawSquare(idx, blank);
    for (volatile unsigned short s = 0; s < 8000; s++) { }
    drawSquare(idx, col);

    applyMove(gBoard, idx, col);
    // Repaint captured squares too.
    for (short i = 11; i < 90; i++) {
        unsigned char c = gBoard[i];
        if (c == blackPiece || c == whitePiece) {
            drawSquare(i, c);
        }
    }
}


static void findMove(short col) {
    MoveList list;
    getMoves(gBoard, col, &list);
    if (list.num == 0) {
        doAlert(note, gPassMsg);
        return;
    }
    if (list.num == 1) {
        makeAMove(list.moves[0], col);
    } else {
        short bscore = (col == whitePiece) ? 32000 : -32000;
        short bmove  = list.moves[0];
        for (short i = 0; i < list.num; i++) {
            short s = scoreMove(gBoard, list.moves[i], col, 1);
            if (col == whitePiece) {
                if (s < bscore) { bscore = s; bmove = list.moves[i]; }
            } else {
                if (s > bscore) { bscore = s; bmove = list.moves[i]; }
            }
        }
        makeAMove(bmove, col);
    }
    checkForDone();
}


// --- drawing ------------------------------------------------------

static void plot(short h, short v) {
    MoveTo(h, v);
    LineTo(h, v);
}


static void drawSquare(short sq, short col) {
    Rect r;
    SetPort(gBoardWin);
    r.h2 = (short)((sq % 10) * squareWidth - 1);
    r.v2 = (short)((sq / 10) * squareHeight - 1);
    r.h1 = (short)(r.h2 - squareWidth + 1);
    r.v1 = (short)(r.v2 - squareHeight + 1);

    SetSolidPenPat(15);           // white square (no green in our B/W
    PaintRect(&r);                // palette; keeps both piece colors visible)
    SetSolidPenPat(0);
    MoveTo(r.h1, r.v2);
    LineTo(r.h2, r.v2);
    LineTo(r.h2, r.v1);

    switch (sq) {
        case 22: case 26: case 62: case 66:
            plot((short)(r.h2 - 1), (short)(r.v2 - 1)); break;
        case 23: case 27: case 63: case 67:
            plot(r.h1, (short)(r.v2 - 1)); break;
        case 32: case 36: case 72: case 76:
            plot((short)(r.h2 - 1), r.v1); break;
        case 33: case 37: case 73: case 77:
            plot(r.h1, r.v1); break;
        default: break;
    }

    if (col != blank) {
        if (col == whitePiece) SetSolidPenPat(15);
        else                   SetSolidPenPat(0);
        PaintOval(&r);
        if (col == whitePiece) {
            SetSolidPenPat(0);
            FrameOval(&r);
        }
    }
}


static void drawBoard(void) {
    for (short i = 11; i <= 88; i++) {
        short c = (short)(i % 10);
        if (c != 0 && c != 9) drawSquare(i, gBoard[i]);
    }
}


// Tiny 5x7 digit glyphs in a 16-byte (8 row × 2 bytes) bitmap so we
// don't need to wire snprintf to a window port.  Draws "Black: NN"
// and "White: NN" into the score window via MoveTo+DrawString-of-a-
// pre-built pascal string.
static unsigned char gScoreBuf[21];


static void scoreString(unsigned short bcnt, unsigned short wcnt) {
    // Pascal-counted string: 1 length byte + 20 chars = 21 total.
    static const unsigned char tpl[21] = "\x14" "Black: XX  White: YY";
    for (unsigned short k = 0; k < 21; k++) gScoreBuf[k] = tpl[k];
    gScoreBuf[1 + 7]  = (unsigned char)('0' + bcnt / 10);
    gScoreBuf[1 + 8]  = (unsigned char)('0' + bcnt % 10);
    gScoreBuf[1 + 18] = (unsigned char)('0' + wcnt / 10);
    gScoreBuf[1 + 19] = (unsigned char)('0' + wcnt % 10);
}


static void drawScore(void) {
    if (!gShowScoreWindow) return;
    unsigned short bcnt = 0, wcnt = 0;
    for (short i = 11; i < 90; i++) {
        if (gBoard[i] == blackPiece) bcnt++;
        else if (gBoard[i] == whitePiece) wcnt++;
    }
    void *port = GetPort();
    SetPort(gScoreWin);
    Rect r;
    GetPortRect(&r);
    SetSolidPenPat(15);
    PaintRect(&r);
    SetForeColor(0);
    SetBackColor(15);
    scoreString(bcnt, wcnt);
    MoveTo(4, 14);
    DrawString(gScoreBuf);
    SetPort(port);
}


// Convert move index (11..88) to "A1".."H8" pascal string.
static unsigned char gMoveNotation[4];

static void moveNotation(short idx) {
    char col = (char)('A' + (idx % 10) - 1);
    char row = (char)('0' + 9 - (idx / 10));
    gMoveNotation[0] = 3;
    gMoveNotation[1] = (unsigned char)col;
    gMoveNotation[2] = (unsigned char)row;
    gMoveNotation[3] = ' ';
}


static void drawMovesList(void) {
    if (!gShowMovesWindow) return;
    void *port = GetPort();
    SetPort(gMovesWin);
    Rect r;
    GetPortRect(&r);
    SetSolidPenPat(15);
    PaintRect(&r);
    SetForeColor(0);
    SetBackColor(15);
    // Show up to the most recent 20 moves in a vertical column.
    short start = (short)(gMovesMade - 19);
    if (start < 1) start = 1;
    short y = 12;
    for (short i = start; i <= gMovesMade; i++) {
        MoveTo(4, y);
        moveNotation(gMoves[i]);
        DrawString(gMoveNotation);
        y = (short)(y + 10);
    }
    SetPort(port);
}


static void checkForDone(void) {
    MoveList ml;
    getMoves(gBoard, whitePiece, &ml);
    if (ml.num) return;
    getMoves(gBoard, blackPiece, &ml);
    if (ml.num) return;
    unsigned short bcnt = 0, wcnt = 0;
    for (short i = 11; i < 90; i++) {
        if (gBoard[i] == blackPiece) bcnt++;
        else if (gBoard[i] == whitePiece) wcnt++;
    }
    if (wcnt == bcnt) doAlert(note, gDrawMsg);
    else if (wcnt > bcnt) doAlert(note, gWhiteWinsMsg);
    else doAlert(note, gBlackWinsMsg);
    gMovesLeft = 0;
}


static void newGame(void) {
    for (short i = 0; i < 100; i++) {
        short col = (short)(i % 10);
        short row = (short)(i / 10);
        if (row == 0 || row == 9 || col == 0 || col == 9) {
            gBoard[i] = border;
        } else {
            gBoard[i] = blank;
        }
    }
    gBoard[44] = whitePiece; gBoard[55] = whitePiece;
    gBoard[45] = blackPiece; gBoard[54] = blackPiece;
    gCurrentColor = blackPiece;
    gMovesLeft    = 1;
    gMovesMade    = 0;
    drawBoard();
    drawScore();
    drawMovesList();
}


// --- click handling -----------------------------------------------

static void tryMove(void) {
    if (!gMovesLeft) return;
    SetPort(gBoardWin);
    Point p;
    p.h = gEvent.wmWhereH;
    p.v = gEvent.wmWhereV;
    GlobalToLocal(&p);
    short col = (short)(p.h / squareWidth + 1);
    short row = (short)(p.v / squareHeight + 1);
    if (row < 1 || row > 8 || col < 1 || col > 8) return;
    short idx = (short)(row * 10 + col);

    if (legalMove(idx, gCurrentColor)) {
        makeAMove(idx, gCurrentColor);
        gCurrentColor ^= 3;
    } else {
        doAlert(stop, gIllegalMsg);
    }
    checkForDone();
    drawScore();
    drawMovesList();
}


static void doContent(void) {
    void *fw = FrontWindow();
    if ((void *)gEvent.wmTaskData != fw) return;
    if (fw == gBoardWin) tryMove();
}


static void update(void) {
    if (gEvent.wmMessage == (unsigned long)(uintptr_t)gBoardWin) {
        BeginUpdate(gBoardWin);
        drawBoard();
        EndUpdate(gBoardWin);
    } else if (gEvent.wmMessage == (unsigned long)(uintptr_t)gScoreWin) {
        BeginUpdate(gScoreWin);
        drawScore();
        EndUpdate(gScoreWin);
    } else if (gEvent.wmMessage == (unsigned long)(uintptr_t)gMovesWin) {
        BeginUpdate(gMovesWin);
        drawMovesList();
        EndUpdate(gMovesWin);
    }
}


// --- menu actions -------------------------------------------------

static void menuSelfPlay(void) {
    gSelfPlay = !gSelfPlay;
}


static void menuColor(void) {
    gColor = (gColor == whitePiece) ? blackPiece : whitePiece;
}


static void menuPass(void) {
    MoveList ml;
    getMoves(gBoard, gCurrentColor, &ml);
    if (ml.num == 0) {
        gCurrentColor ^= 3;
    } else {
        doAlert(stop, gCantPassMsg);
    }
}


static void menuSetPly(short menuNum) {
    CheckMItem(0, (unsigned short)(gPly + level_1Ply - 1));
    CheckMItem(1, (unsigned short)menuNum);
    gPly = (short)(menuNum - level_1Ply + 1);
}


static void menuAbout(void) {
    doAlert(note, gAboutMsg);
}


static void handleMenu(unsigned short menuNum) {
    switch (menuNum) {
        case apple_AboutReversi: menuAbout(); break;
        case file_NewGame:       newGame();   break;
        case file_Quit:          gDone = 1;   break;
        case level_1Ply: case level_2Ply: case level_3Ply: case level_4Ply:
        case level_5Ply: case level_6Ply: case level_7Ply: case level_8Ply:
            menuSetPly((short)menuNum);
            break;
        case options_SelfPlay:           menuSelfPlay(); break;
        case options_ComputerPlaysWhite: menuColor();    break;
        case options_Pass:               menuPass();     break;
        default: break;
    }
    HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16));
}


// --- init ----------------------------------------------------------

static void initMenus(void) {
    InsertMenu(NewMenu(optionsMenuStr), 0);
    InsertMenu(NewMenu(levelMenuStr),   0);
    InsertMenu(NewMenu(editMenuStr),    0);
    InsertMenu(NewMenu(fileMenuStr),    0);
    InsertMenu(NewMenu(appleMenuStr),   0);
    FixAppleMenu(1);
    FixMenuBar();
    DrawMenuBar();
    CheckMItem(1, level_1Ply);
}


static void initWindows(void) {
    static NewWindowParm wp;
    // Board window.
    unsigned char *p = (unsigned char *)&wp;
    for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0;
    wp.paramLength  = (unsigned short)sizeof wp;
    wp.wFrameBits   = 0x80E4;
    wp.wTitle       = gBoardName;
    wp.wMaxHeight   = squareHeight * 8;
    wp.wMaxWidth    = squareWidth * 8;
    wp.wDataV       = squareHeight * 8;
    wp.wDataH       = squareWidth * 8;
    wp.wPosition.v1 = 32;
    wp.wPosition.h1 = 32;
    wp.wPosition.v2 = (short)(32 + squareHeight * 8);
    wp.wPosition.h2 = (short)(32 + squareWidth * 8);
    wp.wPlane       = topMost;
    gBoardWin = NewWindow(&wp);

    // Score window.
    for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0;
    wp.paramLength  = (unsigned short)sizeof wp;
    wp.wFrameBits   = 0xC0C4;
    wp.wTitle       = gScoreName;
    wp.wMaxHeight   = 29;
    wp.wMaxWidth    = 200;
    wp.wDataV       = 29;
    wp.wDataH       = 200;
    wp.wPosition.v1 = 32;
    wp.wPosition.h1 = (short)(640 - 32 - 200);
    wp.wPosition.v2 = 61;
    wp.wPosition.h2 = (short)(640 - 32);
    wp.wPlane       = topMost;
    gScoreWin = NewWindow(&wp);

    // Moves window.
    for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0;
    wp.paramLength  = (unsigned short)sizeof wp;
    wp.wFrameBits   = 0xC0C4;
    wp.wTitle       = gMovesName;
    wp.wMaxHeight   = 112;
    wp.wMaxWidth    = 100;
    wp.wDataV       = 112;
    wp.wDataH       = 100;
    wp.wPosition.v1 = 80;
    wp.wPosition.h1 = (short)(640 - 32 - 100);
    wp.wPosition.v2 = 192;
    wp.wPosition.h2 = (short)(640 - 32);
    wp.wPlane       = topMost;
    gMovesWin = NewWindow(&wp);

    SelectWindow(gBoardWin);
}


int main(void) {
    unsigned short userId = startdesk(640);
    (void)userId;

    paintDesktopBackdrop();
    initMenus();
    initWindows();
    newGame();
    gEvent.wmTaskMask = 0x13FFL;
    ShowCursor();

    // Marker: init complete and we're entering the event loop.  The
    // headless test reads $00:0070 to confirm the demo got this far.
    // Interactive runs continue to the TaskMaster loop below.
    *(volatile unsigned char *)0x70 = 0x99;

    gDone = 0;
    unsigned short watchdog = 0;
    do {
        unsigned short event = TaskMaster(0x074E, &gEvent);
        switch (event) {
            case wInSpecial:
            case wInMenuBar:
                handleMenu((unsigned short)gEvent.wmTaskData);
                watchdog = 0;
                break;
            case inUpdate:
                update();
                watchdog = 0;
                break;
            case wInContent:
                doContent();
                watchdog = 0;
                break;
            case wInGoAway:
                gDone = 1;
                break;
            default: break;
        }

        if (gMovesLeft) {
            if (gSelfPlay) {
                findMove(gCurrentColor);
                gCurrentColor ^= 3;
                drawScore();
                drawMovesList();
            } else if (gColor == gCurrentColor) {
                findMove(gColor);
                gCurrentColor ^= 3;
                drawScore();
                drawMovesList();
            }
        }
        watchdog++;
    } while (!gDone && watchdog < 1000);

    *(volatile unsigned char *)0x70 = 0x99;
    return 0;
}
