//#define DEBUG_SET_POSITION // to avoid long games to reproduce test positions
//#define DIAGNOSTICS 2           // 1: silent checks  2: loud checks

#ifdef PIZERO
#include <stdio.h>
#include <stdlib.h>
#include <pitrex/pitrexio-gpio.h>
#include <vectrex/vectrexInterface.h>
#undef MAX

FILE *logfile = NULL;

#define INFTY 30000
typedef short int16_t;
typedef short INTEGER;
typedef signed char int8_t;
typedef unsigned char uint8_t;

// These procedures approximate the Vectrex (6809) routines as used in Vide
// and allow us to run the same code on 32 bit ARM as we do on the 6809.

// This will let us add more useful diagnostics.

static int8_t current_intensity = 0x7F;
static int8_t current_scale = 0x7f;
static int8_t current_buttons = 0;
static int Pos_X = 0, Pos_Y = 0;

static inline void Wait_Recal(void) {
  v_WaitRecal();
}
static inline void Moveto_d(int8_t y, int8_t x) { // rel
  Pos_X += x*current_scale; Pos_Y += y*current_scale;
}
static inline void Reset0Ref(void) {
  Pos_X = 0; Pos_Y = 0;
}
static inline void Intensity_7F(void) {
  v_setBrightness(current_intensity = 0x7F);
}
static inline void Intensity_a(int8_t A) {
  v_setBrightness(current_intensity = A);
}
static inline void Print_Str_d(int8_t y, int8_t x, char *ends_x80) {
  v_printStringRaster(x+16,y,ends_x80, 64, -7, 0x80);
}

void Draw_VLp(int8_t *list) {
  for (;;) {
    switch (*list) {
    case -1: // linetorel
      v_directDraw32(Pos_X, Pos_Y, Pos_X + list[2]*current_scale, Pos_Y + list[1]*current_scale, current_intensity);
      Pos_X += list[2]*current_scale; Pos_Y += list[1]*current_scale;
      break;
    case 0: // movetorel
      Pos_X += list[2]*current_scale; Pos_Y += list[1]*current_scale;
      break;
    case 1:
      return;
    }
    list += 3;
  }
}

// Not the same algorithm as the Vectrex Random() but doesn't matter for our
// purposes since the calls to Random() are timing-dependent.
uint8_t _x = 12;
uint8_t _a = 34;
uint8_t _b = 56; 
uint8_t _c = 78; 

static inline void initRandom(uint8_t s1, uint8_t s2, uint8_t s3, uint8_t x0) {
  _x = x0;
  _a = s1; _b = s2; _c = s3;
  _x++;
  _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
}

static inline uint8_t Random(void)
{
  _x++; _a = (_a^_c^_x); _b = (_b+_a); _c = ((_c+(_b>>1))^_a);
  return _c;
}

static int8_t Vec_Joy_1_X = 0, Vec_Joy_1_Y = 0;
static inline void Joy_Digital(void) { // must be called on each frame
  v_readJoystick1Digital();
  Vec_Joy_1_X = currentJoy1X;
  Vec_Joy_1_Y = currentJoy1Y;
}

static inline void Read_Btns(void) {
  v_readButtons();
  current_buttons = currentButtonState;
}

static inline int8_t buttons_pressed (void) {
  //static int8_t bit[5] = { 0x88, 0x44, 0x22, 0x11, 0xFF };
  return current_buttons;
}

// Allows for either controller...
static int8_t button_1_1_held (void) {
  return current_buttons & 0x1;
}

static int8_t button_1_2_held (void) {
  return current_buttons & 0x2;
}

static int8_t button_1_3_held (void) {
  return current_buttons & 0x4;
}

static int8_t button_1_4_held (void) {
  return current_buttons & 0x8;
}

static inline void enable_controller_1_x (void) {
}

static inline void enable_controller_1_y (void) {
}

#else

#define INFTY 30000L
typedef long int16_t;
typedef long INTEGER;
typedef int int8_t;
typedef unsigned int uint8_t;

#endif

/*
    This is a manual translation from IMP9 to C of Ken Chisholm's "Draft4" program,
    as originally used for students on EMAS in the 70's.  Ken wrote a learning version
    of Draft4 in the style of Samuel's Checkers, but this version forgoes the learning
    code, while using the weights calculated by that code after years of play. 
    It has very low dynamic memory requirements and runs within the 1K of Vectrex
    RAM

    The original code is at:
        http://history.dcs.ed.ac.uk/archive/apps/emas-games/gamespd_drafts.txt

    Graham Toal, 12/17/2020

    Current bugs: 1) computer piece sometimes gets to row 1 but is not crowned!???

 */

#ifndef PIZERO
#include <vectrex.h>
#include "controller.h"
#endif

#define WHITE_INTENSITY 120
#define BLACK_INTENSITY 70
#define DIM 45

#define BIG_SCALE 0x7F
#define SMALL_SCALE 0x1F

static inline void set_scale (unsigned int s) {
#ifdef PIZERO
  current_scale = s;
#else
  VIA_t1_cnt_lo = s;
#endif
}

static const int8_t white[] = {
  0, 6, 6,
  -1, 0 * 44, 1 * 44,
  -1, 1 * 44, 0 * 44,
  -1, 0 * 44, -1 * 44,
  -1, -1 * 44, 0 * 44,
  1
};

static const int8_t white_crowned[] = {
  0, 6, 6,
  -1, 0 * 44, 1 * 44,
  -1, 1 * 44, 0 * 44,
  -1, 0 * 44, -1 * 44,
  -1, -1 * 44, 0 * 44,
  0, -6, -6,
  -1, 0 * 56, 1 * 56,
  -1, 1 * 56, 0 * 56,
  -1, 0 * 56, -1 * 56,
  -1, -1 * 56, 0 * 56,
  1
};

static const int8_t black[] = {
  0, 56 / 2 - 23, 1 * 56 / 2 + 1,
  -1, 1 * 56 / 2, 1 * 56 / 2,
  -1, 1 * 56 / 2, -1 * 56 / 2,
  -1, -1 * 56 / 2, -1 * 56 / 2,
  -1, -1 * 56 / 2, 1 * 56 / 2,
  1
};

static const int8_t black_crowned[] = {
  0, 56 / 2 - 23, 1 * 56 / 2 + 1,
  -1, 1 * 56 / 2, 1 * 56 / 2,
  -1, 1 * 56 / 2, -1 * 56 / 2,
  -1, -1 * 56 / 2, -1 * 56 / 2,
  -1, -1 * 56 / 2, 1 * 56 / 2,
  0, -4, -2,
  -1, 1 * 72 / 2, 1 * 72 / 2,
  -1, 1 * 72 / 2, -1 * 72 / 2,
  -1, -1 * 72 / 2, -1 * 72 / 2,
  -1, -1 * 72 / 2, 1 * 72 / 2,
  1
};

static const int8_t box[] = {
  0, -10, -11,
  -1, 0 * 80, 1 * 80,
  -1, 1 * 80, 0 * 80,
  -1, 0 * 80, -1 * 80,
  -1, -1 * 80, 0 * 80,
  1
};

