shithub: moonfish

Download patch

ref: 0c791a6085faf8e51c2a00babf4b04e9d4402637
author: zamfofex <zamfofex@twdb.moe>
date: Fri Oct 6 12:30:08 EDT 2023

first commit!

--- /dev/null
+++ b/README.md
@@ -1,0 +1,78 @@
+moonfish
+===
+
+**moonfish** is a chess bot inspired by [sunfish], written in C rather than Python. It is not nearly as complete, but it works well enough!
+
+[sunfish]: <https://github.com/thomasahle/sunfish>
+
+features
+---
+
+- very simple NNUE evaluation (same as sunfish)
+- alpha/beta pruning search
+- cute-looking UCI TUI
+
+deficiencies
+---
+
+These are things that I plan to fix eventually.
+
+- the bot will never make certain moves
+  - no castling
+  - no en passant
+  - no underpromotion
+- the TUI will also prevent you from making those kinds of moves
+- the TUI allows illegal moves that leave the king in check
+- no quiescence search
+- no iterative deepening
+- no transposition table
+- no support for `go infinite` or `go mate`
+
+building
+---
+
+~~~
+make
+~~~
+
+The UCI TUI, called “play” may also be built.
+
+~~~
+make play
+~~~
+
+usage
+---
+
+moonfish is a UCI bot, which means you can use it with any UCI program (though see “deficiencies” above).
+
+However, note that you need an NNUE network in order for it to work! You can take `tanh.pickle` from [sunfish]’s repository, then convert it to moonfish’s format using the `convert.py` script. This should produce a `tanh.moon` file in the same directory, ready to be used for moonfish!
+
+~~~
+python3 convert.py tanh.pickle
+~~~
+
+Then you can invoke `./moonfish tanh.moon` to start its UCI interface. If you’re using a GUI, make sure to add the path to `tanh.moon` as the sole argument to moonfish.
+
+However, note that moonfish comes with its own UCI TUI, called “play”. You can use it with any UCI engine you’d like! Simply invoke `./play` followed by a time control, then the command of whichever engine you want to play against. The color of your pieces will be decided randomly.
+
+~~~
+# (start a game against Stockfish)
+./play 1+0 stockfish
+
+# (start a game against Leela)
+./play 5+2 lc0
+
+# (start a game against moonfish)
+./play 15+10 ./moonfish tanh.moon
+~~~
+
+license
+---
+
+[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>
+[AGPLv3]: <https://www.gnu.org/licenses/agpl-3.0>
+[GPLv3+]: <https://www.gnu.org/licenses/gpl-faq.html#VersionThreeOrLater>
--- /dev/null
+++ b/chess.c
@@ -1,0 +1,423 @@
+#include "moonfish.h"
+
+static char moonfish_delta(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from, unsigned char *to, signed char delta)
+{
+	*to += delta;
+	
+	if (ctx->board[*to] <= moonfish_our_king) return 0;
+	
+	(*moves)->from = from;
+	(*moves)->to = *to;
+	(*moves)->piece = ctx->board[from];
+	(*moves)->promotion = ctx->board[from];
+	(*moves)->captured = ctx->board[*to];
+	(*moves)++;
+	
+	if (ctx->board[*to] != moonfish_empty) return 0;
+	
+	return 1;
+}
+
+static void moonfish_slide(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from, signed char delta)
+{
+	unsigned char to = from;
+	while (moonfish_delta(ctx, moves, from, &to, delta))
+	{
+	}
+}
+
+static void moonfish_jump(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from, signed char delta)
+{
+	unsigned char to = from;
+	moonfish_delta(ctx, moves, from, &to, delta);
+}
+
+static void moonfish_move_knight(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from)
+{
+	moonfish_jump(ctx, moves, from, 1 + 20);
+	moonfish_jump(ctx, moves, from, -1 + 20);
+	moonfish_jump(ctx, moves, from, 1 - 20);
+	moonfish_jump(ctx, moves, from, -1 - 20);
+	moonfish_jump(ctx, moves, from, 2 + 10);
+	moonfish_jump(ctx, moves, from, -2 + 10);
+	moonfish_jump(ctx, moves, from, 2 - 10);
+	moonfish_jump(ctx, moves, from, -2 - 10);
+}
+
+static void moonfish_move_bishop(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from)
+{
+	moonfish_slide(ctx, moves, from, 1 + 10);
+	moonfish_slide(ctx, moves, from, -1 + 10);
+	moonfish_slide(ctx, moves, from, 1 - 10);
+	moonfish_slide(ctx, moves, from, -1 - 10);
+}
+
+static void moonfish_move_rook(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from)
+{
+	moonfish_slide(ctx, moves, from, 10);
+	moonfish_slide(ctx, moves, from, -10);
+	moonfish_slide(ctx, moves, from, 1);
+	moonfish_slide(ctx, moves, from, -1);
+}
+
+static void moonfish_move_queen(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from)
+{
+	moonfish_move_rook(ctx, moves, from);
+	moonfish_move_bishop(ctx, moves, from);
+}
+
+static void moonfish_move_king(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from)
+{
+	moonfish_jump(ctx, moves, from, 1 + 10);
+	moonfish_jump(ctx, moves, from, -1 + 10);
+	moonfish_jump(ctx, moves, from, 1 - 10);
+	moonfish_jump(ctx, moves, from, -1 - 10);
+	moonfish_jump(ctx, moves, from, 10);
+	moonfish_jump(ctx, moves, from, -10);
+	moonfish_jump(ctx, moves, from, 1);
+	moonfish_jump(ctx, moves, from, -1);
+}
+
+static void moonfish_move_pawn(struct moonfish *ctx, struct moonfish_move **moves, unsigned char from)
+{
+	unsigned char promotion;
+	
+	promotion = moonfish_our_pawn;
+	if (from > 80) promotion = moonfish_our_queen;
+	
+	if (ctx->board[from + 10] == moonfish_empty)
+	{
+		(*moves)->piece = moonfish_our_pawn;
+		(*moves)->promotion = promotion;
+		(*moves)->captured = moonfish_empty;
+		(*moves)->from = from;
+		(*moves)->to = from + 10;
+		(*moves)++;
+				
+		if (from < 40)
+		{
+			if (ctx->board[from + 20] == moonfish_empty)
+			{
+				(*moves)->from = from;
+				(*moves)->to = from + 20;
+				(*moves)->piece = moonfish_our_pawn;
+				(*moves)->promotion = moonfish_our_pawn;
+				(*moves)->captured = moonfish_empty;
+				(*moves)++;
+			}
+		}
+	}
+	
+	if (ctx->board[from + 9] >= moonfish_their_pawn)
+	if (ctx->board[from + 9] != moonfish_empty)
+	{
+		(*moves)->piece = moonfish_our_pawn;
+		(*moves)->promotion = promotion;
+		(*moves)->captured = ctx->board[from + 9];
+		(*moves)->from = from;
+		(*moves)->to = from + 9;
+		(*moves)++;
+	}
+	
+	if (ctx->board[from + 11] >= moonfish_their_pawn)
+	if (ctx->board[from + 11] != moonfish_empty)
+	{
+		(*moves)->piece = moonfish_our_pawn;
+		(*moves)->promotion = promotion;
+		(*moves)->captured = ctx->board[from + 11];
+		(*moves)->from = from;
+		(*moves)->to = from + 11;
+		(*moves)++;
+	}
+}
+
+void moonfish_moves(struct moonfish *ctx, struct moonfish_move *moves, unsigned char from)
+{
+	char piece;
+	piece = ctx->board[from];
+	if (piece == moonfish_our_pawn) moonfish_move_pawn(ctx, &moves, from);
+	if (piece == moonfish_our_knight) moonfish_move_knight(ctx, &moves, from);
+	if (piece == moonfish_our_bishop) moonfish_move_bishop(ctx, &moves, from);
+	if (piece == moonfish_our_rook) moonfish_move_rook(ctx, &moves, from);
+	if (piece == moonfish_our_queen) moonfish_move_queen(ctx, &moves, from);
+	if (piece == moonfish_our_king) moonfish_move_king(ctx, &moves, from);
+	moves->piece = moonfish_outside;
+}
+
+static void moonfish_swap(struct moonfish *ctx, int i, int j)
+{
+	unsigned char piece;
+	piece = ctx->board[i];
+	ctx->board[i] = ctx->board[j];
+	ctx->board[j] = piece;
+}
+
+static void moonfish_flip_horizontally(struct moonfish *ctx)
+{
+	int x, y;
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 4 ; x++)
+	{
+		moonfish_swap(ctx,
+			(x + 1) + (y + 2) * 10,
+			(8 - x) + (y + 2) * 10
+		);
+	}
+}
+
+static void moonfish_flip_vertically(struct moonfish *ctx)
+{
+	int x, y;
+	
+	for (y = 0 ; y < 4 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+	{
+		moonfish_swap(ctx,
+			(x + 1) + (y + 2) * 10,
+			(x + 1) + (9 - y) * 10
+		);
+	}
+}
+
+static void moonfish_rotate(struct moonfish *ctx)
+{
+	int x, y;
+	
+	moonfish_flip_horizontally(ctx);
+	moonfish_flip_vertically(ctx);
+	
+	ctx->white = !ctx->white;
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+	{
+		if (ctx->board[(x + 1) + (y + 2) * 10] != moonfish_empty)
+			ctx->board[(x + 1) + (y + 2) * 10] ^= 0x30;
+	}
+}
+
+void moonfish_play(struct moonfish *ctx, struct moonfish_move *move)
+{
+	ctx->board[move->from] = moonfish_empty;
+	ctx->board[move->to] = move->promotion;
+	moonfish_rotate(ctx);
+}
+
+void moonfish_unplay(struct moonfish *ctx, struct moonfish_move *move)
+{
+	moonfish_rotate(ctx);
+	ctx->board[move->from] = move->piece;
+	ctx->board[move->to] = move->captured;
+}
+
+void moonfish_chess(struct moonfish *ctx)
+{
+	static unsigned char pieces[] = {4, 2, 3, 5, 6, 3, 2, 4};
+	int x, y;
+	
+	for (y = 0 ; y < 12 ; y++)
+	for (x = 0 ; x < 10 ; x++)
+		ctx->board[x + y * 10] = moonfish_outside;
+	
+	for (x = 0 ; x < 8 ; x++)
+	{
+		ctx->board[(x + 1) + 20] = pieces[x] | 0x10;
+		ctx->board[(x + 1) + 30] = moonfish_our_pawn;
+		ctx->board[(x + 1) + 40] = moonfish_empty;
+		ctx->board[(x + 1) + 50] = moonfish_empty;
+		ctx->board[(x + 1) + 60] = moonfish_empty;
+		ctx->board[(x + 1) + 70] = moonfish_empty;
+		ctx->board[(x + 1) + 80] = moonfish_their_pawn;
+		ctx->board[(x + 1) + 90] = pieces[x] | 0x20;
+	}
+	
+	ctx->white = 1;
+}
+
+void moonfish_show(struct moonfish *ctx)
+{
+	static char chs[] = "pnbrqk";
+	
+	int x, y;
+	char ch;
+	unsigned char piece;
+	
+	for (y = 7 ; y >= 0 ; y--)
+	{
+		printf("   ");
+		
+		for (x = 0 ; x < 8 ; x++)
+		{
+			piece = ctx->board[(x + 1) + (y + 2) * 10];
+			if (piece == moonfish_empty)
+			{
+				printf("  ");
+				continue;
+			}
+			
+			ch = chs[(piece & 0xF) - 1];
+			
+			if (piece <= moonfish_our_king || !ctx->white)
+			if (piece > moonfish_our_king || ctx->white)
+				ch += 'A' - 'a';
+			
+			printf("%c ", ch);
+		}
+		printf("\n");
+	}
+}
+
+#include "moonfish.h"
+
+void moonfish_play_uci(struct moonfish *ctx, char *name)
+{
+	struct moonfish_move move;
+	int x, y;
+	
+	x = name[0] - 'a';
+	y = name[1] - '1';
+	
+	if (!ctx->white)
+	{
+		x = 7 - x;
+		y = 7 - y;
+	}
+	
+	move.from = (x + 1) + (y + 2) * 10;
+	
+	x = name[2] - 'a';
+	y = name[3] - '1';
+	
+	if (!ctx->white)
+	{
+		x = 7 - x;
+		y = 7 - y;
+	}
+	
+	move.to = (x + 1) + (y + 2) * 10;
+	
+	move.piece = ctx->board[move.from];
+	move.captured = ctx->board[move.to];
+	move.promotion = move.piece;
+	
+	if (move.piece == moonfish_our_king && move.from == 0x25)
+	{
+		if (move.to == 0x23)
+		{
+			ctx->board[0x21] = moonfish_empty;
+			ctx->board[0x24] = moonfish_our_rook;
+		}
+		if (move.to == 0x27)
+		{
+			ctx->board[0x28] = moonfish_empty;
+			ctx->board[0x26] = moonfish_our_rook;
+		}
+	}
+	
+	if (move.piece == moonfish_our_pawn)
+	if ((move.to - move.from) % 10)
+	if (move.captured == moonfish_empty)
+		ctx->board[move.to - 10] = moonfish_empty;
+	
+	if (name[4] == 'q') move.promotion = moonfish_our_queen;
+	if (name[4] == 'r') move.promotion = moonfish_our_rook;
+	if (name[4] == 'b') move.promotion = moonfish_our_bishop;
+	if (name[4] == 'n') move.promotion = moonfish_our_knight;
+	
+	moonfish_play(ctx, &move);
+}
+
+void moonfish_to_uci(char *name, struct moonfish_move *move, int white)
+{
+	int x, y;
+	
+	x = move->from % 10 - 1;
+	y = move->from / 10 - 2;
+	
+	if (!white)
+	{
+		x = 7 - x;
+		y = 7 - y;
+	}
+	
+	name[0] = x + 'a';
+	name[1] = y + '1';
+	
+	x = move->to % 10 - 1;
+	y = move->to / 10 - 2;
+	
+	if (!white)
+	{
+		x = 7 - x;
+		y = 7 - y;
+	}
+	
+	name[2] = x + 'a';
+	name[3] = y + '1';
+	
+	if (move->promotion != move->piece)
+	{
+		name[4] = 'q';
+		name[5] = 0;
+	}
+	else
+	{
+		name[4] = 0;
+	}
+}
+
+void moonfish_fen(struct moonfish *ctx, char *fen)
+{
+	int x, y;
+	unsigned char type, color;
+	char ch;
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+		ctx->board[(x + 1) + (y + 2) * 10] = moonfish_empty;
+	
+	x = 0;
+	y = 0;
+	
+	for (;;)
+	{
+		ch = *fen++;
+		
+		if (ch == ' ') break;
+		
+		if (ch == '/')
+		{
+			x = 0;
+			y++;
+			continue;
+		}
+		
+		if (ch >= '0' && ch <= '9')
+		{
+			x += ch - '0';
+			continue;
+		}
+		
+		if (ch >= 'A' && ch <= 'Z')
+			ch -= 'A' - 'a',
+			color = 0x10;
+		else
+			color = 0x20;
+		
+		type = 0;
+		if (ch == 'p') type = 1;
+		if (ch == 'n') type = 2;
+		if (ch == 'b') type = 3;
+		if (ch == 'r') type = 4;
+		if (ch == 'q') type = 5;
+		if (ch == 'k') type = 6;
+		
+		ctx->board[(x + 1) + (9 - y) * 10] = type | color;
+		
+		x++;
+	}
+	
+	ctx->white = 1;
+	if (*fen == 'b') moonfish_rotate(ctx);
+}
--- /dev/null
+++ b/convert.py
@@ -1,0 +1,22 @@
+import pickle, sys
+
+if len(sys.argv) != 2:
+   print("usage: python3 " + sys.argv[0] + " <file-name>", file = sys.stderr)
+   exit(1)
+
+input_name = sys.argv[1]
+output_name = sys.argv[1].removesuffix(".pickle") + ".moon"
+
+model = pickle.load(open(input_name, "rb"))
+
+file = open(output_name, "wb")
+
+file.write(b"\x00Lun")
+file.write(model["ars"][0])
+file.write(model["ars"][1])
+file.write(model["ars"][3])
+file.write(model["ars"][4])
+file.write(model["ars"][5])
+file.write(bytearray([int(model["scale"] * 256)]))
+
+file.close()
--- /dev/null
+++ b/main.c
@@ -1,0 +1,222 @@
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "moonfish.h"
+
+static char *moonfish_strdup(char *str)
+{
+	char *result;
+	result = malloc(strlen(str) + 1);
+	strcpy(result, str);
+	return result;
+}
+
+int main(int argc, char **argv)
+{
+	FILE *file;
+	struct moonfish *ctx;
+	char line[512], *arg;
+	struct moonfish_move move;
+	char name[6];
+	int score;
+	
+	if (argc != 2)
+	{
+		if (argc > 0) fprintf(stderr, "usage: %s <file-name>\n", argv[0]);
+		return 1;
+	}
+	
+	ctx = malloc(sizeof *ctx);
+	if (ctx == NULL)
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	file = fopen(argv[1], "rb");
+	if (file == NULL)
+	{
+		perror(argv[0]);
+		free(ctx);
+		return 1;
+	}
+	
+	if (moonfish_nnue(ctx, file))
+	{
+		fprintf(stderr, "%s: could not parse network\n", argv[0]);
+		fclose(file);
+		free(ctx);
+		return 1;
+	}
+	
+	fclose(file);
+	
+	printf("   (moonfish by zamfofex)\n");
+	printf("   (inspired by sunfish by tahle)\n");
+	printf("   (simple UCI interface)\n");
+	printf("   (try typing 'help')\n");
+	
+	printf("\n");
+	printf(
+		"        .-, *  z     *\n"
+		"  *   /  /    z___ *\n"
+		"     |  | *   /-  \\/(\n"
+		"   * |  \\     \\___/\\(\n"
+		" *    \\  \\  *          *\n"
+		"        `--    *\n\n"
+	);
+	
+	moonfish_chess(ctx);
+	
+	for (;;)
+	{
+		errno = 0;
+		if (fgets(line, sizeof line, stdin) == NULL)
+		{
+			if (errno)
+			{
+				free(ctx);
+				perror(argv[0]);
+				return 1;
+			}
+			
+			break;
+		}
+		
+		arg = strtok(line, "\r\n\t ");
+		if (arg == NULL) continue;
+		
+		if (!strcmp(arg, "play"))
+		{
+			while ((arg = strtok(NULL, "\r\n\t ")) != NULL)
+				moonfish_play_uci(ctx, arg);
+		}
+		else if (!strcmp(arg, "go"))
+		{
+			moonfish_best_move(ctx, &move);
+			moonfish_to_uci(name, &move, ctx->white);
+			printf("bestmove %s\n", name);
+		}
+		else if (!strcmp(arg, "eval"))
+		{
+			score = moonfish_best_move(ctx, &move);
+			printf("score %d\n", score / 127);
+		}
+		else if (!strcmp(arg, "show"))
+		{
+			moonfish_show(ctx);
+		}
+		else if (!strcmp(arg, "restart"))
+		{
+			moonfish_chess(ctx);
+		}
+		else if (!strcmp(arg, "fen"))
+		{
+			arg = strtok(NULL, "\r\n");
+			if (arg == NULL)
+			{
+				fprintf(stderr, "malformed 'fen' command\n");
+				free(ctx);
+				return 1;
+			}
+			moonfish_fen(ctx, arg);
+		}
+		else if (!strcmp(arg, "echo"))
+		{
+			arg = strtok(NULL, "\r\n");
+			if (arg == NULL) arg = "";
+			printf("%s\n", arg);
+		}
+		else if (!strcmp(arg, "quit"))
+		{
+			break;
+		}
+		else if (!strcmp(arg, "position"))
+		{
+			arg = strtok(NULL, "\r\n\t ");
+			if (arg == NULL)
+			{
+				fprintf(stderr, "incomplete 'position' command\n");
+				free(ctx);
+				return 1;
+			}
+			
+			if (!strcmp(arg, "fen"))
+			{
+				arg = strtok(NULL, "\r\n");
+				moonfish_fen(ctx, arg);
+				
+				arg = strstr(arg, "moves");
+				if (arg != NULL)
+				{
+					do arg--;
+					while (*arg == '\t' || *arg == ' ');
+					arg = moonfish_strdup(arg);
+					strtok(arg, "\r\n\t ");
+					free(arg);
+				}
+			}
+			else if (!strcmp(arg, "startpos"))
+			{
+				moonfish_chess(ctx);
+			}
+			else
+			{
+				fprintf(stderr, "malformed 'position' command\n");
+				free(ctx);
+				return 1;
+			}
+			
+			arg = strtok(NULL, "\r\n\t ");
+			if (arg != NULL && !strcmp(arg, "moves"))
+			{
+				while ((arg = strtok(NULL, "\r\n\t ")) != NULL)
+					moonfish_play_uci(ctx, arg);
+			}
+		}
+		else if (!strcmp(arg, "uci"))
+		{
+			printf("id name moonfish\n");
+			printf("id author zamfofex\n");
+			printf("uciok\n");
+		}
+		else if (!strcmp(arg, "isready"))
+		{
+			printf("readyok\n");
+		}
+		else if (!strcmp(arg, "debug") || !strcmp(arg, "setoption") || !strcmp(arg, "ucinewgame") || !strcmp(arg, "stop"))
+		{
+		}
+		else if (!strcmp(arg, "help"))
+		{
+			printf("   'show'          - shows the current position as ASCII\n");
+			printf("   'play <moves>'  - plays the given moves on the current position\n");
+			printf("   'eval'          - shows the evaluation of the current position\n");
+			printf("   'go'            - finds the best move in the current position\n");
+			printf("   'restart'       - resets the board to the initial position\n");
+			printf("   'fen <FEN>'     - sets the board to the given FEN\n");
+			printf("   'echo <message> - shows the given message (useful for scripts)\n");
+			printf("   'quit'          - self-explanatory\n");
+			printf("   for UCI compatibility:\n");
+			printf("   'debug', 'setoption' - ignored\n");
+			printf("   'ucinewgame', 'stop' - ignored\n");
+			printf("   'uci', 'isready'     - handled per UCI\n");
+			printf("   'position startpos [moves <moves>]'  - equivalent to 'restart' then 'play <moves>'\n");
+			printf("   'position fen <FEN> [moves <moves>]' - equivalent to 'fen <FEN>' then 'play <moves>'\n");
+		}
+		else
+		{
+			fprintf(stderr, "%s: unknown command '%s'\n", argv[0], arg);
+			free(ctx);
+			return 1;
+		}
+		
+		fflush(stdout);
+	}
+	
+	free(ctx);
+	
+	printf("   (good night)\n");
+	return 0;
+}
--- /dev/null
+++ b/makefile
@@ -1,0 +1,7 @@
+CFLAGS = -ansi -O3 -Wall -Wextra -Wpedantic
+
+moonfish: *.c moonfish.h
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o moonfish *.c
+
+play: tools/play.c chess.c moonfish.h
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -pthread -D_POSIX_C_SOURCE=200809L -o play chess.c tools/play.c
--- /dev/null
+++ b/math.c
@@ -1,0 +1,35 @@
+#include "moonfish.h"
+
+/*
+our approximation:
+tanh(x) ~ (x**5/945 + x**3/9 + x)/(x**4/63 + (4 * x**2)/9 + 1)
+*/
+
+int moonfish_tanh(int x)
+{
+	long int x5, x4, x3, x2, x1, r;
+	
+	x5 = 1, x4 = 1, x3 = 1, x2 = 1, x1 = 1;
+	
+	x5 *= x; x5 *= x; x5 *= x; x5 *= x; x5 *= x;
+	x4 *= x; x4 *= x; x4 *= x; x4 *= x;
+	x3 *= x; x3 *= x; x3 *= x;
+	x2 *= x; x2 *= x;
+	x1 *= x;
+	
+	x5 /= 945;
+	x4 /= 63;
+	x3 /= 9;
+	x2 *= 4;
+	x2 /= 9;
+	x1 *= 127;
+	
+	x5 /= 127 * 127 * 127;
+	x4 /= 127 * 127 * 127;
+	x3 /= 127;
+	x2 /= 127;
+	
+	r = (x5 + x3 + x1) / (x4 + x2 + 127);
+	if (r > 127) return 127;
+	return r;
+}
--- /dev/null
+++ b/moonfish.h
@@ -1,0 +1,71 @@
+#ifndef MOONFISH
+#define MOONFISH
+
+#include <stdio.h>
+
+enum
+{
+	moonfish_our_pawn = 0x11,
+	moonfish_our_knight = 0x12,
+	moonfish_our_bishop = 0x13,
+	moonfish_our_rook = 0x14,
+	moonfish_our_queen = 0x15,
+	moonfish_our_king = 0x16,
+	
+	moonfish_their_pawn = 0x21,
+	moonfish_their_knight = 0x22,
+	moonfish_their_bishop = 0x23,
+	moonfish_their_rook = 0x24,
+	moonfish_their_queen = 0x25,
+	moonfish_their_king = 0x26,
+	
+	moonfish_outside = 0,
+	moonfish_empty = 0xFF,
+	
+	moonfish_omega = 10000000
+};
+
+struct moonfish_nnue
+{
+	int values0[6][64][10];
+	int values[2][6][64][10];
+	signed char pst0[64 * 6], pst1[10 * 6 * 6], pst3[10 * 10 * 2];
+	signed char layer1[180], layer2[10];
+	char scale;
+};
+
+struct moonfish
+{
+	struct moonfish_nnue nnue;
+	unsigned char board[120];
+	char white;
+};
+
+struct moonfish_move
+{
+	unsigned char from, to;
+	unsigned char piece;
+	unsigned char promotion;
+	unsigned char captured;
+};
+
+int moonfish_nnue(struct moonfish *ctx, FILE *file);
+
+void moonfish_chess(struct moonfish *ctx);
+void moonfish_fen(struct moonfish *ctx, char *fen);
+
+void moonfish_moves(struct moonfish *ctx, struct moonfish_move *moves, unsigned char from);
+
+void moonfish_play(struct moonfish *ctx, struct moonfish_move *move);
+void moonfish_unplay(struct moonfish *ctx, struct moonfish_move *move);
+
+void moonfish_show(struct moonfish *ctx);
+
+int moonfish_tanh(int value);
+
+int moonfish_best_move(struct moonfish *ctx, struct moonfish_move *move);
+
+void moonfish_play_uci(struct moonfish *ctx, char *name);
+void moonfish_to_uci(char *name, struct moonfish_move *move, int white);
+
+#endif
--- /dev/null
+++ b/nnue.c
@@ -1,0 +1,79 @@
+#include "moonfish.h"
+
+static int moonfish_read_array(signed char *array, unsigned int length, FILE *file)
+{
+	unsigned int i;
+	int ch;
+	
+	for (i = 0 ; i < length ; i++)
+	{
+		ch = fgetc(file);
+		if (ch == EOF) return 1;
+		array[i] = ch;
+	}
+	
+	return 0;
+}
+
+static int moonfish_read_nnue(struct moonfish *ctx, FILE *file)
+{
+	int ch;
+	unsigned int i, j;
+	signed char v;
+	
+	if (fgetc(file) != 0) return 1;
+	if (fgetc(file) != 'L') return 1;
+	if (fgetc(file) != 'u') return 1;
+	if (fgetc(file) != 'n') return 1;
+	if (moonfish_read_array(ctx->nnue.pst0, sizeof ctx->nnue.pst0, file)) return 1;
+	if (moonfish_read_array(ctx->nnue.pst1, sizeof ctx->nnue.pst1, file)) return 1;
+	if (moonfish_read_array(ctx->nnue.pst3, sizeof ctx->nnue.pst3, file)) return 1;
+	if (moonfish_read_array(ctx->nnue.layer1, sizeof ctx->nnue.layer1, file)) return 1;
+	if (moonfish_read_array(ctx->nnue.layer2, sizeof ctx->nnue.layer2, file)) return 1;
+	
+	for (i = 0 ; i < 4 ; i++)
+	for (j = 0 ; j < 6 * 8 ; j++)
+	{
+		v = ctx->nnue.pst0[i * 6 * 8 + j];
+		ctx->nnue.pst0[i * 6 * 8 + j] = ctx->nnue.pst0[(7 - i) * 6 * 8 + j];
+		ctx->nnue.pst0[(7 - i) * 6 * 8 + j] = v;
+	}
+	
+	ch = fgetc(file);
+	if (ch == EOF) return 1;
+	ctx->nnue.scale = ch;
+	
+	if (fgetc(file) != EOF) return 1;
+	
+	return 0;
+}
+
+int moonfish_nnue(struct moonfish *ctx, FILE *file)
+{
+	int p, s, d, o, c, value;
+	
+	if (moonfish_read_nnue(ctx, file)) return 1;
+	
+	for (p = 0 ; p < 6 ; p++)
+	for (s = 0 ; s < 64 ; s++)
+	for (o = 0 ; o < 10 ; o++)
+	{
+		value = 0;
+		for (d = 0 ; d < 6 ; d++)
+			value += ctx->nnue.pst0[s * 6 + d] * ctx->nnue.pst1[o * 6 * 6 + d * 6 + p];
+		ctx->nnue.values0[p][s][o] = value / 127;
+	}
+	
+	for (c = 0 ; c < 2 ; c++)
+	for (p = 0 ; p < 6 ; p++)
+	for (s = 0 ; s < 64 ; s++)
+	for (o = 0 ; o < 10 ; o++)
+	{
+		value = 0;
+		for (d = 0 ; d < 10 ; d++)
+			value += ctx->nnue.values0[p][s][d] * ctx->nnue.pst3[o * 10 * 2 + d * 2 + c];
+		ctx->nnue.values[c][p][s][o] = value / 127;
+	}
+	
+	return 0;
+}
--- /dev/null
+++ b/search.c
@@ -1,0 +1,148 @@
+#include "moonfish.h"
+
+static int moonfish_evaluate(struct moonfish *ctx)
+{
+	int features[18] = {0};
+	int x, y;
+	int i, j;
+	unsigned char piece, color, type;
+	int scale;
+	int *white_values, *black_values;
+	int hidden[10], score;
+	
+	scale = 0;
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+	{
+		piece = ctx->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];
+		
+		scale += white_values[0];
+		scale -= black_values[0];
+		
+		for (i = 0 ; i < 9 ; i++)
+		{
+			features[i] += white_values[i + 1];
+			features[i + 9] += black_values[i + 1];
+		}
+	}
+	
+	for (i = 0 ; i < 10 ; i++)
+	{
+		hidden[i] = 0;
+		for (j = 0 ; j < 18 ; j++)
+			hidden[i] += ctx->nnue.layer1[i * 18 + j] * moonfish_tanh(features[i]) / 127;
+	}
+	
+	score = 0;
+	for (i = 0 ; i < 10 ; i++)
+		score += moonfish_tanh(hidden[i]) * ctx->nnue.layer2[i] / 127;
+	
+	return score * 360 + scale * ctx->nnue.scale * 360 / 256;
+}
+
+/*
+static int moonfish_quiesce(struct moonfish *ctx, int alpha, int beta, int depth)
+{
+	int x, y;
+	struct moonfish_move moves[32];
+	struct moonfish_move *move;
+	int score;
+	
+	score = moonfish_evaluate(ctx);
+	if (depth == 0) return score;
+	
+	if (score >= beta) return beta;
+	if (alpha < score) alpha = score;
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+	{
+		moonfish_moves(ctx, moves, (x + 1) + (y + 2) * 10);
+		
+		for (move = moves ; move->piece != moonfish_outside ; move++)
+		{
+			if (move->captured == moonfish_empty) continue;
+			if (move->captured == moonfish_their_king) return moonfish_omega * depth;
+			
+			moonfish_play(ctx, move);
+			score = -moonfish_quiesce(ctx, -beta, -alpha, depth - 1);
+			moonfish_unplay(ctx, move);
+			
+			if (score >= beta) return beta;
+			if (score > alpha) alpha = score;
+		}
+	}
+	
+	return alpha;
+}
+*/
+
+static int moonfish_search(struct moonfish *ctx, int alpha, int beta, int depth)
+{
+	int x, y;
+	struct moonfish_move moves[32];
+	struct moonfish_move *move;
+	int score;
+	
+	/* if (depth == 0) return moonfish_quiesce(ctx, alpha, beta, 6); */
+	if (depth == 0) return moonfish_evaluate(ctx);
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+	{
+		moonfish_moves(ctx, moves, (x + 1) + (y + 2) * 10);
+		
+		for (move = moves ; move->piece != moonfish_outside ; move++)
+		{
+			if (move->captured == moonfish_their_king) return moonfish_omega * depth;
+			
+			moonfish_play(ctx, move);
+			score = -moonfish_search(ctx, -beta, -alpha, depth - 1);
+			moonfish_unplay(ctx, move);
+			
+			if (score >= beta) return beta;
+			if (score > alpha) alpha = score;
+		}
+	}
+	
+	return alpha;
+}
+
+int moonfish_best_move(struct moonfish *ctx, struct moonfish_move *best_move)
+{
+	int x, y;
+	struct moonfish_move moves[512];
+	struct moonfish_move *move;
+	int score, best_score;
+	
+	best_score = -50 * moonfish_omega;
+	
+	for (y = 0 ; y < 8 ; y++)
+	for (x = 0 ; x < 8 ; x++)
+	{
+		moonfish_moves(ctx, moves, (x + 1) + (y + 2) * 10);
+		
+		for (move = moves ; move->piece != moonfish_outside ; move++)
+		{
+			moonfish_play(ctx, move);
+			score = -moonfish_search(ctx, -10 * moonfish_omega, 10 * moonfish_omega, 4);
+			moonfish_unplay(ctx, move);
+			
+			if (score > best_score)
+			{
+				*best_move = *move;
+				best_score = score;
+			}
+		}
+	}
+	
+	return best_score;
+}
--- /dev/null
+++ b/tools/play.c
@@ -1,0 +1,639 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <pthread.h>
+#include <termios.h>
+#include <signal.h>
+
+#include "../moonfish.h"
+
+struct moonfish_fancy
+{
+	struct moonfish ctx;
+	int white;
+	int our_time, their_time;
+	long int our_nano, their_nano;
+	int increment;
+	char *our_name, *their_name;
+	pthread_mutex_t *mutex;
+	struct timespec timespec;
+	char *argv0;
+	int x, y;
+};
+
+static int moonfish_spawn(char *argv0, char **argv, int *in, int *out)
+{
+	int p1[2], p2[2];
+	int pid;
+	
+	if (pipe(p1) == -1) return 1;
+	if (pipe(p2) == -1) return 1;
+	
+	pid = fork();
+	if (pid == -1) return 1;
+	
+	if (pid)
+	{
+		*in = p1[1];
+		*out = p2[0];
+		close(p1[0]);
+		close(p2[1]);
+		return 0;
+	}
+	
+	dup2(p1[0], 0);
+	dup2(p2[1], 1);
+	
+	close(p1[0]);
+	close(p1[1]);
+	close(p2[0]);
+	close(p2[1]);
+	
+	execvp(argv[0], argv);
+	fprintf(stderr, "%s: %s: %s", argv0, argv[0], strerror(errno));
+	
+	exit(1);
+}
+
+static char *moonfish_next(FILE *file)
+{
+	static char line[2048];
+	
+	for (;;)
+	{
+		if (fgets(line, sizeof line, file) == NULL)
+			return NULL;
+		return line;
+	}
+}
+
+static char *moonfish_wait(FILE *file, char *name)
+{
+	char *line, *arg;
+	
+	for (;;)
+	{
+		line = moonfish_next(file);
+		if (line == NULL) exit(1);
+		
+		arg = strtok(line, "\r\n\t ");
+		if (arg == NULL) continue;
+		if (!strcmp(line, name))
+			return strtok(NULL, "\r\n\t ");
+	}
+}
+
+static void moonfish_fancy_square(struct moonfish_fancy *fancy, int x, int y)
+{
+	unsigned char piece;
+	
+	if (x + 1 == fancy->x && y + 1 == fancy->y)
+		printf("\x1B[48;5;219m");
+	else if (x % 2 == y % 2)
+		printf("\x1B[48;5;111m");
+	else
+		printf("\x1B[48;5;69m");
+	
+	if (fancy->white == (fancy->ctx.white ? 1 : 0)) y = 7 - y;
+	else x = 7 - x;
+	
+	piece = fancy->ctx.board[(x + 1) + (y + 2) * 10];
+	
+	if (piece == moonfish_empty)
+	{
+		printf("  ");
+		return;
+	}
+	
+	if (!fancy->ctx.white) piece ^= 0x30;
+	
+	if (piece >> 4 == 1)
+		printf("\x1B[38;5;253m");
+	else
+		printf("\x1B[38;5;240m");
+	
+	switch (piece & 0xF)
+	{
+	case 1:
+		printf("\xE2\x99\x9F ");
+		return;
+	case 2:
+		printf("\xE2\x99\x9E ");
+		return;
+	case 3:
+		printf("\xE2\x99\x9D ");
+		return;
+	case 4:
+		printf("\xE2\x99\x9C ");
+		return;
+	case 5:
+		printf("\xE2\x99\x9B ");
+		return;
+	case 6:
+		printf("\xE2\x99\x9A ");
+		return;
+	}
+}
+
+static void moonfish_time(int time, char *name)
+{
+	printf("   \x1B[48;5;248m\x1B[38;5;235m %2d:%02d \x1B[0m %s\n", time / 60, time % 60, name);
+}
+
+static void moonfish_fancy(struct moonfish_fancy *fancy)
+{
+	unsigned char x, y;
+	int our_time, their_time;
+	time_t delta;
+	struct timespec timespec;
+	int done;
+	
+	printf("\x1B[10A");
+	
+	if (clock_gettime(CLOCK_MONOTONIC, &timespec))
+	{
+		perror(fancy->argv0);
+		exit(1);
+	}
+	
+	our_time = fancy->our_time;
+	their_time = fancy->their_time;
+	
+	delta = timespec.tv_sec - fancy->timespec.tv_sec;
+	if (timespec.tv_nsec - fancy->timespec.tv_nsec > 500000000L) delta++;
+	
+	if (fancy->white == (fancy->ctx.white ? 1 : 0))
+		our_time -= delta;
+	else
+		their_time -= delta;
+	
+	done = 0;
+	
+	if (our_time < 0)
+	{
+		done = 1;
+		our_time = 0;
+	}
+	
+	if (their_time < 0)
+	{
+		done = 1;
+		their_time = 0;
+	}
+	
+	moonfish_time(their_time, fancy->their_name);
+	
+	for (y = 0 ; y < 8 ; y++)
+	{
+		printf("   ");
+		for (x = 0 ; x < 8 ; x++)
+			moonfish_fancy_square(fancy, x, y);
+		printf("\x1B[0m\n");
+	}
+	
+	moonfish_time(our_time, fancy->our_name);
+	
+	if (done) exit(0);
+}
+
+static void *moonfish_start(void *data)
+{
+	struct moonfish_fancy *fancy;
+	
+	fancy = data;
+	
+	for (;;)
+	{
+		pthread_mutex_lock(fancy->mutex);
+		
+		moonfish_fancy(fancy);
+		
+		pthread_mutex_unlock(fancy->mutex);
+		sleep(1);
+	}
+	
+	return NULL;
+}
+
+static struct termios moonfish_termios;
+static FILE *moonfish_engine = NULL;
+
+static void moonfish_exit(void)
+{
+	tcsetattr(0, TCSANOW, &moonfish_termios);
+	printf("\x1B[?1000l\n");
+	fflush(stdout);
+	if (moonfish_engine != NULL) fprintf(moonfish_engine, "quit\n");
+}
+
+static void moonfish_signal(int signal)
+{
+	(void) signal;
+	exit(1);
+}
+
+static int moonfish_play_from(struct moonfish *ctx, struct moonfish_move *found, int x0, int y0, int x1, int y1)
+{
+	struct moonfish_move moves[32];
+	struct moonfish_move *move;
+	
+	y0 = 9 - y0;
+	y1 = 9 - y1;
+	
+	moonfish_moves(ctx, moves, x0 + (y0 + 1) * 10);
+	
+	for (move = moves ; move->piece != moonfish_outside ; move++)
+	{
+		if (move->to == x1 + (y1 + 1) * 10)
+		{
+			*found = *move;
+			return 0;
+		}
+	}
+	
+	return 1;
+}
+
+static void moonfish_reset_time(struct moonfish_fancy *fancy)
+{
+	struct timespec timespec;
+	int *time;
+	long int *nano;
+	
+	if (clock_gettime(CLOCK_MONOTONIC, &timespec))
+	{
+		perror(fancy->argv0);
+		exit(1);
+	}
+	
+	if (fancy->white)
+	{
+		if (fancy->ctx.white) time = &fancy->their_time, nano = &fancy->their_nano;
+		else time = &fancy->our_time, nano = &fancy->our_nano;
+	}
+	else
+	{
+		if (fancy->ctx.white) time = &fancy->our_time, nano = &fancy->our_nano;
+		else time = &fancy->their_time, nano = &fancy->their_nano;
+	}
+	
+	timespec.tv_sec -= fancy->timespec.tv_sec;
+	timespec.tv_nsec -= fancy->timespec.tv_nsec;
+	
+	while (timespec.tv_nsec < 0)
+	{
+		timespec.tv_nsec += 1000000000L;
+		timespec.tv_sec--;
+	}
+	
+	*time -= timespec.tv_sec;
+	*nano -= timespec.tv_nsec;
+	
+	while (*nano < 0)
+	{
+		*nano += 1000000000L;
+		(*time)--;
+	}
+	
+	*time += fancy->increment;
+}
+
+static void moonfish_go(struct moonfish_fancy *fancy, char *names, char *name, FILE *in, FILE *out)
+{
+	int white_time, black_time;
+	char *arg;
+	
+	if (fancy->white == (fancy->ctx.white ? 1 : 0))
+	{
+		white_time = fancy->our_time * 1000 + fancy->our_nano / 1000000;
+		black_time = fancy->their_time * 1000 + fancy->their_nano / 1000000;
+	}
+	else
+	{
+		white_time = fancy->their_time * 1000 + fancy->their_nano / 1000000;
+		black_time = fancy->our_time * 1000 + fancy->our_nano / 1000000;
+	}
+	
+	fprintf(in, "isready\n");
+	moonfish_wait(out, "readyok");
+	
+	fprintf(in, "position startpos");
+	if (names[0] != 0) fprintf(in, " moves %s", names);
+	fprintf(in, "\n");
+	
+	fprintf(in, "isready\n");
+	moonfish_wait(out, "readyok");
+	
+	fprintf(in, "go wtime %d btime %d", white_time, black_time);
+	if (fancy->increment > 0)
+		fprintf(in, "winc %d binc %d", fancy->increment * 1000, fancy->increment * 1000);
+	fprintf(in, "\n");
+	
+	arg = moonfish_wait(out, "bestmove");
+	strcpy(name, arg);
+	
+	pthread_mutex_lock(fancy->mutex);
+	moonfish_play_uci(&fancy->ctx, arg);
+}
+
+int main(int argc, char **argv)
+{
+	static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+	static char names[4096] = "";
+	
+	int in_fd, out_fd;
+	FILE *in, *out;
+	struct moonfish_fancy *fancy;
+	char *arg;
+	pthread_t thread;
+	struct termios termios;
+	struct sigaction action;
+	int ch, ch0;
+	int ox, oy;
+	int x1, y1;
+	char *name;
+	struct moonfish_move move;
+	
+	name = names;
+	
+	if (argc < 3)
+	{
+		if (argc > 0) fprintf(stderr, "usage: %s <time-control> <command>\n", argv[0]);
+		return 1;
+	}
+	
+	if (moonfish_spawn(argv[0], argv + 2, &in_fd, &out_fd))
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	if (tcgetattr(0, &moonfish_termios))
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	if (atexit(&moonfish_exit))
+	{
+		moonfish_exit();
+		return 1;
+	}
+	
+	action.sa_handler = &moonfish_signal;
+	sigemptyset(&action.sa_mask);
+	action.sa_flags = 0;
+	
+	if (sigaction(SIGTERM, &action, NULL) || sigaction(SIGINT, &action, NULL) || sigaction(SIGQUIT, &action, NULL))
+	{
+		perror(argv[0]);
+		moonfish_exit();
+		return 1;
+	}
+	
+	termios = moonfish_termios;
+	termios.c_lflag &= ~(ECHO | ICANON);
+	
+	if (tcsetattr(0, TCSANOW, &termios))
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	fancy = malloc(sizeof *fancy);
+	if (fancy == NULL)
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	fancy->argv0 = argv[0];
+	fancy->mutex = &mutex;
+	fancy->white = time(NULL) % 2;
+	
+	fancy->x = 0;
+	fancy->y = 0;
+	
+	fancy->their_name = argv[2];
+	fancy->our_name = getlogin();
+	if (fancy->our_name == NULL) fancy->our_name = "you";
+	
+	if (sscanf(argv[1], "%d+%d", &fancy->our_time, &fancy->increment) != 2)
+	{
+		fprintf(stderr, "%s: unknown time control '%s'\n", argv[0], argv[1]);
+		return 1;
+	}
+	
+	fancy->our_nano = 0;
+	fancy->their_nano = 0;
+	
+	fancy->our_time *= 60;
+	fancy->their_time = fancy->our_time;
+	
+	in = fdopen(in_fd, "w");
+	if (in == NULL)
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	out = fdopen(out_fd, "r");
+	if (out == NULL)
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	errno = 0;
+	if (setvbuf(in, NULL, _IOLBF, 0))
+	{
+		if (errno) perror(argv[0]);
+		return 1;
+	}
+	
+	errno = 0;
+	if (setvbuf(out, NULL, _IOLBF, 0))
+	{
+		if (errno) perror(argv[0]);
+		return 1;
+	}
+	
+	fprintf(in, "uci\n");
+	
+	for (;;)
+	{
+		arg = moonfish_next(out);
+		if (arg == NULL)
+		{
+			fprintf(stderr, "%s: UCI error\n", argv[0]);
+			return 1;
+		}
+		
+		arg = strtok(arg, "\r\n\t ");
+		if (arg == NULL) continue;
+		if (!strcmp(arg, "uciok")) break;
+		
+		if (strcmp(arg, "id")) continue;
+		
+		arg = strtok(NULL, "\r\n\t ");
+		if (arg == NULL) continue;
+		
+		if (strcmp(arg, "name")) continue;
+		arg = strtok(NULL, "\r\n");
+		
+		while (strchr("\r\n\t ", *arg) != NULL) { }
+		if (*arg != 0) fancy->their_name = strdup(arg);
+	}
+	
+	fprintf(in, "isready\n");
+	moonfish_wait(out, "readyok");
+	fprintf(in, "ucinewgame\n");
+	fprintf(in, "isready\n");
+	moonfish_wait(out, "readyok");
+	
+	if (clock_gettime(CLOCK_MONOTONIC, &fancy->timespec))
+	{
+		perror(argv[0]);
+		return 1;
+	}
+	
+	printf("\n\n\n\n\n\n\n\n\n\n\n");
+	
+	printf("\x1B[6n");
+	fflush(stdout);
+	
+	for (;;)
+	{
+		ch = getchar();
+		if (ch == EOF) return 0;
+		if (ch == 0x1B) break;
+	}
+	
+	if (scanf("[%d;%dR", &oy, &ox) != 2) return 1;
+	oy -= 11;
+	
+	moonfish_chess(&fancy->ctx);
+	
+	pthread_create(&thread, NULL, &moonfish_start, fancy);
+	
+	if (fancy->white == (fancy->ctx.white ? 0 : 1))
+	{
+		*name++ = ' ';
+		moonfish_go(fancy, names + 1, name, in, out);
+		name += strlen(name);
+		moonfish_reset_time(fancy);
+		pthread_mutex_unlock(fancy->mutex);
+		moonfish_fancy(fancy);
+	}
+	
+	printf("\x1B[?1000h");
+	fflush(stdout);
+	
+	for (ch0 = 0 ; ch0 != EOF ; ch0 = getchar())
+	{
+		if (fancy->white && !fancy->ctx.white) continue;
+		if (!fancy->white && fancy->ctx.white) continue;
+		
+		if (ch0 != 0x1B) continue;
+		ch0 = getchar();
+		if (ch0 == EOF) break;
+		
+		if (ch0 != 0x5B) continue;
+		ch0 = getchar();
+		if (ch0 == EOF) break;
+		
+		if (ch0 != 0x4D) continue;
+		ch0 = getchar();
+		if (ch0 == EOF) break;
+		
+		if (ch0 == 0x20 && fancy->x == 0)
+		{
+			pthread_mutex_lock(fancy->mutex);
+			
+			ch = getchar();
+			if (ch == EOF) break;
+			fancy->x = ch - 0x21 - ox;
+			
+			ch = getchar();
+			if (ch == EOF) break;
+			fancy->y = ch - 0x21 - oy;
+			
+			fancy->x /= 2;
+			
+			if (fancy->x < 1 || fancy->x > 8) fancy->x = 0;
+			if (fancy->y < 1 || fancy->y > 8) fancy->x = 0;
+			
+			moonfish_fancy(fancy);
+			
+			pthread_mutex_unlock(fancy->mutex);
+			
+			continue;
+		}
+		
+		if (fancy->x != 0)
+		if (ch0 == 0x20 || ch0 == 0x23)
+		{
+			ch = getchar();
+			if (ch == EOF) break;
+			x1 = ch - 0x21 - ox;
+			
+			ch = getchar();
+			if (ch == EOF) break;
+			y1 = ch - 0x21 - oy;
+			
+			x1 /= 2;
+			
+			if (x1 < 1 || x1 > 8) x1 = 0;
+			if (y1 < 1 || y1 > 8) x1 = 0;
+			
+			if (x1 == 0) continue;
+			if (y1 == 0) continue;
+			
+			if (moonfish_play_from(&fancy->ctx, &move, fancy->x, fancy->y, x1, y1) == 0)
+			{
+				*name++ = ' ';
+				moonfish_to_uci(name, &move, fancy->ctx.white);
+				name += strlen(name);
+				
+				pthread_mutex_lock(fancy->mutex);
+				
+				moonfish_play(&fancy->ctx, &move);
+				moonfish_reset_time(fancy);
+				fancy->x = 0;
+				moonfish_fancy(fancy);
+				
+				pthread_mutex_unlock(fancy->mutex);
+				
+				printf("\x1B[?1000l");
+				fflush(stdout);
+				
+				*name++ = ' ';
+				moonfish_go(fancy, names + 1, name, in, out);
+				name += strlen(name);
+				moonfish_reset_time(fancy);
+				pthread_mutex_unlock(fancy->mutex);
+				
+				printf("\x1B[?1000h");
+				fflush(stdout);
+			}
+			else
+			{
+				pthread_mutex_lock(fancy->mutex);
+				if (ch0 == 0x20 && x1 == fancy->x && y1 == fancy->y)
+					fancy->x = 0;
+				else
+					fancy->x = x1,
+					fancy->y = y1;
+				x1 = 0;
+				y1 = 0;
+				moonfish_fancy(fancy);
+				pthread_mutex_unlock(fancy->mutex);
+			}
+		}
+	}
+	
+	return 0;
+}
--