shithub: moonfish

Download patch

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+]) &copy; 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
--