ref: 3e86752a094eee5acfda25e65f699a3cb4ab7aeb
parent: 27d3f1c044b4846b1315bbeb033a1d840f04aff9
author: zamfofex <zamfofex@twdb.moe>
date: Fri Oct 13 12:58:09 EDT 2023
add simple threading support
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@
- alpha/beta pruning search
- cute custom UCI TUI
- custom Lichess integration
+- optional threaded search support (enabled by default)
limitations
---
@@ -32,7 +33,6 @@
- no support for `go infinite` or `go mate`
- no move name or FEN validation (may lead to potential exploits)
- the bot will ignore time control information
-- search is single threaded
building
---
@@ -76,7 +76,7 @@
license
---
-[GNU][GPL] [AGPL] ([v3][AGPLv3] [or later][GPLv3+]) © zamfofex 2023
+[GNU][GPL] [AGPL] ([v3][AGPLv3] [or later][GPLv3+]) © zamfofex 2023
[GPL]: <https://www.gnu.org/licenses/>
[AGPL]: <https://www.gnu.org/licenses/why-affero-gpl.html>
--- a/makefile
+++ b/makefile
@@ -1,11 +1,18 @@
CFLAGS ?= -ansi -O3 -Wall -Wextra -Wpedantic
-cc = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
+cc := $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)
+src := *.c
+
+moonfish_cc := $(cc)
+tools_cc := $(cc) -pthread -D_POSIX_C_SOURCE=200809L
+
+moonfish:
+
ifeq ($(inbuilt_network),yes)
-moonfish: moonfish.h *.c net/tanh.c tanh.o
- $(cc) -D_POSIX_C_SOURCE=200809L -DMOONFISH_INBUILT_NET -o moonfish net/tanh.c tanh.o *.c
+src += net/tanh.c tanh.o
+moonfish_cc += -D_POSIX_C_SOURCE=200809L -DMOONFISH_INBUILT_NET
tanh.o: tanh.moon
$(LD) -r -b binary -o tanh.o tanh.moon
@@ -13,18 +20,22 @@
tanh.moon: tanh.pickle
python3 convert.py tanh.pickle
-else
+endif
-moonfish: moonfish.h *.c
- $(cc) -o moonfish *.c
+ifneq ($(has_pthread),no)
+moonfish_cc += -DMOONFISH_HAS_PTHREAD -pthread
+
endif
+moonfish: moonfish.h $(src)
+ $(moonfish_cc) -o moonfish $(src)
+
play: moonfish.h tools/tools.h tools/play.c tools/utils.c chess.c
- $(cc) -pthread -D_POSIX_C_SOURCE=200809L -o play tools/play.c tools/utils.c chess.c
+ $(tools_cc) -o play tools/play.c tools/utils.c chess.c
lichess: tools/tools.h tools/lichess.c tools/utils.c tools/play.c
- $(cc) -pthread -D_POSIX_C_SOURCE=200809L -std=c99 -o lichess tools/lichess.c tools/utils.c -lbearssl -lcjson
+ $(tools_cc) -std=c99 -o lichess tools/lichess.c tools/utils.c -lbearssl -lcjson
.PHONY: all clean
--- a/moonfish.h
+++ b/moonfish.h
@@ -3,6 +3,10 @@
#include <stdio.h>
+#ifdef MOONFISH_HAS_PTHREAD
+#include <pthread.h>
+#endif
+
enum
{moonfish_our_pawn = 0x11,
--- a/search.c
+++ b/search.c
@@ -1,6 +1,6 @@
#include "moonfish.h"
-static int moonfish_evaluate(struct moonfish *ctx)
+static int moonfish_evaluate(struct moonfish_chess *chess, struct moonfish_nnue *nnue)
{ int features[18] = {0};int x, y;
@@ -15,14 +15,14 @@
for (y = 0 ; y < 8 ; y++)
for (x = 0 ; x < 8 ; x++)
{- piece = ctx->chess.board[(x + 1) + (y + 2) * 10];
+ piece = chess->board[(x + 1) + (y + 2) * 10];
if (piece == moonfish_empty) continue;
color = (piece >> 4) - 1;
type = (piece & 0xF) - 1;
- white_values = ctx->nnue.values[color][type][63 - (x + y * 8)];
- black_values = ctx->nnue.values[color ^ 1][type][x + y * 8];
+ white_values = nnue->values[color][type][63 - (x + y * 8)];
+ black_values = nnue->values[color ^ 1][type][x + y * 8];
scale += white_values[0];
scale -= black_values[0];
@@ -38,17 +38,17 @@
{hidden[i] = 0;
for (j = 0 ; j < 18 ; j++)
- hidden[i] += ctx->nnue.layer1[i * 18 + j] * moonfish_tanh(features[j]) / 127;
+ hidden[i] += nnue->layer1[i * 18 + j] * moonfish_tanh(features[j]) / 127;
}
score = 0;
for (i = 0 ; i < 10 ; i++)
- score += moonfish_tanh(hidden[i]) * ctx->nnue.layer2[i] / 127;
+ score += moonfish_tanh(hidden[i]) * nnue->layer2[i] / 127;
- return score * 360 + scale * ctx->nnue.scale * 360 / 256;
+ return score * 360 + scale * nnue->scale * 360 / 256;
}
-static int moonfish_search(struct moonfish *ctx, int alpha, int beta, int depth)
+static int moonfish_search(struct moonfish_chess *chess, struct moonfish_nnue *nnue, int alpha, int beta, int depth)
{int x, y;
struct moonfish_move moves[32];
@@ -57,7 +57,7 @@
if (depth <= 0)
{- score = moonfish_evaluate(ctx);
+ score = moonfish_evaluate(chess, nnue);
if (depth <= -3) return score;
if (score >= beta) return beta;
@@ -67,7 +67,7 @@
for (y = 0 ; y < 8 ; y++)
for (x = 0 ; x < 8 ; x++)
{- moonfish_moves(&ctx->chess, moves, (x + 1) + (y + 2) * 10);
+ moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
for (move = moves ; move->piece != moonfish_outside ; move++)
{@@ -74,9 +74,9 @@
if (depth <= 0 && move->captured == moonfish_empty) continue;
if (move->captured == moonfish_their_king) return moonfish_omega * (depth + 10);
- moonfish_play(&ctx->chess, move);
- score = -moonfish_search(ctx, -beta, -alpha, depth - 1);
- moonfish_unplay(&ctx->chess, move);
+ moonfish_play(chess, move);
+ score = -moonfish_search(chess, nnue, -beta, -alpha, depth - 1);
+ moonfish_unplay(chess, move);
if (score >= beta) return beta;
if (score > alpha) alpha = score;
@@ -86,9 +86,101 @@
return alpha;
}
+#if MOONFISH_HAS_PTHREAD
+
+#include <stdlib.h>
+#include <string.h>
+
+struct moonfish_search_info
+{+ pthread_t thread;
+ struct moonfish_move move;
+ struct moonfish_chess chess;
+ struct moonfish_nnue *nnue;
+ int depth;
+ int score;
+};
+
+static void *moonfish_start_search(void *data)
+{+ struct moonfish_search_info *info;
+ info = data;
+ info->score = -moonfish_search(&info->chess, info->nnue, -100 * moonfish_omega, 100 * moonfish_omega, info->depth);
+ return NULL;
+}
+
int moonfish_best_move(struct moonfish *ctx, struct moonfish_move *best_move)
{int x, y;
+ struct moonfish_move *move, moves[32];
+ int best_score;
+ int count, i;
+ struct moonfish_search_info *infos;
+ int result;
+
+ infos = malloc(256 * sizeof *infos);
+ count = 0;
+ best_score = -200 * moonfish_omega;
+
+ for (y = 0 ; y < 8 ; y++)
+ for (x = 0 ; x < 8 ; x++)
+ {+ moonfish_moves(&ctx->chess, moves, (x + 1) + (y + 2) * 10);
+
+ for (move = moves ; move->piece != moonfish_outside ; move++)
+ {+ moonfish_play(&ctx->chess, move);
+
+ if (!moonfish_validate(&ctx->chess))
+ {+ moonfish_unplay(&ctx->chess, move);
+ continue;
+ }
+
+ infos[count].move = *move;
+ infos[count].chess = ctx->chess;
+ infos[count].nnue = &ctx->nnue;
+ infos[count].depth = 3;
+ result = pthread_create(&infos[count].thread, NULL, &moonfish_start_search, infos + count);
+ if (result)
+ {+ free(infos);
+ fprintf(stderr, "moonfish: %s\n", strerror(result));
+ exit(1);
+ }
+
+ moonfish_unplay(&ctx->chess, move);
+
+ count++;
+ }
+ }
+
+ for (i = 0 ; i < count ; i++)
+ {+ result = pthread_join(infos[i].thread, NULL);
+ if (result)
+ {+ free(infos);
+ fprintf(stderr, "moonfish: %s\n", strerror(result));
+ exit(1);
+ }
+
+ if (infos[i].score > best_score)
+ {+ *best_move = infos[i].move;
+ best_score = infos[i].score;
+ }
+ }
+
+ free(infos);
+ return best_score;
+}
+
+#else
+
+int moonfish_best_move(struct moonfish *ctx, struct moonfish_move *best_move)
+{+ int x, y;
struct moonfish_move moves[32];
struct moonfish_move *move;
int score, best_score;
@@ -110,7 +202,7 @@
continue;
}
- score = -moonfish_search(ctx, -100 * moonfish_omega, 100 * moonfish_omega, 3);
+ score = -moonfish_search(&ctx->chess, &ctx->nnue, -100 * moonfish_omega, 100 * moonfish_omega, 3);
moonfish_unplay(&ctx->chess, move);
if (score > best_score)
@@ -123,3 +215,5 @@
return best_score;
}
+
+#endif
--
⑨