// 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.
//
// Phase 4.1 migration: menu mini-format strings, AlertTemplate,
// NewWindowParm boilerplate folded into iigs/uiBuilder.h.

#include "iigs/toolbox.h"
#include "iigs/desktop.h"
#include "iigs/uiBuilder.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


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 {
    short num;
    unsigned char moves[60];
} MoveList;


// --- alphabetised forward decls -----------------------------------
static void checkForDone(void);
static void doContent(void);
static short endScore(const unsigned char *board);
static void findMove(short col);
static void getMoves(const unsigned char *board, short color, MoveList *out);
static short legalMove(short idx, short color);
static void makeAMove(short idx, short col);
static void menuAbout(void);
static void menuColor(void);
static void menuPass(void);
static void menuSelfPlay(void);
static void menuSetPly(short menuNum);
static void newGame(void);
static void onAbout(uint16_t cmdId);
static void onMenuPick(uint16_t menuId, uint16_t itemId);
static void onNewGame(uint16_t cmdId);
static void onPass(uint16_t cmdId);
static void onPlyN(uint16_t cmdId);
static void onQuit(uint16_t cmdId);
static void onSelfPlay(uint16_t cmdId);
static void onTogglePlayer(uint16_t cmdId);
static void scoreString(unsigned short bcnt, unsigned short wcnt);
static short score(const unsigned char *board);
static short scoreMove(unsigned char *board, short idx, short col, short level);
static void drawBoard(void);
static void drawMovesList(void);
static void drawScore(void);
static void drawSquare(short sq, short col);
static void initWindows(void);
static void moveNotation(short idx);
static void plot(short h, short v);
static void tryMove(void);
static void update(void);
static void handleMenuLegacy(unsigned short menuNum);


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.
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
};


// --- menu spec via uiBuilder --------------------------------------
static const UiMenuItemT gEditItems[] = {
    { edit_UndoLastMove, "Undo Last Move", 'Z', MI_DISABLED },
    { 512,               (const char *)0,   0,  MI_DIVIDER | MI_DISABLED },
    { 271, "Cut",   'X', MI_DISABLED },
    { 272, "Copy",  'C', MI_DISABLED },
    { 273, "Paste", 'V', MI_DISABLED },
    { 274, "Clear",  0,  MI_DISABLED },
};

static const UiMenuItemT gLevelItems[] = {
    { level_1Ply, "1 Ply", 0, 0 },
    { level_2Ply, "2 Ply", 0, 0 },
    { level_3Ply, "3 Ply", 0, 0 },
    { level_4Ply, "4 Ply", 0, 0 },
    { level_5Ply, "5 Ply", 0, 0 },
    { level_6Ply, "6 Ply", 0, 0 },
    { level_7Ply, "7 Ply", 0, 0 },
    { level_8Ply, "8 Ply", 0, 0 },
};

static const UiMenuItemT gOptionsItems[] = {
    { options_SelfPlay,           "Self Play",             0, 0 },
    { options_ComputerPlaysWhite, "Computer Plays Black",  0, 0 },
    { 514,                         (const char *)0,        0, MI_DIVIDER | MI_DISABLED },
    { options_Pass,               "Pass",                  0, 0 },
    { options_ShowScoreWindow,    "Show Score Window",     0, 0 },
    { options_ShowMovesWindow,    "Show Moves Window",     0, 0 },
};

static const UiMenuItemT gFileItems[] = {
    { file_NewGame, "New Game", 'N', 0 },
    { 513,           (const char *)0, 0, MI_DIVIDER | MI_DISABLED },
    { file_Quit,    "Quit",     'Q', 0 },
};

static const UiMenuItemT gAppleItems[] = {
    { apple_AboutReversi, "About Reversi", 0, 0 },
};

static const UiMenuT gMenus[] = {
    { 1, "Apple",     MN_APPLE, 1, gAppleItems   },
    { 2, "  File",    0,        3, gFileItems    },
    { 3, "  Edit",    0,        6, gEditItems    },
    { 4, "  Level",   0,        8, gLevelItems   },
    { 5, "  Options", 0,        6, gOptionsItems },
};


static const UiCmdHandlerT gCmdTable[] = {
    { apple_AboutReversi,         onAbout        },
    { file_NewGame,               onNewGame      },
    { file_Quit,                  onQuit         },
    { level_1Ply,                 onPlyN         },
    { level_2Ply,                 onPlyN         },
    { level_3Ply,                 onPlyN         },
    { level_4Ply,                 onPlyN         },
    { level_5Ply,                 onPlyN         },
    { level_6Ply,                 onPlyN         },
    { level_7Ply,                 onPlyN         },
    { level_8Ply,                 onPlyN         },
    { options_SelfPlay,           onSelfPlay     },
    { options_ComputerPlaysWhite, onTogglePlayer },
    { options_Pass,               onPass         },
};


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


