shithub: moonfish

ref: 01a9a23796629848e5049fe78143f32e890f06f7
dir: /chess.c/

View raw version
/* moonfish is licensed under the AGPL (v3 or later) */
/* copyright 2023, 2024 zamfofex */

#include "moonfish.h"

static int moonfish_piece_scores[] = {82, 337, 365, 477, 1025, 0};

static signed char moonfish_piece_square_scores[] =
{
	0, 0, 0, 0,
	81, 93, 84, 43,
	48, 41, 16, -13,
	22, 9, 15, -18,
	14, 0, 4, -26,
	-3, 0, 14, -19,
	-19, 2, 18, -28,
	0, 0, 0, 0,
	
	6, -65, -52, -128,
	29, 67, -17, -45,
	74, 83, 66, -1,
	45, 44, 17, 6,
	20, 17, 12, -10,
	14, 14, 8, -19,
	-2, 3, -33, -24,
	-25, -43, -20, -64,
	
	-31, -62, 5, -18,
	8, 20, 17, -36,
	37, 46, 37, -9,
	43, 28, 6, -3,
	30, 12, 11, -1,
	14, 21, 16, 5,
	3, 18, 24, 2,
	-17, -13, -21, -27,
	
	57, 20, 36, 37,
	71, 62, 29, 35,
	26, 35, 40, 5,
	25, 21, -9, -22,
	4, -9, -10, -29,
	-7, -8, -15, -39,
	-5, -4, -11, -57,
	16, 4, -25, -22,
	
	35, 36, 21, 8,
	-7, 26, -5, 15,
	18, 31, 15, 22,
	-8, 0, -14, -13,
	-6, -6, -11, -6,
	-3, -4, 8, -4,
	5, 13, -5, -17,
	-2, -17, -24, -25,
	
	-35, -9, 12, -26,
	-7, -12, -19, 0,
	-18, 4, 23, -15,
	-28, -18, -17, -26,
	-42, -35, -17, -50,
	-45, -26, -14, -20,
	-53, -12, 8, 4,
	-23, -8, 30, 0,
};

static void moonfish_create_move(struct moonfish_chess *chess, struct moonfish_move *move, unsigned char from, unsigned char to)
{
	move->from = from;
	move->to = to;
	move->piece = chess->board[from];
	move->promotion = chess->board[from];
	move->captured = chess->board[to];
	move->castle = chess->castle;
	move->score = chess->score;
	move->passing = chess->passing;
}

void moonfish_move(struct moonfish_chess *chess, struct moonfish_move *move, unsigned char from, unsigned char to)
{
	moonfish_create_move(chess, move, from, to);
	if (chess->board[from] % 16 == moonfish_pawn)
	{
		if (chess->board[from] / 16 == 1)
		{
			if (from > 80) move->promotion = moonfish_white_queen;
		}
		else
		{
			if (from < 40) move->promotion = moonfish_black_queen;
		}
	}
}

static void moonfish_jump(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from, signed char to)
{
	moonfish_create_move(chess, (*moves)++, from, to);
}

static void moonfish_slide(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from, signed char delta)
{
	unsigned char to;
	
	to = from;
	for (;;)
	{
		to += delta;
		if (chess->board[to] == moonfish_outside) break;
		if (chess->board[to] / 16 == chess->board[from] / 16) break;
		moonfish_jump(chess, moves, from, to);
		if (chess->board[to] != moonfish_empty) break;
	}
}

static void moonfish_move_knight(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	moonfish_jump(chess, moves, from, from + 21);
	moonfish_jump(chess, moves, from, from + 19);
	moonfish_jump(chess, moves, from, from - 19);
	moonfish_jump(chess, moves, from, from - 21);
	moonfish_jump(chess, moves, from, from + 12);
	moonfish_jump(chess, moves, from, from + 8);
	moonfish_jump(chess, moves, from, from - 8);
	moonfish_jump(chess, moves, from, from - 12);
}

static void moonfish_move_bishop(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	moonfish_slide(chess, moves, from, 11);
	moonfish_slide(chess, moves, from, 9);
	moonfish_slide(chess, moves, from, -9);
	moonfish_slide(chess, moves, from, -11);
}

