shithub: moonfish

Download patch

ref: c1ded1119a9e646894d398e0e833624969a1afef
parent: e0a7e3b7a9c0258a03edc4ff5d4de372ae7f3213
author: zamfofex <zamfofex@twdb.moe>
date: Sun Oct 13 22:45:54 EDT 2024

add preliminary PGN support to analysis TUI

--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,4 @@
 !/tools/https.c
 !/tools/perft.c
 !/tools/learn.c
+!/tools/pgn.c
--- a/makefile
+++ b/makefile
@@ -19,6 +19,7 @@
 	$(cc) $(filter %.c,$^) -o $@ $(cflags) -D_POSIX_C_SOURCE=200809L
 
 play analyse: cflags := -pthread
+analyse: tools/pgn.c
 lichess chat: tools/https.c
 lichess: cflags := -ltls -lssl -lcrypto -lcjson
 chat: cflags := -ltls -lssl -lcrypto
--- a/tools/analyse.c
+++ b/tools/analyse.c
@@ -570,6 +570,8 @@
 {
 	int i;
 	
+	if (fancy->in == NULL) return;
+	
 	fancy->taken = 0;
 	if (fancy->idle == 0) fancy->stop = 1;
 	
@@ -644,6 +646,7 @@
 	static struct moonfish_arg args[] =
 	{
 		{"F", "fen", "<FEN>", NULL, "the position to analyse"},
+		{"G", "pgn", "<file-name>", NULL, "PGN game to load"},
 		{NULL, NULL, NULL, NULL, NULL},
 	};
 	
@@ -660,6 +663,7 @@
 	char *value;
 	char **options;
 	int i;
+	FILE *file;
 	
 	/* handle command line arguments */
 	
@@ -668,6 +672,8 @@
 	if (command_count < 1) moonfish_usage(args, format, argv[0]);
 	options = command;
 	
+	if (args[0].value != NULL && args[1].value != NULL) moonfish_usage(args, format, argv[0]);
+	
 	for (;;)
 	{
 		value = strchr(*command, '=');
@@ -704,6 +710,9 @@
 	fancy->pv[0] = 0;
 	fancy->idle = 1;
 	fancy->stop = 0;
+	fancy->fen = NULL;
+	fancy->in = NULL;
+	fancy->out = NULL;
 	
 	fancy->x = 0;
 	fancy->y = 0;
@@ -722,14 +731,39 @@
 	fancy->plies[0].best[0] = 0;
 	
 	moonfish_chess(&fancy->plies[0].chess);
-	if (args[0].value == NULL)
+	
+	if (args[0].value != NULL)
 	{
-		fancy->fen = NULL;
+		fancy->fen = args[0].value;
+		if (moonfish_from_fen(&fancy->plies[0].chess, fancy->fen) != 0)
+		{
+			fprintf(stderr, "%s: invalid FEN\n", argv[0]);
+		}
 	}
-	else
+	
+	if (args[1].value != NULL)
 	{
-		fancy->fen = args[0].value;
-		moonfish_from_fen(&fancy->plies[0].chess, fancy->fen);
+		file = fopen(args[1].value, "r");
+		if (file == NULL)
+		{
+			perror(argv[0]);
+			return 1;
+		}
+		
+		for (;;)
+		{
+			if (moonfish_pgn(file, &fancy->plies[fancy->i].chess, &move, fancy->i == 0 ? 1 : 0) != 0) break;
+			moonfish_play(fancy, &move);
+		}
+		
+		fancy->fen = malloc(128);
+		if (fancy->fen == NULL)
+		{
+			perror(argv[0]);
+			return 1;
+		}
+		
+		moonfish_to_fen(&fancy->plies[0].chess, fancy->fen);
 	}
 	
 	/* configure the terminal for displaying the user interface */
--- /dev/null
+++ b/tools/pgn.c
@@ -1,0 +1,192 @@
+/* moonfish is licensed under the AGPL (v3 or later) */
+/* copyright 2023, 2024 zamfofex */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "../moonfish.h"
+#include "tools.h"
+
+static int moonfish_pgn_token(FILE *file)
+{
+	int ch;
+	
+	for (;;)
+	{
+		ch = getc(file);
+		if (ch == EOF) return ch;
+		
+		if (ch == '{')
+		{
+			for (;;)
+			{
+				ch = getc(file);
+				if (ch == EOF) return ch;
+				if (ch == '}') break;
+			}
+			continue;
+		}
+		
+		if (ch == ';')
+		{
+			for (;;)
+			{
+				ch = getc(file);
+				if (ch == EOF) return ch;
+				if (ch == '\r' || ch == '\n') break;
+			}
+			continue;
+		}
+		
+		if (strchr("\r\n\t ", ch) == NULL) return ch;
+	}
+}
+
+static int moonfish_isalnum(int ch)
+{
+	if (ch == '-' || ch == '_' || ch == '/') return 1;
+	if (isalnum(ch)) return 1;
+	return 0;
+}
+
+static int moonfish_pgn_skip(FILE *file, int ch)
+{
+	if (ch == '"')
+	{
+		for (;;)
+		{
+			ch = getc(file);
+			if (ch == '\\') ch = getc(file);
+			if (ch == EOF) return 1;
+			if (ch == '"') break;
+		}
+		return 0;
+	}
+	
+	if (ch == '(')
+	{
+		for (;;)
+		{
+			ch = moonfish_pgn_token(file);
+			if (ch == EOF) return 1;
+			if (ch == ')') break;
+		}
+		return 0;
+	}
+	
+	for (;;)
+	{
+		ch = getc(file);
+		if (ch == EOF) return 1;
+		if (moonfish_isalnum(ch) == 0) return 0;
+	}
+}
+
+int moonfish_pgn(FILE *file, struct moonfish_chess *chess, struct moonfish_move *move, int allow_attr)
+{
+	int i;
+	char buffer[256];
+	int ch;
+	
+	ch = moonfish_pgn_token(file);
+	
+	for (;;)
+	{
+		if (ch == EOF) return -1;
+		if (ch == '*') return 1;
+		
+		if (moonfish_isalnum(ch) != 0)
+		{
+			i = 0;
+			buffer[i++] = ch;
+			
+			for (;;)
+			{
+				ch = getc(file);
+				if (moonfish_isalnum(ch) == 0) break;
+				if (i >= (int) sizeof buffer - 1) break;
+				buffer[i++] = ch;
+			}
+			
+			buffer[i] = 0;
+			
+			if (!strcmp(buffer, "1/2-1/2")) return 1;
+			if (!strcmp(buffer, "1-0")) return 1;
+			if (!strcmp(buffer, "0-1")) return 1;
+			
+			if (buffer[0] >= '0' && buffer[0] <= '9')
+			{
+				ch = moonfish_pgn_token(file);
+				continue;
+			}
+			
+			if (moonfish_from_san(chess, move, buffer) != 0) return -1;
+			return 0;
+		}
+		
+		if (ch == '[')
+		{
+			if (allow_attr == 0) return -1;
+			
+			i = 0;
+			ch = moonfish_pgn_token(file);
+			if (ch == EOF) return -1;
+			if (moonfish_isalnum(ch) == 0) return -1;
+			buffer[i++] = ch;
+			
+			for (;;)
+			{
+				ch = moonfish_pgn_token(file);
+				if (ch == EOF) return -1;
+				if (moonfish_isalnum(ch) == 0) break;
+				if (i >= (int) sizeof buffer - 1)
+				{
+					if (moonfish_pgn_skip(file, ch) != 0) return -1;
+					break;
+				}
+				buffer[i++] = ch;
+			}
+			
+			buffer[i] = 0;
+			if (strcmp(buffer, "FEN"))
+			{
+				for (;;)
+				{
+					ch = moonfish_pgn_token(file);
+					if (ch == EOF) return -1;
+					if (ch == ']') break;
+					if (moonfish_pgn_skip(file, ch) != 0) return -1;
+				}
+				
+				ch = moonfish_pgn_token(file);
+				continue;
+			}
+			
+			i = 0;
+			ch = moonfish_pgn_token(file);
+			if (ch != '"') return -1;
+			
+			for (;;)
+			{
+				ch = getc(file);
+				if (ch == '"') break;
+				if (ch == '\\') ch = getc(file);
+				if (ch == EOF) return -1;
+				if (i >= (int) sizeof buffer - 1) return -1;
+				buffer[i++] = ch;
+			}
+			
+			buffer[i] = 0;
+			if (moonfish_from_fen(chess, buffer) != 0) return -1;
+			
+			ch = moonfish_pgn_token(file);
+			if (ch != ']') return -1;
+			
+			ch = moonfish_pgn_token(file);
+			continue;
+		}
+		
+		moonfish_pgn_skip(file, ch);
+		ch = moonfish_pgn_token(file);
+	}
+}
--- a/tools/tools.h
+++ b/tools/tools.h
@@ -15,6 +15,9 @@
 	char *description;
 };
 
+struct moonfish_chess;
+struct moonfish_move;
+
 void moonfish_spawn(char **argv, FILE **in, FILE **out, char *directory);
 
 char *moonfish_next(FILE *file);
@@ -23,5 +26,7 @@
 void moonfish_usage(struct moonfish_arg *args, char *rest_format, char *argv0);
 
 int moonfish_int(char *arg, int *result);
+
+int moonfish_pgn(FILE *file, struct moonfish_chess *chess, struct moonfish_move *move, int allow_attr);
 
 #endif
--