// --- 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;
}


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;
}


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

    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);
    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) {
        uiBuilderAlert(UA_NOTE, "I cannot move, so I\rmust pass.\r");
        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);
    PaintRect(&r);
    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]);
        }
    }
}


static unsigned char gScoreBuf[21];


static void scoreString(unsigned short bcnt, unsigned short wcnt) {
    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;
    unsigned short 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);
}


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);
    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;
    unsigned short wcnt = 0;
    for (short i = 11; i < 90; i++) {
        if (gBoard[i] == blackPiece) {
            bcnt++;
        } else if (gBoard[i] == whitePiece) {
            wcnt++;
        }
    }
    if (wcnt == bcnt) {
        uiBuilderAlert(UA_NOTE, "The game is over.  It\ris a draw.");
    } else if (wcnt > bcnt) {
        uiBuilderAlert(UA_NOTE, "White wins the game.");
    } else {
        uiBuilderAlert(UA_NOTE, "Black wins the game.");
    }
    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 {
        uiBuilderAlert(UA_STOP, "Illegal move -\rtry again.");
    }
    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 {
        uiBuilderAlert(UA_STOP, "You have legal moves\rso you cannot pass.\r");
    }
}


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) {
    uiBuilderAlert(UA_NOTE,
                   "Reversi 1.0\r"
                   "Copyright 1989\r"
                   "Byte Works, Inc.\r\r"
                   "By Mike Westerfield");
}


static void onAbout(uint16_t cmdId) {
    (void)cmdId;
    menuAbout();
}


static void onNewGame(uint16_t cmdId) {
    (void)cmdId;
    newGame();
}


static void onQuit(uint16_t cmdId) {
    (void)cmdId;
    gDone = 1;
}


static void onPlyN(uint16_t cmdId) {
    menuSetPly((short)cmdId);
}


static void onSelfPlay(uint16_t cmdId) {
    (void)cmdId;
    menuSelfPlay();
}


static void onTogglePlayer(uint16_t cmdId) {
    (void)cmdId;
    menuColor();
}


static void onPass(uint16_t cmdId) {
    (void)cmdId;
    menuPass();
}


static void onMenuPick(uint16_t menuId, uint16_t itemId) {
    (void)menuId;
    uiBuilderDispatch(itemId, gCmdTable, (uint16_t)(sizeof gCmdTable / sizeof gCmdTable[0]));
    HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16));
}


static void handleMenuLegacy(unsigned short menuNum) {
    onMenuPick(0, (uint16_t)menuNum);
}


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

static void initWindows(void) {
    UiWindowT spec;

    // Board window: 0x80E4 = fTitle | fVis | fMove | fInfo + fPage
    spec.title          = "Reversi";
    spec.frameBits      = 0x80E4;
    spec.position.v1    = 32;
    spec.position.h1    = 32;
    spec.position.v2    = (int16_t)(32 + squareHeight * 8);
    spec.position.h2    = (int16_t)(32 + squareWidth * 8);
    spec.maxHeight      = (int16_t)(squareHeight * 8);
    spec.maxWidth       = (int16_t)(squareWidth * 8);
    spec.refCon         = 0;
    spec.contentDefProc = (void *)0;
    gBoardWin = uiBuilderOpenWindow(&spec);

    // Score window: 0xC0C4 = fTitle | fClose | fVis | fMove | fInfo
    spec.title          = "Scores";
    spec.frameBits      = 0xC0C4;
    spec.position.v1    = 32;
    spec.position.h1    = (int16_t)(640 - 32 - 200);
    spec.position.v2    = 61;
    spec.position.h2    = (int16_t)(640 - 32);
    spec.maxHeight      = 29;
    spec.maxWidth       = 200;
    gScoreWin = uiBuilderOpenWindow(&spec);

    // Moves window.
    spec.title          = "Moves";
    spec.frameBits      = 0xC0C4;
    spec.position.v1    = 80;
    spec.position.h1    = (int16_t)(640 - 32 - 100);
    spec.position.v2    = 192;
    spec.position.h2    = (int16_t)(640 - 32);
    spec.maxHeight      = 112;
    spec.maxWidth       = 100;
    gMovesWin = uiBuilderOpenWindow(&spec);

    SelectWindow(gBoardWin);
}


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

    paintDesktopBackdrop();
    uiBuilderInstallMenuBar(gMenus, (uint16_t)(sizeof gMenus / sizeof gMenus[0]));
    CheckMItem(1, level_1Ply);
    initWindows();
    newGame();
    gEvent.wmTaskMask = 0x13FFL;
    ShowCursor();

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

    gDone = 0;
    unsigned short watchdog = 0;
    do {
        unsigned short event = TaskMaster(0x074E, (void *)&gEvent);
        switch (event) {
            case wInSpecial:
            case wInMenuBar:
                handleMenuLegacy((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;
}