static const int8_t *Piece[] = {
  white, black, white_crowned, black_crowned, box
};

// Original code used 32 bit ints and INFTY was a hard-coded 100000
// Reducing to a 16 bit int doesn't seem to have harmed the program.

#ifdef TRUE
#undef TRUE
#undef FALSE
#endif

#define TRUE 1
#define FALSE 0

#define mob_wt 6
#define back_wt 4
#define cent_wt 4
#define adv1_wt 450
#define adv2_wt 50
#define cramp_wt 6

//static int8_t MON = 'Q'; // 'M' or 'Q' at "Your move:" for extra monitoring.

static int8_t a_win = FALSE;
static int8_t search_limit = 3;
static int8_t Cancel_Request = 0;

#define CROWN 1800

static const int8_t BACK1p[5] = { 77, 75, 73, 71, 64 };
#define BACK1(index) BACK1p[(index)-1]

static const int8_t BACK2p[5] = { 0, 2, 4, 6, 13 };
#define BACK2(index) BACK2p[(index)-1]

static const int8_t CENTSQx[8] = { 51, 53, 42, 44, 33, 35, 24, 26 };
#define CENTSQ(index) CENTSQx[index]

static const int8_t MOVEx[8] = { 9, 18, 11, 22, -9, -18, -11, -22 };
#define MOVE(index) MOVEx[index]

#ifdef DIAGNOSTICS

