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