shithub: moonfish

Download patch

ref: 71d5b0ec838cc99a34d1c3669fb014adb75d097f
parent: f4a1bc7b7e2a7f68b74ae757e452cab54819b3e9
author: zamfofex <zamfofex@twdb.moe>
date: Sat Jan 25 07:35:21 EST 2025

fix some concurrency issues

--- a/chess.c
+++ b/chess.c
@@ -317,24 +317,6 @@
 	return 1;
 }
 
-int moonfish_finished(struct moonfish_chess *chess)
-{
-	struct moonfish_move moves[32];
-	int x, y;
-	int i, count;
-	
-	for (y = 0 ; y < 8 ; y++) {
-		for (x = 0 ; x < 8 ; x++) {
-			count = moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
-			for (i = 0 ; i < count ; i++) {
-				if (moonfish_validate(&moves[i].chess)) return 0;
-			}
-		}
-	}
-	
-	return 1;
-}
-
 int moonfish_equal(struct moonfish_chess *a, struct moonfish_chess *b)
 {
 	int x, y, i;
@@ -361,6 +343,24 @@
 #ifndef moonfish_mini
 
 #include <ctype.h>
+
+int moonfish_finished(struct moonfish_chess *chess)
+{
+	struct moonfish_move moves[32];
+	int x, y;
+	int i, count;
+	
+	for (y = 0 ; y < 8 ; y++) {
+		for (x = 0 ; x < 8 ; x++) {
+			count = moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
+			for (i = 0 ; i < count ; i++) {
+				if (moonfish_validate(&moves[i].chess)) return 0;
+			}
+		}
+	}
+	
+	return 1;
+}
 
 int moonfish_move(struct moonfish_chess *chess, struct moonfish_move *found, int from, int to)
 {
--- a/main.c
+++ b/main.c
@@ -26,6 +26,8 @@
 	thrd_t thread;
 #endif
 	struct moonfish_option *options;
+	struct moonfish_result result;
+	struct moonfish_options search_options;
 };
 
 static int moonfish_getoption(struct moonfish_option *options, char *name)
@@ -37,21 +39,54 @@
 	return -1;
 }
 
-static moonfish_result_t moonfish_go(void *data)
+static moonfish_result_t moonfish_go0(void *data)
 {
-	static struct moonfish_result result, pv_result;
-	static struct moonfish_options options;
-	static struct moonfish_chess chess;
 	static struct moonfish_move pv[256];
+	static struct moonfish_result result;
+	static struct moonfish_chess chess;
 	
+	struct moonfish_info *info;
+	int i, j, count;
+	char name[6];
+	
+	info = data;
+	
+	moonfish_best_move(info->root, &info->result, &info->search_options);
+	
+	count = moonfish_getoption(info->options, "MultiPV");
+	for (i = 0 ; i < count ; i++) {
+		count = sizeof pv / sizeof *pv;
+		moonfish_pv(info->root, pv, &result, i, &count);
+		if (count == 0) continue;
+		printf("info depth 1 score cp %d nodes %ld multipv %d pv", result.score, result.node_count, i + 1);
+		moonfish_root(info->root, &chess);
+		for (j = 0 ; j < count ; j++) {
+			moonfish_to_uci(&chess, pv + j, name);
+			chess = pv[j].chess;
+			printf(" %s", name);
+		}
+		printf("\n");
+	}
+	
+	printf("info depth 1 score cp %d nodes %ld\n", info->result.score, info->result.node_count);
+	
+	moonfish_root(info->root, &chess);
+	moonfish_to_uci(&chess, &info->result.move, name);
+	
+	printf("bestmove %s\n", name);
+	fflush(stdout);
+	return moonfish_value;
+}
+
+static void moonfish_go(struct moonfish_info *info)
+{
+	static struct moonfish_chess chess;
+	
 	long int our_time, their_time, *xtime, time;
 	char *arg, *end;
-	char name[6];
-	struct moonfish_info *info;
 	long int node_count;
-	int i, j, pv_count, count;
 	
-	info = data;
+	info->searching = 1;
 	
 	our_time = -1;
 	their_time = -1;
@@ -129,38 +164,29 @@
 		}
 	}
 	
