1 /*
2 
3     3Dc, a game of 3-Dimensional Chess
4     Copyright (C) 1995  Paul Hicks
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 
20     E-Mail: paulh@euristix.ie
21 */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 #include <sys/time.h>
28 #include <X11/X.h>
29 #include <X11/Intrinsic.h>
30 
31 #include "machine.h"
32 
33 #include "3Dc.h"
34 
35 #define MAX_RETRIES 100 /* Number of times to guess a tricky move */
36 
37 Colour bwToMove = WHITE;
38 Local Colour computer = NOCOL;
39 Local Boolean gamePaused = FALSE;
40 
41 Local Boolean SetupAutoplay(char *);
42 Local void DoMain3DcLoop(void);
43 
44 int
main(int argc,char ** argv)45 main(int argc, char **argv)
46 {
47   int argNum;
48 
49   printf("3Dc version %s, Copyright (C) 1995,1996 Paul Hicks\n", VERSION);
50   printf("3Dc comes with ABSOLUTELY NO WARRANTY: see the GPL file for details\n");
51   printf("This is free software: you are welcome to redistribute it\n");
52   printf("    under certain conditions (see the GPL file).\n");
53 
54   Init3Dc();
55   if (Init3DcGFX(argc, argv) == FALSE)
56     return 1;
57 
58   for (argNum = 1; argNum < argc; ++argNum)
59     {
60       if (!strcmp(argv[argNum], "-play"))
61         {
62           if (++argNum >= argc)
63             {
64               fprintf(stderr,
65                       "%s: -play requires a colour (black or white)\n",
66                       argv[0]);
67               return 1;
68             }
69 
70           if (SetupAutoplay(argv[argNum]) == FALSE)
71             {
72               fprintf(stderr,
73                       "%s: %s is not a colour (must be black or white)\n",
74                       argv[0], argv[argNum]);
75               return 1;
76             }
77         } /* End autoplay setup */
78       else if (!strcmp(argv[argNum], "-altdisplay") ||
79                !strcmp(argv[argNum], "-ad"))
80         {
81           /* If no more params   or next param is a new option */
82           if ((++argNum == argc) || argv[argNum][0] == '-')
83             {
84               fprintf(stderr,
85                       "%s: option %s requires a display name parameter\n",
86                       argv[0], argv[argNum -1]);
87               return 1;
88             }
89           else
90             {
91               Open2ndDisplay(argv[argNum]);
92             }
93         } /* End net setup */
94       else /* The help option */
95         {
96           fprintf(stderr, "Usage:\n");
97           fprintf(stderr, "\
98 %s ; play 3Dc, two humans on one display\n\
99 %s -ad|-altdisplay [display] ; black plays on display `display'\n\
100 %s -play colour ; play against the computer, which plays colour\n",
101                  argv[0], argv[0], argv[0]);
102           return 1;
103         }
104     } /* Finish parameters */
105 
106   DoMain3DcLoop();
107 
108   return 0;
109 }
110 
111 /* Set up the computer intelligence and all that */
112 Local Boolean
SetupAutoplay(char * colourName)113 SetupAutoplay(char *colourName)
114 {
115   if (!strcmp(colourName, "black"))
116     computer = BLACK;
117   else if (!strcmp(colourName, "white"))
118     computer = WHITE;
119   else
120     {
121       return FALSE;
122     }
123   return TRUE;
124 }
125 
126 Local void
DoMain3DcLoop(void)127 DoMain3DcLoop(void)
128 {
129   Move *automove;
130   XEvent event;
131   Local Boolean retry = FALSE;
132 
133   while (firstGFX->mainWindow)
134     {
135       /* First thing to do: check for end of game! */
136       if (IsGameFinished() && !gamePaused)
137         FinishGame((bwToMove == BLACK) ? WHITE : BLACK);
138 
139       if ( (bwToMove == computer) && !gamePaused)
140         {
141           if (((retry == FALSE) &&    GenMove(computer, &automove) == TRUE) ||
142               ((retry == TRUE)  && GenAltMove(computer, &automove) == TRUE))
143             {
144               if ( automove == NULL )
145                 {
146                   /*
147                    * Give up, it's too hard for me..
148                    */
149                   PauseGame();
150                   /* Can we delay after this? */
151                   Err3Dc(firstGFX, "Gaah!  I give up.", TRUE);
152                   XFlush( XtDisplay( firstGFX->mainWindow ));
153                   FinishGame((computer == BLACK) ? WHITE : BLACK);
154                 }
155 /*** This assertion fails with stack size of 1---or at least it used to */
156               else if ( (Board[ automove->xyzBefore.zLevel ]
157                               [ automove->xyzBefore.yRank ]
158                               [ automove->xyzBefore.xFile ] == NULL ) ||
159                        (!CHECK( PieceMove( Board[ automove->xyzBefore.zLevel ]
160                                                 [ automove->xyzBefore.yRank ]
161                                                 [ automove->xyzBefore.xFile ],
162                                           automove->xyzAfter.xFile,
163                                           automove->xyzAfter.yRank,
164                                           automove->xyzAfter.zLevel ) )) )
165                 {
166                   /* The move was illegal for some reason
167                    * (in the future I plan to eliminate all
168                    * possibility of getting in here) */
169                   D( printf( "Can't move from (%i,%i,%i) to (%i,%i,%i)\n",
170                             automove->xyzBefore.xFile,
171                             automove->xyzBefore.yRank,
172                             automove->xyzBefore.zLevel,
173                             automove->xyzAfter.xFile,
174                             automove->xyzAfter.yRank,
175                             automove->xyzAfter.zLevel ) );
176 
177                   retry = TRUE;
178                 }
179               else /* Move is legit: do it */
180                 {
181                   retry = FALSE;
182                   PrintMove( automove );
183 
184                   bwToMove = ((computer == WHITE) ? BLACK : WHITE);
185                 } /* End 'found computer move' */
186             } /* Still finding computer's move? */
187         } /* End computer's move */
188 
189       if (XtAppPending(XtWidgetToApplicationContext(firstGFX->mainWindow)))
190         {
191           XtAppNextEvent(XtWidgetToApplicationContext(firstGFX->mainWindow),
192                          &event);
193           XtDispatchEvent(&event);
194         }
195 
196       if ((secondGFX != NULL) &&
197           (XtAppPending(XtWidgetToApplicationContext(secondGFX->mainWindow))))
198         {
199           XtAppNextEvent(XtWidgetToApplicationContext(secondGFX->mainWindow),
200                          &event);
201           XtDispatchEvent(&event);
202         }
203 
204     } /* End game loop */
205 
206   return;
207 }
208 
209 /*************************************************************/
210 /* Utility functions */
211 Global int
MusterIdx(const Title name,const int nth)212 MusterIdx(const Title name, const int nth)
213 {
214   int i, count = 0;
215 
216   for (i = 0; i != name && i < TITLES; ++i)
217     count += titleCount[i];
218 
219   if (i == TITLES)
220     return 47; /* 47 is a hack; it is a legal array index that is only
221                 * valid for pawns */
222 
223   if (nth < titleCount[name])
224     {
225       return count + nth;
226     }
227   /* else */
228   return 47;
229 }
230 
Piece2String(Piece * piece)231 Global char *Piece2String( Piece *piece )
232 {
233   static char *names[] =
234     {
235       "King",   "Queen",    "Bishop", "Knight", "Rook",
236       "Prince", "Princess", "Abbey",  "Cannon", "Galley",
237       "Pawn", ""
238     };
239 
240   return names[piece->nName];
241 }
242 
243 Global Colour
Computer(void)244 Computer(void)
245 {
246   return computer;
247 }
248 
249 Global void
PauseGame(void)250 PauseGame(void)
251 {
252   gamePaused = TRUE;
253   return;
254 }
255 
256 Global void
ResumeGame(void)257 ResumeGame(void)
258 {
259   gamePaused = FALSE;
260   return;
261 }
262 
263 Global Boolean
IsGamePaused(void)264 IsGamePaused(void)
265 {
266   return gamePaused;
267 }
268 
269 Global Boolean
IsGameFinished(void)270 IsGameFinished(void)
271 {
272   Boolean
273     blackKingVisible, whiteKingVisible,
274     blackFirstPrinceVisible, whiteFirstPrinceVisible,
275     blackSecondPrinceVisible, whiteSecondPrinceVisible;
276 
277   blackKingVisible = Muster[BLACK][MusterIdx(king, 0)]->bVisible;
278   whiteKingVisible = Muster[WHITE][MusterIdx(king, 0)]->bVisible;
279   blackFirstPrinceVisible = Muster[BLACK][MusterIdx(prince, 0)]->bVisible;
280   whiteFirstPrinceVisible = Muster[WHITE][MusterIdx(prince, 0)]->bVisible;
281   blackSecondPrinceVisible = Muster[BLACK][MusterIdx(prince, 1)]->bVisible;
282   whiteSecondPrinceVisible = Muster[WHITE][MusterIdx(prince, 1)]->bVisible;
283 
284   if ((!whiteKingVisible ||
285        (!whiteFirstPrinceVisible && !whiteSecondPrinceVisible)) ||
286       (!blackKingVisible ||
287        (!blackFirstPrinceVisible && !blackSecondPrinceVisible)))
288     {
289       return TRUE;
290     }
291 
292   return FALSE;
293 }
294 
295 Global void
FinishGame(const Colour bwWinner)296 FinishGame(const Colour bwWinner)
297 {
298   char winString[19];
299 
300   gamePaused = TRUE;
301   sprintf(winString, "%s player wins!",
302           (bwWinner == BLACK) ? "Black" : "White");
303 
304   XtSetSensitive(firstGFX->undo, FALSE);
305   Err3Dc(firstGFX, winString, TRUE);
306 
307   if (secondGFX != NULL)
308     {
309       XtSetSensitive(secondGFX->undo, FALSE);
310       Err3Dc(secondGFX, winString, TRUE);
311     }
312 }
313 
314 Global void
PrintMove(const Move * move)315 PrintMove( const Move *move )
316 {
317   char *moveString = NULL;
318 
319   if (move != NULL)
320     moveString = (char *)malloc(26);
321 
322   /* moveString is TRUE only if move != NULL too */
323   if (moveString)
324     {
325       Piece *piece, *enemy;
326       Coord pos;
327 
328       piece = Board[ move->xyzAfter.zLevel]
329                    [ move->xyzAfter.yRank ]
330                    [ move->xyzAfter.xFile ];
331 
332       CHECK( piece != NULL );
333 
334       enemy = Muster[(piece->bwSide == WHITE) ? BLACK : WHITE]
335                     [ MusterIdx(king, 0) ];
336       pos = enemy->xyzPos;
337 
338       sprintf( moveString, "%s %c%c%c to %c%c%c%s",
339               Piece2String( piece ),
340               move->xyzBefore.zLevel + 'X',
341               move->xyzBefore.xFile + 'a',
342               move->xyzBefore.yRank + '1',
343               move->xyzAfter.zLevel + 'X',
344               move->xyzAfter.xFile + 'a',
345               move->xyzAfter.yRank + '1',
346               IsKingChecked( piece->bwSide ) ? " check!" : "");
347 
348       /* Display the move: beep if
349        *  1) the computer or
350        *  2) the other player in a network game
351        *     moved or if
352        *  3) the move resulted in a check
353        * This is now changed so that there's no beep when the computer
354        * moves 'cos it's so fast. */
355       Err3Dc( firstGFX, moveString,
356              (/* (Computer() == bwToMove) || */
357               ( (secondGFX != NULL) && (bwToMove == BLACK) ) ||
358               ( IsKingChecked( piece->bwSide ))) ?
359              TRUE : FALSE );
360       if ( secondGFX != NULL )
361         {
362           Err3Dc( secondGFX, moveString, (bwToMove == WHITE) ?
363                  TRUE : FALSE );
364         }
365 
366       /* I think that this isn't allowed becase the string is
367        * still needed by the label widget but it hasn't caused
368        * any problems so far.. */
369       free(moveString);
370     }
371   else
372     {
373       /* Print something, even if out of memory.. */
374       if ( (Computer() == bwToMove) ||
375           ( (secondGFX != NULL) && (bwToMove == BLACK)))
376         Err3Dc(firstGFX, "Opponent has moved", TRUE);
377       else if ( (secondGFX != NULL) && (bwToMove == WHITE) )
378         Err3Dc(secondGFX, "Opponent has moved", TRUE);
379     }
380 }
381