static void moonfish_move_rook(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	moonfish_slide(chess, moves, from, 10);
	moonfish_slide(chess, moves, from, -10);
	moonfish_slide(chess, moves, from, 1);
	moonfish_slide(chess, moves, from, -1);
}

static void moonfish_move_queen(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	moonfish_move_rook(chess, moves, from);
	moonfish_move_bishop(chess, moves, from);
}

static char moonfish_attacked(struct moonfish_chess *chess, unsigned char from, unsigned char to)
{
	int check;
	unsigned char piece;
	
	if (chess->white) piece = moonfish_white_king;
	else piece = moonfish_black_king;
	
	chess->board[from] = moonfish_empty;
	chess->board[to] = piece;
	check = moonfish_check(chess);
	chess->board[from] = piece;
	chess->board[to] = moonfish_empty;
	return check;
}

static void moonfish_castle_low(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	unsigned char to;
	
	to = chess->white ? 22 : 92;
	while (to != from)
		if (chess->board[to++] != moonfish_empty)
			return;
	
	if (moonfish_check(chess)) return;
	if (moonfish_attacked(chess, from, from - 1)) return;
	if (moonfish_attacked(chess, from, from - 2)) return;
	
	moonfish_jump(chess, moves, from, from - 2);
}

static void moonfish_castle_high(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	unsigned char to;
	
	to = chess->white ? 27 : 97;
	while (to != from)
		if (chess->board[to--] != moonfish_empty)
			return;
	
	if (moonfish_check(chess)) return;
	if (moonfish_attacked(chess, from, from + 1)) return;
	if (moonfish_attacked(chess, from, from + 2)) return;
	
	moonfish_jump(chess, moves, from, from + 2);
}

static void moonfish_move_king(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	moonfish_jump(chess, moves, from, from + 11);
	moonfish_jump(chess, moves, from, from + 9);
	moonfish_jump(chess, moves, from, from - 9);
	moonfish_jump(chess, moves, from, from - 11);
	moonfish_jump(chess, moves, from, from + 10);
	moonfish_jump(chess, moves, from, from - 10);
	moonfish_jump(chess, moves, from, from + 1);
	moonfish_jump(chess, moves, from, from - 1);
	
	if (chess->white)
	{
		if (chess->castle.white_oo) moonfish_castle_high(chess, moves, from);
		if (chess->castle.white_ooo) moonfish_castle_low(chess, moves, from);
	}
	else
	{
		if (chess->castle.black_oo) moonfish_castle_high(chess, moves, from);
		if (chess->castle.black_ooo) moonfish_castle_low(chess, moves, from);
	}
}

static void moonfish_pawn_capture(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from, unsigned char to)
{
	if (to == chess->passing)
	{
		moonfish_jump(chess, moves, from, to);
		return;
	}
	
	if (chess->board[to] / 16 != chess->board[from] / 16)
	if (chess->board[to] != moonfish_empty)
	if (chess->board[to] != moonfish_outside)
		moonfish_move(chess, (*moves)++, from, to);
}

static void moonfish_move_pawn(struct moonfish_chess *chess, struct moonfish_move **moves, unsigned char from)
{
	int dy;
	
	dy = chess->white ? 10 : -10;
	
	if (chess->board[from + dy] == moonfish_empty)
	{
		moonfish_move(chess, *moves, from, from + dy);
				
		if (chess->white ? from < 40 : from > 80)
			if (chess->board[from + dy * 2] == moonfish_empty)
				moonfish_move(chess, (*moves)++, from, from + dy * 2);
	}
	
	moonfish_pawn_capture(chess, moves, from, from + dy + 1);
	moonfish_pawn_capture(chess, moves, from, from + dy - 1);
}

void moonfish_moves(struct moonfish_chess *chess, struct moonfish_move *moves, unsigned char from)
{
	unsigned char piece;
	
	piece = chess->board[from];
	
	if (chess->white ? piece / 16 == 1 : piece / 16 == 2)
	{
		if (piece % 16 == moonfish_pawn) moonfish_move_pawn(chess, &moves, from);
		if (piece % 16 == moonfish_knight) moonfish_move_knight(chess, &moves, from);
		if (piece % 16 == moonfish_bishop) moonfish_move_bishop(chess, &moves, from);
		if (piece % 16 == moonfish_rook) moonfish_move_rook(chess, &moves, from);
		if (piece % 16 == moonfish_queen) moonfish_move_queen(chess, &moves, from);
		if (piece % 16 == moonfish_king) moonfish_move_king(chess, &moves, from);
	}
	
	moves->piece = moonfish_outside;
}