static void SHOW_NUM(char *dest, long num) { // left-aligned
#define show_digit(x) *dest++ = (char)((x)+'0')
  int8_t digit, zeroes;

  // This replaces code that used divide by 10 and modulo 10.  Much faster.

  // handles full 16 bit range of -32768:32767  -  Uses negative numbers to avoid the issue of negating -32768

  if (num >= 0) num = -num; else *dest++ = '-';
  digit = 0;
  zeroes = 1; // CLRing is shorter
  // max 11 add/subtracts...
  if (num <= -20000) { num += 20000; digit += 2; zeroes = 0; }
  if (num <= -10000) { num += 10000; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  digit = 0;
  if (num <= -8000) { num += 8000; digit += 8; zeroes = 0; } else if (num <= -4000) { num += 4000; digit += 4; zeroes = 0; }
  if (num <= -2000) { num += 2000; digit += 2; zeroes = 0; }
  if (num <= -1000) { num += 1000; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  digit = 0;
  if (num <= -800) { num += 800; digit += 8; zeroes = 0; } else if (num <= -400) { num += 400; digit += 4; zeroes = 0; }
  if (num <= -200) { num += 200; digit += 2; zeroes = 0; }
  if (num <= -100) { num += 100; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  digit = 0;
  if (num <= -80) { num += 80; digit += 8; zeroes = 0; } else if (num <= -40) { num += 40; digit += 4; zeroes = 0; }
  if (num <= -20) { num += 20; digit += 2; zeroes = 0; }
  if (num <= -10) { num += 10; digit += 1; zeroes = 0; }
  if (!zeroes) show_digit(digit);
  show_digit((int8_t)-num);
  *dest = 0x80;
}

void assertx(int8_t condition, char *condstr, long line) {
  if (!condition) {
    for (;;) {
     int8_t strlen = 0;
      for (;;) {
        if (condstr[strlen] == '\0') break;
        strlen += 1;
      }
      if (strlen < 5) strlen=5; 
      char buffer[strlen+2];
      strlen=0;
      for (;;) {
        buffer[strlen] = condstr[strlen];
        if ('a' <= buffer[strlen] && buffer[strlen] <= 'z') buffer[strlen] += (char)('A' - 'a');
        if (condstr[strlen] == '\0') break;
        strlen += 1;
      }
      buffer[strlen++] = ' ';buffer[strlen++] = ' ';
      buffer[strlen] = 0x80;
      Wait_Recal ();
      Reset0Ref ();
      Intensity_7F ();
      set_scale (BIG_SCALE);
      Print_Str_d ( 40, -120, "ASSERTION\x80");
      Print_Str_d ( 20, -120, buffer);
      Print_Str_d (  0, -120, "FAILS AT LINE\x80");
      SHOW_NUM(buffer, line); Print_Str_d (-20, -120, buffer);
    }
  }
}
#define assert(test) assertx(test, #test, __LINE__)

long CHECK (long index, char *name, int8_t low, int8_t high, long line) {
  if ((index < low) || (index > high)) {
    //fprintf (stderr, "%s[%d] out of bounds at line %ld.  Declared as %s[%d:%d]\n", name, index, line, name, low, high);
    //exit (1);
    for (;;) {
      int8_t strlen = 0;
      for (;;) {
        if (name[strlen] == '\0') break;
        strlen += 1;
      }
      if (strlen < 5) strlen=5; 
      char buffer[strlen+2];
      strlen=0;
      for (;;) {
        buffer[strlen] = name[strlen];
        if ('a' <= buffer[strlen] && buffer[strlen] <= 'z') buffer[strlen] += (char)('A' - 'a');
        if (name[strlen] == '\0') break;
        strlen += 1;
      }
      buffer[strlen++] = ' ';buffer[strlen++] = ' ';
      buffer[strlen] = 0x80;
      Wait_Recal ();
      Reset0Ref ();
      Intensity_7F ();
      set_scale (BIG_SCALE);
      Print_Str_d ( 40, -120, buffer);
      SHOW_NUM(buffer, (long)index); Print_Str_d ( 20, -120, buffer);
      Print_Str_d (  0, -120, "OUT OF BOUNDS AT LINE\x80");
      SHOW_NUM(buffer, line); Print_Str_d (-20, -120, buffer);
      Print_Str_d (-40, -120, "DECLARED AS\x80");
      SHOW_NUM(buffer, (long)low); Print_Str_d (-60, -120, buffer);
      Print_Str_d (-80, -120, "TO \x80");
      SHOW_NUM(buffer, (long)high); Print_Str_d (-100, -120, buffer);
    }                             // for (main polling loop)

  }
  return index;
}
int16_t CKTYPEx(int16_t val, char *s, int line) {
  if (val != CROWN && val != 1000 && val != 0) {
    if (logfile) fprintf(logfile, "Bad TYPE assignment at line %d - value of %s is %d\n", line, s, (int)val);
    exit(0);
  }
  return val;
}
int16_t CKTYPE2x(int16_t val, char *s, int line, INTEGER num) {
  if (val != CROWN && val != 1000 && val != 0) {
    if (logfile) fprintf(logfile, "Bad TYPE assignment at line %d - value of %s is %d  COMP_TYPE(%d)\n", line, s, (int)val, (int)num);
    exit(0);
  }
  return val;
}
#define CKTYPE(val) CKTYPEx(val, #val, __LINE__)
#define CKTYPE2(val,num) CKTYPE2x(val, #val, __LINE__, num)
#else
#define assert(x) do { } while (0)
#define CHECK(index,name,low,high,line) ((long)(index)) // no arrays have > 127 elements
#define CKTYPE(val) (val)
#endif

#define DECLARE(type,name,low,high) type name##p[(high)-(low)+1]; type *name = &name##p[-(low)]
#define ARRAY(index,type,name,low,high) name##p[CHECK(index,#name,low,high,__LINE__)]

#define MAX_RECURSIVE_DEPTH 12  // ORIGINALLY 12 - try using 6 to save stack space...

// Minimax values, to depth of recursion (max plies). So must be 16 bit.
#ifdef OLDSTYLE
DECLARE (INTEGER, MIN, 1, MAX_RECURSIVE_DEPTH);
#define MIN(index) ARRAY(index, INTEGER, MIN, 1, MAX_RECURSIVE_DEPTH)
DECLARE (INTEGER, MAX, 1, MAX_RECURSIVE_DEPTH);
#define MAX(index) ARRAY(index, INTEGER, MAX, 1, MAX_RECURSIVE_DEPTH)
#else
INTEGER MINp[MAX_RECURSIVE_DEPTH+1];
#define MIN(index) MINp[index]
INTEGER MAXp[MAX_RECURSIVE_DEPTH+1];
#define MAX(index) MAXp[index]
#endif

DECLARE (char, REPLY, 1, 9);
#define REPLY(index) ARRAY(index, char, REPLY, 1, 9)

#define ME 1
#define YOU 2

// Initialisation data for COMP and OPP arrays...

typedef struct Player {
  int8_t  COLOUR;
  int8_t  PLAYERNO;
  int8_t  POS[12];
  INTEGER TYPE[12];
  int8_t  NUM;
  int8_t  NUMC;
} Player;

static const Player PLAYER1 = {
  'W',                          // -1 - colour
  ME,                           // 0 - player no (1 or 2)
#ifdef DEBUG_SET_POSITION
  {62, 42, 55, 11, 0, 0, 0, 0, 0, 0, 0, 0},                                       // 1:12 - location of pieces (CPOS)
  {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000},       // 13:24 - type (eg CROWN) (CTYPE)
  4,                            // 25 - count of pieces (CNUM)
#else
  {77, 75, 73, 71, 64, 66, 62, 60, 57, 55, 53, 51},                               // 1:12 - location of pieces (CPOS)
  {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000},       // 13:24 - type (eg CROWN) (CTYPE)
  12,                           // 25 - count of pieces (CNUM)
#endif
  0                             // 26 - count of crowns (CNUMC)
};
// 1:12 maps to 0:11
#define PLAYER1_POS(P)   PLAYER1.POS[(P)-1]
#define PLAYER1_TYPE(P)  PLAYER1.TYPE[(P)-1]

static const Player PLAYER2 = {
  'B',                          // -1 - colour
  YOU,                          // 0 - player no (1 or 2)
#ifdef DEBUG_SET_POSITION
  {33, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},                                          // 1:12 - location of pieces (OPOS)
  {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000},       // 13:24 - type (eg CROWN) (OTYPE)
  2,
#else
  {0, 2, 4, 6, 13, 11, 15, 17, 20, 22, 24, 26},                                   // 1:12 - location of pieces (OPOS)
  {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000},       // 13:24 - type (eg CROWN) (OTYPE) 
  12,                           // 25 - count of pieces (ONUM)
#endif
  0                             // 26 - count of crowns (ONUMC)
};
#define PLAYER2_POS(P)   PLAYER2.POS[(P)-1]
#define PLAYER2_TYPE(P)  PLAYER2.TYPE[(P)-1]

// These two would be better as structs...
Player COMP;
//DECLARE (INTEGER, COMP, -1, 26);
//#define COMP(index) ARRAY(index, INTEGER, COMP, -1, 26)
#define COMP_COLOUR   COMP.COLOUR
#define COMP_PLAYERNO COMP.PLAYERNO
#define COMP_POS(P)   COMP.POS[CHECK(P,#P,1,12,__LINE__)-1]
#define COMP_TYPE(P)  COMP.TYPE[CHECK(P,#P,1,12,__LINE__)-1]
#define COMP_NUM      COMP.NUM
#define COMP_NUMC     COMP.NUMC

#define COMP__COLOUR   COMPp->COLOUR
#define COMP__PLAYERNO COMPp->PLAYERNO
#define COMP__POS(P)   COMPp->POS[CHECK(P,#P,1,12,__LINE__)-1]
#define COMP__TYPE(P)  COMPp->TYPE[CHECK(P,#P,1,12,__LINE__)-1]
#define COMP__NUM      COMPp->NUM
#define COMP__NUMC     COMPp->NUMC

Player OPP;
//DECLARE (INTEGER, OPP, -1, 26);
//#define OPP(index) ARRAY(index, INTEGER, OPP, -1, 26)

#define OPP_COLOUR   OPP.COLOUR
#define OPP_PLAYERNO OPP.PLAYERNO
#define OPP_POS(P)   OPP.POS[CHECK(P,#P,1,12,__LINE__)-1]
#define OPP_TYPE(P)  OPP.TYPE[CHECK(P,#P,1,12,__LINE__)-1]
#define OPP_NUM      OPP.NUM
#define OPP_NUMC     OPP.NUMC

#define OPP__COLOUR   OPPp->COLOUR
#define OPP__PLAYERNO OPPp->PLAYERNO
#define OPP__POS(P)   OPPp->POS[CHECK(P,#P,1,12,__LINE__)-1]
#define OPP__TYPE(P)  OPPp->TYPE[CHECK(P,#P,1,12,__LINE__)-1]
#define OPP__NUM      OPPp->NUM
#define OPP__NUMC     OPPp->NUMC

#ifdef OLDSTYLE
DECLARE (int8_t, PSTACK, 1, MAX_RECURSIVE_DEPTH);       // piece position take stack
#define PSTACK(index) ARRAY(index, int8_t, PSTACK, 1, MAX_RECURSIVE_DEPTH)
DECLARE (INTEGER, TSTACK, 1, MAX_RECURSIVE_DEPTH);      // piece type stack
#define TSTACK(index) ARRAY(index, INTEGER, TSTACK, 1, MAX_RECURSIVE_DEPTH)
DECLARE (int8_t, NSTACK, 1, MAX_RECURSIVE_DEPTH);       // piece number stack
#define NSTACK(index) ARRAY(index, int8_t, NSTACK, 1, MAX_RECURSIVE_DEPTH)
#else
// waste a byte to simplify code later...
int8_t PSTACKp[MAX_RECURSIVE_DEPTH+1];
#define PSTACK(index) PSTACKp[index]
INTEGER TSTACKp[MAX_RECURSIVE_DEPTH+1];
#define TSTACK(index) TSTACKp[index]
int8_t NSTACKp[MAX_RECURSIVE_DEPTH+1];
#define NSTACK(index) NSTACKp[index]
#endif

static int8_t SP;               // stack pointer
INTEGER VALUEB, NODES;          // Maybe I can be an int8_t too?
int8_t P, M, PIECE, DIF, MODIF, MORE, best_piece, best_move, best_take, JMAN, POS, OLDPOS, NEWPOS, LASTPOS, COMPOS, ply_number, expected_move;
char A, B, C;

#ifdef PIZERO
void DEBUG_EVERYTHING(int line) {
  int8_t P, I, J, POS;
  int8_t BOARDp[80];
#define BOARD(index) BOARDp[(index)+1]
  if (!logfile) return;
  fprintf(logfile, "-- %d -- %ld\n", line, SP);
  for (I = 0; I <= 79; I++) BOARDp[I] = ' ';
  // BOARD is not really needed, can move to board locations with
  // cursor addressing on linux or vector graphics on Vectrex
  for (I = 0; I <= 7; I++) {
    for (J = 0; J <= 6; J += 2) {
      BOARD (10 * I + J + (I & 1)) = '#';
    }
  }
  for (I = 1; I <= 12; I++) {
    if (I <= COMP_NUM) {
      POS = COMP_POS(I);
      BOARD (POS) = (CKTYPE(COMP_TYPE(I)) == CROWN) ? 'X' : 'x';
    }
    if (I <= OPP_NUM) {
      POS = OPP_POS(I);
      BOARD (POS) = (CKTYPE(OPP_TYPE(I)) == CROWN) ? 'O' : 'o';
    }
  }
  fprintf(logfile, "\n  A B C D E F G H");
  for (I = 70; I >= 0; I -= 10) {
    fputc('\n', logfile);
    fputc((char) (I / 10 + '1'), logfile);
    for (J = 0; J <= 7; J++) {
      fputc(' ', logfile);
      fputc((char) BOARD (J + I), logfile);
    }
    fputc(' ', logfile);
    fputc((char) (I / 10 + '1'), logfile);
  }
  fprintf(logfile, "\n  A B C D E F G H\n\n");
			      
  fprintf(logfile, "COMP NUM  %d  NUMC %d\n", COMP_NUM, COMP_NUMC);
  fprintf(logfile, "COMP POS ");
  for (P = 0; P <= 11; P++) {
    fprintf(logfile, "%d ", COMP.POS[P]);
  }
  fprintf(logfile, "\n");
  fprintf(logfile, "COMP TYPE ");
  for (P = 0; P <= 11; P++) {
    fprintf(logfile, "%d ", COMP.TYPE[P]);
  }
  fprintf(logfile, "\n");

  fprintf(logfile, "OPP NUM  %d  NUMC %d\n", OPP_NUM, OPP_NUMC);
  fprintf(logfile, "OPP POS ");
  for (P = 0; P <= 11; P++) {
    fprintf(logfile, "%d ", OPP.POS[P]);
  }
  fprintf(logfile, "\n");
  fprintf(logfile, "OPP TYPE ");
  for (P = 0; P <= 11; P++) {
    fprintf(logfile, "%d ", OPP.TYPE[P]);
  }
  fprintf(logfile, "\n");
  fflush(logfile);
}
#endif

//*   P L A Y I N G   F U N C T I O N S

int8_t valid_pos (int8_t POS) {
int8_t I;
  // on the board?
  I = POS >> 1;
  if (POS < 0 || POS > 77 || I == 4 || I == 14 || I == 24 || I == 34) {
    return -1;
  }
  // square vacant?
  for (I = 1; I <= 12; I++) {
    if ((I <= COMP_NUM && POS == COMP_POS (I)) || (I <= OPP_NUM && POS == OPP_POS (I))) {
      return 1;
    }
  }
  return 0;
}

int8_t valid_move (int8_t M, int8_t P, Player *COMPp, Player *OPPp) {
int8_t CPOS, I, T;

  assert(1 <= P && P <= 12);
  if (CKTYPE(COMP__TYPE (P)) != CROWN) {
    if (COMP__PLAYERNO == 1) {   // player 1
      if ((M & 4) == 0) {
        return -1;              // backward move
      }
    } else {                    // else player 2
      if ((M & 4) != 0) {
        return -1;              // backward move
      }
    }
  }
  CPOS = COMP__POS (P);
  if (valid_pos (CPOS + MOVE (M))) {
    return -1;
  }
  if ((M & 1) == 0) {
    return 0;                   // ordinary move
  }
  if (OPP_NUM == 0) {
    return -1;
  }
  I = CPOS + MOVE (M - 1);      // else take-move
  for (T = 1; T <= OPP__NUM; T++) {
    if (I == OPP__POS (T)) {
      return T;                 // piece to take?
    }
  }
  return -1;
}


// THIS MAY BE THE BUG!  COMP_NUM etc define in terms of COMP, *not* COMPp

int8_t pending_move (Player *COMPp, Player *OPPp) {
int8_t P, M, PMCOMPNUM;         // might be able to reuse COMP_NUM ?

  PMCOMPNUM = COMP__NUM;
  if (!PMCOMPNUM) {
    return -1;                  // no pieces left!!
  }
  assert(1 <= PMCOMPNUM && PMCOMPNUM <= 12);
  for (P = 1; P <= PMCOMPNUM; P++) {
    for (M = 1; M <= 7; M += 2) {
      if (valid_move (M, P, COMPp, OPPp) > 0) {
        return P;               // return piece doing take
      }
    }
  }
  // ordinary moves
  for (P = 1; P <= PMCOMPNUM; P++) {
    for (M = 0; M <= 6; M += 2) {
      if (!valid_move (M, P, COMPp, OPPp)) {
        return 0;               // move ok
      }
    }
  }
  return -1;                    // no moves
}

static inline int8_t CROWNING (int8_t POS, int8_t PLAYER) {  // assumes valid pos
  return ((PLAYER == 1 && POS < 7) || (PLAYER == 2 && POS > 70));
}

static inline INTEGER MODI (INTEGER N) {
  return (N < 0) ? -N : N;
}

static inline int8_t MOD8 (int8_t N) {
  return (N < 0) ? -N : N;
}

INTEGER value_of_side (Player *COMPp, Player *OPPp) {
// bases have already been adjusted
INTEGER V1, V2, V3, V4, V5, V6, V7;
int8_t P, PP, M, MM, MV, ADV1, VM, PLAYER;
INTEGER *CTYPEp;
int8_t *CPOSp;

#define CPOS (*CPOSp)
#define CTYPE (*CTYPEp)

  PLAYER = COMP__PLAYERNO;

  // back control
  V2 = 0;
  if (!OPP__NUMC) {              // OPP has no crowns yet
    if (PLAYER == 1) {          // player 1
      for (P = 1; P <= 5; P++) {
        if (COMP__POS (P) == BACK1 (P))
          V2 += back_wt;
      }
    } else {                    // player 2
      for (P = 1; P <= 5; P++) {
        if (COMP__POS (P) == BACK2 (P))
          V2 += back_wt;
      }
    }
  }
  V1 = V3 = V4 = V5 = V6 = V7 = 0;
  for (P = 1; P <= COMP__NUM; P++) {
    CPOSp = &COMP__POS (P);
    CTYPEp = &COMP__TYPE (P);
    ADV1 = 0;

    // add up piece values
    V1 += CKTYPE(CTYPE);
    for (M = 0; M <= 7; M++) {
      VM = valid_move (M, P, COMPp, OPPp);
      MV = MOVE (M);
      // centre control
      if (CPOS == CENTSQ (M)) {
        V4 += cent_wt;
        if (CKTYPE(CTYPE) == CROWN)
          V4 += cent_wt + 1;
      }
      
      if (VM >= 0) {            // mobility
        V3 += mob_wt;
        if (VM > 0)
          V3 += mob_wt;
        if (CKTYPE(CTYPE) != CROWN) {
          // advancement 1
          if ((ADV1 == 0) && CROWNING (CPOS + MV, PLAYER)) {
            V5 += adv1_wt;
            ADV1 = 1;
          }
          // advancement 2
          if (VM == 0) {            // ignore jumps to crown
            CPOS += MV;
            for (MM = 0; MM <= 6; MM += 2) {
              if (CROWNING (CPOS + MOVE (MM), PLAYER)
                  && valid_move (MM, P, COMPp, OPPp) >= 0) {
                V6 += adv2_wt;
                break;
              }
            }
            CPOS -= MV;
          }
        }
        // cramp
        if ((VM == 0) && CKTYPE(CTYPE) == CROWN && OPP__NUM > 0) {
          CPOS += MV;
          for (PP = 1; PP <= OPP__NUM; PP++) {
            for (MM = 1; MM <= 7; MM += 2) {
              if (valid_move (MM, PP, OPPp, COMPp) >= 0) {
                V5 -= cramp_wt;
                break;
              }
            }
          }
          CPOS -= MV;
        }
      }
    }
  }
  return V1 + V2 + V3 + V4 + V5 + V6 + V7;
#undef FRAME
#undef CPOS
#undef CTYPE
}

INTEGER value_of_pos (Player *COMPp, Player *OPPp) {
INTEGER VALUE;

  if (pending_move (COMPp, OPPp) >= 0) {
    VALUE = value_of_side (COMPp, OPPp) - value_of_side (OPPp, COMPp) - ply_number;
  } else {
    VALUE = -INFTY + ply_number;        // no mobility!!
  }
  return (COMP__PLAYERNO == ME) ? VALUE : -VALUE;
}

#ifdef OLDSTYLE
void make_move (int8_t MV, int8_t P, int8_t *Tp, Player *COMPp, Player *OPPp) {
#define T (*Tp)
INTEGER *CTYPEp, *OTYPEp;
int8_t *CPOSp, *OPOSp, *ONUMp, *ONUMCp;

#define CPOS (*CPOSp)
#define CTYPE (*CTYPEp)
#define OPOS (*OPOSp)
#define OTYPE (*OTYPEp)
#define ONUM (*ONUMp)
#define ONUMC (*ONUMCp)

  CPOSp = &COMP__POS (P);
  CTYPEp = &COMP__TYPE (P);
  CPOS = CPOS + MV;
  if (T) {                      // a take
    OPOSp = &OPP__POS (T);
    OTYPEp = &OPP__TYPE (T);
    ONUMp = &OPP__NUM;
    ONUMCp = &OPP__NUMC;
    SP += 1;
    PSTACK (SP) = OPOS;         // save position of taken piece
    TSTACK (SP) = CKTYPE(OTYPE);        // save piece type
    NSTACK (SP) = T;            // save piece number
    OPOS = OPP__POS (ONUM);      // remove piece from board
    ONUM -= 1;                  // reduce piece count for opp
    if (CKTYPE(OTYPE) == CROWN) ONUMC -= 1;
    OTYPE = CKTYPE(OPP__TYPE (ONUM + 1)); // (really OPP_TYPE(ONUM), but value before ONUM was decremented above) Could substitute ONUM--?
  }
  if (CKTYPE(CTYPE) != CROWN && CROWNING (CPOS, COMP__PLAYERNO)) {
    CTYPE = CKTYPE(CROWN);              // crown piece
    COMP__NUMC += 1;             // increase crown count
    T = 0;                      // no more takes
  }
#undef T
#ifdef PIZERO
  DEBUG_EVERYTHING(__LINE__);
#endif
}
#else
void make_move (int8_t MV, int8_t P, int8_t *Tp, Player *COMPp, Player *OPPp) {
#define T (*Tp)
  // This is fairly major surgery, considering that it was working before.
  // might need to revert.  geting a gcc register spill error
#ifdef PIZERO
  DEBUG_EVERYTHING(__LINE__);
#endif
  COMP__POS (P) += MV;
  if (T) {                      // a take
    SP += 1;
    PSTACK (SP) = OPP__POS (T);         // save position of taken piece
    TSTACK (SP) = CKTYPE(OPP__TYPE (T));        // save piece type
    NSTACK (SP) = T;            // save piece number
    OPP__POS (T) = OPP__POS (OPP__NUM);      // remove piece from board
    OPP__NUM -= 1;                  // reduce piece count for opp
    if (CKTYPE(OPP__TYPE (T)) == CROWN) OPP__NUMC -= 1;
    OPP__TYPE (T) = CKTYPE(OPP__TYPE (OPP__NUM + 1)); // (really OPP_TYPE(ONUM), but value before ONUM was decremented above) Could substitute ONUM--?
  }
  if (CKTYPE(COMP__TYPE (P)) != CROWN && CROWNING (COMP__POS (P), COMP__PLAYERNO)) {
    COMP__TYPE (P) = CKTYPE(CROWN);              // crown piece
    COMP__NUMC += 1;             // increase crown count
    T = 0;                      // no more takes
  }
#undef T
#ifdef PIZERO
  DEBUG_EVERYTHING(__LINE__);
#endif
}
#endif

void Display_board (int8_t ignore);

// This is the expensive recursion.  Minimise local variables as much as possible.
INTEGER try_possible_moves (int8_t PLY, INTEGER DEPTH, Player *COMPp, Player *OPPp) {
static INTEGER VALUE;           // ONLY VARIABLES THAT DON'T NEED TO SURVIVE RECURSION!!!
INTEGER OLDTYPE;
int8_t M, MM, MMM, MMF, TAKES, OLDPOS, APT, P, T, TT;
INTEGER *MAXPLYp, *MINPLYp;

#define MAXPLY (*MAXPLYp)
#define MINPLY (*MINPLYp)

#define PURSUIT(PLY,M) (PLY == 1 ? 1 : (~M) & 1)

  NODES += 1;
  APT = pending_move (COMPp, OPPp);
  if ((DEPTH >= search_limit && APT <= 0) || PLY > MAX_RECURSIVE_DEPTH) {
    ply_number = PLY - 1;
    return value_of_pos (COMPp, OPPp);
  }
  MINPLYp = &MIN (PLY);
  MINPLY = INFTY;
  MAXPLYp = &MAX (PLY);
  MAXPLY = -INFTY;
  if (APT >= 0) {               // COMP able to move
    if (APT > 0) {              // take priority
      P = APT;
      APT = 2;
      MMM = 1;
      MMF = 7;
      if (PLY == 1)
        search_limit = 2;
    } else {
      P = 1;
      APT = 2;
      MMM = 0;
      MMF = 6;
    }
    for (P = P; P <= COMP__NUM; P++) {
      OLDPOS = COMP__POS (P);    // STACK THESE TO PRESERVE ACROSS RECURSION
      OLDTYPE = CKTYPE(COMP__TYPE (P));
      for (M = MMM; M <= MMF; M += APT) {
        TAKES = 0;
        T = valid_move (M, P, COMPp, OPPp);
        if (T >= 0) {           // valid move
          MM = M;
          TT = T;
	  do {
	    //another_take:
            if (TT > 0) TAKES += 1;
            // Hack! to flash "THINKING" on screen. Somewhat erratic.
            Wait_Recal (); Reset0Ref (); Intensity_7F (); Print_Str_d (0, -120, "      THINKING...\x80");
            make_move (MOVE (MM), P, &TT, COMPp, OPPp);   // try this move
            if (TT > 0) {         // another take
              for (MM = 1; MM <= 7; MM += 2) {
                TT = valid_move (MM, P, COMPp, OPPp);
                if (TT > 0) break; // goto another_take;
              }
            }
	  } while (TT > 0);
          VALUE = try_possible_moves (PLY + 1, DEPTH + PURSUIT (PLY, M), OPPp, COMPp);
          COMP__POS (P) = OLDPOS;
          COMP__TYPE (P) = CKTYPE(OLDTYPE);
          OPP__NUM += TAKES;
          while (TAKES > 0) {
            TT = NSTACK (SP);
            OPP__POS (TT) = PSTACK (SP);
            OPP__TYPE (TT) = CKTYPE(TSTACK (SP));
            TAKES -= 1;
            SP -= 1;
          }
          if (COMP__PLAYERNO == ME) {
            if (VALUE > MAXPLY) {
              MAXPLY = VALUE;
              if (PLY == 1) {
                best_move = M;
                best_take = T;
                best_piece = P;
              }
            }
            if (PLY != 1 && MAXPLY >= MIN (PLY - 1)) {
              return MAXPLY;
            }
          } else {
            if (VALUE < MINPLY)
              MINPLY = VALUE;
            if (MINPLY <= MAX (PLY - 1)) {
              return MINPLY;
            }
          }
        } // if (T>=0)
      } // for M
    } // for P
  } // if APT >= 0
  return (COMP__PLAYERNO == ME) ? MAXPLY : MINPLY;
}

// TRANSLATE AND OUTPUT MOVES - we can either remove this completely,
// or update it so that Draft4's moves are displayed on-screen.

void TROUT (int8_t OLDPOS, int8_t NEWPOS, int8_t * MODEp) {
#define MODE (*MODEp)
//int8_t OLDX, OLDY, NEWX, NEWY;
  (void)OLDPOS;(void)NEWPOS;
  //OLDY = OLDPOS / 10;
  //OLDX = OLDPOS - 10 * OLDY;
  //NEWY = NEWPOS / 10;
  //NEWX = NEWPOS - 10 * NEWY;
  if (MODE == 0) { // MODE == 1 is when adding subsequent moves after jumps to output.
    // p ("\nDRAFT4's move is ");
    MODE = 1;
  } else {
    // p (" , ");
  }
  // print_symbol ((char) OLDX + 'A');
  // print_symbol ((char) OLDY + '1');
  // print_symbol ('-');
  // print_symbol ((char) NEWX + 'A');
  // print_symbol ((char) NEWY + '1');
}

int8_t position_of (char S1, char S2) {
  return (int8_t) ((S1 - 'A') + 10 * (S2 - '1'));
}

#define STATE_HUMAN_TO_MOVE 0
#define STATE_COMPLETED_HARDWIRED_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

static int8_t STATE = STATE_CHOOSE_FIRST_PLAYER;
static int8_t STATE_PieceNum = 0;
static int8_t STATE_FromX = 0;
static int8_t STATE_FromY = 0;
static int8_t STATE_ToPos = 0;

#define STATE_TIMEOUT STATE_ToPos       /* re-using a variable which is not needed when outputting error */
static int8_t STATE_ToX = 0;
static int8_t STATE_ToY = 0;
static int8_t STATE_icon_style = 0;

static inline void POS2XY (int8_t POS, int8_t *XP, int8_t *YP) {
  int8_t X, Y;

  Y = POS / 10;
  X = POS - 10 * Y;             // 1:8
  X -= 4;
  Y -= 4;
  X *= 20;
  Y *= 20;
  *XP = X;
  *YP = Y;
}

static inline void GetIcon (int8_t I, int8_t * XP, int8_t * YP, int8_t * icon_style) {
  if (I <= 12) {
    *icon_style = COMP_COLOUR == 'B' ? 0 : 1;
    POS = COMP_POS (I);
    if (CKTYPE(COMP_TYPE (I)) == CROWN)
      *icon_style |= 2;
  } else {
    I -= 12; assert(1 <= I && I <= 12);
    *icon_style = COMP_COLOUR == 'B' ? 1 : 0;
    POS = OPP_POS (I);
    if (CKTYPE(OPP_TYPE (I)) == CROWN)
      *icon_style |= 2;
  }
  POS2XY (POS, XP, YP);
}

static inline void Display_Piece (int8_t X, int8_t Y, int8_t style) {
  set_scale (BIG_SCALE);
  Reset0Ref ();
  Moveto_d (Y, X);
  set_scale (SMALL_SCALE);
  Draw_VLp ((int8_t *) Piece[style]);
}

static int Dim = 0;
void Display_board (int8_t IgnoringP) {
  int8_t icon_style, I, X, Y, intensity_of_missing_piece=0x7F;

  // If Piece IgnoringP is on the board, *do not* draw it.
  // IgnoringP in range 1:12 and 13:24

  // Computer's pieces:
  Intensity_a(BLACK_INTENSITY-(uint8_t)Dim);
  for (I = 1; I <= COMP_NUM; I++) {
    if (IgnoringP != I) {
      GetIcon (I, &X, &Y, &icon_style);
      Display_Piece (X, Y, icon_style);
    } else intensity_of_missing_piece = BLACK_INTENSITY-Dim;
  }

  IgnoringP -= 12;
  // Human's pieces:
  Intensity_a(WHITE_INTENSITY-(uint8_t)Dim);
  for (I = 1; I <= OPP_NUM; I++) {
    if (IgnoringP != I) {
      GetIcon (I + 12, &X, &Y, &icon_style);
      Display_Piece (X, Y, icon_style);
    } else intensity_of_missing_piece = WHITE_INTENSITY-Dim;
  }

  Intensity_a((uint8_t)intensity_of_missing_piece); // assumes missing piece is displayed immediately after...
}

void Animate (int8_t PieceNum, int8_t NewPos) {
  STATE_PieceNum = PieceNum;
  STATE_ToPos = NewPos;
  GetIcon (PieceNum, &STATE_FromX, &STATE_FromY, &STATE_icon_style);
  POS2XY (STATE_ToPos, &STATE_ToX, &STATE_ToY);
}

static int8_t Cursor_X = 0, Cursor_Y = 0;

#ifdef NEVER
static const int8_t Cursor[] = {        // Not using this any more
  0, -8, 0,
  -1, 16, 0,
  0, -8, -8,
  -1, 0, 16,
  1
};
#endif

static int8_t Flash = 0;

#define HUMAN 1
#define COMPUTER 2
static int8_t First_player_was;
void Reinitialise_nearly_everything(void); // except 'First_player_was'

void Execute_state_machine(void) {
  int8_t I;
  switch (STATE) {            // if this slows us down too much, try non-std jump table...
    case STATE_CHOOSE_FIRST_PLAYER:
      STATE_TIMEOUT++;
      I = (int8_t) Random ()&3;   // makes initial choice timing-dependent
      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 (  0, -120, "BUTTON 1 YOU START []\x80");
        Print_Str_d (-20, -120, "BUTTON 2  I START  <>\x80");
        Print_Str_d (-50, -120, "BUTTON 3 CANCEL SELECT\x80");
        Print_Str_d (-70, -120, "BUTTON 4 SELECT/PLACE\x80");
      }
      // Print_Str_d (-100, -120, "0123456789012345678901234\x80"); // fills screen, balanced. Actually room for 1 more at each end.
      Print_Str_d (-120, -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_COMPLETED_HARDWIRED_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;
        if (Cancel_Request == 0) {
          for (;;) { // This is the only place we don't use the state machine
            Flash++;
            Wait_Recal ();
            Joy_Digital ();
            Read_Btns ();
            Reset0Ref ();
            Intensity_7F ();
            set_scale (BIG_SCALE);
            Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
            Intensity_7F();
            Reset0Ref(); Print_Str_d (  0, -120, "BUTTON 1  YOU RESIGN\x80");
            Reset0Ref(); Print_Str_d (-20, -120, "BUTTON 2  RESUME PLAY\x80");
            if (button_1_1_held ()) {
              STATE = STATE_OTHER_PLAYER_STARTS;
              return;
            }
            if (button_1_2_held ()) {
              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 = -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 = -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 = -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 = -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 = -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 = -1;
              STATE = STATE_YOU_MUST_TAKE_TIMEOUT;
#ifdef PIZERO
              if (logfile) {
                fprintf(logfile, "STATE_YOU_MUST_TAKE_TIMEOUT PIECE=%d MODIF=%d expected_move=%d NEWPOS %d OLDPOS %d\n",
			    PIECE, MODIF, expected_move, NEWPOS, OLDPOS);
		       fflush(logfile);
              }
#endif
              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 = -1;
              STATE = STATE_NOT_CROWNED_TIMEOUT;
              return;
            }
            if (!(MODIF == 11 || MODIF == 9 || MODIF == 22 || MODIF == 18)) {
              STATE_TIMEOUT = -1;
              STATE = STATE_NO_SUCH_MOVE_TIMEOUT;
              return;
            }
	    
#ifdef OLDSTYLE
            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))
                  break;
              }
              assert(I <= COMP_NUM); // a little worried that the control variable in C is different on exit from IMP
              if (COMPOS != COMP_POS (I)) {
                REPLY (3) = 0x80;
                STATE_TIMEOUT = -1;
                STATE = STATE_NOT_JUMPING_MY_PIECE_TIMEOUT;
                return;
              }
              JMAN = I;
            } else
              JMAN = 0;
#else
            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 = -1;
                STATE = STATE_NOT_JUMPING_MY_PIECE_TIMEOUT;
                return;
              }
            }
#endif

            // 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 ( /* ignoring */ 0);
            REPLY(1) = REPLY (3) = 0x80; // remove the coords from the display
            return;
          }
        }
      }
      Display_board ( /* ignoring */ 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) {
#ifdef PIZERO
        if (logfile) fprintf(logfile, "STATE_PieceNum = %d (1:12)  STATE_ToPos = %d\n", STATE_PieceNum, STATE_ToPos);
#endif
        //COMP_POS (STATE_PieceNum) = STATE_ToPos;
        STATE = STATE_END_HUMAN_MOVE_ANIMATION;
      } else {
        STATE_FromX += (STATE_ToX - STATE_FromX > 0) ? 1 : -1;
        STATE_FromY += (STATE_ToY - STATE_FromY > 0) ? 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_HUMAN_MUST_TAKE_ANOTHER_TIMEOUT:
      Print_Str_d (-10, -120, "  TAKE ANOTHER PIECE!\x80");
      // For now, the human could cheat and move any of his pieces.
      // However the one that just took an opponent piece is pre-selected
      // so with luck it won't occur to them before I've had a chance to
      // fix it!  (Note there is currently a problem with the pre-selected
      // piece being disallowed. You have to unselect and reselect it.)

      // TO DO: should that MODIF < 12 test from above appear here somewhere?

      // The superimposed print only worked as a drop-through - with this now
      // being a proper independent state, this message will only flash up for
      // one frame... so converting to a _TIMEOUT instead...
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_COMPLETED_HARDWIRED_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;
        STATE = STATE_HUMAN_TO_MOVE;
      } else {
        STATE_FromX += (STATE_ToX - STATE_FromX > 0) ? 1 : -1;
        STATE_FromY += (STATE_ToY - STATE_FromY > 0) ? 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) ?
