// THIS IS CURRENTLY UNDER DEVELOPMENT!  Don't expect to see much
// happening right now.  At the moment I'm just testing the intro
// sequence, so it does not start the game play!

//#define DEBUG_SET_POSITION // set this to use a hard-coded initial test position

// On plain Vectrex, drawing a grid is flickery at first, until enough pieces
// have been removed from the board that the vector count is down.  (Not a
// problem on the PiTrex)

/* baseline 33.4K cycles */
#define INTERLACE 1
//#define DRAWGRID 1 /* 6.5K cycles with interlace on, 13K with interlace off */
//#define DRAWHATCH 1 /* 5.5K cycles */
//#define DRAWHATCHBACK 1 /* 5.5K cycles */
// total 45.6K cycles when both enabled and interlace on, 52.1K with interlace off */

#ifdef PIZERO
// Must only ask for diagnostics on PiTrex ... 
#define DIAGNOSTICS 2           // 1: silent checks  2: loud checks

#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 Draw_Line_d(int8_t y, int8_t x) { // rel
  v_directDraw32(Pos_X, Pos_Y, Pos_X + x*current_scale, Pos_Y + y*current_scale, current_intensity);
  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,y,ends_x80, 80, -9, 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;
#if defined(DIAGNOSTICS) && DIAGNOSTICS == 2
  if (current_buttons & 0x1) {
    Intensity_7F();
    Print_Str_d(-128,-16, "1  \x80");
  }
  if (current_buttons & 0x2) {
    Intensity_7F();
    Print_Str_d(-128,-8, "2  \x80");
  }
  if (current_buttons & 0x4) {
    Intensity_7F();
    Print_Str_d(-128,0, "3  \x80");
  }
  if (current_buttons & 0x8) {
    Intensity_7F();
    Print_Str_d(-128,8, "4  \x80");
  }
#endif
}

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

static int8_t button_1_1_held (void) {
#if defined(DIAGNOSTICS) && DIAGNOSTICS == 2
  if (current_buttons & 0x1) {
    Intensity_7F();
    Print_Str_d(-118,-16, "1  \x80");
  }
#endif
  return current_buttons & 0x1;
}

static int8_t button_1_2_held (void) {
#if defined(DIAGNOSTICS) && DIAGNOSTICS == 2
  if (current_buttons & 0x2) {
    Intensity_7F();
    Print_Str_d(-118,-8, "2  \x80");
  }
#endif
  return current_buttons & 0x2;
}

static int8_t button_1_3_held (void) {
#if defined(DIAGNOSTICS) && DIAGNOSTICS == 2
  if (current_buttons & 0x4) {
    Intensity_7F();
    Print_Str_d(-118,0, "3  \x80");
  }
#endif
  return current_buttons & 0x4;
}

