ref: e475abcb1367ff05b438c580c3aa63e5befbe345
dir: /tools/analyse.c/
/* moonfish is licensed under the AGPL (v3 or later) */
/* copyright 2023, 2024 zamfofex */
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <termios.h>
#include <signal.h>
#include <poll.h>
#include "../moonfish.h"
#include "tools.h"
struct moonfish_ply {
struct moonfish_chess chess;
char name[6];
char best[6];
char san[16];
int white, black, draw;
int score;
int checkmate;
int depth;
int ephemeral;
int count;
struct moonfish_ply *main;
};
struct moonfish_fancy {
struct moonfish_ply plies[256];
int i, count;
pthread_mutex_t *mutex;
int x, y;
FILE *in, *out;
char *fen;
int ox, oy;
int offset;
char pv[32];
int time, taken;
int ephemeral;
int stop_count;
};
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");
}
y = 7 - y;
piece = fancy->plies[fancy->i].chess.board[(x + 1) + (y + 2) * 10];
if (piece == moonfish_empty) {
printf(" ");
return;
}
if (piece / 16 == 1) printf("\x1B[38;5;253m");
else printf("\x1B[38;5;240m");
switch (piece % 16) {
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_evaluation(struct moonfish_fancy *fancy)
{
int score;
int white, black, draw;
int white_mod, black_mod;
struct moonfish_ply *ply;
ply = fancy->plies + fancy->i;
if (ply->checkmate != 0) {
score = 1;
white = 0;
black = 0;
if (ply->checkmate > 0) white = 1;
else black = 1;
}
else {
white = ply->white;
black = ply->black;
score = white + black + ply->draw;
if (score == 0) {
white = 1;
black = 1;
score = 2;
}
}
white *= 16;
black *= 16;
white_mod = white % score;
black_mod = black % score;
white /= score;
black /= score;
if (ply->draw == 0) {
while (16 - white - black > 0) {
if (white_mod > black_mod) white++;
else black++;
}
}
draw = 16 - white - black;
while (black > 1) {
printf("\x1B[48;5;240m \x1B[0m\x1B[B\x08");
black -= 2;
}
if (black) {
if (draw) {
printf("\x1B[38;5;67m\x1B[48;5;240m\xE2\x96\x84\x1B[0m\x1B[B\x08");
draw--;
}
else {
printf("\x1B[38;5;253m\x1B[48;5;240m\xE2\x96\x84\x1B[0m\x1B[B\x08");
white--;
}
}
while (draw > 1) {
printf("\x1B[48;5;67m \x1B[0m\x1B[B\x08");
draw -= 2;
}
if (draw) {
printf("\x1B[38;5;253m\x1B[48;5;67m\xE2\x96\x84\x1B[0m\x1B[B\x08");
black--;
}
while (white > 1) {
printf("\x1B[48;5;253m \x1B[0m\x1B[B\x08");
white -= 2;
}
}
static void moonfish_scoresheet_move(struct moonfish_fancy *fancy, int i)
{
struct moonfish_ply *ply, *prev;
int score;
int length;
ply = fancy->plies + i;
length = strlen(ply->san);
if (i >= fancy->count || ply->name[0] == 0) {
printf("%12s", "");
return;
}
if (i == fancy->i) printf("\x1B[48;5;248m\x1B[38;5;235m");
if (fancy->ephemeral && ply->ephemeral) {
printf("\x1B[38;5;240m(%s)", ply->san);
printf("%*s\x1B[0m", 10 - length, "");
return;
}
printf(" %s", ply->san);
if (moonfish_finished(&ply->chess)) {
if (moonfish_checkmate(&ply->chess)) printf("\x1B[38;5;160m# ");
else printf("\x1B[38;5;111m(0)");
printf("%*s\x1B[0m", 8 - length, "");
return;
}
if (ply->checkmate) {
printf("\x1B[38;5;162m#");
if (ply->checkmate > -10 && ply->checkmate < 10) {
printf("#%+d", ply->checkmate);
}
else {
if (ply->checkmate > 0) printf("#+X");
else printf("#-X");
}
printf("%*s\x1B[0m", 8 - length, "");
return;
}
if (i <= 0) {
printf("%*s", 11 - length, "");
return;
}
prev = ply - 1;
if (!ply->ephemeral && prev->ephemeral) prev = prev->main;
score = ply->score + prev->score;
if (prev->checkmate != 0 || score > 200) {
printf("\x1B[38;5;124m?? ");
}
else {
if (score > 100) printf("\x1B[38;5;173m? ");
else printf(" ");
}
printf("%*s\x1B[0m", 8 - length, "");
}
static void moonfish_scoresheet(struct moonfish_fancy *fancy)
{
int i, j;
i = fancy->offset * 2;
if (fancy->plies[0].chess.white) i++;
for (j = 0 ; j < 6 ; j++) {
printf("\x1B[23G");
moonfish_scoresheet_move(fancy, i);
moonfish_scoresheet_move(fancy, i + 1);
printf("\x1B[B");
i += 2;
}
}
static void moonfish_fancy_score(struct moonfish_fancy *fancy)
{
struct moonfish_ply *ply;
int score;
if (fancy->stop_count != 0) printf("\x1B[38;5;103m");
else printf("\x1B[38;5;111m");
printf("(+)\x1B[0m ");
ply = fancy->plies + fancy->i;
score = ply->score;
if (!ply->chess.white) score *= -1;
if (moonfish_finished(&ply->chess)) {
if (moonfish_checkmate(&ply->chess)) {
printf("\x1B[38;5;160m#\x1B[0m");
}
else {
printf("0");
}
}
else {
if (ply->checkmate != 0) {
printf("#%+d", ply->checkmate);
}
else {
if (score < 0) printf("-%d.%02d", -score / 100, -score % 100);
else printf("%d.%02d", score / 100, score % 100);
}
}
printf(" (depth %d)", ply->depth);
if (fancy->stop_count != 0) printf(" in %d.%ds of %ds", fancy->taken / 1000, fancy->taken % 1000 / 100, fancy->time / 1000);
else printf(" in %ds", fancy->time / 1000);
printf("\x1B[0K");
}
static void moonfish_fancy(struct moonfish_fancy *fancy)
{
unsigned char x, y;
printf("\x1B[%dH", fancy->oy);
for (y = 0 ; y < 8 ; y++) {
printf(" ");
for (x = 0 ; x < 8 ; x++) moonfish_fancy_square(fancy, x, y);
printf("\x1B[0m\n");
}
printf("\x1B[%d;23H", fancy->oy);
moonfish_fancy_score(fancy);
printf("\x1B[%dH", fancy->oy + 1);
moonfish_scoresheet(fancy);
printf("\x1B[%d;21H", fancy->oy);
moonfish_evaluation(fancy);
printf("\x1B[%d;23H", fancy->oy + 7);
printf("best:%s\x1B[0K\n", fancy->pv);
fflush(stdout);
}
static void *moonfish_start(void *data)
{
static char line[2048];
static struct moonfish_ply ply;
static char san[16];
static struct moonfish_move move;
struct moonfish_fancy *fancy;
char *arg;
int score;
char *buffer;
unsigned int i, length;
int changed;
int pv;
fancy = data;
pthread_mutex_lock(fancy->mutex);
changed = 0;
for (;;) {
if (changed) moonfish_fancy(fancy);
changed = 0;
pthread_mutex_unlock(fancy->mutex);
if (fgets(line, sizeof line, fancy->out) == NULL) exit(1);
pthread_mutex_lock(fancy->mutex);
arg = strtok_r(line, "\r\n\t ", &buffer);
if (arg == NULL) continue;
if (!strcmp(arg, "bestmove")) {
fancy->stop_count--;
if (fancy->stop_count < 0) fancy->stop_count = 0;
changed = 1;
continue;
}
if (fancy->stop_count != 1) continue;
if (strcmp(arg, "info")) continue;
ply = fancy->plies[fancy->i];
ply.depth = 0;
pv = 0;
for (;;) {
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL) break;
if (!strcmp(arg, "lowerbound") || !strcmp(arg, "upperbound")) {
changed = 0;
break;
}
if (!strcmp(arg, "depth")) {
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &ply.depth) || ply.depth < 0) {
fprintf(stderr, "malformed 'depth' in 'info' command\n");
exit(1);
}
continue;
}
if (!strcmp(arg, "multipv")) {
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &pv) || pv <= 0) {
fprintf(stderr, "malformed 'multipv' in 'info' command\n");
exit(1);
}
continue;
}
if (!strcmp(arg, "pv")) {
changed = 1;
if (pv < 1) pv = 1;
i = 0;
while (i < sizeof fancy->pv - 1) {
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL) break;
if (moonfish_from_uci(&ply.chess, &move, arg)) {
fprintf(stderr, "invalid move: '%s'\n", arg);
exit(1);
}
if (i == 0 && pv == 1) {
strcpy(ply.best, arg);
}
if (pv > 1) {
ply.chess = move.chess;
i = 1;
continue;
}
moonfish_to_san(&ply.chess, &move, san, 0);
length = strlen(san);
if (i + length > sizeof fancy->pv - 2) break;
ply.chess = move.chess;
fancy->pv[i++] = ' ';
strcpy(fancy->pv + i, san);
i += length;
}
ply.chess = fancy->plies[fancy->i].chess;
continue;
}
if (!strcmp(arg, "wdl")) {
changed = 1;
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &ply.white) || ply.white < 0) {
fprintf(stderr, "malformed 'wdl' win in 'info' command\n");
exit(1);
}
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &ply.draw) || ply.draw < 0) {
fprintf(stderr, "malformed 'wdl' draw in 'info' command\n");
exit(1);
}
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &ply.black) || ply.black < 0) {
fprintf(stderr, "malformed 'wdl' loss in 'info' command\n");
exit(1);
}
ply.checkmate = 0;
if (!ply.chess.white) {
score = ply.white;
ply.white = ply.black;
ply.black = score;
}
continue;
}
if (!strcmp(arg, "time")) {
changed = 1;
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &fancy->taken) || fancy->taken < 0) {
fprintf(stderr, "malformed 'time' in 'info' command\n");
exit(1);
}
continue;
}
if (strcmp(arg, "score")) continue;
changed = 1;
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL) break;
if (!strcmp(arg, "cp")) {
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &score)) {
fprintf(stderr, "malformed 'cp' in 'info score' command\n");
exit(1);
}
ply.score = score;
ply.white = 100;
ply.black = 100;
ply.draw = 0;
ply.checkmate = 0;
if (score > 0) ply.white += score;
else ply.black -= score;
if (!ply.chess.white) {
score = ply.white;
ply.white = ply.black;
ply.black = score;
}
continue;
}
if (!strcmp(arg, "mate")) {
arg = strtok_r(NULL, "\r\n\t ", &buffer);
if (arg == NULL || moonfish_int(arg, &score)) {
fprintf(stderr, "malformed 'mate' in 'info score' command\n");
exit(1);
}
if (!ply.chess.white) score *= -1;
ply.white = 0;
ply.black = 0;
ply.draw = 0;
ply.checkmate = score;
ply.score = 0;
continue;
}
}
if (changed) fancy->plies[fancy->i] = ply;
}
}
static struct termios moonfish_termios;
static FILE *moonfish_engine = NULL;
static void moonfish_exit(void)
{
if (moonfish_engine != NULL) fprintf(moonfish_engine, "quit\n");
tcsetattr(0, TCSANOW, &moonfish_termios);
printf("\n\x1B[?1000l\x1B[?25h");
fflush(stdout);
}
static void moonfish_signal(int signal)
{
(void) signal;
exit(1);
}
static int moonfish_move_from(struct moonfish_chess *chess, struct moonfish_move *found, int x0, int y0, int x1, int y1)
{
y0 = 10 - y0;
y1 = 10 - y1;
return moonfish_move(chess, found, x0 + y0 * 10, x1 + y1 * 10);
}
static void moonfish_analyse(struct moonfish_fancy *fancy)
{
int i;
if (fancy->in == NULL) return;
fancy->taken = 0;
fancy->stop_count++;
fprintf(fancy->in, "stop\n");
fprintf(fancy->in, "position ");
if (fancy->fen == NULL) fprintf(fancy->in, "startpos");
else fprintf(fancy->in, "fen %s", fancy->fen);
if (fancy->i > 0) {
fprintf(fancy->in, " moves");
i = 0;
for (;;) {
i += fancy->plies[i].count + 1;
if (i > fancy->i) break;
fprintf(fancy->in, " %s", fancy->plies[i].name);
}
i = fancy->plies[fancy->i].main - fancy->plies;
for (;;) {
i++;
if (i > fancy->i) break;
fprintf(fancy->in, " %s", fancy->plies[i].name);
}
}
fprintf(fancy->in, "\n");
fprintf(fancy->in, "go movetime %d\n", fancy->time);
fancy->pv[0] = 0;
moonfish_fancy(fancy);
}
static void moonfish_go(struct moonfish_fancy *fancy)
{
fancy->time = 4000;
moonfish_analyse(fancy);
}
static void moonfish_bump(struct moonfish_fancy *fancy)
{
fancy->time += 2000;
moonfish_analyse(fancy);
}
static void moonfish_scroll(struct moonfish_fancy *fancy)
{
int i;
i = fancy->i + 2;
if (fancy->plies[0].chess.white) i--;
if (i < fancy->offset * 2 + 2) fancy->offset = i / 2 - 1;
if (i > fancy->offset * 2 + 12) fancy->offset = i / 2 - 6;
if (fancy->offset < 0) fancy->offset = 0;
}
static void moonfish_play(struct moonfish_fancy *fancy, struct moonfish_move *move)
{
struct moonfish_ply *ply, *next;
int i, count;
char name[6];
fancy->x = 0;
ply = fancy->plies + fancy->i;
count = ply->count;
next = ply + count + 1;
if (move == NULL) name[0] = 0;
else moonfish_to_uci(&fancy->plies[fancy->i].chess, move, name);
if (!ply->ephemeral && fancy->i + count + 1 < fancy->count && !strcmp(name, next->name)) {
i = fancy->i;
for (;;) {
fancy->plies[i].count -= count;
if (!fancy->plies[i].ephemeral) break;
i--;
}
for (i = fancy->i + 1 ; i < fancy->count ; i++) {
fancy->plies[i].main -= count;
}
memmove(ply + 1, next, (fancy->count - fancy->i - count - 1) * sizeof *ply);
fancy->i++;
fancy->count -= count;
moonfish_scroll(fancy);
moonfish_go(fancy);
return;
}
if (fancy->count - count + 1 >= (int) (sizeof fancy->plies / sizeof *fancy->plies)) return;
i = fancy->i;
for (;;) {
fancy->plies[i].count -= count - 1;
if (fancy->plies[i].ephemeral == 0) break;
i--;
}
for (i = fancy->i + 1 ; i < fancy->count ; i++) {
fancy->plies[i].main -= count - 1;
}
if (fancy->i + count < fancy->count - 1 && next[-1].name[0] == 0) {
ply++;
next++;
}
memmove(ply + 1, next - 1, (fancy->count - fancy->i - count) * sizeof *ply);
fancy->count -= count - 1;
fancy->i++;
fancy->plies[fancy->i] = fancy->plies[fancy->i - 1];
fancy->plies[fancy->i].depth = 0;
fancy->plies[fancy->i].score *= -1;
fancy->plies[fancy->i].best[0] = 0;
fancy->plies[fancy->i].ephemeral = 1;
fancy->plies[fancy->i].count = 0;
i = fancy->i;
ply = fancy->plies + i;
ply->chess.white = ply[-1].main->chess.white ^ 1;
strcpy(fancy->plies[fancy->i].name, name);
if (move == NULL) return;
moonfish_to_san(&fancy->plies[fancy->i].chess, move, fancy->plies[fancy->i].san, 1);
ply->chess = move->chess;
if (i + 1 < fancy->count && ply->chess.white == ply[1].chess.white) {
moonfish_play(fancy, NULL);
fancy->i = i;
}
moonfish_scroll(fancy);
moonfish_go(fancy);
}
int main(int argc, char **argv)
{
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static char *format = "<UCI-options>... [--] <cmd> <args>...";
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},
};
struct moonfish_fancy *fancy;
pthread_t thread;
struct termios termios;
struct sigaction action;
int ch, ch0;
int x1, y1;
struct moonfish_move move;
int error;
char **command;
int command_count;
char *value;
char **options;
int i;
FILE *file;
/* handle command line arguments */
command = moonfish_args(args, format, argc, argv);
command_count = argc - (command - argv);
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, '=');
if (value == NULL) break;
if (strchr(*command, '\n') != NULL || strchr(*command, '\r') != NULL) moonfish_usage(args, format, argv[0]);
command_count--;
command++;
if (command_count <= 0) moonfish_usage(args, format, argv[0]);
}
if (!strcmp(*command, "--")) {
command_count--;
command++;
if (command_count <= 0) moonfish_usage(args, format, argv[0]);
}
/* initialise data structures */
fancy = malloc(sizeof *fancy);
if (fancy == NULL) {
perror("malloc");
return 1;
}
fancy->mutex = &mutex;
fancy->offset = 0;
fancy->pv[0] = 0;
fancy->fen = NULL;
fancy->in = NULL;
fancy->out = NULL;
fancy->ephemeral = 0;
fancy->stop_count = 0;
fancy->x = 0;
fancy->y = 0;
fancy->i = 0;
fancy->count = 1;
strcpy(fancy->plies[0].san, "...");
strcpy(fancy->plies[0].name, "...");
fancy->plies[0].white = 0;
fancy->plies[0].black = 0;
fancy->plies[0].draw = 0;
fancy->plies[0].checkmate = 0;
fancy->plies[0].depth = 0;
fancy->plies[0].score = 0;
fancy->plies[0].best[0] = 0;
fancy->plies[0].ephemeral = 0;
fancy->plies[0].count = 0;
fancy->plies[0].main = fancy->plies;
moonfish_chess(&fancy->plies[0].chess);
if (args[0].value != NULL) {
fancy->fen = args[0].value;
if (moonfish_from_fen(&fancy->plies[0].chess, fancy->fen)) {
moonfish_usage(args, format, argv[0]);
}
}
if (args[1].value != NULL) {
file = fopen(args[1].value, "r");
if (file == NULL) {
perror("fopen");
return 1;
}
for (;;) {
if (moonfish_pgn(file, &fancy->plies[fancy->i].chess, &move, fancy->i == 0 ? 1 : 0)) break;
moonfish_play(fancy, &move);
}
for (i = 0 ; i < fancy->count ; i++) {
fancy->plies[i].ephemeral = 0;
fancy->plies[i].count = 0;
fancy->plies[i].main = fancy->plies + i;
}
fclose(file);
fancy->fen = malloc(128);
if (fancy->fen == NULL) {
perror("malloc");
return 1;
}
moonfish_to_fen(&fancy->plies[0].chess, fancy->fen);
fancy->ephemeral = 1;
}
/* configure the terminal for displaying the user interface */
if (tcgetattr(0, &moonfish_termios)) {
perror("tcgetattr");
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("sigaction");
return 1;
}
termios = moonfish_termios;
termios.c_lflag &= ~(ECHO | ICANON);
if (tcsetattr(0, TCSANOW, &termios)) {
perror("tcsetattr");
return 1;
}
printf("\n\n\n\n\n\n\n\n\n");
printf("\x1B[8A");
printf("\x1B[6n");
fflush(stdout);
for (;;) {
ch = getchar();
if (ch == EOF) return 1;
if (ch == 0x1B) break;
}
if (scanf("[%d;%dR", &fancy->oy, &fancy->ox) != 2) return 1;
printf("\x1B[?1000h\x1B[?25l");
fflush(stdout);
/* begin setting up UCI bot */
moonfish_spawn(command, &fancy->in, &fancy->out, NULL);
fprintf(fancy->in, "uci\n");
moonfish_wait(fancy->out, "uciok");
for (;;) {
value = strchr(*options, '=');
if (value == NULL) break;
fprintf(fancy->in, "setoption name %.*s value %s\n", (int) (value - *options), *options, value + 1);
options++;
}
fprintf(fancy->in, "ucinewgame\n");
moonfish_go(fancy);
/* start thread to communicate with the bot */
error = pthread_create(&thread, NULL, &moonfish_start, fancy);
if (error) {
fprintf(stderr, "pthread_create: %s\n", strerror(error));
return 1;
}
/* main UI loop */
pthread_mutex_lock(fancy->mutex);
for (;;) {
pthread_mutex_unlock(fancy->mutex);
ch0 = getchar();
pthread_mutex_lock(fancy->mutex);
if (ch0 == EOF) break;
if (ch0 != 0x1B) continue;
ch0 = getchar();
if (ch0 == EOF) break;
if (ch0 != 0x5B) continue;
ch0 = getchar();
if (ch0 == EOF) break;
/* handle up arrow */
if (ch0 == 'A') {
if (fancy->i == 0) continue;
fancy->i = 0;
moonfish_scroll(fancy);
moonfish_go(fancy);
continue;
}
/* handle down arrow */
if (ch0 == 'B') {
if (fancy->i == fancy->count - 1) continue;
fancy->i = fancy->count - 1;
moonfish_scroll(fancy);
moonfish_go(fancy);
continue;
}
/* handle right arrow */
if (ch0 == 'C') {
if (fancy->i >= fancy->count - 1) continue;
if (!fancy->plies[fancy->i].ephemeral && fancy->ephemeral) {
fancy->i += fancy->plies[fancy->i].count;
}
if (fancy->i < fancy->count - 1) fancy->i++;
if (!fancy->plies[fancy->i].name[0]) fancy->i++;
moonfish_scroll(fancy);
moonfish_go(fancy);
continue;
}
/* handle left arrow */
if (ch0 == 'D') {
if (fancy->i == 0) continue;
if (!fancy->plies[fancy->i].ephemeral && fancy->ephemeral) {
fancy->i = fancy->plies[fancy->i - 1].main - fancy->plies;
}
else {
fancy->i--;
}
moonfish_scroll(fancy);
moonfish_go(fancy);
continue;
}
/* 'M' means "mouse button" */
/* (only handle them henceforth) */
if (ch0 != 'M') continue;
/* which mouse button? */
ch0 = getchar();
if (ch0 == EOF) break;
/* mouse 'x' coordinate */
ch = getchar();
if (ch == EOF) break;
x1 = ch - 0x21 - fancy->ox;
/* mouse 'y' coordinate */
ch = getchar();
if (ch == EOF) break;
y1 = ch - 0x21 - fancy->oy + 2;
/* handle scroll up */
if (ch0 == 0x60) {
if (fancy->offset > 0) {
fancy->offset--;
moonfish_fancy(fancy);
}
continue;
}
/* handle scroll down */
if (ch0 == 0x61) {
if (fancy->offset < fancy->count / 2 - 6) {
fancy->offset++;
moonfish_fancy(fancy);
}
continue;
}
/* "(+)" button clicked */
if (ch0 == 0x20 && y1 == 1 && x1 >= 21 && x1 <= 23) {
moonfish_bump(fancy);
continue;
}
/* move name clicked (on scoresheet) */
if (ch0 == 0x20 && y1 >= 2 && y1 <= 7 && x1 >= 21 && x1 <= 44) {
i = (fancy->offset + y1) * 2 - 4;
if (fancy->plies[0].chess.white) i++;
if (x1 > 32) i++;
if (i < fancy->count && fancy->plies[i].name[0] != 0) {
fancy->i = i;
moonfish_go(fancy);
}
continue;
}
/* "best move" button clicked */
if (ch0 == 0x20 && y1 == 8 && x1 >= 21 && x1 <= 40) {
if (fancy->plies[fancy->i].best[0] != 0) {
if (moonfish_from_uci(&fancy->plies[fancy->i].chess, &move, fancy->plies[fancy->i].best)) {
fprintf(stderr, "invalid best move: %s\n", fancy->plies[fancy->i].best);
return 1;
}
moonfish_play(fancy, &move);
}
continue;
}
/* only handle clicks on the board henceforth */
if (x1 < 2 || x1 > 17 || y1 < 1 || y1 > 8) continue;
x1 /= 2;
/* mouse down with no square selected: select the square under the mouse */
if (ch0 == 0x20 && fancy->x == 0) {
fancy->x = x1;
fancy->y = y1;
moonfish_fancy(fancy);
continue;
}
/* only handle cases where a square is selected henceforth */
if (fancy->x == 0) continue;
/* handle mouse down: if the clicked square is the selected square, deselect it */
if (ch0 == 0x20 && x1 == fancy->x && y1 == fancy->y) {
fancy->x = 0;
moonfish_fancy(fancy);
continue;
}
/* handle mouse up or mouse down: if it forms a valid move, play it on the board */
if (ch0 == 0x20 || ch0 == 0x23) {
if (!moonfish_move_from(&fancy->plies[fancy->i].chess, &move, fancy->x, fancy->y, x1, y1)) {
moonfish_play(fancy, &move);
continue;
}
}
/* handle mouse down: when there isn't a valid move under the mouse, just select the new square */
if (ch0 == 0x20) {
fancy->x = x1;
fancy->y = y1;
moonfish_fancy(fancy);
continue;
}
}
return 0;
}