// been getting some bogus resignations recently!
        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;
      }
      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 = -1;
        STATE = STATE_CHUCKLE_TIMEOUT;
      } else STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_CHUCKLE_TIMEOUT: // "He-He! I am going to win!!"
      Print_Str_d (+10, -120, "      HEH HEH HEH!\x80");
      Print_Str_d (-10, -120, "   I AM GOING TO WIN!\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_OTHER_PLAYER_STARTS:
      Reinitialise_nearly_everything();
      if (First_player_was == HUMAN) {
        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_COMPLETED_HARDWIRED_OPENING_MOVE;
      } else {
	STATE = STATE_HUMAN_TO_MOVE;
      }
      First_player_was = (HUMAN+COMPUTER)-First_player_was;
      return;
    
    case STATE_NO_PIECES_LEFT_STOP: // "I have no pieces left so I suppose you have won"
      Print_Str_d (+10, -120, "   I HAVE NO PIECES LEFT\x80");
      Print_Str_d (-10, -120, "    SO I GUESS YOU WIN!\x80");
      Print_Str_d (-40, -120, "     PRESS ANY BUTTON\x80");
      Print_Str_d (-60, -120, "     FOR ANOTHER GAME\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if ((buttons_pressed()&15) != 0) STATE = STATE_OTHER_PLAYER_STARTS;
      return;

    case STATE_I_CANNOT_MOVE_STOP: // "I cannot move any of my pieces so you win"
      Print_Str_d (+10, -128, "I CANNOT MOVE ANY PIECES\x80");
      Print_Str_d (-10, -128, "       SO YOU WIN!\x80");
      Print_Str_d (-40, -120, "    PRESS ANY BUTTON\x80");
      Print_Str_d (-60, -120, "    FOR ANOTHER GAME\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if ((buttons_pressed()&15) != 0) STATE = STATE_OTHER_PLAYER_STARTS;
      return;

    case STATE_I_RESIGN_STOP: // "I resign"
      Print_Str_d (0, -120  , "      I RESIGN!\x80");
      Print_Str_d (-40, -120, "  PRESS ANY BUTTON\x80");
      Print_Str_d (-60, -120, "  FOR ANOTHER GAME\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if ((buttons_pressed()&15) != 0) STATE = STATE_OTHER_PLAYER_STARTS;
      return;

    case STATE_YOU_CANNOT_MOVE_STOP: // "You cannot move any of your pieces so I win"
      Print_Str_d (+10, -120, "YOU CANNOT MOVE ANY PIECES\x80");
      Print_Str_d (-10, -120, "       SO I WIN!\x80");
      Print_Str_d (-40, -120, "   PRESS ANY BUTTON\x80");
      Print_Str_d (-60, -120, "   FOR ANOTHER GAME\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if ((buttons_pressed()&15) != 0) STATE = STATE_OTHER_PLAYER_STARTS;
      return;

    case STATE_YOU_HAVE_NO_PIECES_STOP: // "You have no pieces left so I win"
      Print_Str_d (+10, -120, "YOU HAVE NO PIECES LEFT\x80");
      Print_Str_d (-10, -120, "       SO I WIN!\x80");
      Print_Str_d (-40, -120, "   PRESS ANY BUTTON\x80");
      Print_Str_d (-60, -120, "   FOR ANOTHER GAME\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0; 
      if ((buttons_pressed()&15) != 0) STATE = STATE_OTHER_PLAYER_STARTS;
      return;

    case STATE_YOU_HAVE_NO_PIECE_ON_TIMEOUT: // "You do not have a piece on square "
      Reset0Ref ();
      Print_Str_d (+10, -120, "THERE IS NO PIECE ON\x80");
      Print_Str_d (+10, 108, &REPLY (1));
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_FROM_SQUARE_NOT_EXIST_TIMEOUT: // The square ?? does not exist!"
      Reset0Ref ();
      Print_Str_d (+10, -120, "   NO SUCH SQUARE AS\x80");
      Print_Str_d (+10, 108, &REPLY (1));
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_TO_SQUARE_NOT_EXIST_TIMEOUT: // You cannot move to square ??; it does not exist!"
      Reset0Ref ();
      Print_Str_d (+10, -120, "   NO SUCH SQUARE AS\x80");
      Print_Str_d (+10, 108, &REPLY (4));
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_TO_SQUARE_OCCUPIED_TIMEOUT: // "You cannot move to square ??; it is already occupied!"
      Reset0Ref ();
      Print_Str_d (+10, -120, "  OCCUPIED SQUARE\x80");
      REPLY(6) = 0x80; // REPLY 4:5 SHOULD BE THE DEST SQUARE
      Print_Str_d (+10, 80, &REPLY (4));
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_YOU_MUST_TAKE_TIMEOUT: // "You MUST take the piece that I am offering you"
      Reset0Ref ();
      Print_Str_d (+10, -120, "YOU MUST TAKE THE PIECE\x80");
      Print_Str_d (-10, -120, "   I AM OFFERING YOU\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;    // WON'T WORK WITH MULTIPLE TAKES
      return;

    case STATE_NOT_CROWNED_TIMEOUT: // "You cannot move that piece backwards!"
      Reset0Ref ();
      Print_Str_d (+10, -128, "YOU CANNOT MOVE BACKWARDS\x80");
      Print_Str_d (-10, -128, "     WITHOUT A CROWN\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;    // WON'T WORK WITH MULTIPLE TAKES
      return;

    case STATE_NO_SUCH_MOVE_TIMEOUT: // "That move does not exist in my rule book!"
      Reset0Ref ();
      Print_Str_d (+10, -120, "THAT MOVE DOES NOT EXIST\x80");
      Print_Str_d (-10, -120, "    IN MY RULE BOOK!\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;    // WON'T WORK WITH MULTIPLE TAKES
      return;

    case STATE_NOT_JUMPING_MY_PIECE_TIMEOUT: // "You cannot do that.  You are not jumping one of my pieces"
      Reset0Ref ();
      Print_Str_d (+10, -120, "THAT IS NOT A JUMP OVER\x80");
      Print_Str_d (-10, -120, "   ONE OF MY PIECES!\x80");
      Dim=DIM; Display_board ( /* ignoring */ 0); Dim=0;
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;    // MAY NEED A DIFFERENT TARGET TO RESUME HUMAN'S PLAY?
      return;

  } // switch
}

void Reinitialise_nearly_everything(void) { // except 'First_player_was'

  // init everything for consistency *and* to ensure a clean restart on next game
  Cancel_Request = 0; expected_move = 0;  a_win = FALSE;  search_limit = 3;  SP = 0;  VALUEB = 0; NODES = 0;
  // most of these probably don't need initialising...
  P=M=PIECE=DIF=MODIF=MORE=best_piece=best_move=best_take=JMAN=POS=OLDPOS=NEWPOS=LASTPOS=COMPOS=ply_number=expected_move=0;
  A=B=C=0;

  STATE = STATE_CHOOSE_FIRST_PLAYER; // should alternate after the first game
  STATE_PieceNum = STATE_FromX = STATE_FromY = STATE_ToPos = STATE_ToX = STATE_ToY = STATE_icon_style = 0;
  Cursor_X = Cursor_Y = Flash = 0;
  REPLY (1) = REPLY (2) = ' '; REPLY (3) = 0x80;

  // setting up the pieces
  COMP_COLOUR = PLAYER1.COLOUR; OPP_COLOUR = PLAYER2.COLOUR;
  COMP_PLAYERNO = PLAYER1.PLAYERNO; OPP_PLAYERNO = PLAYER2.PLAYERNO;
  for (P = 1; P <= 12; P++) {
    COMP_POS (P) = PLAYER1_POS(P); OPP_POS (P) = PLAYER2_POS(P);
    COMP_TYPE (P) = CKTYPE(PLAYER1_TYPE(P)); OPP_TYPE (P) = CKTYPE(PLAYER2_TYPE(P));
  }
  COMP_NUM = PLAYER1.NUM; OPP_NUM = PLAYER2.NUM;
  COMP_NUMC = PLAYER1.NUMC; OPP_NUMC = PLAYER2.NUMC;

  STATE = STATE_CHOOSE_FIRST_PLAYER;

}

int main (void) {
#ifdef PIZERO
  logfile = fopen("/tmp/draft4.log", "w");

  vectrexinit(1);
  v_setName("draft4");
  v_init();
  //usePipeline = 1;
  v_setRefresh(60);
#endif
  
  Reinitialise_nearly_everything();
  enable_controller_1_x (); enable_controller_1_y ();

  // TO DO: Before we start, should make a quick alignment of the overlay...
  for (;;) { // MAIN LOOP
    Flash++;
    Wait_Recal ();
    Joy_Digital ();
    Read_Btns ();
    Reset0Ref ();
    Intensity_7F ();
    set_scale (BIG_SCALE);

    Execute_state_machine();
  }

  return 0;
} // main
