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