static int moonfish_table(int from, unsigned char piece)
{
	int x, y;
	unsigned char type, color;
	int score;
	
	if (piece == moonfish_empty) return 0;
	
	x = from % 10 - 1;
	y = from / 10 - 2;
	
	type = (piece % 16) - 1;
	color = (piece / 16) - 1;
	
	if (color == 0) y = 7 - y;
	
	if (x < 4) x = 4 - x;
	else x %= 4;
	
	score = moonfish_piece_scores[type] + moonfish_piece_square_scores[x + y * 4 + type * 32];
	if (color != 0) score *= -1;
	
	return score;
}

void moonfish_play(struct moonfish_chess *chess, struct moonfish_move *move)
{
	int x0, x1;
	int dy;
	unsigned char piece;
	
	chess->score -= moonfish_table(move->from, move->piece);
	chess->score += moonfish_table(move->to, move->promotion);
	chess->score -= moonfish_table(move->to, move->captured);
	
	chess->board[move->from] = moonfish_empty;
	chess->board[move->to] = move->promotion;
	
	chess->passing = 0;
	
	if (move->piece % 16 == moonfish_pawn)
	{
		if ((move->to - move->from) / 10 == 2) chess->passing = move->from + 10;
		if ((move->from - move->to) / 10 == 2) chess->passing = move->from - 10;
		
		if ((move->to - move->from) % 10)
		if (move->captured == moonfish_empty)
		{
			if (chess->white) dy = 10, piece = moonfish_black_pawn;
			else dy = -10, piece = moonfish_white_pawn;
			
			chess->score -= moonfish_table(move->to - dy, piece);
			chess->board[move->to - dy] = moonfish_empty;
		}
	}
	
	if (move->piece % 16 == moonfish_king)
	{
		x0 = 0;
		if (move->from == 25 && move->to == 27) x0 = 28, x1 = 26;
		if (move->from == 25 && move->to == 23) x0 = 21, x1 = 24;
		if (move->from == 95 && move->to == 97) x0 = 98, x1 = 96;
		if (move->from == 95 && move->to == 93) x0 = 91, x1 = 94;
		if (x0)
		{
			piece = chess->white ? moonfish_white_rook : moonfish_black_rook;
			chess->score -= moonfish_table(x0, piece);
			chess->score += moonfish_table(x1, piece);
			chess->board[x0] = moonfish_empty;
			chess->board[x1] = piece;
		}
		
		if (chess->white)
		{
			chess->castle.white_oo = 0;
			chess->castle.white_ooo = 0;
		}
		else
		{
			chess->castle.black_oo = 0;
			chess->castle.black_ooo = 0;
		}
	}
	
	if (move->piece == moonfish_white_rook)
	{
		if (move->from % 10 == 1) chess->castle.white_ooo = 0;
		if (move->from % 10 == 8) chess->castle.white_oo = 0;
	}
	if (move->piece == moonfish_black_rook)
	{
		if (move->from % 10 == 1) chess->castle.black_ooo = 0;
		if (move->from % 10 == 8) chess->castle.black_oo = 0;
	}
	
	if (move->captured == moonfish_white_rook)
	{
		if (move->to % 10 == 1) chess->castle.white_ooo = 0;
		if (move->to % 10 == 8) chess->castle.white_oo = 0;
	}
	if (move->captured == moonfish_black_rook)
	{
		if (move->to % 10 == 1) chess->castle.black_ooo = 0;
		if (move->to % 10 == 8) chess->castle.black_oo = 0;
	}
	
	chess->white ^= 1;
}

