Source code for /engineering/REVERSEM/REVERSEM.CPPOriginal file REVERSEM.CPP
   1 /*--------------------------------------------------------------------------
   2 
   3   REVERSEM :
   4 
   5   UNIT     : Main unit
   6 
   7   COPYRIGHT (C) 1994 Erich P. Gatejen  ALL RIGHTS RESERVED
   8   This is Freeware.  You may distribute it as you wish.  However, you may not
   9   distribute a modified version without my consent.
  10 
  11   Feel free to cut and paste any algorthm.
  12 
  13   NOTE: XTILE is (C) 1992, 1994 by myself.  It is NOT freeware.  You may use
  14 	it for personal projects, but otherwise, it is not to be distributed
  15 	outside this REVERSEM package.  (Contact me if you wish to do
  16 	otherwise)
  17 
  18 ---------------------------------------------------------------------------*/
  19 
  20 // ------------------------------------------------------------------------
  21 // --- INCLUDES
  22 // ------------------------------------------------------------------------
  23 #include<stdlib.h>
  24 #include<stdio.h>
  25 extern "C" {
  26    #include "xtile21!.h"
  27 };
  28 #include<conio.h>
  29 #include<dos.h>
  30 #include<alloc.h>
  31 #include"heap.hpp"
  32 #include"reversem.hpp"
  33 #include"eval.hpp"
  34 #include"spots.hpp"
  35 #include"board.hpp"
  36 #include"graphics.hpp"
  37 
  38 // ------------------------------------------------------------------------
  39 // --- DEFINITIONS
  40 // ------------------------------------------------------------------------
  41 
  42 // --- In game status
  43 enum  STATUS { STATUS_CONT = 0, STATUS_END, STATUS_DONE, STATUS_NEW };
  44 
  45 // --- Timing definitions  (all in miliseconds)
  46 #define  BUTTON_TIME	500
  47 
  48 
  49 // ------------------------------------------------------------------------
  50 // --- CLASSES
  51 // ------------------------------------------------------------------------
  52 
  53 // --- This prototype is need here!
  54 void *getnew( void );
  55 
  56 // --- MMNode.  A minimax node --------------------------------------------
  57 class  MMNode: public Board {
  58 
  59    MMNode	*Children[MAX_CHILDREN];
  60 
  61    int		NumberChildren;
  62 
  63    SpotStates   Owner;
  64 
  65    static       unsigned int	CurrentPly;
  66    static	Go		AGoTo;
  67 
  68  public:
  69 
  70    // Constructors
  71    MMNode( Board  *OldBoard, SpotStates  Who   	     ) : Board( OldBoard ) {
  72       Owner = Who;
  73    };
  74 
  75    MMNode( Spot   TheSpots[BOARDXSIZE][BOARDYSIZE],
  76 	   SpotStates Who		             ) : Board( TheSpots ) {
  77       Owner = Who;
  78    };
  79 
  80    MMNode( Spot   TheSpots[BOARDXSIZE][BOARDYSIZE],
  81 	   Go     GoTo,     SpotStates Who	     ) : Board( TheSpots ) {
  82       // This will make the move that creates this board
  83       Owner = Who;
  84       DoGo( GoTo, Owner );
  85    };
  86    ~MMNode( void ) {};
  87 
  88    void  SetPly( unsigned int Ply ) { CurrentPly = Ply; };
  89 
  90    // Enter the tree,  call for root only
  91    heuristic  TREE( SpotStates    ThePlayer,
  92 		    Go	         &TheMove    );
  93 
  94    // Enter the tree,  call all non-root nodes
  95    heuristic  TREE( heuristic     UseThresh,  // For A/B pruning
  96 		    heuristic     PassThresh,
  97 		    BOOLEAN	  Pass  = FALSE  // So we can tell an end move
  98 		  );
  99 
 100    // DAMN THING IS DRIVING ME NUTS!!!  Overload new.
 101    void * operator new( size_t size ) { return( getnew() ); };
 102    void operator delete( void *p  ) {};  // Don't delete anything.
 103 					 // The heap will handle it
 104    // OK, lesson learned.  DO NOT recursively delete!!!
 105    // and don't use Borlands heap.  It blows up EVERY time!!!
 106 };
 107 
 108 
 109 
 110 // ------------------------------------------------------------------------
 111 // --- DATA DECLARATIONS
 112 // ------------------------------------------------------------------------
 113 
 114 // --- Static members of MMNode
 115    unsigned int	MMNode::CurrentPly;
 116    Go		MMNode::AGoTo;
 117 
 118 // --- Global variables
 119 
 120    // Plys for each Brain levels
 121    unsigned int  MaxPlys4Level[EXPERIMENTAL+1] = {
 122 
 123       2, // Simpleton
 124       3, // Dullard
 125       3, // Average
 126       4, // Swift
 127       5, // Genius           // <- 5 may be too high, I'm not sure.
 128       6, // Experimental
 129 
 130       // NOTE: With the heap limit set the way it is, any plys over
 131       // 5 may cause it to actually get stupider <sic>.
 132 
 133    };
 134 
 135    // Boards
 136    Board	MasterBoard;
 137 
 138    // Default the brain setting at the lowest level.
 139    BRAIN	BrainSetting = SIMPLETON;
 140 
 141 // --- Define the graphics control item
 142    GraphicsControl		GCI;
 143 
 144 // --- Give use a bigger stack!!!
 145    extern unsigned _stklen = ( 16 * 1024 );
 146 
 147 // --- Define the heap for the minimax tree.
 148    Heap   MMHeap( sizeof( class MMNode ) );
 149 
 150 // --- Running score
 151    int	  RunningScore = 0;
 152 
 153 // --- Flag show heuristic
 154    BOOLEAN  ShowH = FALSE;
 155 
 156 
 157 // ------------------------------------------------------------------------
 158 // --- FUNCTIONS PROTOTYPES
 159 // ------------------------------------------------------------------------
 160 void  ComputerMoves( void );
 161 void  HelpHuman( void );
 162 heuristic  MINiMAX( SpotStates    ThePlayer,
 163 		    Go	         &TheMove,
 164 		    Board	  TheBoard     );
 165 
 166 
 167 // -----------------------------------------------------------------------
 168 // --- FUNCTIONS
 169 // -----------------------------------------------------------------------
 170 
 171 // -- Heap allocate fof the overloaded new -------------------------------
 172 void *getnew( void ) {
 173       return ( MMHeap.Allocate() );
 174 };
 175 
 176 // -- Computer moves -----------------------------------------------------
 177 void  ComputerMoves( void ) {
 178 
 179    Go		AGo;
 180 
 181    heuristic	H;
 182 
 183    char Buffer[40];
 184 
 185    // Say I'm thinking
 186    GCI.Me.ShowPicture( PIC_THINKING );
 187    GCI.Me.SayText( "OK.  GIVE ME A 'SEC.", NULL, NULL );
 188    delay( 200 );
 189 
 190    // Amove exists.  Generate the move we want
 191    MasterBoard.BrainIs( BrainSetting );
 192    H = MINiMAX(   SPOT_BLACK,  AGo, MasterBoard    );
 193 
 194    // OK.  Show where I wanna go
 195    GCI.Me.ShowPicture( PIC_LOOKRIGHT );
 196    GCI.Me.SayText( "I WILL GO HERE", NULL, NULL );
 197    GCI.Here( AGo );
 198    delay( 1000 );
 199 
 200    // We have a move.  Apply it and show it.
 201    MasterBoard.DoGo( AGo, SPOT_BLACK );
 202    GCI.ShowBoard( MasterBoard );
 203 
 204 
 205    // ---  OK.  Respond to the move.
 206 
 207    // If on, show H
 208    if ( ShowH ) {
 209       itoa( H, Buffer, 10);
 210       XSet_Box( 112, 235, 80, 5, 0 );
 211       XString4_C( 116, 235, 15, Buffer );
 212    };
 213 
 214    // Show my reaction
 215    if ( H > A_EXCELLENT ) {
 216       GCI.Me.ShowPicture( PIC_HALFSMILE );
 217       GCI.Me.SayText( "HE HE HE HE", "NOT LOOKING TO BAD", "FOR ME HERE" );
 218    } else
 219    if ( H > A_GOOD ) {
 220       GCI.Me.ShowPicture( PIC_HALFSMILE );
 221       GCI.Me.SayText( "WELL, NOT BAD.", NULL, NULL );
 222    } else
 223    if ( H > A_OK ) {
 224       GCI.Me.ShowPicture( PIC_LOOKRIGHT );
 225       GCI.Me.SayText( "HMMMMM...", "I'M NOT SURE WHAT TO", "THINK HERE." );
 226    } else
 227    if ( H > A_UNKNOWN ) {
 228       GCI.Me.ShowPicture( PIC_CALM );
 229       GCI.Me.SayText( "WELL.  THIS COULD GO", "EITHER WAY.", NULL );
 230    } else
 231    if ( H > A_UNGOOD ) {
 232       GCI.Me.ShowPicture( PIC_CURIOUSFROWN );
 233       GCI.Me.SayText( "UGG.", "THIS DOESN'T LOOK", "PROMISING" );
 234    } else
 235    if ( H > A_BAD ) {
 236       GCI.Me.ShowPicture( PIC_LOOKRIGHTC );
 237       GCI.Me.SayText( "OH.", "THIS IS NOT GOOD!", NULL );
 238    } else {
 239       GCI.Me.ShowPicture( PIC_FROWN );
 240       GCI.Me.SayText( "I'M DEAD.", NULL, NULL );
 241    };
 242 
 243 };
 244 
 245 // -- Help human with a move -----------------------------------------------
 246 void  HelpHuman( void ) {
 247 
 248    Go		AGo;
 249 
 250    char Buffer[40];
 251 
 252    // Say I'm thinking
 253    GCI.Me.ShowPicture( PIC_THINKING );
 254    GCI.Me.SayText( "OK.  GIVE ME A 'SEC.", NULL, NULL );
 255    delay( 200 );
 256 
 257    // Amove exists.  Generate the move we want
 258    MasterBoard.BrainIs( BrainSetting );
 259    MINiMAX(   SPOT_WHITE,  AGo, MasterBoard    );
 260 
 261    // OK.  Show where I wanna go
 262    GCI.Me.ShowPicture( PIC_IDEA );
 263    GCI.Me.SayText( "I HAVE AN IDEA", NULL, NULL );
 264    delay( 1000 );
 265 
 266    // OK.  Show where I wanna go
 267    GCI.Me.ShowPicture( PIC_LOOKRIGHT );
 268    GCI.Me.SayText( "I WOULD GO HERE", NULL, NULL );
 269    GCI.Here( AGo );
 270    delay( 1000 );
 271 
 272 
 273 
 274 };
 275 
 276 
 277 // -- Handle the end of a game ---------------------------------------------
 278 void  EndGame( void ) {
 279 
 280    int 	PlayerCount;
 281 
 282    // Count the players points and determine a win or loss
 283    PlayerCount = MasterBoard.Count( SPOT_WHITE );
 284 
 285    if ( PlayerCount == 0 ) {
 286 
 287       // A draw
 288       GCI.Me.ShowPicture( PIC_CALM );
 289       GCI.Me.SayText( "HEY, HOW DID THAT", "HAPPENED.", "WE TIED!" );
 290 
 291    } else if ( PlayerCount < 0 ) {
 292 
 293       // A loss
 294       GCI.Me.ShowPicture( PIC_VICTORY );
 295       GCI.Me.SayText( "HA HA HA HA HA", "I WIN!", NULL );
 296 
 297 
 298    } else {
 299 
 300       // A win
 301       GCI.Me.ShowPicture( PIC_WHATUPSET );
 302       GCI.Me.SayText( "HEY!!!", "HOW DID YOU DO THAT.", "YOU WIN" );
 303 
 304    }
 305 
 306    RunningScore += PlayerCount;
 307    GCI.Score( RunningScore );
 308 
 309 };
 310 
 311 // -- Minimax function ------------------------------------------------------
 312 heuristic  MINiMAX( SpotStates    ThePlayer,
 313 		    Go	         &TheMove,
 314 		    Board	  TheBoard     ) {
 315 
 316    int  	X, Y;
 317 
 318    heuristic	H;
 319 
 320    register int  Step;
 321 
 322    heuristic	PassThresh = VERY_BAD_THING;
 323    heuristic    UseThresh  = VERY_GOOD_THING;
 324 
 325    Go		ChildGoTo[MAX_CHILDREN];
 326    heuristic    ChildValue[MAX_CHILDREN];
 327    MMNode	*Children[MAX_CHILDREN];
 328    unsigned int NumberChildren;
 329 
 330 
 331    // OK, do the drudgery.  Make the children..  For this player
 332    // Only here will we have to remeber the moves.
 333    NumberChildren = 0;
 334    for ( X = 0; X < BOARDXSIZE; X++ ) {
 335       for ( Y = 0; Y < BOARDYSIZE; Y++ ) {
 336 
 337 	 ChildGoTo[NumberChildren].XIs( X );
 338 	 ChildGoTo[NumberChildren].YIs( Y );
 339 
 340 	 if ( TheBoard.ValidGo( ChildGoTo[NumberChildren], ThePlayer ) ) {
 341 
 342 	    // ---- Found a child.  Build 'em ----
 343 
 344 	    // Reset the Heap
 345 	    MMHeap.Reset();
 346 
 347 	    Children[NumberChildren] =
 348 	       new MMNode( TheBoard.Spots, ChildGoTo[NumberChildren],
 349 			   ThePlayer					 );
 350 
 351 	    // If it is a NULL pointer, we are not getting anymore
 352 	    if ( Children[NumberChildren] == NULL ) {
 353 	       X = BOARDXSIZE;  // So it breaks out of both 'for's
 354 	       break;
 355 	    }
 356 
 357 	    Children[NumberChildren]->SetPly( 1 );
 358 
 359 	    // Oh No!  recursion!
 360 	    ChildValue[NumberChildren] =
 361 	       Children[NumberChildren]->TREE( -PassThresh, -UseThresh );
 362 					       // WARNING!!!  ^^^^^^^
 363 	    // Do A/B pruning
 364 	    H = ChildValue[NumberChildren];	// Negate it
 365 
 366 	    H = -H;
 367 	    if ( H > PassThresh ) {
 368 
 369 	       // A better value for the pass threshhold
 370 	       PassThresh = H;
 371 
 372 	    }
 373 
 374 	    // Prepare for another iteration.
 375 	    NumberChildren++;
 376 	    if ( NumberChildren == MAX_CHILDREN ) {
 377 	       X = BOARDXSIZE;
 378 	       break;
 379 	    }
 380 
 381 	 }
 382       }
 383    }
 384 
 385    // Find the best move and value..  and return it
 386    H = ChildValue[0];
 387    X = 0;
 388    for ( Step = 1; Step < NumberChildren; Step++ ) {
 389       if ( ChildValue[Step] > H ) {
 390 	 H = ChildValue[Step];
 391 	 X = Step;
 392       }
 393    };
 394    TheMove = ChildGoTo[X];
 395 
 396    return( H );
 397 
 398 
 399 };
 400 
 401 
 402 
 403 
 404 // ------------------------------------------------------------------------
 405 // --- MEMBERS
 406 // ------------------------------------------------------------------------
 407 
 408 // --- MMNode class ---------------------------------------------------------
 409 
 410 // -- The recursive tree function
 411 heuristic  MMNode::TREE( heuristic     UseThresh,  // For A/B pruning
 412 			 heuristic     PassThresh,
 413 			 BOOLEAN       Pass  	   // So we can tell an end move
 414 		  ) {
 415 
 416    int  	X, Y;
 417    SpotStates   OtherGuy;
 418    heuristic	H;
 419 
 420    register int  Step;
 421 
 422    // Who's the other guy?  (Just flip the owner)
 423    if( Owner == SPOT_BLACK )
 424       OtherGuy = SPOT_WHITE;
 425    else
 426       OtherGuy = SPOT_BLACK;
 427 
 428    // OK, have I reached the end of my plys for the level?
 429    if ( CurrentPly == MaxPlys4Level[BrainLevel] ) {
 430 
 431       // Is is a win or loss situation.
 432       if (Pass) {
 433 
 434 	 // If no more children can be created, then it is
 435 	 for ( X = 0; X < BOARDXSIZE; X++ ) {
 436 	    for ( Y = 0; Y < BOARDYSIZE; Y++ ) {
 437 
 438 	       AGoTo.XIs( X );
 439 	       AGoTo.YIs( Y );
 440 
 441 	       if ( ValidGo( AGoTo, OtherGuy ) )
 442 		  return ( Evaluate(Owner) );
 443 	    }
 444 	 }
 445 
 446 	 // No valid moves.  It is the end of a game.
 447 	 return( WinOrLose(Owner) );
 448 
 449       } // end if pass situation
 450 
 451       // Just evaluate self and unroll recusive call
 452       return( Evaluate(Owner) );
 453 
 454    };
 455 
 456    // OK, do the drudgery.  Make the children
 457    NumberChildren = 0;
 458    for ( X = 0; X < BOARDXSIZE; X++ ) {
 459       for ( Y = 0; Y < BOARDYSIZE; Y++ ) {
 460 
 461 	 AGoTo.XIs( X );
 462 	 AGoTo.YIs( Y );
 463 
 464 	 if ( ValidGo( AGoTo, OtherGuy ) ) {
 465 
 466 	    // Found a child.  Build 'em
 467 	    Children[NumberChildren] = new MMNode( Spots, AGoTo, OtherGuy );
 468 
 469 	    // If it is a NULL pointer, we are not getting anymore
 470 	    if ( Children[NumberChildren] == NULL ) {
 471 	       X = BOARDXSIZE;
 472 	       break;
 473 	    }
 474 
 475 	    // Otherwise, tick up one more ply
 476 	    CurrentPly++;
 477 
 478 	    // Oh No!  recursion!
 479 	    H = Children[NumberChildren]->TREE( -PassThresh, -UseThresh );
 480 
 481 	    // Tack back the ply one
 482 	    CurrentPly--;
 483 
 484 	    // Do A/B pruning
 485 	    H = -H;	// Negate it
 486 	    if ( H > PassThresh ) {
 487 
 488 	       // A better value for the pass threshhold
 489 	       PassThresh = H;
 490 
 491 	    } else
 492 	    if ( PassThresh >= UseThresh ) {
 493 
 494 	       // not a better value.  Stop pursuing this branch
 495 	       return( PassThresh );
 496 
 497 	    };
 498 
 499 	    // Prepare for another iteration.
 500 	    NumberChildren++;
 501 	    if ( NumberChildren == MAX_CHILDREN ) {
 502 	       X = BOARDXSIZE;
 503 	       break;
 504 	    }
 505 
 506 	 }
 507       }
 508    }
 509 
 510    // Ok, if no children, it is a pass.  If this function was called with
 511    // a pass, then it is a game ender.  Evaluate as a winner or loser.
 512    // Else, make a single pass child, and recurse into it
 513    if ( NumberChildren == 0 ) {
 514 
 515       // Account for no children due to lack of memory
 516       if ( Children[NumberChildren] == NULL ) return( Evaluate(Owner) );
 517 
 518       if ( Pass ) {
 519 
 520 	 return( WinOrLose(Owner) );
 521 
 522       } else {
 523 
 524 	 // Build the child if we can.  Else return current board eval
 525 	 Children[NumberChildren] = new MMNode( Spots, OtherGuy );
 526 
 527 	 // Otherwise, tick up one more ply
 528 	 CurrentPly++;
 529 
 530 	 // Oh No!  recursion!
 531 	 PassThresh =
 532 	    Children[NumberChildren]->TREE( -PassThresh, -UseThresh, TRUE );
 533 
 534 	 // Tack back the ply one
 535 	 CurrentPly--;
 536 
 537 	 NumberChildren = 1;
 538 
 539       } // end if pass
 540 
 541    }
 542 
 543    // This joker has explored all it's children.
 544    return( PassThresh );
 545 
 546 };
 547 
 548 // ------------------------------------------------------------------------
 549 // --- PRIMARY GAME ROUND ROUTINE - StartGame
 550 // ------------------------------------------------------------------------
 551 
 552 STATUS  StartGame( SpotStates  WhoStarts ) {
 553 
 554    STATUS	TheStatus = STATUS_CONT;
 555    SpotStates   WhosTurn  = WhoStarts;
 556    USER_ACTION  Command;
 557 
 558    BOOLEAN	Pass = FALSE;
 559    Go		AGo;
 560 
 561    // Reset the master board and show it
 562    MasterBoard.InitBoard2Start();
 563    GCI.ShowBoard( MasterBoard );
 564 
 565    // Keep playing
 566    while( TheStatus == STATUS_CONT ) {
 567 
 568       if ( WhosTurn == SPOT_BLACK ) {
 569 
 570 	 // Computer turn
 571 
 572 	 // Is there a move?
 573 	 if ( !MasterBoard.MoveExists( WhosTurn ) ) {
 574 
 575 	    // No move exists.  If end game, end.  Else pass.
 576 	    if ( Pass ) {
 577 
 578 	       // End game
 579 	       TheStatus = STATUS_END;
 580 	       EndGame();
 581 
 582 	    } else {
 583 
 584 	       // Pass
 585 	       GCI.Me.ShowPicture( PIC_CURIOUSFROWN );
 586 	       GCI.Me.SayText( "HMMM!!!", "LOOKS LIKE I HAVE TO", "PASS" );
 587 	       delay( 1000 );
 588 	       Pass = TRUE;
 589 
 590 	    } // end if game end
 591 
 592 	 } else {
 593 
 594 	    // Clear the pass flag and move
 595 	    Pass = FALSE;
 596 	    ComputerMoves();
 597 
 598 	 }
 599 
 600       } else {
 601 
 602 	 // Player turn
 603 
 604 	 // Is there a move?
 605 	 if ( !MasterBoard.MoveExists( WhosTurn ) ) {
 606 
 607 	    // No move exists.  If end game, end.  Else pass.
 608 	    if ( Pass ) {
 609 
 610 	       // End game
 611 	       TheStatus = STATUS_END;
 612 	       EndGame();
 613 
 614 	    } else {
 615 
 616 	       // Pass
 617 	       GCI.YouMustPass();
 618 	       Pass = TRUE;
 619 
 620 	    } // end if game end
 621 
 622 	 } else {
 623 
 624 	    // Show valid moves
 625 	    GCI.ShowValidMoves( MasterBoard );
 626 
 627 	    // Clear the pass flag
 628 	    Pass = FALSE;
 629 
 630 	    // Loop until the player does something that shifts turns or quits
 631 	    do {
 632 
 633 	       Command = GCI.WaitUser( AGo );
 634 
 635 	       // Switch through actions
 636 	       switch( Command ) {
 637 
 638 		  // Set brain levels
 639 		  case  CLICK_BRAIN1:
 640 				      BrainSetting = SIMPLETON;
 641 				      GCI.BrainSetting( SIMPLETON );
 642 				      break;
 643 		  case  CLICK_BRAIN2:
 644 				      BrainSetting = DULLARD;
 645 				      GCI.BrainSetting( DULLARD );
 646 				      break;
 647 		  case  CLICK_BRAIN3:
 648 				      BrainSetting = AVERAGE;
 649 				      GCI.BrainSetting( AVERAGE );
 650 				      break;
 651 		  case  CLICK_BRAIN4:
 652 				      BrainSetting = SWIFT;
 653 				      GCI.BrainSetting( SWIFT );
 654 				      break;
 655 		  case  CLICK_BRAIN5:
 656 				      BrainSetting = GENIUS;
 657 				      GCI.BrainSetting( GENIUS );
 658 				      break;
 659 		  case  CLICK_BRAIN6:
 660 				      BrainSetting = EXPERIMENTAL;
 661 				      GCI.BrainSetting( EXPERIMENTAL );
 662 				      break;
 663 
 664 		  case  CLICK_HELP:   GCI.PushMainButton( HELP_BUTTON );
 665 				      HelpHuman();
 666 				      GCI.PopMainButton( HELP_BUTTON );
 667 				      break;
 668 
 669 		  case  CLICK_DONE:   GCI.PushMainButton( DONE_BUTTON );
 670 				      GCI.Me.ShowPicture( PIC_ARMSCROSS );
 671 				      GCI.Me.SayText( "CHICKEN!", "ARE YA?", NULL );
 672 				      delay( 1000 );
 673 				      GCI.PopMainButton( DONE_BUTTON );
 674 
 675 				      // End it all
 676 				      if (GCI.YouSure()) {
 677 
 678 					 TheStatus = STATUS_DONE;
 679 					 Command   = CLICK_DONE;
 680 
 681 				      }  else {
 682 
 683 					 GCI.Me.ShowPicture( PIC_CALM);
 684 					 GCI.Me.SayText( "GOOD", "THAT'S BETTER", NULL );
 685 					 delay( 1000 );
 686 
 687 					 // Dummy command so it will
 688 					 // continue allowing user to click
 689 					 Command = CLICK_HELP;
 690 
 691 				      }
 692 				      break;
 693 
 694 		  case  CLICK_NEW :   GCI.PushMainButton( NEW_BUTTON );
 695 				      GCI.Me.ShowPicture( PIC_WHAT );
 696 				      GCI.Me.SayText( "NEW GAME?", "I'M GONNA HAVE TO", "DOCK YOU 100 POINTS" );
 697 				      delay( 1000 );
 698 				      GCI.PopMainButton( NEW_BUTTON );
 699 
 700 				      // End it all
 701 				      if (GCI.YouSure()) {
 702 
 703 					 GCI.Me.ShowPicture( PIC_CALM);
 704 					 GCI.Me.SayText( "OK", "YOU'RE THE BOSS", NULL );
 705 
 706 					 RunningScore -= 100;
 707 					 GCI.Score( RunningScore );
 708 
 709 					 TheStatus = STATUS_NEW;
 710 					 Command   = CLICK_DONE;
 711 
 712 				      }  else {
 713 
 714 					 GCI.Me.ShowPicture( PIC_CALM);
 715 					 GCI.Me.SayText( "GOOD", "THAT'S BETTER", NULL );
 716 					 delay( 1000 );
 717 
 718 				      }
 719 				      break;
 720 
 721 		  case  CLICK_BOARD:
 722 				      if ( MasterBoard.ValidGo(AGo, SPOT_WHITE) ) {
 723 
 724 					 // A valid player move
 725 					 MasterBoard.DoGo( AGo, SPOT_WHITE );
 726 					 GCI.ShowBoard( MasterBoard );
 727 
 728 					 // Break out
 729 					 Command = CLICK_DONE;
 730 				      }
 731 				      break;
 732 
 733 
 734 	       } //end case
 735 
 736 
 737 	    } while( Command != CLICK_DONE );
 738 
 739 	 } // end if move exists
 740 
 741       } // end if player turn
 742 
 743       // Flip turn
 744       if   ( WhosTurn == SPOT_WHITE ) WhosTurn = SPOT_BLACK;
 745       else WhosTurn = SPOT_WHITE;
 746 
 747    } // end while play
 748 
 749    return( TheStatus );
 750 
 751 
 752 };
 753 
 754 
 755 
 756 // ------------------------------------------------------------------------
 757 // --- MAIN
 758 // ------------------------------------------------------------------------
 759 void  main( int  argn,  char  *argc[] ) {
 760 
 761    //                   SORRY!! ^^^ will cause a warning
 762 
 763    USER_ACTION	Command;
 764    BOOLEAN	YouFirst;
 765 
 766    Go		DummyGo;
 767 
 768    STATUS	ReturnStatus;
 769 
 770    randomize();
 771 
 772    // Should we show the heuristic?
 773    if ( argn > 1 ) ShowH = TRUE;
 774 
 775    // First time in, so go ahead and init main screen
 776    GCI.InitMainScreen();
 777 
 778    // Scrub it
 779    ReturnStatus = STATUS_CONT;
 780 
 781    // Loop through user actions
 782    do {
 783 
 784       // Check for a new game request during last game
 785       if ( ReturnStatus == STATUS_NEW ) {
 786 	 Command = CLICK_NEW;
 787 
 788       } else
 789 	 Command = GCI.WaitUser( DummyGo );
 790 
 791 
 792       // Switch through actions
 793       switch( Command ) {
 794 
 795 	 // Set brain levels
 796 	 case  CLICK_BRAIN1:
 797 			     BrainSetting = SIMPLETON;
 798 			     GCI.BrainSetting( SIMPLETON );
 799 			     break;
 800 	 case  CLICK_BRAIN2:
 801 			     BrainSetting = DULLARD;
 802 			     GCI.BrainSetting( DULLARD );
 803 			     break;
 804 	 case  CLICK_BRAIN3:
 805 			     BrainSetting = AVERAGE;
 806 			     GCI.BrainSetting( AVERAGE );
 807 			     break;
 808 	 case  CLICK_BRAIN4:
 809 			     BrainSetting = SWIFT;
 810 			     GCI.BrainSetting( SWIFT );
 811 			     break;
 812 	 case  CLICK_BRAIN5:
 813 			     BrainSetting = GENIUS;
 814 			     GCI.BrainSetting( GENIUS );
 815 			     break;
 816 	 case  CLICK_BRAIN6:
 817 			     BrainSetting = EXPERIMENTAL;
 818 			     GCI.BrainSetting( EXPERIMENTAL );
 819 			     break;
 820 
 821 	 case  CLICK_HELP:   GCI.PushMainButton( HELP_BUTTON );
 822 			     delay( BUTTON_TIME );
 823 			     GCI.PopMainButton( HELP_BUTTON );
 824 			     break;
 825 
 826 	 case  CLICK_NEW:    GCI.PushMainButton( NEW_BUTTON );
 827 			     delay( BUTTON_TIME );
 828 
 829 			     GCI.PopMainButton( NEW_BUTTON );
 830 			     YouFirst = GCI.WhoFirst();
 831 			     if( YouFirst )
 832 				ReturnStatus = StartGame(SPOT_WHITE);
 833 			     else
 834 				ReturnStatus = StartGame(SPOT_BLACK);
 835 
 836 			     // Check for quit
 837 			     if ( ReturnStatus == STATUS_DONE )
 838 				Command = CLICK_DONE;
 839 
 840 			     break;
 841 
 842 	 case  CLICK_DONE:   GCI.PushMainButton( DONE_BUTTON );
 843 			     delay( BUTTON_TIME );
 844 			     GCI.PopMainButton( DONE_BUTTON );
 845 			     break;
 846 
 847 
 848       } //end case
 849 
 850 
 851    } while( Command != CLICK_DONE );
 852 
 853    // Done
 854    GCI.Me.ShowPicture( PIC_BYE );
 855    GCI.Me.SayText( "GOODBYE!", NULL, NULL );
 856    delay( 3000 );
 857 
 858 };
 859 
 860