/* ############################################################################ # 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 #include #include #include #ifdef __PROPELLER__ #include #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. +------------------------------------------------------------------ */