static int8_t button_1_4_held (void) {
#if defined(DIAGNOSTICS) && DIAGNOSTICS == 2
  if (current_buttons & 0x8) {
    Intensity_7F();
    Print_Str_d(-118,8, "4  \x80");
  }
#endif
  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

    Ken's documentation and other versions are at http://gtoal.com/vectrex/chess/
    (sorry about the 'chess' directory name; I started on a chess project first but
     switched to draughts after a colleague on VFU said he was already working on
     a chess engine. By them I'd posted the URL and didn't want to change it...)

    Graham Toal, 12/17/2020

    TO DO: optimise graphics
    Bugs: when pressing B3 for menu, selecting something like offering a draw justs
    returns immediately to the game; but doing it again handles it properly.  I'm
    not sure if it is a logic bug or just a problem reading the buttons.  I added
    some code to display what buttons are pressed and also what button presses we
    are reading (though the latter may only flash up from one frame so may be hard
    to spot.)  There are other related bugs concerning display of notifications
    that will probably be down to the same cause.  There may have been related issues
    caused by dimming the text out of existence (!) but I think I fixed those.
 */

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

#define FULLBRIGHT 127
#define BACKGROUND_INTENSITY 30 /* 70 */
// Vectrex brightness must be set so that board grid is faint, matching what you see in Vide
#define WHITE_INTENSITY 90
#define BLACK_INTENSITY 50
#define DIM 40
#define MAXDIM (127-40)
static int8_t Dim;

static int8_t Flash; // increments every frame with wrap-around

#define BIG_SCALE 0x7F
#define SMALL_SCALE 0x1F

#define MEDIUM_SCALE 0x5A
#define TINY_SCALE 0x1
#define TINY_SCALE_CROWNED 0x2

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

// bottom, squares
static const int8_t white[] = {
  0, 8, 8,
  -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, 8, 8,
  -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
};

// top, diamonds
static const int8_t black[] = {
  0, 56 / 2 - 28, 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 - 28, 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 int white_big[] = {
// 0,   0,   0,  // adjustment for corner/center!
  -1, 120, 120, -1, 120,0,  -1, 120,-120, -1,0,-120,
  -1,-120,-120, -1,-120,0,  -1,-120, 120, -1,0, 120,
  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

// TO DO: if we end up with more global booleans,
// remember we can pack 8 of them into a byte to save space,
// either explicitly or using a struct containing bitfields..
static int8_t a_win;
static int8_t search_limit;
static int8_t Cancel_Request;

#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 so we might be tempted to use
// and int8_t here, HOWEVER we need 'long' here if we have *any* 
// arrays indexed by '-1'.  Made that mistake once and it created
// a huge number of hard-to-find bugs!
#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

// 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
// wasting 2*2 bytes here to simplify code. Elements 0 are not used.
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]; // 1:12 still, bsse address is tweaked
  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
  // put any set position in here for testing...
  {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... we can afford to be profligate with
// RAM now that we have reduced the footprint enough that everything fits,
// including 12 levels of recursion for the main evaluation code.
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;          // MUST be 16-bits
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");
#ifdef PIZERO
            Dim=DIM;Display_board(0);Dim=0;
#endif
            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'));
}

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

static int8_t STATE;
static int8_t STATE_PieceNum;
static int8_t STATE_FromX;
static int8_t STATE_FromY;
static int8_t STATE_ToPos;

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

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
//#ifdef OLDSTYLE
  X -= 4;
  Y -= 4;
  X *= 20;
  Y *= 20;
//#endif
  *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 New_Piece(int8_t x, int8_t y, int8_t style) {
#define XX 14
#define YY 14
  int8_t xtarget, ytarget;
  (void)style;
  //y = POS/10; x = POS - y*10; y+=1;
  xtarget = (x-4)*2*XX+XX;
  ytarget = (y-4)*2*YY+YY;
#undef XX
#undef YY
  Reset0Ref ();
  //Intensity_a (0x7f);
  set_scale (MEDIUM_SCALE);
  Moveto_d (ytarget+5, xtarget-1); // small adjustments
  set_scale (TINY_SCALE);
  Draw_VLp ((int8_t *)white_big);
}

static inline void Display_Piece (int8_t X, int8_t Y, int8_t style) {
  if (style == -1) {
    New_Piece(X/20+4, Y/20+4, style); // <--- fix
  } else if (style == 0) { /* white uncrowned */
    set_scale(BIG_SCALE);
    Reset0Ref();
    Moveto_d(Y+3,X+9);
    set_scale(TINY_SCALE);
    Intensity_a(WHITE_INTENSITY);
    Draw_VLp((int8_t *)white_big);
  } else if (style == 1) { /* black uncrowned */
    set_scale(BIG_SCALE);
    Reset0Ref();
    Moveto_d(Y+3,X+9);
    set_scale(TINY_SCALE);
    Intensity_a(BLACK_INTENSITY);
    Draw_VLp((int8_t *)white_big);
  } else if (style == 2) { /* white crowned */
    set_scale(BIG_SCALE);
    Reset0Ref();
    Moveto_d(Y+3,X+9);
    set_scale(TINY_SCALE);
    Intensity_a(WHITE_INTENSITY);
    Draw_VLp((int8_t *)white_big);
    Moveto_d(-20,35);
    set_scale(TINY_SCALE_CROWNED);
    Draw_VLp((int8_t *)white_big);
  } else if (style == 3) { /* black crowned */
    set_scale(BIG_SCALE);
    Reset0Ref();
    Moveto_d(Y+3,X+9);
    set_scale(TINY_SCALE);
    Intensity_a(BLACK_INTENSITY);
    Draw_VLp((int8_t *)white_big);
    Moveto_d(-20,35);
    set_scale(TINY_SCALE_CROWNED);
    Draw_VLp((int8_t *)white_big);
  } else {
    set_scale (BIG_SCALE);
    Reset0Ref ();
    Moveto_d (Y, X);
    set_scale (SMALL_SCALE);
    Draw_VLp ((int8_t *) Piece[style]);
  }
}

void Fadein_text(void) {
  //Intensity_a((uint8_t)((int8_t)FULLBRIGHT - ((int8_t)DIM-(int8_t)Dim)));
  if (STATE_TIMEOUT==0) Intensity_a((uint8_t)(DIM)); else Intensity_a((uint8_t)(Dim));
  Reset0Ref ();
}

void Dim_Board(void) {
  if (STATE_TIMEOUT < 0) Dim += 1;
  else if (STATE_TIMEOUT > 0) Dim -= 1;
  if (Dim >= MAXDIM) Dim = MAXDIM;
  if (Dim < 0) Dim = 0;
  Display_board (0);
}

void Display_board (int8_t IgnoringP) {
  int8_t icon_style, I, J, X, Y, intensity_of_missing_piece=0x7F;
  (void)J;
#ifdef DRAWGRID
  set_scale(0x51);
  Intensity_a(BACKGROUND_INTENSITY-(uint8_t)Dim);
  // verticals
#ifdef INTERLACE
  if (Flash&1)
#endif
  for (I = 0; I < 9; I++) {
    Reset0Ref(); Intensity_a(BACKGROUND_INTENSITY-(uint8_t)Dim);Moveto_d(120, (I-4)*31-4); Draw_Line_d(-124,0);Draw_Line_d(-124,0);
  }
  // horizontals
#ifdef INTERLACE
  if (!(Flash&1))
#endif
  for (I = 0; I < 9; I++) {
    Reset0Ref(); Intensity_a(BACKGROUND_INTENSITY-(uint8_t)Dim);Moveto_d((I-4)*31-4, 120); Draw_Line_d(0,-124);Draw_Line_d(0,-124);
  }
#endif
  // and optionally, diagonals through the unused square colour
  // (Officially, the pieces are black and red, and the board is red and green!)
#ifdef DRAWHATCH
  set_scale(0x51);
  for (J = 120; J >= -4; J-=124) {
    for (I = 0; I < 8; I+=2) {
      Reset0Ref(); Intensity_a(BACKGROUND_INTENSITY-(uint8_t)Dim);Moveto_d(J, (I-4)*31-4);
      X = 4*31; Y = -124;
      if (I > 4) { X = (8-I)*31; Y = ((4-(8-I))*31)-124; }
      Draw_Line_d(Y,X);
    }
    Reset0Ref(); Intensity_a(BACKGROUND_INTENSITY-(uint8_t)Dim);Moveto_d(J-62, -128); Draw_Line_d(-62,62);
  }
#endif
#ifdef DRAWHATCHBACK
  // hatch in other direction.  needs different calculation - not a mirror...
#endif
  // 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, Cursor_Y;

#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

#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:
      if (1==1) { // 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;
      }
      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 ( 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");
      }

      // Calibrating text size and trying to look similar
      // on Vectrex, Vide, and PiTrex:
      //Print_Str_d ( -70, -128, "01234567890123456789012\x80");
      //Print_Str_d ( -90, -120, "THERE IS NO PIECE ON\x80");
      //Print_Str_d ( -90, 108, "A8\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_DRAW_DECLINED:
      Fadein_text();
      Print_Str_d (+10, -120, "   SORRY, BUT I THINK\x80");
      Print_Str_d (-10, -120, "   I'LL KEEP PLAYING...\x80");
      Dim_Board();
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      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
            Flash++;
            Wait_Recal ();
            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 = -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;
              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 (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_HUMAN_MUST_TAKE_ANOTHER_TIMEOUT:
      Fadein_text();
      Print_Str_d (-10, -120, "  TAKE ANOTHER PIECE!\x80");
      // OR... Print_Str_d (-10, -120, "  MULTIPLE JUMP!\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?

      Dim_Board();
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_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 = -1;
        STATE = STATE_CHUCKLE_TIMEOUT;
      } else STATE = STATE_HUMAN_TO_MOVE;
      return;

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

    case STATE_OTHER_PLAYER_STARTS_ON_DRAW:
      Fadein_text();
      Print_Str_d (0, -120, "   I ACCEPT A DRAW!\x80");
      Dim_Board();
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_OTHER_PLAYER_STARTS;
      return;

    case STATE_OTHER_PLAYER_STARTS:
      Fadein_text();
      if (First_player_was == HUMAN) {
        Print_Str_d (0, -120, " I PLAY FIRST THIS TIME\x80");
      } else {
        Print_Str_d (0, -120, "YOUR TURN TO PLAY FIRST\x80");
      }
      Dim_Board();
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_OTHER_PLAYER_STARTS_NOW;
      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;
    
    case STATE_NO_PIECES_LEFT_STOP: // "I have no pieces left so I suppose you have won"
      Fadein_text();
      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_Board();
      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"
      Fadein_text();
      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_Board();
      if ((buttons_pressed()&15) != 0) STATE = STATE_OTHER_PLAYER_STARTS;
      return;

    case STATE_I_RESIGN_STOP: // "I resign"
      Fadein_text();
      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_Board();
      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"
      Fadein_text();
      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_Board();
      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"
      Fadein_text();
      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_Board();
      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 "
      Fadein_text();
      Print_Str_d (+10, -120, "YOU HAVE NO PIECE ON\x80");
      Print_Str_d (+10, 108, &REPLY (1));
      Dim_Board();
      if (--STATE_TIMEOUT == 0)
        STATE = STATE_HUMAN_TO_MOVE;
      return;

    case STATE_FROM_SQUARE_NOT_EXIST_TIMEOUT: // The square ?? does not exist!"
      Fadein_text();
      Print_Str_d (+10, -120, "   NO SUCH SQUARE AS\x80");
      Print_Str_d (+10, 108, &REPLY (1));
      Dim_Board();
      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!"
      Fadein_text();
      Print_Str_d (+10, -120, "   NO SUCH SQUARE AS\x80");
      Print_Str_d (+10, 108, &REPLY (4));
      Dim_Board();
      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!"
      Fadein_text();
      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_Board();
      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"
      Fadein_text();
      Print_Str_d (+10, -120, "YOU MUST TAKE THE PIECE\x80");
      Print_Str_d (-10, -120, "   I AM OFFERING YOU\x80");
      Dim_Board();
      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!"
      Fadein_text();
      Print_Str_d (+10, -128, "YOU CANNOT MOVE BACKWARDS\x80");
      Print_Str_d (-10, -128, "     WITHOUT A CROWN\x80");
      Dim_Board();
      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!"
      Fadein_text();
      Print_Str_d (+10, -120, "THAT MOVE DOES NOT EXIST\x80");
      Print_Str_d (-10, -120, "    IN MY RULE BOOK!\x80");
      Dim_Board();
      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"
      Fadein_text();
      Print_Str_d (+10, -120, "THAT IS NOT A JUMP OVER\x80");
      Print_Str_d (-10, -120, "   ONE OF MY PIECES!\x80");
      Dim_Board();
      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...
  Dim=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));
  }
  for (P = 1; P <= MAX_RECURSIVE_DEPTH; P++) {
    MAX(P) = MIN(P) = 0;
  }
  P = 0;
  COMP_NUM = PLAYER1.NUM; OPP_NUM = PLAYER2.NUM;
  COMP_NUMC = PLAYER1.NUMC; OPP_NUMC = PLAYER2.NUMC;

  STATE = STATE_CHOOSE_FIRST_PLAYER;

}

