/* 3Dc, a game of 3-Dimensional Chess Copyright (C) 1995 Paul Hicks This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. E-Mail: paulh@euristix.ie */ #include #include #include #include #include #include #include #include #include "machine.h" #include "3Dc.h" #define MAX_RETRIES 100 /* Number of times to guess a tricky move */ Colour bwToMove = WHITE; Local Colour computer = NOCOL; Local Boolean gamePaused = FALSE; Local Boolean SetupAutoplay(char *); Local void DoMain3DcLoop(void); int main(int argc, char **argv) { int argNum; printf("3Dc version %s, Copyright (C) 1995,1996 Paul Hicks\n", VERSION); printf("3Dc comes with ABSOLUTELY NO WARRANTY: see the GPL file for details\n"); printf("This is free software: you are welcome to redistribute it\n"); printf(" under certain conditions (see the GPL file).\n"); Init3Dc(); if (Init3DcGFX(argc, argv) == FALSE) return 1; for (argNum = 1; argNum < argc; ++argNum) { if (!strcmp(argv[argNum], "-play")) { if (++argNum >= argc) { fprintf(stderr, "%s: -play requires a colour (black or white)\n", argv[0]); return 1; } if (SetupAutoplay(argv[argNum]) == FALSE) { fprintf(stderr, "%s: %s is not a colour (must be black or white)\n", argv[0], argv[argNum]); return 1; } } /* End autoplay setup */ else if (!strcmp(argv[argNum], "-altdisplay") || !strcmp(argv[argNum], "-ad")) { /* If no more params or next param is a new option */ if ((++argNum == argc) || argv[argNum][0] == '-') { fprintf(stderr, "%s: option %s requires a display name parameter\n", argv[0], argv[argNum -1]); return 1; } else { Open2ndDisplay(argv[argNum]); } } /* End net setup */ else /* The help option */ { fprintf(stderr, "Usage:\n"); fprintf(stderr, "\ %s ; play 3Dc, two humans on one display\n\ %s -ad|-altdisplay [display] ; black plays on display `display'\n\ %s -play colour ; play against the computer, which plays colour\n", argv[0], argv[0], argv[0]); return 1; } } /* Finish parameters */ DoMain3DcLoop(); return 0; } /* Set up the computer intelligence and all that */ Local Boolean SetupAutoplay(char *colourName) { if (!strcmp(colourName, "black")) computer = BLACK; else if (!strcmp(colourName, "white")) computer = WHITE; else { return FALSE; } return TRUE; } Local void DoMain3DcLoop(void) { Move *automove; XEvent event; Local Boolean retry = FALSE; while (firstGFX->mainWindow) { /* First thing to do: check for end of game! */ if (IsGameFinished() && !gamePaused) FinishGame((bwToMove == BLACK) ? WHITE : BLACK); if ( (bwToMove == computer) && !gamePaused) { if (((retry == FALSE) && GenMove(computer, &automove) == TRUE) || ((retry == TRUE) && GenAltMove(computer, &automove) == TRUE)) { if ( automove == NULL ) { /* * Give up, it's too hard for me.. */ PauseGame(); /* Can we delay after this? */ Err3Dc(firstGFX, "Gaah! I give up.", TRUE); XFlush( XtDisplay( firstGFX->mainWindow )); FinishGame((computer == BLACK) ? WHITE : BLACK); } /*** This assertion fails with stack size of 1---or at least it used to */ else if ( (Board[ automove->xyzBefore.zLevel ] [ automove->xyzBefore.yRank ] [ automove->xyzBefore.xFile ] == NULL ) || (!CHECK( PieceMove( Board[ automove->xyzBefore.zLevel ] [ automove->xyzBefore.yRank ] [ automove->xyzBefore.xFile ], automove->xyzAfter.xFile, automove->xyzAfter.yRank, automove->xyzAfter.zLevel ) )) ) { /* The move was illegal for some reason * (in the future I plan to eliminate all * possibility of getting in here) */ D( printf( "Can't move from (%i,%i,%i) to (%i,%i,%i)\n", automove->xyzBefore.xFile, automove->xyzBefore.yRank, automove->xyzBefore.zLevel, automove->xyzAfter.xFile, automove->xyzAfter.yRank, automove->xyzAfter.zLevel ) ); retry = TRUE; } else /* Move is legit: do it */ { retry = FALSE; PrintMove( automove ); bwToMove = ((computer == WHITE) ? BLACK : WHITE); } /* End 'found computer move' */ } /* Still finding computer's move? */ } /* End computer's move */ if (XtAppPending(XtWidgetToApplicationContext(firstGFX->mainWindow))) { XtAppNextEvent(XtWidgetToApplicationContext(firstGFX->mainWindow), &event); XtDispatchEvent(&event); } if ((secondGFX != NULL) && (XtAppPending(XtWidgetToApplicationContext(secondGFX->mainWindow)))) { XtAppNextEvent(XtWidgetToApplicationContext(secondGFX->mainWindow), &event); XtDispatchEvent(&event); } } /* End game loop */ return; } /*************************************************************/ /* Utility functions */ Global int MusterIdx(const Title name, const int nth) { int i, count = 0; for (i = 0; i != name && i < TITLES; ++i) count += titleCount[i]; if (i == TITLES) return 47; /* 47 is a hack; it is a legal array index that is only * valid for pawns */ if (nth < titleCount[name]) { return count + nth; } /* else */ return 47; } Global char *Piece2String( Piece *piece ) { static char *names[] = { "King", "Queen", "Bishop", "Knight", "Rook", "Prince", "Princess", "Abbey", "Cannon", "Galley", "Pawn", "" }; return names[piece->nName]; } Global Colour Computer(void) { return computer; } Global void PauseGame(void) { gamePaused = TRUE; return; } Global void ResumeGame(void) { gamePaused = FALSE; return; } Global Boolean IsGamePaused(void) { return gamePaused; } Global Boolean IsGameFinished(void) { Boolean blackKingVisible, whiteKingVisible, blackFirstPrinceVisible, whiteFirstPrinceVisible, blackSecondPrinceVisible, whiteSecondPrinceVisible; blackKingVisible = Muster[BLACK][MusterIdx(king, 0)]->bVisible; whiteKingVisible = Muster[WHITE][MusterIdx(king, 0)]->bVisible; blackFirstPrinceVisible = Muster[BLACK][MusterIdx(prince, 0)]->bVisible; whiteFirstPrinceVisible = Muster[WHITE][MusterIdx(prince, 0)]->bVisible; blackSecondPrinceVisible = Muster[BLACK][MusterIdx(prince, 1)]->bVisible; whiteSecondPrinceVisible = Muster[WHITE][MusterIdx(prince, 1)]->bVisible; if ((!whiteKingVisible || (!whiteFirstPrinceVisible && !whiteSecondPrinceVisible)) || (!blackKingVisible || (!blackFirstPrinceVisible && !blackSecondPrinceVisible))) { return TRUE; } return FALSE; } Global void FinishGame(const Colour bwWinner) { char winString[19]; gamePaused = TRUE; sprintf(winString, "%s player wins!", (bwWinner == BLACK) ? "Black" : "White"); XtSetSensitive(firstGFX->undo, FALSE); Err3Dc(firstGFX, winString, TRUE); if (secondGFX != NULL) { XtSetSensitive(secondGFX->undo, FALSE); Err3Dc(secondGFX, winString, TRUE); } } Global void PrintMove( const Move *move ) { char *moveString = NULL; if (move != NULL) moveString = (char *)malloc(26); /* moveString is TRUE only if move != NULL too */ if (moveString) { Piece *piece, *enemy; Coord pos; piece = Board[ move->xyzAfter.zLevel] [ move->xyzAfter.yRank ] [ move->xyzAfter.xFile ]; CHECK( piece != NULL ); enemy = Muster[(piece->bwSide == WHITE) ? BLACK : WHITE] [ MusterIdx(king, 0) ]; pos = enemy->xyzPos; sprintf( moveString, "%s %c%c%c to %c%c%c%s", Piece2String( piece ), move->xyzBefore.zLevel + 'X', move->xyzBefore.xFile + 'a', move->xyzBefore.yRank + '1', move->xyzAfter.zLevel + 'X', move->xyzAfter.xFile + 'a', move->xyzAfter.yRank + '1', IsKingChecked( piece->bwSide ) ? " check!" : ""); /* Display the move: beep if * 1) the computer or * 2) the other player in a network game * moved or if * 3) the move resulted in a check * This is now changed so that there's no beep when the computer * moves 'cos it's so fast. */ Err3Dc( firstGFX, moveString, (/* (Computer() == bwToMove) || */ ( (secondGFX != NULL) && (bwToMove == BLACK) ) || ( IsKingChecked( piece->bwSide ))) ? TRUE : FALSE ); if ( secondGFX != NULL ) { Err3Dc( secondGFX, moveString, (bwToMove == WHITE) ? TRUE : FALSE ); } /* I think that this isn't allowed becase the string is * still needed by the label widget but it hasn't caused * any problems so far.. */ free(moveString); } else { /* Print something, even if out of memory.. */ if ( (Computer() == bwToMove) || ( (secondGFX != NULL) && (bwToMove == BLACK))) Err3Dc(firstGFX, "Opponent has moved", TRUE); else if ( (secondGFX != NULL) && (bwToMove == WHITE) ) Err3Dc(secondGFX, "Opponent has moved", TRUE); } }