-	options.max_time = time;
-	options.our_time = our_time;
-	options.thread_count = moonfish_getoption(info->options, "Threads");
-	options.node_count = node_count;
+	info->search_options.max_time = time;
+	info->search_options.our_time = our_time;
+	info->search_options.thread_count = moonfish_getoption(info->options, "Threads");
+	info->search_options.node_count = node_count;
 	
-	moonfish_best_move(info->root, &result, &options);
-	info->searching = 0;
-	
-	pv_count = moonfish_getoption(info->options, "MultiPV");
-	for (i = 0 ; i < pv_count ; i++) {
-		count = sizeof pv / sizeof *pv;
-		moonfish_pv(info->root, pv, &pv_result, i, &count);
-		if (count == 0) continue;
-		printf("info depth 1 score cp %d nodes %ld multipv %d pv", pv_result.score, pv_result.node_count, i + 1);
-		moonfish_root(info->root, &chess);
-		for (j = 0 ; j < count ; j++) {
-			moonfish_to_uci(&chess, pv + j, name);
-			chess = pv[j].chess;
-			printf(" %s", name);
+#ifdef moonfish_no_threads
+	moonfish_go0(info);
+#else
+	if (info->has_thread) {
+		if (thrd_join(info->thread, NULL) != thrd_success) {
+			fprintf(stderr, "could not join thread\n");
+			exit(1);
 		}
-		printf("\n");
 	}
+	info->has_thread = 1;
+	moonfish_unstop(info->root);
+	if (thrd_create(&info->thread, &moonfish_go0, info) != thrd_success) {
+		fprintf(stderr, "could not create thread\n");
+		exit(1);
+	}
+#endif
 	
-	printf("info depth 1 score cp %d nodes %ld\n", result.score, result.node_count);
-	
-	moonfish_root(info->root, &chess);
-	moonfish_to_uci(&chess, &result.move, name);
-	
-	printf("bestmove %s\n", name);
-	fflush(stdout);
-	
-	return moonfish_value;
+	info->searching = 0;
 }
 
 static void moonfish_position(struct moonfish_root *root)
@@ -322,29 +348,11 @@
 		if (arg == NULL) continue;
 		
 		if (!strcmp(arg, "go")) {
-			
-#ifdef moonfish_no_threads
-			moonfish_go(&info);
-#else
 			if (info.searching) {
 				fprintf(stderr, "cannot start search while searching\n");
 				exit(1);
 			}
-			info.searching = 1;
-			if (info.has_thread) {
-				if (thrd_join(info.thread, NULL) != thrd_success) {
-					fprintf(stderr, "could not join thread\n");
-					exit(1);
-				}
-			}
-			info.has_thread = 1;
-			moonfish_unstop(info.root);
-			if (thrd_create(&info.thread, &moonfish_go, &info) != thrd_success) {
-				fprintf(stderr, "could not create thread\n");
-				exit(1);
-			}
-#endif
-			
+			moonfish_go(&info);
 			continue;
 		}
 		
--- a/scripts/check.txt
+++ b/scripts/check.txt
@@ -1,105 +1,105 @@
 perft 0: 1
-info depth 1 score cp -1 nodes 4352
+info depth 1 score cp 2 nodes 4096
 bestmove b1c3
 perft 0: 1
-info depth 1 score cp 85 nodes 4352
+info depth 1 score cp 116 nodes 4096
 bestmove e2a6
 perft 0: 1
-info depth 1 score cp 64 nodes 4352
+info depth 1 score cp 64 nodes 4096
 bestmove b4c4
 perft 0: 1
-info depth 1 score cp -522 nodes 4352
-bestmove d2d4
+info depth 1 score cp -498 nodes 4096
+bestmove b4c5
 perft 0: 1
-info depth 1 score cp -522 nodes 4352
-bestmove d7d5
+info depth 1 score cp -498 nodes 4096
+bestmove b5c4
 perft 0: 1
-info depth 1 score cp 577 nodes 4352
-bestmove d7c8r
+info depth 1 score cp 304 nodes 4096
+bestmove d7c8q
 perft 0: 1
-info depth 1 score cp 20 nodes 4352
-bestmove a1d1
+info depth 1 score cp 35 nodes 4096
+bestmove c4d5
 perft 1: 20
-info depth 1 score cp 0 nodes 8448
+info depth 1 score cp 0 nodes 8192
 bestmove b1c3
 perft 1: 48
-info depth 1 score cp 84 nodes 8448
+info depth 1 score cp 84 nodes 8192
 bestmove e2a6
 perft 1: 14
-info depth 1 score cp 38 nodes 8448
+info depth 1 score cp 64 nodes 8192
 bestmove b4c4
 perft 1: 6
-info depth 1 score cp -111 nodes 8448
+info depth 1 score cp -113 nodes 8192
 bestmove c4c5
 perft 1: 6
-info depth 1 score cp -111 nodes 8448
+info depth 1 score cp -113 nodes 8192
 bestmove c5c4
 perft 1: 44
-info depth 1 score cp 619 nodes 8448
+info depth 1 score cp 619 nodes 8192
 bestmove d7c8r
 perft 1: 46
-info depth 1 score cp 36 nodes 8448
+info depth 1 score cp 37 nodes 8192
 bestmove c3d5
 perft 2: 400
-info depth 1 score cp 9 nodes 12544
+info depth 1 score cp 9 nodes 12288
 bestmove b1c3
 perft 2: 2039
-info depth 1 score cp 203 nodes 12544
+info depth 1 score cp 202 nodes 12288
 bestmove e2a6
 perft 2: 191
-info depth 1 score cp 38 nodes 12544
+info depth 1 score cp 38 nodes 12288
 bestmove b4c4
 perft 2: 264
-info depth 1 score cp -345 nodes 12544
+info depth 1 score cp -345 nodes 12288
 bestmove c4c5
 perft 2: 264
-info depth 1 score cp -345 nodes 12544
+info depth 1 score cp -345 nodes 12288
 bestmove c5c4
 perft 2: 1486
-info depth 1 score cp 640 nodes 12544
+info depth 1 score cp 640 nodes 12288
 bestmove d7c8r
 perft 2: 2079
-info depth 1 score cp 25 nodes 12544
+info depth 1 score cp 22 nodes 12288
 bestmove c3d5
 perft 3: 8902
-info depth 1 score cp 9 nodes 16640
+info depth 1 score cp 9 nodes 16384
 bestmove b1c3
 perft 3: 97862
-info depth 1 score cp 217 nodes 16640
+info depth 1 score cp 217 nodes 16384
 bestmove e2a6
 perft 3: 2812
-info depth 1 score cp 38 nodes 16640
+info depth 1 score cp 32 nodes 16384
 bestmove b4c4
 perft 3: 9467
-info depth 1 score cp -345 nodes 16640
+info depth 1 score cp -443 nodes 16384
 bestmove c4c5
 perft 3: 9467
-info depth 1 score cp -345 nodes 16640
+info depth 1 score cp -443 nodes 16384
 bestmove c5c4
 perft 3: 62379
-info depth 1 score cp 640 nodes 16640
+info depth 1 score cp 640 nodes 16384
 bestmove d7c8q
 perft 3: 89890
-info depth 1 score cp 161 nodes 16640
+info depth 1 score cp 161 nodes 16384
 bestmove c3d5
 perft 4: 197281
-info depth 1 score cp 19 nodes 20736
+info depth 1 score cp 19 nodes 20480
 bestmove g1f3
 perft 4: 4085603
-info depth 1 score cp 172 nodes 20736
+info depth 1 score cp 178 nodes 20480
 bestmove e2a6
 perft 4: 43238
-info depth 1 score cp 32 nodes 20736
+info depth 1 score cp 26 nodes 20480
 bestmove b4c4
 perft 4: 422333
-info depth 1 score cp -443 nodes 20736
+info depth 1 score cp -443 nodes 20480
 bestmove c4c5
 perft 4: 422333
-info depth 1 score cp -443 nodes 20736
+info depth 1 score cp -443 nodes 20480
 bestmove c5c4
 perft 4: 2103487
-info depth 1 score cp 632 nodes 20736
+info depth 1 score cp 632 nodes 20480
 bestmove d7c8r
 perft 4: 3894594
-info depth 1 score cp 164 nodes 20736
+info depth 1 score cp 164 nodes 20480
 bestmove c3d5
--- a/search.c
+++ b/search.c
@@ -60,7 +60,7 @@
 	_Atomic long int visits;
 	_Atomic unsigned char bounds[2];
 	_Atomic short int count;
-	_Atomic unsigned char ignored;
+	_Atomic unsigned char ignored, uses;
 	unsigned char from, index;
 };
 