void moonfish_unplay(struct moonfish_chess *chess, struct moonfish_move *move)
{
	int x0, x1;
	int dy;
	unsigned char piece;
	
	chess->white ^= 1;
	
	chess->board[move->from] = move->piece;
	chess->board[move->to] = move->captured;
	chess->castle = move->castle;
	chess->score = move->score;
	chess->passing = move->passing;
	
	if (move->piece % 16 == moonfish_pawn)
	{
		if ((move->to - move->from) % 10)
		if (move->captured == moonfish_empty)
		{
			if (chess->white) dy = 10, piece = moonfish_black_pawn;
			else dy = -10, piece = moonfish_white_pawn;
			
			chess->score += moonfish_table(move->to - dy, piece);
			chess->board[move->to - dy] = piece;
		}
	}
	
	
	if (move->piece % 16 == moonfish_king)
	{
		x0 = 0;
		if (move->from == 25 && move->to == 27) x0 = 28, x1 = 26;
		if (move->from == 25 && move->to == 23) x0 = 21, x1 = 24;
		if (move->from == 95 && move->to == 97) x0 = 98, x1 = 96;
		if (move->from == 95 && move->to == 93) x0 = 91, x1 = 94;
		if (x0)
		{
			chess->board[x1] = moonfish_empty;
			chess->board[x0] = chess->white ? moonfish_white_rook : moonfish_black_rook;
		}
	}
}

void moonfish_chess(struct moonfish_chess *chess)
{
	char pieces[] = {moonfish_rook, moonfish_knight, moonfish_bishop, moonfish_queen, moonfish_king, moonfish_bishop, moonfish_knight, moonfish_rook};
	int x, y;
	
	chess->white = 1;
	chess->castle.white_oo = 0;
	chess->castle.white_ooo = 0;
	chess->castle.black_oo = 0;
	chess->castle.black_ooo = 0;
	chess->score = 0;
	chess->passing = 0;
	
	for (y = 0 ; y < 12 ; y++)
	for (x = 0 ; x < 10 ; x++)
		chess->board[x + y * 10] = moonfish_outside;
	
	for (x = 0 ; x < 8 ; x++)
	{
		chess->board[(x + 1) + 20] = pieces[x] | 0x10;
		chess->board[(x + 1) + 90] = pieces[x] | 0x20;
		chess->board[(x + 1) + 30] = moonfish_white_pawn;
		chess->board[(x + 1) + 80] = moonfish_black_pawn;
		
		for (y = 4 ; y < 8 ; y++)
			chess->board[(x + 1) + y * 10] = moonfish_empty;
	}
}

static void moonfish_from_xy(struct moonfish_chess *chess, struct moonfish_move *move, int x0, int y0, int x1, int y1)
{
	moonfish_create_move(chess, move, (x0 + 1) + (y0 + 2) * 10, (x1 + 1) + (y1 + 2) * 10);
}

void moonfish_from_uci(struct moonfish_chess *chess, struct moonfish_move *move, char *name)
{
	int x0, y0;
	int x1, y1;
	unsigned char color;
	
	x0 = name[0] - 'a';
	y0 = name[1] - '1';
	x1 = name[2] - 'a';
	y1 = name[3] - '1';
	
	moonfish_from_xy(chess, move, x0, y0, x1, y1);
	if (move->piece % 16 == moonfish_king && x0 == 4)
	{
		if (x1 == 0) x1 = 2;
		if (x1 == 7) x1 = 6;
		moonfish_from_xy(chess, move, x0, y0, x1, y1);
	}
	
	color = chess->white ? 0x10 : 0x20;
	if (name[4] == 'q') move->promotion = color | moonfish_queen;
	if (name[4] == 'r') move->promotion = color | moonfish_rook;
	if (name[4] == 'b') move->promotion = color | moonfish_bishop;
	if (name[4] == 'n') move->promotion = color | moonfish_knight;
}

void moonfish_to_uci(char *name, struct moonfish_move *move)
{
	int x, y;
	
	x = move->from % 10 - 1;
	y = move->from / 10 - 2;
	
	name[0] = x + 'a';
	name[1] = y + '1';
	
	x = move->to % 10 - 1;
	y = move->to / 10 - 2;
	
	name[2] = x + 'a';
	name[3] = y + '1';
	
	if (move->promotion != move->piece)
	{
		if (move->promotion % 16 == moonfish_queen) name[4] = 'q';
		if (move->promotion % 16 == moonfish_rook) name[4] = 'r';
		if (move->promotion % 16 == moonfish_bishop) name[4] = 'b';
		if (move->promotion % 16 == moonfish_knight) name[4] = 'n';
		name[5] = 0;
	}
	else
	{
		name[4] = 0;
	}
}

#ifndef moonfish_mini

