8 Commits

11 changed files with 218 additions and 49 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ brchess
/obj/ /obj/
/lib/ /lib/
/.deps/ /.deps/
/notused/

View File

@@ -28,6 +28,8 @@ k4n2/4P3/8/8/8/8/4p3/K4N2 w - - 0 1
# position after 1.e4 # position after 1.e4
rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1 rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1
# position after 1.Nh3
rnbqkbnr/pppppppp/8/8/8/7N/PPPPPPPP/RNBQKB1R b KQkq - 1 1
# white castled, and can e.p. on c6 black can castle # white castled, and can e.p. on c6 black can castle
# white is a pawn down # white is a pawn down

View File

@@ -29,6 +29,7 @@
#include "move.h" #include "move.h"
#include "fen.h" #include "fen.h"
#include "eval.h" #include "eval.h"
#include "eval-simple.h"
#include "search.h" #include "search.h"
struct command { struct command {
@@ -60,11 +61,13 @@ int do_prmoves(pos_t *, char*);
int do_prpieces(pos_t *pos, char *arg); int do_prpieces(pos_t *pos, char *arg);
int do_memstats(pos_t *, char*); int do_memstats(pos_t *, char*);
int do_eval(pos_t *, char*); int do_eval(pos_t *, char*);
int do_simple_eval(pos_t *, char*);
int do_move(pos_t *, char*); int do_move(pos_t *, char*);
int do_quit(pos_t *, char*); int do_quit(pos_t *, char*);
int do_debug(pos_t *, char*); int do_debug(pos_t *, char*);
int do_depth(pos_t *, char*); int do_depth(pos_t *, char*);
int do_search(pos_t *, char*); int do_search(pos_t *, char*);
int do_pvs(pos_t *, char*);
struct command commands[] = { struct command commands[] = {
{ "help", do_help, "Display this text" }, { "help", do_help, "Display this text" },
@@ -79,10 +82,12 @@ struct command commands[] = {
{ "prpieces", do_prpieces, "Print Pieces (from pieces lists)" }, { "prpieces", do_prpieces, "Print Pieces (from pieces lists)" },
{ "memstats", do_memstats, "Generate next move list" }, { "memstats", do_memstats, "Generate next move list" },
{ "eval", do_eval, "Eval current position" }, { "eval", do_eval, "Eval current position" },
{ "simple-eval", do_simple_eval, "Simple eval current position" },
{ "do_move", do_move, "execute nth move on current position" }, { "do_move", do_move, "execute nth move on current position" },
{ "debug", do_debug, "Set log level to LEVEL" }, { "debug", do_debug, "Set log level to LEVEL" },
{ "depth", do_depth, "Set search depth to N" }, { "depth", do_depth, "Set search depth to N" },
{ "search", do_search, "Search best move" }, { "search", do_search, "Search best move (negamax)" },
{ "pvs", do_pvs, "Search best move (Principal Variation Search)" },
{ NULL, (int(*)()) NULL, NULL } { NULL, (int(*)()) NULL, NULL }
}; };
@@ -277,6 +282,13 @@ int do_eval(__unused pos_t *pos, __unused char *arg)
return 1; return 1;
} }
int do_simple_eval(__unused pos_t *pos, __unused char *arg)
{
eval_t eval = eval_simple(pos);
printf("eval = %d centipawns\n", eval);
return 1;
}
int do_fen(pos_t *pos, char *arg) int do_fen(pos_t *pos, char *arg)
{ {
fen2pos(pos, arg); fen2pos(pos, arg);
@@ -425,13 +437,46 @@ int do_depth(__unused pos_t *pos, char *arg)
int do_search(pos_t *pos, __unused char *arg) int do_search(pos_t *pos, __unused char *arg)
{ {
int debug_level = debug_level_get(); int debug_level = debug_level_get();
long long timer1, timer2;
float nodes_sec;
timer1 = debug_timer_elapsed();
negamax(pos, depth, pos->turn == WHITE ? 1 : -1); negamax(pos, depth, pos->turn == WHITE ? 1 : -1);
timer2 = debug_timer_elapsed();
nodes_sec = (float) pos->node_count / ((float) (timer2 - timer1) / (float)NANOSEC);
debug_level_set(1); debug_level_set(1);
log(1, "best="); log(1, "best=");
move_print(0, pos->bestmove, 0); move_print(0, pos->bestmove, 0);
log(1, " negamax=%d\n", pos->bestmove->negamax); log(1, " negamax=%d\n", pos->bestmove->negamax);
debug_level_set(debug_level); debug_level_set(debug_level);
printf("Total nodes: %lu time=%lld %.0f nodes/sec\n",
pos->node_count, timer2 - timer1, nodes_sec);
return 1;
}
int do_pvs(pos_t *pos, __unused char *arg)
{
int debug_level = debug_level_get();
long long timer1, timer2;
float nodes_sec;
eval_t _pvs;
timer1 = debug_timer_elapsed();
moves_gen_eval_sort(pos);
_pvs = pvs(pos, depth, EVAL_MIN, EVAL_MAX, pos->turn == WHITE ? 1 : -1);
timer2 = debug_timer_elapsed();
nodes_sec = (float) pos->node_count / ((float) (timer2 - timer1) / (float)NANOSEC);
debug_level_set(1);
log(1, "best=");
if (pos->bestmove) {
move_print(0, pos->bestmove, 0);
log(1, " pvs=%d stored=%d\n", _pvs, pos->bestmove->negamax);
} else {
log(1, "<no-best-move>");
}
debug_level_set(debug_level);
printf("Total nodes: %lu time=%lld %.0f nodes/sec\n",
pos->node_count, timer2 - timer1, nodes_sec);
return 1; return 1;
} }
@@ -459,6 +504,7 @@ int main(int ac, char **av)
pos_pool_init(); pos_pool_init();
pos = pos_get(); pos = pos_get();
debug_init(1, stderr, true); debug_init(1, stderr, true);
eval_simple_init();
while ((opt = getopt(ac, av, "d:f:")) != -1) { while ((opt = getopt(ac, av, "d:f:")) != -1) {
switch (opt) { switch (opt) {

View File

@@ -164,7 +164,7 @@ eval_t eval_simple(pos_t *pos)
pos->eval_simple_phase = ENDGAME; pos->eval_simple_phase = ENDGAME;
# ifdef DEBUG_EVAL # ifdef DEBUG_EVAL
log_f(1, "phase = %s.\n", eg? "endgame": "midgame"); log_f(5, "phase = %s.\n", eg? "endgame": "midgame");
# endif # endif
for (int color = WHITE; color <= BLACK; ++color) { for (int color = WHITE; color <= BLACK; ++color) {
@@ -173,25 +173,26 @@ eval_t eval_simple(pos_t *pos)
u64 _t; u64 _t;
# ifdef DEBUG_EVAL # ifdef DEBUG_EVAL
log_f(1, "p=%u bb=%d %s %s: count=%d val=%ld ", piece, bb, color? "black": "white", log_f(5, "p=%u bb=%d %s %s: count=%d val=%ld ", piece, bb, color? "black": "white",
P_SYM(piece), popcount64(pos->bb[color][bb]), P_SYM(piece), popcount64(pos->bb[color][bb]),
popcount64(pos->bb[color][bb]) * P_VALUE(piece)); popcount64(pos->bb[color][bb]) * P_VALUE(piece));
# endif # endif
eval[color] += popcount64(pos->bb[color][bb]) * P_LETTER(piece); eval[color] += popcount64(pos->bb[color][bb]) * P_LETTER(piece);
bit_for_each64_2(cur, _t, pos->bb[color][piece]) { bit_for_each64_2(cur, _t, pos->bb[color][bb]) {
# ifdef DEBUG_EVAL # ifdef DEBUG_EVAL
log(1, "sq=%d:%d ", cur, gg[color][bb][cur]); log(5, "sq=%d:%d ", cur, gg[color][bb][cur]);
# endif # endif
eval[color] += gg[color][bb][cur]; eval[color] += gg[color][bb][cur];
} }
# ifdef DEBUG_EVAL # ifdef DEBUG_EVAL
log(1, "\n"); log(5, "\n");
# endif # endif
} }
} }
# ifdef DEBUG_EVAL # ifdef DEBUG_EVAL
log_f(1, "white: %d black:%d\n", eval[WHITE], eval[BLACK]); log_f(2, "eval:%d white:%d black:%d\n", eval[WHITE] - eval[BLACK],
eval[WHITE], eval[BLACK]);
# endif # endif
return eval[WHITE] - eval[BLACK]; return eval[WHITE] - eval[BLACK];

View File

@@ -18,6 +18,7 @@
#include "position.h" #include "position.h"
#include "eval.h" #include "eval.h"
#include "eval-simple.h"
inline eval_t eval_material(pos_t *pos, bool color) inline eval_t eval_material(pos_t *pos, bool color)
{ {
@@ -51,18 +52,18 @@ inline eval_t eval_square_control(pos_t *pos, bool color)
eval_t eval(pos_t *pos) eval_t eval(pos_t *pos)
{ {
eval_t material[2] = {0}, control[2] = {0}; eval_t simple = 0, control[2] = {0};
if (pos->eval != EVAL_INVALID) if (pos->eval != EVAL_INVALID)
return pos->eval; return pos->eval;
/* 1) pieces value */ /* 1) pieces value */
material[WHITE] = eval_material(pos, WHITE); //material[WHITE] = eval_material(pos, WHITE);
material[BLACK] = eval_material(pos, BLACK); //material[BLACK] = eval_material(pos, BLACK);
simple = eval_simple(pos);
# ifdef DEBUG_EVAL # ifdef DEBUG_EVAL
log_f(2, "material: W:%d B:%d diff=%d\n", log_f(2, "eval_simple=%d\n", simple);
material[WHITE], material[BLACK], material[WHITE] - material[BLACK]);
# endif # endif
/* 2) square control: 10 square controls diff = 1 pawn */ /* 2) square control: 10 square controls diff = 1 pawn */
@@ -83,7 +84,7 @@ eval_t eval(pos_t *pos)
(pos->mobility[WHITE] - pos->mobility[BLACK]) * 10); (pos->mobility[WHITE] - pos->mobility[BLACK]) * 10);
# endif # endif
eval_t res = material[WHITE] - material[BLACK] + eval_t res = simple +
(control[WHITE] - control[BLACK]) * 10 + (control[WHITE] - control[BLACK]) * 10 +
(pos->mobility[WHITE] - pos->mobility[BLACK]) * 10; (pos->mobility[WHITE] - pos->mobility[BLACK]) * 10;
# ifdef DEBUG_EVAL # ifdef DEBUG_EVAL

View File

@@ -19,13 +19,12 @@
#include "chessdefs.h" #include "chessdefs.h"
#include "piece.h" #include "piece.h"
#define EVAL_MAX \ /* max pieces eval is KING_VALUE + 9*QUEEN_VALUE + 2*ROOK_VALUE + 2*BISHOP_VALUE
KING_VALUE + \ * + 2*KNIGHT_VALUE which around 30000.
QUEEN_VALUE * 9 + \ * We are on secure sire with -50000/+50000
ROOK_VALUE * 2 + \ */
BISHOP_VALUE * 2 + \
KNIGHT_VALUE * 2
#define EVAL_MAX (50000)
#define EVAL_MIN (-EVAL_MAX) #define EVAL_MIN (-EVAL_MAX)
#define EVAL_INVALID INT_MIN #define EVAL_INVALID INT_MIN

View File

@@ -16,12 +16,15 @@
#include <br.h> #include <br.h>
#include <list.h> #include <list.h>
#include <list_sort.h>
#include <debug.h> #include <debug.h>
#include "chessdefs.h" #include "chessdefs.h"
#include "board.h" #include "board.h"
#include "piece.h" #include "piece.h"
#include "move.h" #include "move.h"
#include "eval.h"
#include "eval-simple.h"
static pool_t *moves_pool; static pool_t *moves_pool;
@@ -37,6 +40,7 @@ static struct vector {
[KING] = { 8, 0, { -1, -16, 1, 16, -15, -17, 15, 17 }} [KING] = { 8, 0, { -1, -16, 1, 16, -15, -17, 15, 17 }}
}; };
/* squares needed to be empty & not controlled by opponent for castle. /* squares needed to be empty & not controlled by opponent for castle.
* For black castle, same values 7 rows higher (>> 7*8) * For black castle, same values 7 rows higher (>> 7*8)
*/ */
@@ -74,6 +78,7 @@ void moves_pool_stats()
* M_PR_NUM: print also move number * M_PR_NUM: print also move number
* M_PR_LONG: print long notation * M_PR_LONG: print long notation
* M_PR_NL: print a newline after move * M_PR_NL: print a newline after move
* M_PR_EVAL: print move eval
* *
* @return: 0 if nothing printed, 1 otherwise * @return: 0 if nothing printed, 1 otherwise
*/ */
@@ -117,6 +122,8 @@ int move_print(int movenum, move_t *move, move_flags_t flags)
log(1, "e.p."); log(1, "e.p.");
if (move->promotion) if (move->promotion)
log(1, "=%s", P_SYM(move->promotion)); log(1, "=%s", P_SYM(move->promotion));
if (flags & M_PR_EVAL)
log(1, "[ev:%d] ", move->eval);
end: end:
log(1, " "); log(1, " ");
} }
@@ -193,6 +200,7 @@ static move_t *move_add(pos_t *pos, piece_t piece, square_t from,
move->flags = M_NORMAL; move->flags = M_NORMAL;
if (move->capture) if (move->capture)
move->flags |= M_CAPTURE; move->flags |= M_CAPTURE;
move->pos = NULL;
# ifdef DEBUG_MOVE # ifdef DEBUG_MOVE
log_i(3, "added %s %s move from %c%c to %c%c\n", log_i(3, "added %s %s move from %c%c to %c%c\n",
COLOR(move->piece)? "black": "white", COLOR(move->piece)? "black": "white",
@@ -219,6 +227,8 @@ void move_del(struct list_head *ptr)
FILE2C(F88(move->to)), RANK2C(R88(move->to))); FILE2C(F88(move->to)), RANK2C(R88(move->to)));
# endif # endif
if (move->pos)
pos_del(move->pos);
list_del(ptr); list_del(ptr);
pool_add(moves_pool, move); pool_add(moves_pool, move);
return; return;
@@ -659,32 +669,94 @@ int moves_gen_king_moves(pos_t *pos, bool color, bool doit)
return count; return count;
} }
static int moves_cmp_eval(__unused void *data, const struct list_head *h1, const struct list_head *h2)
{
move_t *m1 = list_entry(h1, move_t, list);
move_t *m2 = list_entry(h2, move_t, list);
return m2->eval_simple - m1->eval_simple;
}
/** /**
* moves_gen_all() - calculate all moves * moves_sort() sort - sort moves list, best eval first.
* @pos: &position.
*/
void moves_sort(pos_t *pos)
{
list_sort(NULL, &pos->moves[pos->turn], moves_cmp_eval);
}
/**
* moves_gen_all_eval_sort() - calculate/generate/sort moves for side to play.
* @pos: &position * @pos: &position
* *
* Compute pseudo moves for both sides. * Generate positions for each move for player to move.
* For each of them generate opponents moves, calculate eval, and sort the moves list.
*/
void moves_gen_eval_sort(pos_t *pos)
{
move_t *move;
pos_t *newpos;
moves_gen_all(pos);
list_for_each_entry(move, &pos->moves[pos->turn], list) {
newpos = move_do(pos, move);
move->pos = newpos;
//move_print(0, move, 0);
move->eval_simple = eval_simple(newpos);
newpos->eval_simple = move->eval_simple;
}
moves_sort(pos);
//moves_print(pos, 0);
}
/**
* moves_gen_all() - calculate all moves, and generate moves for side to play.
* @pos: &position
*
* Compute pseudo moves for both sides, and generate moves for player to move.
*/ */
void moves_gen_all(pos_t *pos) void moves_gen_all(pos_t *pos)
{ {
//log_f(1, "turn=%d opponent=%d\n", pos->turn, OPPONENT(pos->turn)); //log_f(1, "turn=%d opponent=%d\n", pos->turn, OPPONENT(pos->turn));
if (!pos->moves_generated) {
if (!pos->moves_counted) {}
moves_gen(pos, OPPONENT(pos->turn), false, false); moves_gen(pos, OPPONENT(pos->turn), false, false);
moves_gen(pos, pos->turn, true, true); moves_gen(pos, pos->turn, true, true);
if (!pos->moves_counted)
moves_gen_king_moves(pos, OPPONENT(pos->turn), false); moves_gen_king_moves(pos, OPPONENT(pos->turn), false);
pos->moves_counted = true;
pos->moves_generated = true;
}
} }
/**
* moves_gen_all_nomoves() - calculate number of moves for each player.
* @pos: &position
*/
void moves_gen_all_nomoves(pos_t *pos)
{
//log_f(1, "turn=%d opponent=%d\n", pos->turn, OPPONENT(pos->turn));
if (!pos->moves_counted) {
moves_gen(pos, OPPONENT(pos->turn), false, false);
moves_gen(pos, pos->turn, false, true);
moves_gen_king_moves(pos, OPPONENT(pos->turn), false);
pos->moves_counted = true;
}
}
/** /**
* move_do() - execute move in a duplicated position. * move_do() - execute move in a duplicated position.
* @pos: &pos_t struct on which move will be applied * @pos: &pos_t struct on which move will be applied
* @move: &move_t struct to apply * @move: &move_t struct to apply
* *
* @return: &new position
*/ */
pos_t *move_do(pos_t *pos, move_t *move) pos_t *move_do(pos_t *pos, move_t *move)
{ {
# ifdef DEBUG_MOVE # ifdef DEBUG_MOVE
//log(1, "new move: "); //log(1, "new move: ");
move_print(0, move, M_PR_NL | M_PR_LONG); //move_print(0, move, M_PR_NL | M_PR_LONG);
# endif # endif
pos_t *new = pos_dup(pos); pos_t *new = pos_dup(pos);
@@ -693,8 +765,6 @@ pos_t *move_do(pos_t *pos, move_t *move)
square_t from = move->from, to = move->to; square_t from = move->from, to = move->to;
u64 bb_from = SQ88_2_BB(from), bb_to = SQ88_2_BB(to); u64 bb_from = SQ88_2_BB(from), bb_to = SQ88_2_BB(to);
//printf("Piece color=%d\n", color);
if (move->capture || piece == PAWN) /* 50 moves */ if (move->capture || piece == PAWN) /* 50 moves */
new->clock_50 = 0; new->clock_50 = 0;
else else

View File

@@ -36,6 +36,7 @@ typedef unsigned char move_flags_t;
#define M_PR_NCAPT 0x02 #define M_PR_NCAPT 0x02
#define M_PR_NUM 0x04 #define M_PR_NUM 0x04
#define M_PR_NL 0x08 #define M_PR_NL 0x08
#define M_PR_EVAL 0x20 /* separate captures */
#define M_PR_SEPARATE 0x40 /* separate captures */ #define M_PR_SEPARATE 0x40 /* separate captures */
#define M_PR_LONG 0x80 #define M_PR_LONG 0x80
@@ -46,6 +47,9 @@ typedef struct move_s {
piece_t promotion; /* promoted piece */ piece_t promotion; /* promoted piece */
move_flags_t flags; move_flags_t flags;
eval_t negamax; eval_t negamax;
eval_t eval;
eval_t eval_simple;
pos_t *pos;
struct list_head list; /* next move */ struct list_head list; /* next move */
} move_t; } move_t;
@@ -62,7 +66,12 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *piece, bool doit, bool do_king);
int pseudo_moves_pawn(pos_t *pos, piece_list_t *piece, bool doit); int pseudo_moves_pawn(pos_t *pos, piece_list_t *piece, bool doit);
int moves_gen(pos_t *pos, bool color, bool doit, bool do_king); int moves_gen(pos_t *pos, bool color, bool doit, bool do_king);
int moves_gen_king_moves(pos_t *pos, bool color, bool doit); int moves_gen_king_moves(pos_t *pos, bool color, bool doit);
void moves_sort(pos_t *pos);
void moves_gen_eval_sort(pos_t *pos);
void moves_gen_all(pos_t *pos); void moves_gen_all(pos_t *pos);
void moves_gen_all_nomoves(pos_t *pos);
pos_t *move_do(pos_t *pos, move_t *move); pos_t *move_do(pos_t *pos, move_t *move);
void move_undo(pos_t *pos, move_t *move); void move_undo(pos_t *pos, move_t *move);

View File

@@ -22,6 +22,7 @@
#include "move.h" #include "move.h"
#include "fen.h" #include "fen.h"
#include "piece.h" #include "piece.h"
#include "eval.h"
static pool_t *pos_pool; static pool_t *pos_pool;
@@ -199,6 +200,7 @@ pos_t *pos_clear(pos_t *pos)
} }
SET_WHITE(pos->turn); SET_WHITE(pos->turn);
pos->node_count = 0;
pos->castle = 0; pos->castle = 0;
pos->clock_50 = 0; pos->clock_50 = 0;
pos->curmove = 0; pos->curmove = 0;
@@ -213,6 +215,8 @@ pos_t *pos_clear(pos_t *pos)
pos->controlled[BLACK] = 0; pos->controlled[BLACK] = 0;
pos->mobility[WHITE] = 0; pos->mobility[WHITE] = 0;
pos->mobility[BLACK] = 0; pos->mobility[BLACK] = 0;
pos->moves_generated = false;
pos->moves_counted = false;
/* remove pieces / moves */ /* remove pieces / moves */
pieces_del(pos, WHITE); pieces_del(pos, WHITE);
pieces_del(pos, BLACK); pieces_del(pos, BLACK);
@@ -260,8 +264,13 @@ pos_t *pos_get()
* pos_dup() - duplicate a position. * pos_dup() - duplicate a position.
* @pos: &position to duplicate. * @pos: &position to duplicate.
* *
* New position is the same as source one, with duplicated pieces list * New position is the same as source one (with duplicated pieces list),
* and empty moves list and NULL bestmove. * except:
* - moves list is empty
* - bestmove is NULL
* - nodecount is set to zero
* - eval is set to EVAL_INVALID
* - moves_generated ans moves_counted are unset
* *
* @return: The new position. * @return: The new position.
* *
@@ -280,7 +289,6 @@ pos_t *pos_dup(pos_t *pos)
for (int color = 0; color < 2; ++color) { for (int color = 0; color < 2; ++color) {
INIT_LIST_HEAD(&new->pieces[color]); INIT_LIST_HEAD(&new->pieces[color]);
INIT_LIST_HEAD(&new->moves[color]); INIT_LIST_HEAD(&new->moves[color]);
new->bestmove = NULL;
/* duplicate piece list */ /* duplicate piece list */
piece_list = &pos->pieces[color]; /* white/black piece list */ piece_list = &pos->pieces[color]; /* white/black piece list */
@@ -290,6 +298,11 @@ pos_t *pos_dup(pos_t *pos)
piece_add(new, oldpiece->piece, oldpiece->square); piece_add(new, oldpiece->piece, oldpiece->square);
} }
} }
new->bestmove = NULL;
new->node_count = 0;
new->eval = EVAL_INVALID;
new->moves_generated = false;
new->moves_counted = false;
} }
return new; return new;
} }