@@ -131,18 +131,19 @@
 	
 #ifdef moonfish_no_threads
 	count = node->count;
-	if (count <= 0) return;
 #else
 	for (;;) {
 		count = node->count;
-		if (count == -1) continue;
-		if (count <= 0) return;
-		if (atomic_compare_exchange_strong(&node->count, &count, -1)) break;
+		if (count < 0) continue;
+		if (atomic_compare_exchange_strong(&node->count, &count, -2)) break;
 	}
+	for (;;) {
+		if (node->uses == 0) break;
+	}
 #endif
 	
 	for (i = 0 ; i < count ; i++) moonfish_discard(node->children + i);
-	free(node->children);
+	if (count > 0) free(node->children);
 	node->count = 0;
 }
 
@@ -154,6 +155,7 @@
 	node->ignored = 0;
 	node->bounds[0] = 0;
 	node->bounds[1] = 1;
+	node->uses = 0;
 }
 
 static int moonfish_compare(const void *ax, const void *bx)
@@ -174,12 +176,6 @@
 	int child_count;
 	struct moonfish_move moves[32];
 	
-	if (node->count == -2) return;
-	if (moonfish_finished(chess)) {
-		node->count = -2;
-		return;
-	}
-	
 	node->children = NULL;
 	child_count = 0;
 	