// We will be updating the x and y positions in situ, and extending the array dynamically by
// poking a '1' into the first element of the entry after the last active object.  All
// asteroids must be the same number of vectors or use moves to pad. (the Procustean option)

// make the move/remove into a single separate ram array
// and just have one copy of each asteroid. use pointers to it...
// (although that stops working when we use rotation)

#define ASTEROIDS 24

//#ifdef NEVER
void intro(void) {
#define X 14
#define Y 14
#define XX 14
#define YY 14
// using a copy means that this code can be merged with a program such
// as 'Draft4' and used before the initialisation, overlaying dx and dy
// on top of variables that will be used elsewhere in the program, or
// perhaps part of the stack.
const int cdx[ASTEROIDS] = {
   1,1,1,1, 2,2,2,2, -1,-1,-1,-1, -2,-2,-2,-2, 1,2,-2,-1, 1,2,-2,-1
};
const int cdy[ASTEROIDS] = {
   -2,-1,1,2,  -2,-1,1,2,  -2,-1,1,2,  -2,-1,1,2, -2,1,1,2, -1,-2,2,1
};
int dx[ASTEROIDS], dy[ASTEROIDS];
int x[ASTEROIDS], y[ASTEROIDS];
#define MARGIN 20
   unsigned int direction=0;
   int done;
   int base, row, tmp1;		// , tmp2, other, diffx, diffy;
   //int8_t xtarget, ytarget;

   for (row = 0; row < ASTEROIDS; row++) {
     dx[row]=cdx[row]; dy[row]=cdy[row];
   }
   row=0;
   {int xx,yy; // lay out the initial board position for the animation:
     for (yy = 1; yy <= 8; yy++) {
       if (yy != 4 && yy != 5) {
         for (xx = ((yy&1)^1)+1; xx <=8; xx+=2) {x[row] = (xx-4)*2*XX-XX; y[row++] = (yy-4)*2*YY-YY;}
       }
     }
    }

   base = 0; direction = 0;
   do {
      // now move asteroids around invisibly and quickly
      for (row = 0; row < ASTEROIDS; row++) {
        // Bounce off walls? Marginally more expensive than wrapping.
        tmp1 = x[row] + dx[base + row]; // overflow test
        if ((dx[base + row] > 0 && x[row] > 0 && tmp1+MARGIN < 0)
          || (dx[base + row] < 0 && x[row] < 0 && tmp1-MARGIN > 0)) {
          dx[base + row] = -dx[base + row]; // bounce?
        }
        x[row] = x[row] + dx[base + row];
        
        tmp1 = y[row] + dy[base + row];
        if ((dy[base + row] > 0 && y[row] > 0 && tmp1+MARGIN < 0)
          || (dy[base + row] < 0 && y[row] < 0 && tmp1-MARGIN > 0)) {
          dy[base + row] = -dy[base + row];
        }
        y[row] = y[row] + dy[base + row];
      }
      //base ^= ASTEROIDS;
      direction++;
   } while (direction != 255); // change constant depending on length and complexity of animation

   // reverse the movement vectors
   for (tmp1 = 0; tmp1 < ASTEROIDS; tmp1++) {
     dx[tmp1] = -dx[tmp1];  dy[tmp1] = -dy[tmp1];
   }

   // visibly unwind...
   do {
      direction--;
      //base ^= ASTEROIDS;

      // now move asteroids around
      for (row = 0; row < ASTEROIDS; row++) {
        // Bounce off walls? Marginally more expensive than wrapping.
        tmp1 = x[row] + dx[base + row]; // overflow test
        if ((dx[base + row] > 0 && x[row] > 0 && tmp1+MARGIN < 0)
          || (dx[base + row] < 0 && x[row] < 0 && tmp1-MARGIN > 0)) {
          dx[base + row] = -dx[base + row]; // bounce?
        }
        x[row] = x[row] + dx[base + row];
        
        tmp1 = y[row] + dy[base + row];
        if ((dy[base + row] > 0 && y[row] > 0 && tmp1+MARGIN < 0)
          || (dy[base + row] < 0 && y[row] < 0 && tmp1-MARGIN > 0)) {
          dy[base + row] = -dy[base + row];
        }
        y[row] = y[row] + dy[base + row];

      }
      Wait_Recal (); Flash++;
      Display_board(0);
      Intensity_a (WHITE_INTENSITY);
      for (row = 0; row < ASTEROIDS; row++) {
        Reset0Ref ();
        set_scale (MEDIUM_SCALE);
        Moveto_d (y[row]-11, x[row]-1);
        set_scale (TINY_SCALE);
        if (row == 12) Intensity_a (BLACK_INTENSITY);
        Draw_VLp ((int8_t *)white_big);
      }

   } while (direction);

    (void)tmp1;(void)base;(void)done;(void)direction; // freeze result
    direction = 24;
    while (--direction != 0) {
      Wait_Recal (); Flash++;
      Display_board(0);
      Intensity_a (WHITE_INTENSITY); // use the constant (name?)
      for (row = 0; row < ASTEROIDS; row++) {
        Reset0Ref ();
        set_scale (MEDIUM_SCALE);
        Moveto_d (y[row]-11, x[row]-1);
        set_scale (TINY_SCALE);
        if (row == 12) Intensity_a (BLACK_INTENSITY);
        Draw_VLp ((int8_t *)white_big);
      }
    }

#undef X
#undef Y
#undef XX
#undef YY
}
//#endif

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

  vectrexinit(1);
  v_setName("draft4");
  v_init();
  usePipeline = 1;
  v_setRefresh(60);
#endif

  intro(); // to do: handle diamonds, and fade in board as well...

  Reinitialise_nearly_everything();
  enable_controller_1_x (); enable_controller_1_y ();

  for (;;) { // MAIN LOOP
    Flash++;
    Wait_Recal ();
    Joy_Digital ();
    Read_Btns ();
    Reset0Ref ();
    Intensity_7F ();
    set_scale (BIG_SCALE);

    Execute_state_machine();

#if defined(DIAGNOSTICS) && DIAGNOSTICS == 2
{   char num[8];
    // show the strength of the computer's position...
    Intensity_7F();
    SHOW_NUM(num, MAX(1));
    Print_Str_d(-120,64, num);
    SHOW_NUM(num, Dim);
    Print_Str_d(-120,-120, num);
}
#endif

  }

  return 0;
} // main