View File

@@ -23,12 +23,17 @@
#include "chessdefs.h" #include "chessdefs.h"
typedef struct pos_s { typedef struct pos_s {
u64 node_count; /* evaluated nodes */
piece_t turn; /* we use only color bit */ piece_t turn; /* we use only color bit */
castle_t castle; castle_t castle;
u16 clock_50; u16 clock_50;
u16 curmove; u16 curmove;
eval_t eval; eval_t eval;
int eval_simple_phase;
eval_t eval_simple;
move_t *bestmove; move_t *bestmove;
bool moves_generated;
bool moves_counted;
board_t board[BOARDSIZE]; board_t board[BOARDSIZE];
square_t en_passant; square_t en_passant;

View File

@@ -37,6 +37,7 @@ eval_t negamax(pos_t *pos, int depth, int color)
pos_t *newpos; pos_t *newpos;
eval_t best = EVAL_MIN, score; eval_t best = EVAL_MIN, score;
pos->node_count++;
if (depth == 0) { if (depth == 0) {
moves_gen_all_nomoves(pos); moves_gen_all_nomoves(pos);
score = eval(pos) * color; score = eval(pos) * color;
@@ -70,53 +71,74 @@ eval_t negamax(pos_t *pos, int depth, int color)
* Calculate the PVS value of @pos. * Calculate the PVS value of @pos.
* See https://en.wikipedia.org/wiki/Principal_variation_search * See https://en.wikipedia.org/wiki/Principal_variation_search
* *
* Moves list should be first generated and evaluated/sorted.
*
* @return: The @pos PVS evaluation. * @return: The @pos PVS evaluation.
*/ */
eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color) eval_t pvs(pos_t *pos, int depth, int alpha, int beta, int color)
{ {
move_t *move; move_t *move;
pos_t *newpos; pos_t *newpos;
eval_t score; eval_t score = EVAL_INVALID;
int n = 0; bool firstchild = true;
pos->node_count++;
if (depth == 0) { if (depth == 0) {
//return quiesce(p, alpha, beta); /* leaf node */ //return quiesce(p, alpha, beta); /* leaf node */
moves_gen_all_nomoves(pos); moves_gen_all(pos);
score = eval(pos) * color; score = eval(pos) * color;
log_f(2, "Terminal: depth=%d ", depth);
log_f(2, "score=%d alpha=%d beta=%d\n", score, alpha, beta);
return score; return score;
} }
moves_gen_eval_sort(pos); moves_gen_all(pos);
//moves_print(pos, M_PR_EVAL); //moves_print(pos, M_PR_EVAL);
/* do the full search for first child */
//move = list_first_entry_or_null(&pos->moves[pos->turn], move_t, list);
list_for_each_entry(move, &pos->moves[pos->turn], list) { list_for_each_entry(move, &pos->moves[pos->turn], list) {
log(1, "%.*s", 5 - depth, " "); newpos = move_do(pos, move);
newpos = move->pos; log(2, "%.*s", 5 - depth, " ");
log_f(1, "depth=%d eval=%d move=", depth, move->eval); if (firstchild) { /* first child */
move_print(0, move, M_PR_EVAL);
log(1, "\n");
if (!n++) { /* first child */
score = -pvs(newpos, depth - 1, -beta, -alpha, -color); score = -pvs(newpos, depth - 1, -beta, -alpha, -color);
log_f(2, "First child depth=%d move=", depth);
//move_print(0, move, 0);
log(2, "score=%d alpha=%d beta=%d\n", score, alpha, beta);
pos->bestmove = move;
} else { } else {
/* search with a null window */ /* search with a null window */
score = -pvs(newpos, depth - 1, -alpha - 1, -alpha, -color); score = -pvs(newpos, depth - 1, -alpha - 1, -alpha, -color);
if (score > alpha && score < beta) { log_f(2, "Other child depth=%d move=", depth);
//move_print(0, move, 0);
log_f(2, "score=%d alpha=%d beta=%d ", score, alpha, beta);
/* for fail-soft: if (score > alpha && score < beta) */
if (score > alpha) {
/* if failed high, do a full re-search */ /* if failed high, do a full re-search */
score = -pvs(newpos, depth - 1, -beta, -score, -color); log_f(2, "doing full search.");
score = -pvs(newpos, depth - 1, -beta, -alpha, -color);
} }
log(2, "\n");
}
pos->node_count += newpos->node_count;
move_undo(newpos, move);
if (score >= beta) { /* fail-hard hard beta cut-off */
log(2, "%.*s", 5 - depth, " ");
log_f(2, "depth=%d score=%d alpha=%d beta=%d beta cut-off.\n",
depth, score, alpha, beta);
return beta;
} }
if (score > alpha) { if (score > alpha) {
log(2, "%.*s", 5 - depth, " ");
log_f(2, "depth=%d setting new alpha from %d to %d\n",
depth, alpha, score);
alpha = score; alpha = score;
pos->bestmove = move; pos->bestmove = move;
} }
move_undo(newpos, move);
move->pos = NULL; move->pos = NULL;
pos->node_count += newpos->node_count;
move->negamax = score; move->negamax = score;
// alpha = max(alpha, score); firstchild = false;
if (alpha > beta) { /* beta cut-off */
break;
}
} }
return alpha; return alpha;