int moonfish_fen(struct moonfish_chess *chess, char *fen)
{
	int x, y;
	unsigned char type, color;
	char ch;
	
	for (y = 0 ; y < 8 ; y++)
	for (x = 0 ; x < 8 ; x++)
		chess->board[(x + 1) + (y + 2) * 10] = moonfish_empty;
	
	x = 0;
	y = 0;
	
	chess->white = 1;
	chess->castle.white_oo = 0;
	chess->castle.white_ooo = 0;
	chess->castle.black_oo = 0;
	chess->castle.black_ooo = 0;
	chess->score = 0;
	chess->passing = 0;
	
	for (;;)
	{
		ch = *fen++;
		
		if (ch == 0) return 0;
		if (ch == ' ') break;
		
		if (ch == '/')
		{
			x = 0;
			y++;
			continue;
		}
		
		if (ch >= '0' && ch <= '9')
		{
			x += ch - '0';
			continue;
		}
		
		if (ch >= 'A' && ch <= 'Z')
			ch -= 'A' - 'a',
			color = 0x10;
		else
			color = 0x20;
		
		type = 0;
		if (ch == 'p') type = 1;
		if (ch == 'n') type = 2;
		if (ch == 'b') type = 3;
		if (ch == 'r') type = 4;
		if (ch == 'q') type = 5;
		if (ch == 'k') type = 6;
		
		chess->board[(x + 1) + (9 - y) * 10] = type | color;
		
		chess->score += moonfish_table((x + 1) + (9 - y) * 10, type | color);
		
		x++;
	}
	
	if (*fen++ == 'b') chess->white = 0;
	if (*fen++ != ' ') return 0;
	
	for (;;)
	{
		ch = *fen++;
		
		if (ch == 0) return 0;
		if (ch == ' ') break;
		
		if (ch == 'K') chess->castle.white_oo = 1;
		if (ch == 'Q') chess->castle.white_ooo = 1;
		if (ch == 'k') chess->castle.black_oo = 1;
		if (ch == 'q') chess->castle.black_ooo = 1;
		
		if (ch >= 'A' && ch <= 'H') return 1;
		if (ch >= 'a' && ch <= 'h') return 1;
	}
	
	if (*fen == '-') return 0;
	
	x = *fen++ - 'a';
	y = *fen++ - '1';
	
	chess->passing = (x + 1) + (y + 2) * 10;
	
	return 0;
}

#endif

int moonfish_validate(struct moonfish_chess *chess)
{
	int x, y;
	struct moonfish_move moves[32];
	struct moonfish_move *move;
	
	for (y = 0 ; y < 8 ; y++)
	for (x = 0 ; x < 8 ; x++)
	{
		moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
		for (move = moves ; move->piece != moonfish_outside ; move++)
			if (move->captured % 16 == moonfish_king)
				return 0;
	}
	
	return 1;
}

int moonfish_check(struct moonfish_chess *chess)
{
	int valid;
	struct moonfish_castle castle;
	unsigned char passing;
	
	castle = chess->castle;
	passing = chess->passing;
	
	chess->castle.white_oo = 0;
	chess->castle.white_ooo = 0;
	chess->castle.black_oo = 0;
	chess->castle.black_ooo = 0;
	
	chess->white ^= 1;
	valid = moonfish_validate(chess);
	chess->white ^= 1;
	
	chess->castle = castle;
	chess->passing = passing;
	
	return valid ^ 1;
}

#ifndef moonfish_mini

int moonfish_finished(struct moonfish_chess *chess)
{
	struct moonfish_move moves[32], *move;
	int x, y;
	int valid;
	
	for (y = 0 ; y < 8 ; y++)
	for (x = 0 ; x < 8 ; x++)
	{
		moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
		for (move = moves ; move->piece != moonfish_outside ; move++)
		{
			moonfish_play(chess, move);
			valid = moonfish_validate(chess);
			moonfish_unplay(chess, move);
			if (valid) return 0;
		}
	}
	
	return 1;
}

int moonfish_checkmate(struct moonfish_chess *chess)
{
	if (!moonfish_check(chess)) return 0;
	return moonfish_finished(chess);
}

int moonfish_stalemate(struct moonfish_chess *chess)
{
	if (moonfish_check(chess)) return 0;
	return moonfish_finished(chess);
}

#endif