// These would be more idiomatic C if I converted them to an enum... #define STATE_HUMAN_TO_MOVE 0 #define STATE_ANIMATE_OPENING_MOVE 1 #define STATE_COMPUTER_TO_MOVE 2 #define STATE_NO_PIECES_LEFT_STOP 3 #define STATE_I_CANNOT_MOVE_STOP 4 #define STATE_I_RESIGN_STOP 5 #define STATE_ANOTHER_COMP_TAKE 6 #define STATE_YOU_HAVE_NO_PIECES_STOP 7 #define STATE_YOU_CANNOT_MOVE_STOP 8 #define STATE_ANOTHER_COMP_TAKE_DONE 9 #define STATE_ANOTHER_COMP_TAKE_ANIMATE 10 #define STATE_ANIMATE_HUMAN_MOVE 11 #define STATE_END_HUMAN_MOVE_ANIMATION 12 #define STATE_HUMAN_MUST_TAKE_ANOTHER_TIMEOUT 13 #define STATE_YOU_HAVE_NO_PIECE_ON_TIMEOUT 14 #define STATE_FROM_SQUARE_NOT_EXIST_TIMEOUT 15 #define STATE_TO_SQUARE_NOT_EXIST_TIMEOUT 16 #define STATE_TO_SQUARE_OCCUPIED_TIMEOUT 17 #define STATE_YOU_MUST_TAKE_TIMEOUT 18 #define STATE_NOT_CROWNED_TIMEOUT 19 #define STATE_NO_SUCH_MOVE_TIMEOUT 20 #define STATE_CHOOSE_FIRST_PLAYER 21 #define STATE_NOT_JUMPING_MY_PIECE_TIMEOUT 22 #define STATE_CHUCKLE_TIMEOUT 23 #define STATE_OTHER_PLAYER_STARTS 24 #define STATE_OTHER_PLAYER_STARTS_ON_DRAW 25 #define STATE_HUMAN_TO_MOVE_DRAW_DECLINED 26 #define STATE_OTHER_PLAYER_STARTS_NOW 27 case STATE_CHOOSE_FIRST_PLAYER: if (1==1) { // initial move First_player_was = COMPUTER; if (I == 1) { Animate (11, 42); // ("D6-C5"); } else if (I == 2) { Animate (11, 44); // ("D6-E5"); } else { Animate (10, 44); // ("F6-E5"); } STATE = STATE_ANIMATE_OPENING_MOVE; return; } // STATE_TIMEOUT++; // Now using global Flash instead... I = (int8_t) Random ()&3; // makes initial choice timing-dependent if ((Flash&128)==0) { // if ((STATE_TIMEOUT&128)==0) { Print_Str_d(+90, -120, "THIS IS A PRE-RELEASE\x80"); Print_Str_d(+70, -120, "VERSION FOR CHRISTMAS!\x80"); #ifdef DIAGNOSTICS Print_Str_d(+50, -120, "(SLOW DUE TO DEBUGGING)\x80"); #endif } else { Print_Str_d(+20, -120, "BUTTON 1 YOU START []\x80"); Print_Str_d( +0, -120, "BUTTON 2 I START <>\x80"); Print_Str_d(-30, -120, "BUTTON 3 CANCEL SELECT\x80"); Print_Str_d(-50, -120, "BUTTON 4 SELECT/PLACE\x80"); } Print_Str_d(-100, -120, " GTOAL@GTOAL.COM\x80"); if ((buttons_pressed () & 3)==3) return; // both pressed! if (buttons_pressed () & 1) { First_player_was = HUMAN; STATE = STATE_HUMAN_TO_MOVE; return; } if (buttons_pressed () & 2) { First_player_was = COMPUTER; // computer initial move if (I == 1) { Animate (11, 42); // ("D6-C5"); } else if (I == 2) { Animate (11, 44); // ("D6-E5"); } else { Animate (10, 44); // ("F6-E5"); } STATE = STATE_ANIMATE_OPENING_MOVE; return; } return; case STATE_HUMAN_TO_MOVE: // OR... use buttons to cycle through valid pieces... left/right/cancel/select? // or... 1-button interface as options are cycled through? // the two lines below *should* have worked for both analog and digital, but they don't #ifdef PIZERO // this is far too fast on Vide. Cursor_Y += Vec_Joy_1_Y; Cursor_X += Vec_Joy_1_X; #else Cursor_Y += (Vec_Joy_1_Y >> (int8_t)6); Cursor_X += (Vec_Joy_1_X >> (int8_t)6); #endif // Update coords of current square // Determine if updating From or To fields if (REPLY (3) != '-') { REPLY (1) = (char) ((Cursor_X) >> 5) + 4 + 'A'; REPLY (2) = (char) ((Cursor_Y) >> 5) + 4 + '1'; OLDPOS = position_of (REPLY (1), REPLY (2)); REPLY (3) = 0x80; } else { REPLY (4) = (char) ((Cursor_X) >> 5) + 4 + 'A'; REPLY (5) = (char) ((Cursor_Y) >> 5) + 4 + '1'; NEWPOS = position_of (REPLY (4), REPLY (5)); if (REPLY(1)==REPLY(4) && REPLY(2)==REPLY(5)) { REPLY (4) = 0x80; } else { REPLY (6) = 0x80; } } Print_Str_d(120, -120, &REPLY (1)); Reset0Ref (); Intensity_a ((unsigned int) (((Flash & 0x20)) ? 40 : 127)); Display_Piece ((((Cursor_X) >> 5) + 0) * 20, (((Cursor_Y) >> 5) + 0) * 20, 4 /*BOX*/); if (button_1_3_held ()) { // Is this piece highlighted? If so, cancel highlight REPLY (3) = 0x80; Cancel_Request += 1; // Implement a timeout on holding B3 for 256 cycles. if (Cancel_Request == 0) { for (;;) { // This is the only place we don't use the state machine Wait_Recal (); Flash++; Joy_Digital (); Read_Btns (); Reset0Ref (); Intensity_7F (); set_scale (BIG_SCALE); Dim_Board(); Intensity_7F(); Reset0Ref(); Print_Str_d( 0, -120, "BUTTON 1 YOU RESIGN\x80"); Reset0Ref(); Print_Str_d(-20, -120, "BUTTON 2 OFFER A DRAW\x80"); // Don't put any options on button 3 since it is still being pressed. Reset0Ref(); Print_Str_d(-40, -120, "BUTTON 4 RESUME PLAY\x80"); // minor bug: offer draw, computer returns silently to play. // but offer a second time and the computer responds, usually to decline. if (button_1_1_held ()) { STATE = STATE_OTHER_PLAYER_STARTS; return; } if (button_1_2_held ()) { if ((MAX(1) < 0) || (COMP_NUM <= 2 && COMP_NUM <= OPP_NUM && COMP_NUMC <= OPP_NUMC)) { // We don't have the RAM to detect loops - there are several positions // where computer has overpowering pieces but doesn't see far enough ahead // to force win, so gets in a loop. No way out other than for the human // to resign, even though the human is forcing a draw... // Accept draw STATE = STATE_OTHER_PLAYER_STARTS_ON_DRAW; } else { // Decline draw, return to game (no message for now) STATE = STATE_HUMAN_TO_MOVE_DRAW_DECLINED; } return; } if (button_1_4_held ()) { // return to game return; } } } } else Cancel_Request = 0; if (button_1_4_held ()) { // if we are over a piece // determine if this is an eligible piece to move. if (REPLY (3) != '-') { REPLY (3) = '-'; REPLY (4) = 0x80; #ifdef NEVER // TO DO:See IMP version, update this section if necessary. if (valid_pos (OLDPOS) != -1) { // Select this as our piece to move... REPLY (3) = '-'; REPLY (4) = 0x80; } else { // don't accept. maybe print a message, and/or beep REPLY (3) = 0x80; } #endif } else { // second part of move REPLY (4) = (char) ((Cursor_X) >> 5) + 4 + 'A'; REPLY (5) = (char) ((Cursor_Y) >> 5) + 4 + '1'; NEWPOS = position_of (REPLY (4), REPLY (5)); if (NEWPOS == OLDPOS) { // just a debounce on the selected piece, ignore REPLY (4) = 0x80; } else { // destination selected! REPLY (6) = 0x80; // and perform the move! { int Found = FALSE; for (P = 1; P <= OPP_NUM; P++) { if (OLDPOS == OPP_POS (P)) { Found = TRUE; break; } } if (!Found) { REPLY (3) = 0x80; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_YOU_HAVE_NO_PIECE_ON_TIMEOUT; // these won't work as part of second take return; } } PIECE = P; DIF = NEWPOS - OLDPOS; MODIF = MOD8 (DIF); // ... more checks missing here ... or may be some duplicated if (valid_pos (OLDPOS) == -1) { REPLY (3) = 0x80; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_FROM_SQUARE_NOT_EXIST_TIMEOUT; // these won't work as part of second take return; } M = valid_pos (NEWPOS); if (M == -1) { REPLY (5) = 0x80; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_TO_SQUARE_NOT_EXIST_TIMEOUT; // these won't work as part of second take return; } if (M == 1) { REPLY (6) = 0x80; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_TO_SQUARE_OCCUPIED_TIMEOUT; // these won't work as part of second take return; } for (P = 1; P <= OPP_NUM; P++) { if (OLDPOS == OPP_POS (P)) break; } if (OLDPOS != OPP_POS (P)) { REPLY (3) = 0x80; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_YOU_HAVE_NO_PIECE_ON_TIMEOUT; // re-using this error message return; } PIECE = P; DIF = NEWPOS - OLDPOS; MODIF = MOD8 (DIF); if (MODIF < 12 && expected_move > 0) { REPLY (3) = 0x80; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_YOU_MUST_TAKE_TIMEOUT; return; } #ifdef NEVER // Now handled elsewhere. We hope. TO DO: check against IMP version and update if necessary // Hopefully something like this is in the human followup jump code. if (MODIF < 12 && MORE == 'M') { p ("That's not part of a multiple jump move\n"); goto say_please_and_goto_read_move; } #endif if (DIF < 0 && CKTYPE(OPP_TYPE (PIECE)) != CROWN) { STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_NOT_CROWNED_TIMEOUT; return; } if (!(MODIF == 11 || MODIF == 9 || MODIF == 22 || MODIF == 18)) { STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_NO_SUCH_MOVE_TIMEOUT; return; } JMAN = 0; if (MODIF > 11) { COMPOS = OLDPOS + (DIF / 2); // (DIF/2) used to be stored in JUMP but it's not needed elsewhere so saving a variable. for (I = 1; I <= COMP_NUM; I++) { if (COMPOS == COMP_POS (I)) { JMAN = I; break; } } if (JMAN == 0) { REPLY (3) = 0x80; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_NOT_JUMPING_MY_PIECE_TIMEOUT; return; } } // make_move (DIF, PIECE, &JMAN, OPPp, COMPp); // Apply human's move Animate (PIECE + 12, OPP_POS (PIECE) + DIF); STATE = STATE_ANIMATE_HUMAN_MOVE; Display_board (0); REPLY(1) = REPLY (3) = 0x80; // remove the coords from the display return; } } } Display_board (0); return; case STATE_ANIMATE_HUMAN_MOVE: Display_board ( /* ignoring */ STATE_PieceNum); Display_Piece (STATE_FromX, STATE_FromY, STATE_icon_style); // Move P one step closer to target // When it gets there, move on to next state. if (STATE_FromX == STATE_ToX && STATE_FromY == STATE_ToY) { //COMP_POS (STATE_PieceNum) = STATE_ToPos; -- This was the stupid bug that caused TYPE corruption! STATE = STATE_END_HUMAN_MOVE_ANIMATION; } else { STATE_FromX += (STATE_ToX > STATE_FromX) ? 1 : -1; STATE_FromY += (STATE_ToY > STATE_FromY) ? 1 : -1; } return; case STATE_END_HUMAN_MOVE_ANIMATION: // TO DO: IF THIS WAS A TAKE, AND THERE IS ANOTHER TAKE POSSIBLE AT THE // DESTINATION, THEN GET THE NEXT TAKE *WITH THIS PIECE* FROM THE USER. // This should apply the first part of the multiple jump, but I'm // getting an error that there is no piece on the square when I // attempt to do the second move: - not sure if a real problem // or a side-effect of the set piece hack for testing. make_move (DIF, PIECE, &JMAN, &OPP, &COMP); if (JMAN <= 0) { // No follow-on moves needed STATE = STATE_COMPUTER_TO_MOVE; return; } // we just jumped a man. Are there any follow-up jumps we must do? #ifdef NEVER if (MORE == 'M') { LASTPOS = NEWPOS; read_again: prompt ("and:"); for (I = 1; I <= 8; I++) { read_symbol (&REPLY (I)); if (REPLY (I) >= 'a') REPLY (I) -= 32; if (REPLY (I) == NL) break; } if (REPLY (1) == '.') goto comp_move; do { REPLY (I) = ' '; I += 1; } while (I != 9); OLDPOS = position_of (REPLY (1), REPLY (2)); NEWPOS = position_of (REPLY (4), REPLY (5)); DIF = NEWPOS - OLDPOS; DIF = MOD8 (DIF); if (DIF > 11 && OLDPOS == LASTPOS) goto TRANS; // below was a problem if user typed "<move>," and did not have a followup move! // but maybe that's what the '.' option was for? p ("That's not part of a multiple jump\nPlease re-type that part\n"); goto read_again; } #endif // position of piece that just jumped is now: STATE_ToPos // TO DO: recalc OLDPOS and NEWPOS here? OK, doing OLDPOS below now... for (M = 0; M <= 7; M++) { // this could be total bullshit... check it very carefully... // got it to work by trial and error assert(13 <= STATE_PieceNum && STATE_PieceNum <= 24); if (valid_move(M, STATE_PieceNum-12, &OPP, &COMP) > 0) { REPLY(1)=REPLY(4); REPLY(2)=REPLY(5); REPLY(3)='-'; REPLY(4)=0x80; OLDPOS = position_of (REPLY (1), REPLY (2)); STATE = STATE_HUMAN_MUST_TAKE_ANOTHER_TIMEOUT; return; } } STATE = STATE_COMPUTER_TO_MOVE; return; case STATE_ANIMATE_OPENING_MOVE: // might be able to merge this with the one below... Display_board ( /* ignoring */ STATE_PieceNum); Display_Piece (STATE_FromX, STATE_FromY, STATE_icon_style); // Move P one step closer to STATE_NewPos. // When it gets there, move on to next state. if (STATE_FromX == STATE_ToX && STATE_FromY == STATE_ToY) { COMP_POS (STATE_PieceNum) = STATE_ToPos; // Done in make_move in all other circumstances... STATE = STATE_HUMAN_TO_MOVE; } else { STATE_FromX += (STATE_ToX > STATE_FromX) ? 1 : -1; STATE_FromY += (STATE_ToY > STATE_FromY) ? 1 : -1; } return; case STATE_COMPUTER_TO_MOVE: // COMPUTER MAKES MOVE if (!COMP_NUM) { STATE = STATE_NO_PIECES_LEFT_STOP; return; } if (pending_move (&COMP, &OPP) == -1) { STATE = STATE_I_CANNOT_MOVE_STOP; return; } // compute move // If in end game then increase search. I = COMP_NUM + OPP_NUM; if (I <= 6) search_limit = 4; // FIND best_move. NODES = 0; SP = 0; VALUEB = try_possible_moves ( /* Ply */ 1, /* Depth */ 1, &COMP, &OPP); if (VALUEB <= -(INFTY - 10L)) { // I wonder if 10 is related to max recursion depth (12) ? STATE = STATE_I_RESIGN_STOP; return; } I = 0; STATE = STATE_ANOTHER_COMP_TAKE; return; case STATE_ANOTHER_COMP_TAKE: // another_computer_take: // TROUT adds post-jump followup moves to initial move string. TROUT ( /* old pos */ COMP_POS (best_piece), /* new pos */ COMP_POS (best_piece) + MOVE (best_move), &I); // delay the internal state change until after the graphics have been updated... // make_move (MOVE (best_move), best_piece, &best_take, COMPp, OPPp); Animate (best_piece, COMP_POS (best_piece) + MOVE (best_move)); STATE = STATE_ANOTHER_COMP_TAKE_ANIMATE; return; case STATE_ANOTHER_COMP_TAKE_ANIMATE: Display_board ( /* ignoring */ STATE_PieceNum); Display_Piece (STATE_FromX, STATE_FromY, STATE_icon_style); // Move P one step closer to STATE_NewPos. // When it gets there, move on to next state. if (STATE_FromX == STATE_ToX && STATE_FromY == STATE_ToY) { // COMP_POS(STATE_PieceNum) = STATE_ToPos; STATE = STATE_ANOTHER_COMP_TAKE_DONE; } else { STATE_FromX += (STATE_ToX > STATE_FromX) ? 1 : -1; STATE_FromY += (STATE_ToY > STATE_FromY) ? 1 : -1; // A safer statement would be: // STATE_FromY += (STATE_ToY > STATE_FromY) ? 1 : ((STATE_ToY < STATE_FromY) ? -1 : 0); // however because moves are on an exact 45 degree diagonal, the 0 case never happens! // Beware however if we ever change the aspect ratio of the board... } return; case STATE_ANOTHER_COMP_TAKE_DONE: make_move (MOVE (best_move), best_piece, &best_take, &COMP, &OPP); if (best_take > 0) { // multiple takes required? for (best_move = 1; best_move <= 7; best_move += 2) { best_take = valid_move (best_move, best_piece, &COMP, &OPP); if (best_take > 0) { STATE = STATE_ANOTHER_COMP_TAKE; return; // goto another_computer_take; } } } if (!OPP_NUM) { STATE = STATE_YOU_HAVE_NO_PIECES_STOP; return; } expected_move = pending_move (&OPP, &COMP); if (expected_move == -1) { STATE = STATE_YOU_CANNOT_MOVE_STOP; return; } if (VALUEB >= (INFTY - 10) && !a_win) { a_win = TRUE; STATE_TIMEOUT = 0; /* (Was -1) */ STATE = STATE_CHUCKLE_TIMEOUT; } else STATE = STATE_HUMAN_TO_MOVE; return; case STATE_OTHER_PLAYER_STARTS_NOW: if (First_player_was == HUMAN) { Reinitialise_nearly_everything(); I = (int8_t)Random()&3; if (I == 1) { Animate (11, 42); // ("D6-C5"); } else if (I == 2) { Animate (11, 44); // ("D6-E5"); } else { Animate (10, 44); // ("F6-E5"); } STATE = STATE_ANIMATE_OPENING_MOVE; } else { Reinitialise_nearly_everything(); STATE = STATE_HUMAN_TO_MOVE; } First_player_was = (HUMAN+COMPUTER)-First_player_was; return;