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