Add castling
This commit is contained in:
309
src/chess.c
309
src/chess.c
@@ -56,7 +56,7 @@ static const double piece_value[] = {
|
|||||||
[KNIGHT] = 3,
|
[KNIGHT] = 3,
|
||||||
[ROOK] = 5,
|
[ROOK] = 5,
|
||||||
[QUEEN] = 9,
|
[QUEEN] = 9,
|
||||||
[KING] = 0,
|
[KING] = 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const double piece_position_bonus[7][BOARD_SIZE * sizeof(double)] = {
|
static const double piece_position_bonus[7][BOARD_SIZE * sizeof(double)] = {
|
||||||
@@ -64,10 +64,10 @@ static const double piece_position_bonus[7][BOARD_SIZE * sizeof(double)] = {
|
|||||||
[PAWN] = {
|
[PAWN] = {
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
|
1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0,
|
||||||
|
1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0,
|
||||||
|
1.2, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.2,
|
||||||
1.7, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.7,
|
1.7, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.7,
|
||||||
1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0,
|
|
||||||
1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0,
|
|
||||||
1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0,
|
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
},
|
},
|
||||||
@@ -85,10 +85,10 @@ static const double piece_position_bonus[7][BOARD_SIZE * sizeof(double)] = {
|
|||||||
0.5, 0.7, 0.8, 0.8, 0.8, 0.8, 0.7, 0.5,
|
0.5, 0.7, 0.8, 0.8, 0.8, 0.8, 0.7, 0.5,
|
||||||
0.6, 0.7, 0.9, 0.9, 0.9, 0.9, 0.7, 0.6,
|
0.6, 0.7, 0.9, 0.9, 0.9, 0.9, 0.7, 0.6,
|
||||||
0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6,
|
0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6,
|
||||||
0.6, 1.0, 1.0, 1.1, 1.1, 1.0, 1.0, 0.6,
|
|
||||||
0.6, 1.0, 1.0, 1.1, 1.1, 1.0, 1.0, 0.6,
|
|
||||||
0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6,
|
0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6,
|
||||||
0.6, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.6,
|
0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6,
|
||||||
|
0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6,
|
||||||
|
0.6, 0.7, 0.9, 0.9, 0.9, 0.9, 0.7, 0.6,
|
||||||
0.5, 0.7, 0.8, 0.8, 0.8, 0.8, 0.7, 0.5,
|
0.5, 0.7, 0.8, 0.8, 0.8, 0.8, 0.7, 0.5,
|
||||||
},
|
},
|
||||||
[ROOK] = {
|
[ROOK] = {
|
||||||
@@ -112,6 +112,8 @@ static const double piece_position_bonus[7][BOARD_SIZE * sizeof(double)] = {
|
|||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
},
|
},
|
||||||
[KING] = {
|
[KING] = {
|
||||||
|
/*a b c d e f g h*/
|
||||||
|
1.4, 1.4, 1.4, 1.0, 1.0, 1.0, 99, 1.6,
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
@@ -119,21 +121,13 @@ static const double piece_position_bonus[7][BOARD_SIZE * sizeof(double)] = {
|
|||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
|
||||||
1.001, 1.001, 1.0, 1.0, 1.0, 1.0, 1.001, 1.001,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TILE(col, row) ((col)-'A'+ROW*((row)-1))
|
|
||||||
//static inline index_t tile(char col, int row)
|
|
||||||
//{
|
|
||||||
// assert(row >= 1);
|
|
||||||
// assert(row <= 8);
|
|
||||||
// assert(col >= 'A');
|
|
||||||
// assert(col <= 'H');
|
|
||||||
// return col-'A' + ROW*(row-1);
|
|
||||||
//}
|
|
||||||
|
|
||||||
static bool friends(piece_t a, piece_t b)
|
#define TILE(col, row) ((col)-'A'+ ROW*((row)-1))
|
||||||
|
|
||||||
|
static inline bool friends(piece_t a, piece_t b)
|
||||||
{
|
{
|
||||||
return a * b > 0;
|
return a * b > 0;
|
||||||
}
|
}
|
||||||
@@ -154,26 +148,44 @@ static int signum(int t)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static index_t row(index_t i)
|
static inline index_t row(index_t i)
|
||||||
{
|
{
|
||||||
return i / ROW;
|
return i / ROW;
|
||||||
}
|
}
|
||||||
|
|
||||||
static index_t column(index_t i)
|
static inline index_t column(index_t i)
|
||||||
{
|
{
|
||||||
return i % ROW;
|
return i % ROW;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool enemies(piece_t a, piece_t b)
|
static inline bool enemies(piece_t a, piece_t b)
|
||||||
{
|
{
|
||||||
return a * b < 0;
|
return a * b < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static piece_t piece_color(piece_t t)
|
static inline piece_t piece_color(piece_t t)
|
||||||
{
|
{
|
||||||
return (piece_t)signum(t);
|
return (piece_t)signum(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char * piece_str(piece_t p) {
|
||||||
|
static const char * const table[] = {
|
||||||
|
[EMPTY] = "EMPTY",
|
||||||
|
[PAWN] = "PAWN",
|
||||||
|
[BISHOP] = "BISHOP",
|
||||||
|
[KNIGHT] = "KNIGHT",
|
||||||
|
[ROOK] = "ROOK",
|
||||||
|
[QUEEN] = "QUEEN",
|
||||||
|
[KING] = "KING",
|
||||||
|
};
|
||||||
|
p = piece_abs(p);
|
||||||
|
if (p >= 0 && (size_t)p < sizeof table / sizeof *table) {
|
||||||
|
return table[piece_abs(p)];
|
||||||
|
} else {
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void board_paint(struct game_state* g)
|
static void board_paint(struct game_state* g)
|
||||||
{
|
{
|
||||||
/* https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode
|
/* https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode
|
||||||
@@ -184,8 +196,9 @@ static void board_paint(struct game_state* g)
|
|||||||
#define FG_BLACK() printf("\033[38;2;0;0;0m")
|
#define FG_BLACK() printf("\033[38;2;0;0;0m")
|
||||||
#define FG_WHITE() printf("\033[38;2;255;255;255m")
|
#define FG_WHITE() printf("\033[38;2;255;255;255m")
|
||||||
#define UNICODE_CHESS_SYMBOL 0x2659
|
#define UNICODE_CHESS_SYMBOL 0x2659
|
||||||
for (size_t i = 0; i < 8; i++) {
|
for (ssize_t i = 7; i >= 0; i--) {
|
||||||
printf("\n %zu ", 8 - i); // number coordinates
|
//for (ssize_t i = 0; i < 8; i++) {
|
||||||
|
printf("\n %zu ", i+1); // number coordinates
|
||||||
for (size_t j = 0; j < 8; j++) {
|
for (size_t j = 0; j < 8; j++) {
|
||||||
piece_t t = g->board[i * 8 + j];
|
piece_t t = g->board[i * 8 + j];
|
||||||
if ((i + j) % 2)
|
if ((i + j) % 2)
|
||||||
@@ -218,13 +231,13 @@ static void move(struct game_state* g, index_t from, index_t to)
|
|||||||
if (piece_abs(g->board[from]) == KING) {
|
if (piece_abs(g->board[from]) == KING) {
|
||||||
g->king_pos[p] = to;
|
g->king_pos[p] = to;
|
||||||
g->king_touched[p] = true;
|
g->king_touched[p] = true;
|
||||||
} else if (from == TILE('A', 7)) {
|
} else if (from == TILE('A', 8)) {
|
||||||
g->a_rook_touched[0] = true;
|
g->a_rook_touched[0] = true;
|
||||||
} else if (from == TILE('A', 0)) {
|
} else if (from == TILE('A', 1)) {
|
||||||
g->a_rook_touched[1] = true;
|
g->a_rook_touched[1] = true;
|
||||||
} else if (from == TILE('H', 0)) {
|
} else if (from == TILE('H', 1)) {
|
||||||
g->h_rook_touched[0] = true;
|
g->h_rook_touched[0] = true;
|
||||||
} else if (from == TILE('H', 7)) {
|
} else if (from == TILE('H', 8)) {
|
||||||
g->h_rook_touched[1] = true;
|
g->h_rook_touched[1] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,22 +247,57 @@ static void move(struct game_state* g, index_t from, index_t to)
|
|||||||
g->turns_without_captures = 0;
|
g->turns_without_captures = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (piece_abs(g->board[from]) == KING) {
|
||||||
|
switch (to) {
|
||||||
|
case TILE('G', 1):
|
||||||
|
g->board[TILE('E', 1)] = EMPTY;
|
||||||
|
g->board[TILE('F', 1)] = ROOK;
|
||||||
|
g->board[TILE('G', 1)] = KING;
|
||||||
|
g->board[TILE('H', 1)] = EMPTY;
|
||||||
|
break;
|
||||||
|
case TILE('C', 1):
|
||||||
|
g->board[TILE('A', 1)] = EMPTY;
|
||||||
|
g->board[TILE('B', 1)] = EMPTY;
|
||||||
|
g->board[TILE('C', 1)] = KING;
|
||||||
|
g->board[TILE('D', 1)] = ROOK;
|
||||||
|
g->board[TILE('E', 1)] = EMPTY;
|
||||||
|
break;
|
||||||
|
case TILE('G', 8):
|
||||||
|
g->board[TILE('E', 8)] = EMPTY;
|
||||||
|
g->board[TILE('F', 8)] = ROOK;
|
||||||
|
g->board[TILE('G', 8)] = KING;
|
||||||
|
g->board[TILE('H', 8)] = EMPTY;
|
||||||
|
break;
|
||||||
|
case TILE('C', 8):
|
||||||
|
g->board[TILE('A', 8)] = EMPTY;
|
||||||
|
g->board[TILE('B', 8)] = EMPTY;
|
||||||
|
g->board[TILE('C', 8)] = KING;
|
||||||
|
g->board[TILE('D', 8)] = ROOK;
|
||||||
|
g->board[TILE('E', 8)] = EMPTY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
g->board[to] = g->board[from];
|
g->board[to] = g->board[from];
|
||||||
g->board[from] = EMPTY;
|
g->board[from] = EMPTY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g->board[to] = g->board[from];
|
||||||
|
g->board[from] = EMPTY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bitmap_t pawn_threatmap(struct game_state* g, index_t index)
|
static bitmap_t pawn_threatmap(struct game_state* g, index_t index)
|
||||||
{
|
{
|
||||||
index_t direction = -1 * piece_color(g->board[index]);
|
const index_t direction = piece_color(g->board[index]);
|
||||||
index_t left = BIT(index + ROW*direction - 1);
|
const index_t left = BIT(index + ROW*direction - 1);
|
||||||
index_t right = BIT(index + ROW*direction + 1);
|
const index_t right = BIT(index + ROW*direction + 1);
|
||||||
|
|
||||||
if (column(index) == 7)
|
if (column(index) == column(TILE('A', 1)))
|
||||||
return left;
|
|
||||||
|
|
||||||
if (column(index) == 0)
|
|
||||||
return right;
|
return right;
|
||||||
|
|
||||||
|
if (column(index) == column(TILE('H', 1)))
|
||||||
|
return left;
|
||||||
|
|
||||||
return left | right;
|
return left | right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,8 +393,10 @@ static bitmap_t knight_threatmap(index_t index)
|
|||||||
for (size_t i = 0; i < N_ELEMS(knight_wheel); i += 2) {
|
for (size_t i = 0; i < N_ELEMS(knight_wheel); i += 2) {
|
||||||
index_t atks = index + knight_wheel[i] + knight_wheel[i+1] * ROW;
|
index_t atks = index + knight_wheel[i] + knight_wheel[i+1] * ROW;
|
||||||
|
|
||||||
if (column(index) + knight_wheel[i] < 0 || column(index) + knight_wheel[i] > 7
|
if (column(index) + knight_wheel[i] < 0
|
||||||
|| row(index) + knight_wheel[i+1] < 0 || row(index) + knight_wheel[i+1] > 7)
|
|| column(index) + knight_wheel[i] > 7
|
||||||
|
|| row(index) + knight_wheel[i+1] < 0
|
||||||
|
|| row(index) + knight_wheel[i+1] > 7)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
threatened |= BIT(atks);
|
threatened |= BIT(atks);
|
||||||
@@ -357,33 +407,40 @@ static bitmap_t knight_threatmap(index_t index)
|
|||||||
|
|
||||||
static bitmap_t king_threatmap(index_t index)
|
static bitmap_t king_threatmap(index_t index)
|
||||||
{
|
{
|
||||||
if (row(index) == 0 && column(index) == 0) {
|
// I fucking hate this function so much
|
||||||
|
if (row(index) == 0) {
|
||||||
|
if (column(index) == 0) {
|
||||||
return BIT(index+1) | BIT(index+ROW+1) | BIT (index+ROW);
|
return BIT(index+1) | BIT(index+ROW+1) | BIT (index+ROW);
|
||||||
}
|
}
|
||||||
else if (row(index) == 0 && column(index) == 7) {
|
else if (column(index) == 7) {
|
||||||
return BIT(index-1) | BIT(index+ROW-1) | BIT (index+ROW);
|
return BIT(index-1) | BIT(index+ROW-1) | BIT (index+ROW);
|
||||||
}
|
}
|
||||||
else if (row(index) == 7 && column(index) == 0) {
|
else {
|
||||||
return BIT(index+1) | BIT(index-ROW+1) | BIT (index-ROW);
|
|
||||||
}
|
|
||||||
else if (row(index) == 7 && column(index) == 7) {
|
|
||||||
return BIT(index-1) | BIT(index-ROW-1) | BIT (index-ROW);
|
|
||||||
}
|
|
||||||
else if (row(index) == 0) {
|
|
||||||
return BIT(index-1) | BIT(index+1) | BIT(index+ROW-1) | BIT(index+ROW) | BIT(index+ROW+1);
|
return BIT(index-1) | BIT(index+1) | BIT(index+ROW-1) | BIT(index+ROW) | BIT(index+ROW+1);
|
||||||
}
|
}
|
||||||
else if (row(index) == 7) {
|
}
|
||||||
|
if (row(index) == 7) {
|
||||||
|
if (column(index) == 0) {
|
||||||
|
return BIT(index+1) | BIT(index-ROW+1) | BIT (index-ROW);
|
||||||
|
}
|
||||||
|
else if (column(index) == 7) {
|
||||||
|
return BIT(index-1) | BIT(index-ROW-1) | BIT (index-ROW);
|
||||||
|
}
|
||||||
|
else {
|
||||||
return BIT(index-1) | BIT(index+1) | BIT(index-ROW-1) | BIT(index-ROW) | BIT(index-ROW+1);
|
return BIT(index-1) | BIT(index+1) | BIT(index-ROW-1) | BIT(index-ROW) | BIT(index-ROW+1);
|
||||||
}
|
}
|
||||||
else if (column(index) == 0) {
|
} else {
|
||||||
|
if (column(index) == 0) {
|
||||||
return BIT(index-ROW) | BIT(index-ROW+1) | BIT(index+1) | BIT(index+ROW+1) | BIT(index+ROW);
|
return BIT(index-ROW) | BIT(index-ROW+1) | BIT(index+1) | BIT(index+ROW+1) | BIT(index+ROW);
|
||||||
}
|
}
|
||||||
else if (column(index) == 7) {
|
else if (column(index) == 7) {
|
||||||
return BIT(index-ROW) | BIT(index-ROW-1) | BIT(index-1) | BIT(index+ROW-1) | BIT(index+ROW);
|
return BIT(index-ROW) | BIT(index-ROW-1) | BIT(index-1) | BIT(index+ROW-1) | BIT(index+ROW);
|
||||||
}
|
} else {
|
||||||
return BIT(index-ROW-1) | BIT(index-ROW) | BIT(index-ROW+1)
|
return BIT(index-ROW-1) | BIT(index-ROW) | BIT(index-ROW+1)
|
||||||
| BIT(index-1) | BIT(index+1)
|
| BIT(index-1) | BIT(index+1)
|
||||||
| BIT(index+ROW-1) | BIT(index+ROW) | BIT(index+ROW+1);
|
| BIT(index+ROW-1) | BIT(index+ROW) | BIT(index+ROW+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bitmap_t queen_threatmap(struct game_state* g, index_t index)
|
static inline bitmap_t queen_threatmap(struct game_state* g, index_t index)
|
||||||
@@ -393,16 +450,17 @@ static inline bitmap_t queen_threatmap(struct game_state* g, index_t index)
|
|||||||
|
|
||||||
static void print_threatmap(bitmap_t threatmap)
|
static void print_threatmap(bitmap_t threatmap)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < 64; i++) {
|
for (ssize_t i=7; i >= 0; i--) {
|
||||||
putc(' ', stdout);
|
|
||||||
|
|
||||||
if (i % 8 == 0)
|
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
if (threatmap & BIT(i))
|
for (size_t j = 0; j < 8; j++) {
|
||||||
|
fputc(' ', stdout);
|
||||||
|
if (threatmap & BIT(i*ROW+j))
|
||||||
fputc('x', stdout);
|
fputc('x', stdout);
|
||||||
else
|
else
|
||||||
fputc('-', stdout);
|
fputc('-', stdout);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
fputc('\n', stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bitmap_t piece_threatmap(struct game_state* g, index_t index)
|
static bitmap_t piece_threatmap(struct game_state* g, index_t index)
|
||||||
@@ -430,7 +488,6 @@ static bitmap_t piece_threatmap(struct game_state* g, index_t index)
|
|||||||
static bitmap_t threatmap(struct game_state* g, int attacker)
|
static bitmap_t threatmap(struct game_state* g, int attacker)
|
||||||
{
|
{
|
||||||
bitmap_t threatened = 0;
|
bitmap_t threatened = 0;
|
||||||
|
|
||||||
for(index_t i = 0; i < BOARD_SIZE; i++) {
|
for(index_t i = 0; i < BOARD_SIZE; i++) {
|
||||||
if (friends(g->board[i], attacker)) {
|
if (friends(g->board[i], attacker)) {
|
||||||
threatened |= piece_threatmap(g, i);
|
threatened |= piece_threatmap(g, i);
|
||||||
@@ -442,33 +499,53 @@ static bitmap_t threatmap(struct game_state* g, int attacker)
|
|||||||
|
|
||||||
static bool pawn_move_ok(struct game_state* g, index_t from, index_t to)
|
static bool pawn_move_ok(struct game_state* g, index_t from, index_t to)
|
||||||
{
|
{
|
||||||
const index_t direction = -1 * piece_color(g->board[from]);
|
const index_t direction = piece_color(g->board[from]);
|
||||||
const index_t diff = (to - from) * direction;
|
const index_t diff = (to - from) * direction;
|
||||||
|
|
||||||
|
//printf("\n");
|
||||||
|
|
||||||
switch (diff) {
|
switch (diff) {
|
||||||
case ROW: /* single move */
|
case ROW: /* single move */
|
||||||
|
//if (!(g->board[to] == EMPTY)) {
|
||||||
|
// printf("pawn must move to empty tile, ");
|
||||||
|
//}
|
||||||
return g->board[to] == EMPTY;
|
return g->board[to] == EMPTY;
|
||||||
|
|
||||||
case ROW - COL: /* diagonal attack */
|
case ROW - COL: /* diagonal attack */
|
||||||
case ROW + COL:
|
case ROW + COL:
|
||||||
|
//if (!(enemies(g->board[to], g->board[from]))) {
|
||||||
|
// printf("pawn can't take its own pieces, ");
|
||||||
|
//}
|
||||||
return enemies(g->board[to], g->board[from]);
|
return enemies(g->board[to], g->board[from]);
|
||||||
|
|
||||||
case 2 * ROW: /* double move */
|
case 2 * ROW: /* double move */
|
||||||
|
//if (!( g->board[to] == EMPTY)) {
|
||||||
|
// printf("pawn must move to empty piece, ");
|
||||||
|
//}
|
||||||
|
//if (!( g->board[from + ROW * direction] == EMPTY)) {
|
||||||
|
// printf("pawn can't jump over piece, ");
|
||||||
|
//}
|
||||||
|
//if (!( (row(from) == 1 || row(from) == 6))) {
|
||||||
|
// printf("pawn can only move two steps from its starting row, ");
|
||||||
|
//}
|
||||||
|
//printf("\n");
|
||||||
return g->board[to] == EMPTY
|
return g->board[to] == EMPTY
|
||||||
&& g->board[from + ROW * direction] == EMPTY
|
&& g->board[from + ROW * direction] == EMPTY
|
||||||
&& (row(from) == 1 || row(from) == 6);
|
&& (row(from) == 1 || row(from) == 6);
|
||||||
|
|
||||||
default: /* any other move is illegal */
|
default: /* any other move is illegal */
|
||||||
|
//printf("pawn move is generally illegal, ");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool king_move_ok(struct game_state* g, index_t from, index_t to)
|
static bool king_move_ok(struct game_state* g, index_t from, index_t to)
|
||||||
{
|
{
|
||||||
// castling is implemented as a separate function, invoked by O-O or O-O-O
|
// castling check is implemented as a separate function
|
||||||
return BIT(to) & king_threatmap(from)
|
return BIT(to) & king_threatmap(from)
|
||||||
&& BIT(to) & ~threatmap(g, -piece_color(g->board[from]));
|
&& BIT(to) & ~threatmap(g, -piece_color(g->board[from]));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_check(struct game_state* g, int player)
|
bool is_check(struct game_state* g, int player)
|
||||||
{
|
{
|
||||||
return BIT(g->king_pos[player == WHITE ? 0 : 1]) & threatmap(g, -player);
|
return BIT(g->king_pos[player == WHITE ? 0 : 1]) & threatmap(g, -player);
|
||||||
@@ -480,15 +557,21 @@ static bool castle_ok(struct game_state* g, int player, enum castle_type t)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const int p = player == WHITE ? 0 : 1;
|
const int p = player == WHITE ? 0 : 1;
|
||||||
const int row = ROW * (player == WHITE ? 7 : 0);
|
const int row = (player == WHITE ? 1 : 8);
|
||||||
|
|
||||||
if (t == CASTLE_KINGSIDE) {
|
if (t == CASTLE_KINGSIDE) {
|
||||||
return g->h_rook_touched[p] == false
|
return g->h_rook_touched[p] == false
|
||||||
&& g->king_touched[p] == false
|
&& g->king_touched[p] == false
|
||||||
&& threatmap(g, -player) & (BIT(TILE('F', row)) | BIT(TILE('G', row)));
|
&& ~threatmap(g, -player) & (BIT(TILE('F', row)) | BIT(TILE('G', row)))
|
||||||
|
&& g->board[TILE('G', row)] == EMPTY
|
||||||
|
&& g->board[TILE('F', row)] == EMPTY;
|
||||||
} else if (t == CASTLE_QUEENSIDE) {
|
} else if (t == CASTLE_QUEENSIDE) {
|
||||||
return g->a_rook_touched[p] == false
|
return g->a_rook_touched[p] == false
|
||||||
&& g->king_touched[p] == false
|
&& g->king_touched[p] == false
|
||||||
&& threatmap(g, -player) & (BIT(TILE('C', row)) | BIT(TILE('D', row)));
|
&& ~threatmap(g, -player) & (BIT(TILE('C', row)) | BIT(TILE('D', row)))
|
||||||
|
&& g->board[TILE('B', row)] == EMPTY
|
||||||
|
&& g->board[TILE('C', row)] == EMPTY
|
||||||
|
&& g->board[TILE('D', row)] == EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "invalid castle_type passed to %s", __func__);
|
fprintf(stderr, "invalid castle_type passed to %s", __func__);
|
||||||
@@ -500,8 +583,9 @@ bool move_ok(struct game_state* g, index_t from, index_t to, int player)
|
|||||||
{
|
{
|
||||||
/* Player must own piece it moves
|
/* Player must own piece it moves
|
||||||
and a player can't capture their own pieces. */
|
and a player can't capture their own pieces. */
|
||||||
if (enemies(player, g->board[from]) || friends(player, g->board[to]))
|
if (enemies(player, g->board[from]) || friends(player, g->board[to])) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool move_ok = false;;
|
bool move_ok = false;;
|
||||||
|
|
||||||
@@ -530,12 +614,11 @@ bool move_ok(struct game_state* g, index_t from, index_t to, int player)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pretend move was done and check if king is checked after
|
typeof(*g) restore = *g;
|
||||||
unsigned char restore[sizeof *g];
|
|
||||||
memcpy(restore, g, sizeof *g);
|
|
||||||
move(g, from, to);
|
move(g, from, to);
|
||||||
bool bad = is_check(g, player);
|
bool bad = is_check(g, player);
|
||||||
memcpy(g, restore, sizeof *g);
|
*g = restore;
|
||||||
|
//memcpy(g, &restore, sizeof restore);
|
||||||
|
|
||||||
if (bad) {
|
if (bad) {
|
||||||
return false;
|
return false;
|
||||||
@@ -586,51 +669,49 @@ static void game_init(struct game_state* g)
|
|||||||
// clang-format off
|
// clang-format off
|
||||||
#if 1
|
#if 1
|
||||||
static const piece_t start[BOARD_SIZE] = {
|
static const piece_t start[BOARD_SIZE] = {
|
||||||
-ROOK, -KNIGHT,-BISHOP,-QUEEN, -KING, -BISHOP,-KNIGHT,-ROOK,
|
ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK,
|
||||||
-PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN,
|
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
|
||||||
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN,
|
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN,
|
||||||
ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
-PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN,
|
||||||
|
-ROOK, -KNIGHT,-BISHOP,-QUEEN, -KING, -BISHOP,-KNIGHT,-ROOK,
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
#else
|
#else
|
||||||
static const piece_t start[BOARD_SIZE] = {
|
static const piece_t start[BOARD_SIZE] = {
|
||||||
-ROOK, -KNIGHT,-BISHOP,-QUEEN, -KING, -BISHOP,-KNIGHT,-ROOK,
|
ROOK, EMPTY, EMPTY, EMPTY, KING, EMPTY, EMPTY, ROOK,
|
||||||
-PAWN, -PAWN, -PAWN, -PAWN, -PAWN, QUEEN, -PAWN, -PAWN,
|
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, ROOK, EMPTY, EMPTY,
|
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN,
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, -KING, EMPTY, EMPTY, EMPTY,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memset(g, 0, sizeof *g);
|
memset(g, 0, sizeof *g);
|
||||||
memcpy(g->board, start, sizeof start);
|
memcpy(g->board, start, sizeof start);
|
||||||
|
|
||||||
g->king_pos[0] = 60;
|
g->king_pos[0] = TILE('E', 1);
|
||||||
g->king_pos[1] = 4;
|
g->king_pos[1] = TILE('E', 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static index_t input_to_index(char input[2])
|
static index_t input_to_index(char input[2])
|
||||||
{
|
{
|
||||||
const char col = toupper(input[0]);
|
const char col = toupper(input[0]);
|
||||||
const char row = toupper(input[1]);
|
const char row = input[1];
|
||||||
|
|
||||||
if (col < 'A' || col > 'H' || row < '1' || row > '8')
|
if (col < 'A' || col > 'H' || row < '1' || row > '8')
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
const int x = col - 'A';
|
return TILE(col, row - '0');
|
||||||
const int y = row - '1';
|
|
||||||
return 8 * (7 - y) + x;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement algebaric notation
|
// TODO: Implement algebaric notation
|
||||||
static int do_turn(int turn_no, struct game_state* g)
|
static int player_move(int turn_no, struct game_state* g)
|
||||||
{
|
{
|
||||||
char input[3] = { 0 };
|
char input[3] = { 0 };
|
||||||
|
|
||||||
@@ -645,7 +726,7 @@ static int do_turn(int turn_no, struct game_state* g)
|
|||||||
if (from == -1)
|
if (from == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
print_threatmap(valid_moves(g, g->king_pos[turn_no % 2], turn_no % 2 ? BLACK : WHITE));
|
print_threatmap(valid_moves(g, from, turn_no % 2 ? BLACK : WHITE));
|
||||||
printf("\nto: ");
|
printf("\nto: ");
|
||||||
scanf(" %2s", input);
|
scanf(" %2s", input);
|
||||||
|
|
||||||
@@ -675,7 +756,11 @@ static double heuristic(struct game_state* g, int player)
|
|||||||
const piece_t piece = g->board[i];
|
const piece_t piece = g->board[i];
|
||||||
const int player = piece_color(piece);
|
const int player = piece_color(piece);
|
||||||
const piece_t type = piece_abs(piece);
|
const piece_t type = piece_abs(piece);
|
||||||
|
if (player == WHITE) {
|
||||||
score += player * piece_value[type] * piece_position_bonus[type][i];
|
score += player * piece_value[type] * piece_position_bonus[type][i];
|
||||||
|
} else {
|
||||||
|
score += player * piece_value[type] * piece_position_bonus[type][BOARD_SIZE-i-1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
@@ -689,15 +774,22 @@ static void print_debug(struct game_state* g, int player)
|
|||||||
// bool h_rook_touched[2];
|
// bool h_rook_touched[2];
|
||||||
// bool king_touched[2];
|
// bool king_touched[2];
|
||||||
//};
|
//};
|
||||||
|
static const char * const bool_str[2] = {"False", "True"};
|
||||||
|
|
||||||
|
printf("White A king touched: %s\n", bool_str[g->king_touched[0]]);
|
||||||
|
printf("White A rook touched: %s\n", bool_str[g->a_rook_touched[0]]);
|
||||||
|
printf("White H rook touched: %s\n", bool_str[g->h_rook_touched[0]]);
|
||||||
|
printf("White can king side castle: %s\n", bool_str[castle_ok(g, WHITE, CASTLE_KINGSIDE)]);
|
||||||
|
printf("White can queen side castle: %s\n", bool_str[castle_ok(g, WHITE, CASTLE_QUEENSIDE)]);
|
||||||
|
printf("White king pos: %c%ld\n", 'A'+((char)column(g->king_pos[0])), 1+row(g->king_pos[0]));
|
||||||
|
|
||||||
|
//printf("Black A king touched: %s\n", bool_str[g->king_touched[1]]);
|
||||||
|
//printf("Black A rook touched: %s\n", bool_str[g->a_rook_touched[1]]);
|
||||||
|
//printf("Black H rook touched: %s\n", bool_str[g->h_rook_touched[1]]);
|
||||||
|
//printf("Black can king side castle: %s\n", bool_str[castle_ok(g, BLACK, CASTLE_KINGSIDE)]);
|
||||||
|
//printf("Black can queen side castle: %s\n", bool_str[castle_ok(g, BLACK, CASTLE_QUEENSIDE)]);
|
||||||
|
//printf("Black king pos: %c%ld\n", 'A'+((char)column(g->king_pos[1])), 1+row(g->king_pos[1]));
|
||||||
|
|
||||||
printf("White king pos: %c%ld\n", 'A'+((char)column(g->king_pos[0])), 8-row(g->king_pos[0]));
|
|
||||||
printf("Black king pos: %c%ld\n", 'A'+((char)column(g->king_pos[1])), 8-row(g->king_pos[1]));
|
|
||||||
printf("White A king touched: %s\n", g->king_touched[0] ? "True" : "False");
|
|
||||||
printf("Black A king touched: %s\n", g->king_touched[1] ? "True" : "False");
|
|
||||||
printf("White A rook touched: %s\n", g->a_rook_touched[0] ? "True" : "False");
|
|
||||||
printf("Black A rook touched: %s\n", g->a_rook_touched[1] ? "True" : "False");
|
|
||||||
printf("White H rook touched: %s\n", g->h_rook_touched[0] ? "True" : "False");
|
|
||||||
printf("Black H rook touched: %s\n", g->h_rook_touched[1] ? "True" : "False");
|
|
||||||
printf("Turns with no capture: %d\n", g->turns_without_captures);
|
printf("Turns with no capture: %d\n", g->turns_without_captures);
|
||||||
printf("Estimated score: %lf\n", heuristic(g, player));
|
printf("Estimated score: %lf\n", heuristic(g, player));
|
||||||
}
|
}
|
||||||
@@ -774,8 +866,19 @@ int main()
|
|||||||
|
|
||||||
game_init(&state);
|
game_init(&state);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
board_paint(&state);
|
||||||
|
print_debug(&state, WHITE);
|
||||||
|
printf("white threatmap:\n");
|
||||||
|
print_threatmap(threatmap(&state, WHITE));
|
||||||
|
printf("black threatmap:\n");
|
||||||
|
print_threatmap(threatmap(&state, BLACK));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
int turn = 0;
|
int turn = 0;
|
||||||
int player = WHITE;
|
int player = WHITE;
|
||||||
|
bool player_intervention = false;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
print_debug(&state, player);
|
print_debug(&state, player);
|
||||||
@@ -784,15 +887,15 @@ int main()
|
|||||||
|
|
||||||
if (is_check(&state, player)) {
|
if (is_check(&state, player)) {
|
||||||
printf("%s in check!\n", player == WHITE ? "White" : "Black");
|
printf("%s in check!\n", player == WHITE ? "White" : "Black");
|
||||||
//print_threatmap(piece_threatmap(&state, state.king_pos[player == WHITE ? 0 : 1]));
|
print_threatmap(piece_threatmap(&state, state.king_pos[player == WHITE ? 0 : 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool player_intervention = false;
|
|
||||||
|
|
||||||
if (player_intervention) {
|
if (player_intervention) {
|
||||||
intervene:
|
intervene:
|
||||||
while (do_turn(turn, &state) == 0)
|
while (player_move(turn, &state) == 0) {
|
||||||
/*noop*/;
|
printf("Valid moves for %s:\n", player == WHITE ? "white" : "black");
|
||||||
|
print_threatmap(valid_moves(&state, state.king_pos[player == WHITE ? 0 : 1], player));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("thinking...\n");
|
printf("thinking...\n");
|
||||||
index_t from = -1, to = -1;
|
index_t from = -1, to = -1;
|
||||||
@@ -801,13 +904,13 @@ intervene:
|
|||||||
printf("computer couldn't think, starting player intervention\n");
|
printf("computer couldn't think, starting player intervention\n");
|
||||||
printf("Valid moves for %s:\n", player == WHITE ? "white" : "black");
|
printf("Valid moves for %s:\n", player == WHITE ? "white" : "black");
|
||||||
print_threatmap(valid_moves(&state, state.king_pos[player == WHITE ? 0 : 1], player));
|
print_threatmap(valid_moves(&state, state.king_pos[player == WHITE ? 0 : 1], player));
|
||||||
printf("\nThreatened pieces\n");
|
printf("\nThreatened pieces by %s\n", -player == WHITE ? "white" : "black");
|
||||||
print_threatmap(threatmap(&state, -player));
|
print_threatmap(threatmap(&state, -player));
|
||||||
player_intervention = true;
|
player_intervention = true;
|
||||||
goto intervene;
|
goto intervene;
|
||||||
}
|
}
|
||||||
move(&state, from, to);
|
move(&state, from, to);
|
||||||
printf("Did %c%ld to %c%ld\n", (char)('A'+column(from)), 8-row(from), (char)('A'+column(to)), 8-row(to));
|
printf("Did %c%ld to %c%ld\n", (char)('A'+column(from)), 1+row(from), (char)('A'+column(to)), 1+row(to));
|
||||||
}
|
}
|
||||||
|
|
||||||
turn += 1;
|
turn += 1;
|
||||||
@@ -831,4 +934,4 @@ intervene:
|
|||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user