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, ×pec))
+ {+ 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, ×pec))
+ {+ 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;
+}
--
⑨