spinix-hive/chess/chess.c

1413 lines
36 KiB
C

/*
############################################################################
# This program implements the game of chess on a processor that has multiple
# cores. It works by generating a list of moves, and then each core will
# get a move from the list and evaluate it. This is done by using pthreads,
# where each pthread is mapped to an additional core. The number of
# pthreads is determined by NUM_PTHREADS. For a dual-core x86 processor,
# NUM_PTHREADS is set to 1. The main thread runs on one core, and the
# pthread runs on the second core. Setting NUM_PTHREADS to 0 will cause the
# program to run a single-threaded mode without using pthreads.
#
# This program also run on the Parallax Propeller processor, and uses 6 of
# the 8 cogs that are available. The number of cogs used is limited by the
# amount of hub memory available.
#
# Copyright (c) 2013, Dave Hein
# MIT Licensed
############################################################################
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#ifdef __PROPELLER__
#include <propeller.h>
#define MAX_DEPTH 4
#define NUM_PTHREADS 5
#define PTHREAD_STACKSIZE 1176
#else
#define MAX_DEPTH 5
#define NUM_PTHREADS 1
#endif
#define uchar unsigned char
#define BOARD_BORDER 2
#define BOARD_WIDTH 12
#define BOARD_HEIGHT 12
#define BOARD_SIZE (BOARD_WIDTH * BOARD_HEIGHT)
#define OUT_OF_BOUNDS 0xff
#define PIECE_MASK 0x07
#define COLOR_MASK 0x80
#define WHITE 0x80
#define BLACK 0x00
#define PIECE_MOVED 0x40
#define POSITION_A8 ((BOARD_WIDTH * 2) + 2)
#define POSITION_H8 ((BOARD_WIDTH * 2) + 9)
#define POSITION_A1 ((BOARD_WIDTH * 9) + 2)
#define POSITION_D5 ((BOARD_WIDTH * 5) + 5)
#define POSITION_E5 ((BOARD_WIDTH * 5) + 6)
#define POSITION_D4 ((BOARD_WIDTH * 6) + 5)
#define POSITION_E4 ((BOARD_WIDTH * 6) + 6)
// Piece numbers
#define PAWN 1
#define KNIGHT 2
#define BISHOP 3
#define ROOK 4
#define QUEEN 5
#define KING 6
// Directions
#define N (-BOARD_WIDTH)
#define S BOARD_WIDTH
#define E 1
#define W (-1)
#define NW (N + W)
#define SW (S + W)
#define NE (N + E)
#define SE (S + E)
#define NNW (N + N + W)
#define NNE (N + N + E)
#define SSW (S + S + W)
#define SSE (S + S + E)
#define WSW (W + S + W)
#define WNW (W + N + W)
#define ESE (E + S + E)
#define ENE (E + N + E)
typedef struct levelS {
uchar board[BOARD_SIZE];
short value;
short best_value;
uchar color;
uchar depth;
uchar old_pos;
uchar new_pos;
uchar best_old;
uchar best_new;
uchar wking_pos;
uchar bking_pos;
uchar en_passant;
} levelT;
typedef void (*FuncPtr)(levelT *);
// Function prototypes
void PerformMove(levelT *level);
void QueueUpMove(levelT *level);
void AnalyzeMoveQueue(levelT *level);
void StartPthreads(void);
void GenerateQueuedMoves(levelT *level);
char *symbols = "xPNBRQKx";
// Directions that the pieces can move
int king_moves[8] = {SW, SE, NW, NE, W, S, E, N};
int knight_moves[8] = {NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW};
// The piece values
int values[] = { 0, 20, 64, 65, 100, 195, 10000, 0};
// Pawn position values
signed char pawn_values[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0,
0, 0, 2, 2, 4, 6, 6, 4, 2, 2, 0, 0,
0, 0, 1, 1, 2, 6, 6, 2, 1, 1, 0, 0,
0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0,
0, 0, 1, -1, -2, 0, 0, -2, -1, 1, 0, 0,
0, 0, 1, 2, 2, -5, -5, 2, 2, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// Knight position values
signed char knight_values[] = {
0, 0,-10, -8 -6, -6, -6, -6, -8,-10, 0, 0,
0, 0, -8, -4, 0, 0, 0, 0, -4, -8, 0, 0,
0, 0, -6, 0, 2, 3, 3, 2, 0, -6, 0, 0,
0, 0, -6, 1, 3, 4, 4, 3, 1, -6, 0, 0,
0, 0, -6, 0, 3, 4, 4, 3, 0, -6, 0, 0,
0, 0, -6, 1, 2, 3, 3, 2, 1, -6, 0, 0,
0, 0, -8, -4, 0, 1, 1, 0, -4, -8, 0, 0,
0, 0,-10, -8 -4, -6, -6, -4, -8,-10, 0, 0};
// Bishop position values
signed char bishop_values[] = {
0, 0, -4, -2, -2, -2, -2, -2, -2, -4, 0, 0,
0, 0, -2, 0, 0, 0, 0, 0, 0, -2, 0, 0,
0, 0, -2, 0, 1, 2, 2, 1, 0, -2, 0, 0,
0, 0, -2, 1, 1, 2, 2, 1, 1, -2, 0, 0,
0, 0, -2, 0, 2, 2, 2, 2, 0, -2, 0, 0,
0, 0, -2, 2, 2, 2, 2, 2, 2, -2, 0, 0,
0, 0, -2, 1, 0, 0, 0, 0, 1, -2, 0, 0,
0, 0, -4, -2, -8, -2, -2, -8, -2, -4, 0, 0};
// King position values
signed char king_values[] = {
0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0,
0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0,
0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0,
0, 0, -6, -8, -8,-10,-10, -8, -8, -6, 0, 0,
0, 0, -4, -6, -6, -8, -8, -6, -6, -4, 0, 0,
0, 0, -2, -4, -4, -4, -4, -4, -4, -2, 0, 0,
0, 0, 4, 4, 0, 0, 0, 0, 4, 4, 0, 0,
0, 0, 4, 6, 2, 0, 0, 2, 6, 4, 0, 0};
// Null position values used for the queen and rooks
signed char null_values[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
signed char *pos_values[8] = { null_values, pawn_values, knight_values,
bishop_values, null_values, null_values, king_values, null_values};
uchar black_rank[8] = {ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK};
uchar white_rank[8] = {ROOK | WHITE, KNIGHT | WHITE, BISHOP | WHITE,
QUEEN | WHITE, KING | WHITE, BISHOP | WHITE, KNIGHT | WHITE, ROOK | WHITE};
// Global variables
FuncPtr MoveFunction; // Function that is called for each move
int movenum; // current move number
int person_old; // postion selected by person to move from
int person_new; // postion selected by person to move to
int playdepth; // number of moves to look ahead
int validmove; // indicates if a human's move is valid
int compcolor; // color that the computer is playing
int human_playing; // indicates that a person is playing
char inbuf[80]; // buffer for human input
// Prepare to search the next level
static void InitializeNextLevel(levelT *level, levelT *next_level)
{
memcpy(next_level, level, sizeof(levelT)); // copy board to next level.
next_level->depth++;
}
static void ChangeColor(levelT *level)
{
level->color ^= COLOR_MASK;
}
static int BoardValue(levelT *level)
{
return level->value;
}
// Convert a numeric position value to a string
char *PositionToString(int position)
{
int row, col;
static char str[3];
row = position / BOARD_WIDTH;
col = position % BOARD_WIDTH;
str[0] = col - 2 + 'a';
str[1] = 10 - row + '0';
str[2] = 0;
return str;
}
// Convert a postion string to a numeric value
int StringToPostion(char *str)
{
unsigned int col = tolower(str[0]) - 'a';
unsigned int row = str[1] - '1';
if (col > 7 || row > 7) return -1;
return (7 - row + 2) * BOARD_WIDTH + col + 2;
}
// Print the board
void PrintBoard(levelT *level)
{
int i, j;
uchar *ptr = level->board + (BOARD_WIDTH * BOARD_BORDER);
printf("\n ");
for (i = 'a'; i <= 'h'; i++) printf("|%c ", i);
printf("|");
for (i = 0; i < 8; i++)
{
printf("\n-+--+--+--+--+--+--+--+--+");
printf("\n%c", '8' - i);
for (j = 2; j < 10; j++)
{
if (ptr[j])
{
printf("|%c", symbols[ptr[j] & PIECE_MASK]);
if (ptr[j] & COLOR_MASK)
printf("W");
else
printf("B");
}
else if ((i^j)&1)
printf("|--");
else
printf("| ");
}
printf("|");
ptr += BOARD_WIDTH;
}
printf("\n-+--+--+--+--+--+--+--+--+");
printf("\n\n");
}
// Determine if the board position contains the current color's piece
int IsMyPiece(levelT *level, int offs)
{
uchar *brd = level->board;
if (brd[offs] == 0) return 0;
if (brd[offs] == OUT_OF_BOUNDS) return 0;
return ((brd[offs] & COLOR_MASK) == level->color);
}
// Determine if the board position contains the other color's piece
int IsOtherPiece(levelT *level, int offs)
{
uchar *brd = level->board;
if (brd[offs] == 0) return 0;
if (brd[offs] == OUT_OF_BOUNDS) return 0;
return ((brd[offs] & COLOR_MASK) != level->color);
}
// Determine if the board position does not contain the
// current color's piece and is in bounds.
int IsMoveOK(levelT *level, int offs)
{
uchar *brd = level->board;
return (!IsMyPiece(level, offs) && brd[offs] != OUT_OF_BOUNDS);
}
// Generate moves in a certain direction for a bishop, rook or queen
void AnalyzeDirectionalMoves(levelT *level, int direction)
{
uchar *brd = level->board;
level->new_pos = level->old_pos;
while (1)
{
level->new_pos += direction;
if (IsMyPiece(level, level->new_pos) || brd[level->new_pos] == OUT_OF_BOUNDS) break;
(*MoveFunction)(level);
if (brd[level->new_pos] != 0) break;
}
}
// Determine if the king's space is under attack
int IsCheck(levelT *level)
{
int color = level->color;
uchar *king_ptr = level->board;
int i, row_step, offs, incr, flags, piece;
if (color)
{
row_step = -BOARD_WIDTH;
king_ptr += level->wking_pos;
}
else
{
row_step = BOARD_WIDTH;
king_ptr += level->bking_pos;
}
color ^= COLOR_MASK;
// Check for pawns
if ((king_ptr[row_step + 1] & (COLOR_MASK | PIECE_MASK)) == (color | PAWN))
return 1;
if ((king_ptr[row_step - 1] & (COLOR_MASK | PIECE_MASK)) == (color | PAWN))
return 1;
// Check for knights
for (i = 0; i < 8; i++)
{
if ((king_ptr[knight_moves[i]] & (COLOR_MASK | PIECE_MASK)) == (color | KNIGHT))
return 1;
}
// Check for king, queen, bishop or rook
for (i = 0; i < 8; i++)
{
offs = incr = king_moves[i];
if ((king_ptr[offs] & (COLOR_MASK | PIECE_MASK)) == (color | KING))
return 1;
while (king_ptr[offs] == 0)
offs += incr;
flags = king_ptr[offs];
if ((flags & COLOR_MASK) != color)
continue;
piece = flags & PIECE_MASK;
if (piece == QUEEN)
return 1;
if (i < 4)
{
if (piece == BISHOP)
return 1;
}
else
{
if (piece == ROOK)
return 1;
}
}
return 0;
}
// This routine catches invalid piece values, and should never be called
void Invalid(levelT *level)
{
printf("Invalid piece\n");
exit(1);
}
// Generate all possible moves for a pawn
void Pawn(levelT *level)
{
int row_step = BOARD_WIDTH;
uchar *brd = level->board;
if (level->color)
row_step = -BOARD_WIDTH;
// Check capture to the left
level->new_pos = level->old_pos - 1 + row_step;
if (IsOtherPiece(level, level->new_pos) || level->en_passant == level->old_pos - 1)
(*MoveFunction)(level);
// Check capture to the right
level->new_pos += 2;
if (IsOtherPiece(level, level->new_pos) || level->en_passant == level->old_pos + 1)
(*MoveFunction)(level);
// Check moving forward one space
level->new_pos -= 1;
if (IsMoveOK(level, level->new_pos) && !IsOtherPiece(level, level->new_pos))
(*MoveFunction)(level);
else
return;
// Check moving forward two spaces
if (brd[level->old_pos] & PIECE_MOVED)
return;
level->new_pos += row_step;
if (IsMoveOK(level, level->new_pos) && !IsOtherPiece(level, level->new_pos))
(*MoveFunction)(level);
}
// Generate all possible moves for a knight
void Knight(levelT *level)
{
int i;
uchar *brd = level->board;
for (i = 0; i < 8; i++)
{
level->new_pos = level->old_pos + knight_moves[i];
if (IsMoveOK(level, level->new_pos))
(*MoveFunction)(level); // then generate move
}
}
// Generate all possible moves for a bishop
void Bishop(levelT *level)
{
int i;
for (i = 0; i < 4; i++)
AnalyzeDirectionalMoves(level, king_moves[i]);
}
// Generate all possible moves for a rook
void Rook(levelT *level)
{
int i;
uchar *brd = level->board;
for (i = 4; i < 8; i++)
AnalyzeDirectionalMoves(level, king_moves[i]);
}
// Generate all possible moves for a queen
void Queen(levelT *level)
{
int i;
for (i = 0; i < 8; i++)
AnalyzeDirectionalMoves(level, king_moves[i]);
}
// Determine if this space is under attack
int IsSpaceUnderAttack(levelT *level, int position)
{
int retval, king_pos;
if (level->color)
{
king_pos = level->wking_pos;
level->wking_pos = position;
retval = IsCheck(level);
level->wking_pos = king_pos;
}
else
{
king_pos = level->bking_pos;
level->bking_pos = position;
retval = IsCheck(level);
level->bking_pos = king_pos;
}
return retval;
}
void CastleRight(levelT *level)
{
uchar *brd = level->board;
int old_pos = level->old_pos;
if (brd[old_pos + 1]) return;
if (brd[old_pos + 2]) return;
if (brd[old_pos + 3] & PIECE_MOVED) return;
if (IsCheck(level)) return;
if (IsSpaceUnderAttack(level, level->old_pos + 1)) return;
level->new_pos = level->old_pos + 2;
(*MoveFunction)(level);
}
void CastleLeft(levelT *level)
{
uchar *brd = level->board;
int old_pos = level->old_pos;
if (brd[old_pos - 1]) return;
if (brd[old_pos - 2]) return;
if (brd[old_pos - 3]) return;
if (brd[old_pos - 4] & PIECE_MOVED) return;
if (IsCheck(level)) return;
if (IsSpaceUnderAttack(level, level->old_pos - 1)) return;
level->new_pos = level->old_pos - 2;
(*MoveFunction)(level);
}
// Generate all possible moves for a king
void King(levelT *level)
{
int i;
uchar *brd = level->board;
// Check 8 single-space moves
for (i = 0; i < 8; i++)
{
level->new_pos = level->old_pos + king_moves[i];
if (IsMoveOK(level, level->new_pos))
(*MoveFunction)(level);
}
// Check castling
if (!(brd[level->old_pos] & PIECE_MOVED))
{
CastleRight(level);
CastleLeft(level);
}
}
FuncPtr PieceFunctions[8] = {Invalid, Pawn, Knight, Bishop, Rook, Queen, King, Invalid};
// Call the piece move generator function if this the color is correct
void MoveIfMyPiece(levelT *level)
{
int piece;
uchar *brd = level->board;
if (IsMyPiece(level, level->old_pos))
{
piece = brd[level->old_pos] & PIECE_MASK;
(*PieceFunctions[piece])(level);
}
}
// Generate all moves on the board and analyze them
void AnalyzeAllMoves(levelT *level)
{
int row, col, rowinc;
if (level->color == BLACK)
{
level->old_pos = POSITION_A8; // start at the left top
rowinc = BOARD_WIDTH - 8;
}
else
{
level->old_pos = POSITION_A1; // start at the left bottom
rowinc = -BOARD_WIDTH - 8;
}
for (row = 0; row < 8; row++)
{
for (col = 0; col < 8; col++)
{
MoveIfMyPiece(level);
level->old_pos++;
}
level->old_pos += rowinc;
}
}
// Remove a piece from the board and subtract its value
void RemovePiece(levelT *level, int position)
{
uchar *brd = level->board;
int entry = brd[position];
int piece = entry & PIECE_MASK;
int value = values[piece];
if (entry == 0) return;
if (entry == OUT_OF_BOUNDS)
{
printf("RemovePiece: %d is out of bounds\n", position);
exit(1);
}
if (entry & COLOR_MASK)
{
value += pos_values[piece][position - (2 * BOARD_WIDTH)];
level->value -= value;
}
else
{
value += pos_values[piece][(BOARD_WIDTH * 10) - 1 - position];
level->value += value;
}
brd[position] = 0;
}
// Add a piece to the board and add its value
void AddPiece(levelT *level, int position, int entry)
{
uchar *brd = level->board;
int piece = entry & PIECE_MASK;
int value = values[piece];
if (brd[position])
{
printf("AddPiece: %d occupied\n", position);
exit(1);
}
if (brd[position])
{
printf("RemovePiece: %d is out of bounds\n", position);
exit(1);
}
if (entry & COLOR_MASK)
{
value += pos_values[piece][position - (2 * BOARD_WIDTH)];
level->value += value;
}
else
{
value += pos_values[piece][(BOARD_WIDTH * 10) - 1 - position];
level->value -= value;
}
brd[position] = entry | PIECE_MOVED;
}
// Move a piece from one place to another and adjust the board's value
void MovePiece(levelT *level, int old_pos, int new_pos)
{
uchar *brd = level->board;
int entry1 = brd[old_pos];
int entry2 = brd[new_pos];
int piece = entry1 & PIECE_MASK;
int value = values[piece];
if (entry1 == 0) return;
if (entry1 == OUT_OF_BOUNDS)
{
printf("MovePiece: %d is out of bounds\n", old_pos);
exit(1);
}
if (entry2)
RemovePiece(level, new_pos);
RemovePiece(level, old_pos);
AddPiece(level, new_pos, entry1);
}
// Move a piece and remove an opponent's piece if taken
// Check for castling, en passant capture and pawn promotion
void PerformMove(levelT *level)
{
uchar *brd = level->board;
int val1 = brd[level->old_pos];
int val2 = brd[level->new_pos];
int en_passant = level->en_passant;
int value = values[val2 & PIECE_MASK];
// Clear en_passant flag. May be set later.
level->en_passant = 0;
// Check if taking opponent's piece
if (val2) RemovePiece(level, level->new_pos);
// Check if moving king
if ((val1 & PIECE_MASK) == KING)
{
// Update it's position
if (val1 & COLOR_MASK)
level->wking_pos = level->new_pos;
else
level->bking_pos = level->new_pos;
// Check for castle right
if (level->new_pos == level->old_pos + 2)
{
if (level->depth == 0) printf("CASTLE RIGHT\n\n");
MovePiece(level, level->old_pos + 3, level->old_pos + 1);
}
// Check for castle left
if (level->new_pos == level->old_pos - 2)
{
if (level->depth == 0) printf("CASTLE LEFT\n\n");
MovePiece(level, level->old_pos - 4, level->old_pos - 1);
}
}
if ((val1 & PIECE_MASK) == PAWN)
{
if (val1 & COLOR_MASK)
{
// Set the en passant flag if moving pawn two spaces
if (level->new_pos == level->old_pos - (2 * BOARD_WIDTH))
{
level->en_passant = level->new_pos;
}
// Check for en passant capture
else if (level->new_pos == en_passant - BOARD_WIDTH)
{
if (level->depth == 0) printf("EN PASSANT\n\n");
RemovePiece(level, en_passant);
}
// Promote pawn to queen if reaching final rank
else if (level->new_pos <= POSITION_H8)
{
RemovePiece(level, level->old_pos);
AddPiece(level, level->new_pos, WHITE | QUEEN);
return;
}
}
else
{
// Set the en passant flag if moving pawn two spaces
if (level->new_pos == level->old_pos + (2 * BOARD_WIDTH))
{
level->en_passant = level->new_pos;
}
// Check for en passant capture
else if (level->new_pos == en_passant + BOARD_WIDTH)
{
if (level->depth == 0) printf("EN PASSANT\n\n");
RemovePiece(level, en_passant);
}
// Promote pawn to queen if reaching final rank
else if (level->new_pos >= POSITION_A1)
{
RemovePiece(level, level->old_pos);
AddPiece(level, level->new_pos, BLACK | QUEEN);
return;
}
}
}
MovePiece(level, level->old_pos, level->new_pos);
}
#ifdef DEBUG
int IsBoardValid(levelT *level)
{
int row, col;
uchar *ptr = level->board;
for (row = 0; row < BOARD_HEIGHT; row++)
{
for (col = 0; col < BOARD_WIDTH; col++)
{
if (*ptr == 0) {}
else if (*ptr == OUT_OF_BOUNDS)
{
if (row >= 2 && row < 10 && col >= 2 && col < 10)
return 0;
}
else if (*ptr == 0x80)
return 0;
else if (*ptr & 0x38)
return 0;
else if (((*ptr) & PIECE_MASK) == 7)
return 0;
ptr++;
}
}
return 1;
}
#endif
// Analyze move from old_pos to new_pos. If we have reached the maximum depth
// check if the board value is better than the values from the previous moves.
// If we have not reached the maximum depth, determine the best counter-move
// at the next level, and check if better than any previous move.
// In the case of a tie, pick the new move 25% of the time.
void AnalyzeMove(levelT *level)
{
levelT next_level;
int update, value;
uchar *ptr = level->board;
if (ptr[level->old_pos] == OUT_OF_BOUNDS || ptr[level->new_pos] == OUT_OF_BOUNDS)
{
printf("BAD MOVE: %2.2x-%2.2x\n", level->old_pos, level->new_pos);
exit(0);
}
InitializeNextLevel(level, &next_level);
PerformMove(&next_level);
if (IsCheck(&next_level)) return;
#ifdef DEBUG
if (!IsBoardValid(level))
{
printf("BAD BOARD!\n");
exit(0);
}
#endif
value = BoardValue(&next_level);
// Stop searching if checkmate
if (value > 5000 || value < -5000)
{
level->best_value = value;
level->best_old = level->old_pos;
level->best_new = level->new_pos;
}
else if (next_level.depth == playdepth)
{
if (level->color)
update = (value > level->best_value);
else
update = (value < level->best_value);
if (update)
{
level->best_value = value;
level->best_old = level->old_pos;
level->best_new = level->new_pos;
}
}
else
{
ChangeColor(&next_level);
if (next_level.color == BLACK)
next_level.best_value = 0x7fff;
else
next_level.best_value = -0x7fff;
next_level.best_old = 0;
next_level.best_new = 0;
AnalyzeAllMoves(&next_level);
if (!next_level.best_old)
{
// Check for check
if (IsCheck(&next_level))
{
if (level->color)
next_level.best_value = 10000;
else
next_level.best_value = -10000;
}
else
next_level.best_value = 0; // Should go for draw only if way behind
}
value = next_level.best_value;
if (value == level->best_value)
{
update = ((rand() & 3) == 0);
}
else
{
if (level->color)
update = (value > level->best_value);
else
update = (value < level->best_value);
}
if (update)
{
level->best_value = value;
level->best_old = level->old_pos;
level->best_new = level->new_pos;
}
}
}
int IsCheckMate(levelT *level)
{
int retval = 0;
int playdepth_save = playdepth;
playdepth = 2;
MoveFunction = AnalyzeMove;
if (level->color == BLACK)
level->best_value = 0x7fff;
else
level->best_value = -0x7fff;
level->best_old = 0;
level->best_new = 0;
AnalyzeAllMoves(level);
if (level->best_value > 5000 || level->best_value < -5000)
{
printf("CHECKMATE\n");
retval = 1;
}
playdepth = playdepth_save;
return retval;
}
// Analyze all possible moves and select the best one
int PerformComputerMove(levelT *level)
{
int value;
#if NUM_PTHREADS
GenerateQueuedMoves(level);
#endif
MoveFunction = AnalyzeMove;
if (level->color == BLACK)
level->best_value = 0x7fff;
else
level->best_value = -0x7fff;
level->best_old = 0;
level->best_new = 0;
#if NUM_PTHREADS
AnalyzeMoveQueue(level);
#else
AnalyzeAllMoves(level);
#endif
// Check if best_old was updated, which indicates at least one move
if (level->best_old)
{
if (level->best_old == level->best_new)
printf("STALEMATE\n");
level->old_pos = level->best_old;
level->new_pos = level->best_new;
PerformMove(level);
}
else
{
printf("Couldn't find a move\n");
printf("STALEMATE\n");
}
value = BoardValue(level);
if (value > 5000 || value < -5000)
{
printf("CHECKMATE\n");
return 0;
}
if (movenum > 200)
{
printf("STALEMATE\n");
return 0;
}
if (level->color)
printf("White's Move %d: ", ++movenum);
else
printf("Blacks's Move %d: ", movenum);
printf(" %s", PositionToString(level->best_old));
printf("-%s\n", PositionToString(level->best_new));
// printf("value = %d, best_value = %d\n", value, level->best_value);
return 1;
}
// Check if the person's intended move matches any generated moves
void CheckPersonMove(levelT *level)
{
levelT next_level;
// Is there a match?
if ((level->old_pos == person_old) && (person_new == level->new_pos))
{
InitializeNextLevel(level, &next_level);
PerformMove(&next_level);
if (!IsCheck(&next_level))
{
validmove = 1;
memcpy(level, &next_level, sizeof(levelT));
}
}
}
// Prompt for a move and check if it's valid
int PerformPersonMove(levelT *level)
{
levelT next_level;
validmove = 0;
if (level->color)
movenum++;
// Loop until we get a valid move
while (!validmove)
{
if (level->color)
printf("White's Move %d: ", movenum);
else
printf("Blacks's Move %d: ", movenum);
gets(inbuf);
if (toupper(inbuf[0]) == 'Q')
__builtin_propeller_clkset(128); // return 0;
person_old = StringToPostion(inbuf);
person_new = StringToPostion(inbuf + 3);
if (person_old >= 0 && person_new >= 0 && inbuf[2] == '-')
{
MoveFunction = CheckPersonMove;
level->old_pos = person_old;
memcpy(&next_level, level, sizeof(levelT));
MoveIfMyPiece(&next_level);
}
}
next_level.depth = 0;
memcpy(level, &next_level, sizeof(levelT));
return 1;
}
// Prompt person for color, and set the computer's color
static void GetColor()
{
printf("Do you want White (Y/N): ");
gets(inbuf);
compcolor = (toupper(inbuf[0]) == 'Y') ? BLACK : WHITE;
}
// Prompt for the playing level
static void GetPlayLevel()
{
playdepth = 0;
while (playdepth < 1 || playdepth > MAX_DEPTH)
{
printf("Enter Play Level (1-%d): ", MAX_DEPTH);
gets(inbuf);
sscanf(inbuf, "%d", &playdepth);
}
}
void Initialize(levelT *level)
{
uchar *ptr = level->board + POSITION_A8;
memset(level->board, OUT_OF_BOUNDS, BOARD_SIZE);
memcpy(ptr, black_rank, 8);
memset(ptr + BOARD_WIDTH, BLACK | PAWN, 8);
memset(ptr + (BOARD_WIDTH * 2), 0, 8);
memset(ptr + (BOARD_WIDTH * 3), 0, 8);
memset(ptr + (BOARD_WIDTH * 4), 0, 8);
memset(ptr + (BOARD_WIDTH * 5), 0, 8);
memset(ptr + (BOARD_WIDTH * 6), WHITE | PAWN, 8);
memcpy(ptr + (BOARD_WIDTH * 7), white_rank, 8);
movenum = 0;
level->depth = 0;
level->color = WHITE;
level->value = 0;
level->wking_pos = POSITION_A1 + 4;
level->bking_pos = POSITION_A8 + 4;
level->en_passant = 0;
}
void PlayChess()
{
int retval;
levelT level;
GetPlayLevel();
printf("Do you want to play against the computer? (Y/N): ");
gets(inbuf);
human_playing = (toupper(inbuf[0]) == 'Y');
if (human_playing)
GetColor();
else
compcolor = WHITE;
Initialize(&level);
PrintBoard(&level);
while (1)
{
if (compcolor == level.color)
retval = PerformComputerMove(&level);
else
retval = PerformPersonMove(&level);
if (!retval) return;
PrintBoard(&level);
if (IsCheck(&level))
printf("Illegal move into check %d\n", level.color);
ChangeColor(&level);
if (IsCheck(&level))
{
if (IsCheckMate(&level)) break;
printf("CHECK\n\n");
}
if (!human_playing)
compcolor ^= COLOR_MASK;
}
}
int main()
{
#ifdef __PROPELLER__
sleep(1);
#endif
printf("Threaded Chess\n");
srand(1);
#if NUM_PTHREADS
StartPthreads();
#endif
while (1)
PlayChess();
return 1;
}
// ****************************************************************
// Pthread code
// ****************************************************************
#if NUM_PTHREADS
int queue_max = 0;
int queue_num;
int queue_index;
uchar queue_old[200];
uchar queue_new[200];
#ifdef __PROPELLER__
int queue_lock;
#else
pthread_mutex_t queue_lock;
#endif
levelT thread_level[NUM_PTHREADS];
volatile int thread_active[NUM_PTHREADS];
pthread_t threads[NUM_PTHREADS];
#ifdef __PROPELLER__
int stacks[NUM_PTHREADS][PTHREAD_STACKSIZE/4];
int mainstackstart = 0;
int mainstackend;
void InitializeMainStack()
{
int i;
int retval = (int)malloc(4);
if (retval)
{
mainstackstart = (retval & ~3) + 80;
mainstackend = ((int)&retval) - 80;
free((void *)retval);
retval = (int)(&retval) - retval;
for (i = mainstackstart; i < mainstackend; i += 4)
*(int *)i = 0xdeadbeef;
}
//printf("Main stack space = %d bytes\n", retval);
}
void CheckPthreadStacks()
{
int i, j;
for (j = mainstackstart; j < 0x8000; j += 4)
{
if (*(int *)j != 0xdeadbeef) break;
}
if (j - mainstackstart < 100)
printf("Main stack space available = %d bytes\n", j - mainstackstart);
for (i = 0; i < NUM_PTHREADS; i++)
{
for (j = 0; j < PTHREAD_STACKSIZE/4; j++)
{
if (stacks[i][j] != 0xdeadbeef) break;
}
if (j * 4 < 100)
printf("Pthread %d stack space available = %d bytes\n", i, j * 4);
}
}
#endif
void GenerateQueuedMoves(levelT *level)
{
MoveFunction = QueueUpMove;
queue_num = 0;
AnalyzeAllMoves(level);
if (queue_max < queue_num)
queue_max = queue_num;
// printf("%d moves queued, %d max queued\n", queue_num, queue_max);
}
static int GetQueuedItem()
{
int index;
#ifdef __PROPELLER__
while (lockset(queue_lock));
index = queue_index;
if (queue_index < queue_num) queue_index++;
lockclr(queue_lock);
#else
pthread_mutex_lock(&queue_lock);
index = queue_index;
if (queue_index < queue_num) queue_index++;
pthread_mutex_unlock(&queue_lock);
#endif
return index;
}
void ProcessMoveQueue(levelT *level)
{
int index;
while (1)
{
index = GetQueuedItem();
if (index >= queue_num) break;
level->old_pos = queue_old[index];
level->new_pos = queue_new[index];
(*MoveFunction)(level);
}
}
// This routine is run in all the pthreads
void *ThreadFunc(void *arg)
{
int instance = (int)arg;
while (1)
{
usleep(1000);
if (!thread_active[instance]) continue;
ProcessMoveQueue(&thread_level[instance]);
thread_active[instance] = 0;
}
return 0;
}
// This routine is run by the main thread
void AnalyzeMoveQueue(levelT *level)
{
int index, update;
queue_index = 0;
// Copy level to other thread's level vars, and set active flag
for (index = 0; index < NUM_PTHREADS; index++)
{
memcpy(&thread_level[index], level, sizeof(levelT));
thread_active[index] = 1;
}
ProcessMoveQueue(level);
#if 0
printf("Analyze %s", PositionToString(level->old_pos));
printf("-%s\n", PositionToString(level->new_pos));
#endif
// Wait for all the ptheads to finish
while (1)
{
for (index = 0; index < NUM_PTHREADS; index++)
{
if (thread_active[index]) break;
}
if (index == NUM_PTHREADS) break;
}
#ifdef __PROPELLER__
CheckPthreadStacks();
#endif
// Find the best move from the different threads
for (index = 0; index < NUM_PTHREADS; index++)
{
if (level->color)
update = (thread_level[index].best_value > level->best_value);
else
update = (thread_level[index].best_value < level->best_value);
if (update)
{
level->best_value = thread_level[index].best_value;
level->best_old = thread_level[index].best_old;
level->best_new = thread_level[index].best_new;
}
}
}
#ifdef __PROPELLER__
void StartPthreads(void)
{
int i, j;
pthread_attr_t attr;
for (i = 0; i < NUM_PTHREADS; i++)
{
thread_active[i] = 0;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, PTHREAD_STACKSIZE);
pthread_attr_setstackaddr(&attr, stacks[i]);
for (j = 0; j < PTHREAD_STACKSIZE/4; j++)
stacks[i][j] = 0xdeadbeef;
if (pthread_create(&threads[i], &attr, ThreadFunc, (void *)i))
{
printf("pthread_create %d failed\n", i);
exit(1);
}
}
InitializeMainStack();
queue_lock = locknew();
}
#else
void StartPthreads()
{
int i;
for (i = 0; i < NUM_PTHREADS; i++)
{
thread_active[i] = 0;
if (pthread_create(&threads[i], 0, ThreadFunc, (void *)i))
{
printf("pthread_create %d failed\n", i);
exit(1);
}
}
pthread_mutex_init(&queue_lock, NULL);
}
#endif
void QueueUpMove(levelT *level)
{
levelT next_level;
uchar *ptr = level->board;
if (ptr[level->old_pos] == OUT_OF_BOUNDS || ptr[level->new_pos] == OUT_OF_BOUNDS)
{
printf("BAD MOVE: %2.2x-%2.2x\n", level->old_pos, level->new_pos);
exit(0);
}
InitializeNextLevel(level, &next_level);
PerformMove(&next_level);
if (IsCheck(&next_level)) return;
#ifdef DEBUG
if (!IsBoardValid(level))
{
printf("BAD BOARD!\n");
exit(0);
}
#endif
queue_old[queue_num] = level->old_pos;
queue_new[queue_num] = level->new_pos;
queue_num++;
}
#endif
/*
+--------------------------------------------------------------------
| TERMS OF USE: MIT License
+--------------------------------------------------------------------
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------
*/