@@ -212,8 +208,9 @@
 		}
 	}
 	
-	if (child_count > 0) qsort(node->children, child_count, sizeof *node, &moonfish_compare);
 	if (child_count == 0 && node->children != NULL) free(node->children);
+	if (child_count > 0) qsort(node->children, child_count, sizeof *node, &moonfish_compare);
+	
 	node->count = child_count;
 }
 
@@ -237,44 +234,62 @@
 	*chess = move.chess;
 }
 
+#ifndef moonfish_no_threads
+
+static void moonfish_clear(struct moonfish_node *node)
+{
+	if (node == NULL) return;
+	moonfish_clear(node->parent);
+	atomic_fetch_add(&node->uses, -1);
+}
+
+#endif
+
 static struct moonfish_node *moonfish_select(struct moonfish_node *node, struct moonfish_chess *chess)
 {
 	struct moonfish_node *next;
 	float max_confidence, confidence;
-	short int i, n;
+	short int i, count;
 	
 	for (;;) {
 		
 #ifdef moonfish_no_threads
-		n = node->count;
-		if (n <= 0) break;
+		count = node->count;
+		if (count == 0) return node;
 #else
-		n = 0;
-		if (atomic_compare_exchange_strong(&node->count, &n, -1)) break;
-		if (n == -1) continue;
-		if (n == -2) break;
+		atomic_fetch_add(&node->uses, 1);
+		for (;;) {
+			count = 0;
+			if (atomic_compare_exchange_strong(&node->count, &count, -1)) return node;
+			if (count == -2) {
+				moonfish_clear(node);
+				return NULL;
+			}
+			if (count > 0) break;
+		}
 #endif
 		
-		next = NULL;
-		max_confidence = -1;
-		
-		for (i = 0 ; i < n ; i++) {
-			if (node->children[i].ignored) continue;
-			if (node->children[i].count == -1) continue;
-			confidence = moonfish_confidence(node->children + i);
-			if (confidence > max_confidence) {
-				next = node->children + i;
-				max_confidence = confidence;
+		for (;;) {
+			
+			next = NULL;
+			max_confidence = -1;
+			
+			for (i = 0 ; i < count ; i++) {
+				if (node->children[i].ignored) continue;
+				if (node->children[i].count == -1) continue;
+				confidence = moonfish_confidence(node->children + i);
+				if (confidence > max_confidence) {
+					next = node->children + i;
+					max_confidence = confidence;
+				}
 			}
+			
+			if (next != NULL) break;
 		}
 		
-		if (next == NULL) continue;
-		
 		node = next;
 		moonfish_node_chess(node, chess);
 	}
-	
-	return node;
 }
 
 static void moonfish_propagate(struct moonfish_node *node)
@@ -281,28 +296,33 @@
 {
 	int i;
 	short int score, child_score;
+	struct moonfish_node *next;
 	
 	while (node != NULL) {
-		score = node->count == -2 ? 0 : SHRT_MIN;
+		score = node->count == 0 ? 0 : SHRT_MIN;
 		for (i = 0 ; i < node->count ; i++) {
 			child_score = -node->children[i].score;
 			if (score < child_score) score = child_score;
 		}
+		next = node->parent;
 		node->score = score;
 #ifdef moonfish_no_threads
 		node->visits++;
 #else
 		atomic_fetch_add(&node->visits, 1);
+		atomic_fetch_add(&node->uses, -1);
 #endif
-		node = node->parent;
+		node = next;
 	}
 }
 
-static void moonfish_propagate_bounds(struct moonfish_node *node, int i)
+static void moonfish_propagate_bounds(struct moonfish_node *node)
 {
-	int j;
+	int i, j;
 	int bound;
 	
+	i = 1;
+	
 	while (node != NULL) {
 		
 		bound = 0;
@@ -325,20 +345,19 @@
 	}
 }
 
-static void moonfish_search(struct moonfish_node *node, struct moonfish_chess *chess0, int count)
+static void moonfish_search(struct moonfish_node *node, struct moonfish_chess *chess0)
 {
 	int i;
 	struct moonfish_node *leaf;
 	struct moonfish_chess chess;
 	
-	for (i = 0 ; i < count ; i++) {
+	for (i = 0 ; i < 0x1000 ; i++) {
 		chess = *chess0;
 		leaf = moonfish_select(node, &chess);
+		if (leaf == NULL) continue;
 		moonfish_expand(leaf, &chess);
+		if (leaf->count == 0 && moonfish_check(&chess)) moonfish_propagate_bounds(leaf);
 		moonfish_propagate(leaf);
-		if (leaf->count == -2 && moonfish_check(&chess)) {
-			moonfish_propagate_bounds(leaf, 1);
-		}
 	}
 }
 
@@ -345,11 +364,8 @@
 static void moonfish_clean(struct moonfish_node *node)
 {
 	int i;
-	
 	for (i = 0 ; i < node->count ; i++) {
-		if (node->children[i].ignored) continue;
-		if (node->children[i].count <= 0) continue;
-		if (node->children[i].visits < node->visits / node->count / 3) {
+		if (node->children[i].visits < node->visits / node->count / 2) {
 			moonfish_discard(node->children + i);
 		}
 		moonfish_clean(node->children + i);
@@ -363,7 +379,7 @@
 	
 	thread = data;
 	
-	moonfish_search(&thread->root->node, &thread->root->chess, 0x100);
+	moonfish_search(&thread->root->node, &thread->root->chess);
 	while (moonfish_clock() - thread->time0 < thread->time) {
 #ifndef moonfish_mini
 		if (thread->root->stop) break;
@@ -374,7 +390,7 @@
 			if (thread->root->node.children[i].ignored) count--;
 		}
 		if (count <= 1) break;
-		moonfish_search(&thread->root->node, &thread->root->chess, 0x1000);
+		moonfish_search(&thread->root->node, &thread->root->chess);
 		if (thread->clean) moonfish_clean(&thread->root->node);
 	}
 	
--