ref: 5b14f83c1561de5a0c35657eb8c613675cbadaef
parent: ba0b9247668c22e10927c3918e3500a5ba0344a2
author: zamfofex <zamfofex@twdb.moe>
date: Sat Apr 6 12:27:34 EDT 2024
add IRC bot
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@
!/tools/lichess.c
!/tools/battle.c
!/tools/ribbon.c
+!/tools/chat.c
!/tools/ugi.c
!/tools/ugi-uci.c
!/tools/uci-ugi.c
--- a/chess.c
+++ b/chess.c
@@ -513,8 +513,52 @@
}
}
+int moonfish_validate(struct moonfish_chess *chess)
+{+ int x, y;
+ struct moonfish_move moves[32];
+ struct moonfish_move *move;
+
+ for (y = 0 ; y < 8 ; y++)
+ for (x = 0 ; x < 8 ; x++)
+ {+ moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
+ for (move = moves ; move->piece != moonfish_outside ; move++)
+ if (move->captured % 16 == moonfish_king)
+ return 0;
+ }
+
+ return 1;
+}
+
+int moonfish_check(struct moonfish_chess *chess)
+{+ int valid;
+ struct moonfish_castle castle;
+ unsigned char passing;
+
+ castle = chess->castle;
+ passing = chess->passing;
+
+ chess->castle.white_oo = 0;
+ chess->castle.white_ooo = 0;
+ chess->castle.black_oo = 0;
+ chess->castle.black_ooo = 0;
+
+ chess->white ^= 1;
+ valid = moonfish_validate(chess);
+ chess->white ^= 1;
+
+ chess->castle = castle;
+ chess->passing = passing;
+
+ return valid ^ 1;
+}
+
#ifndef moonfish_mini
+#include <string.h>
+
int moonfish_fen(struct moonfish_chess *chess, char *fen)
{int x, y;
@@ -606,13 +650,11 @@
return 0;
}
-#endif
-
-int moonfish_validate(struct moonfish_chess *chess)
+int moonfish_finished(struct moonfish_chess *chess)
{+ struct moonfish_move moves[32], *move;
int x, y;
- struct moonfish_move moves[32];
- struct moonfish_move *move;
+ int valid;
for (y = 0 ; y < 8 ; y++)
for (x = 0 ; x < 8 ; x++)
@@ -619,71 +661,332 @@
{moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
for (move = moves ; move->piece != moonfish_outside ; move++)
- if (move->captured % 16 == moonfish_king)
- return 0;
+ {+ moonfish_play(chess, move);
+ valid = moonfish_validate(chess);
+ moonfish_unplay(chess, move);
+ if (valid) return 0;
+ }
}
return 1;
}
-int moonfish_check(struct moonfish_chess *chess)
+int moonfish_checkmate(struct moonfish_chess *chess)
{- int valid;
- struct moonfish_castle castle;
- unsigned char passing;
-
- castle = chess->castle;
- passing = chess->passing;
-
- chess->castle.white_oo = 0;
- chess->castle.white_ooo = 0;
- chess->castle.black_oo = 0;
- chess->castle.black_ooo = 0;
-
- chess->white ^= 1;
- valid = moonfish_validate(chess);
- chess->white ^= 1;
-
- chess->castle = castle;
- chess->passing = passing;
-
- return valid ^ 1;
+ if (!moonfish_check(chess)) return 0;
+ return moonfish_finished(chess);
}
-#ifndef moonfish_mini
+int moonfish_stalemate(struct moonfish_chess *chess)
+{+ if (moonfish_check(chess)) return 0;
+ return moonfish_finished(chess);
+}
-int moonfish_finished(struct moonfish_chess *chess)
+static int moonfish_match_move(struct moonfish_chess *chess, struct moonfish_move *result_move, unsigned char type, unsigned char promotion, int x0, int y0, int x1, int y1, int check, int captured)
{- struct moonfish_move moves[32], *move;
+ int found;
int x, y;
+ struct moonfish_move moves[32];
+ struct moonfish_move *move;
+ int xi0, yi0, xi1, yi1;
int valid;
- for (y = 0 ; y < 8 ; y++)
- for (x = 0 ; x < 8 ; x++)
+ xi0 = 0, xi1 = 8;
+ yi0 = 0, yi1 = 8;
+
+ if (x0) xi0 = x0 - 1, xi1 = x0;
+ if (y0) yi0 = y0 - 1, yi1 = y0;
+
+ found = 0;
+
+ for (y = yi0 ; y < yi1 ; y++)
+ for (x = xi0 ; x < xi1 ; x++)
{moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
for (move = moves ; move->piece != moonfish_outside ; move++)
{+ if (move->piece % 16 != type) continue;
+ if (captured && move->captured == moonfish_empty) continue;
+ if (promotion && promotion != move->promotion % 16) continue;
+ if (move->to % 10 != x1) continue;
+ if (move->to / 10 - 1 != y1) continue;
+
moonfish_play(chess, move);
valid = moonfish_validate(chess);
+ if (valid && check) if (!moonfish_check(chess)) valid = 0;
+ if (valid && check == 2) if (!moonfish_checkmate(chess)) valid = 0;
moonfish_unplay(chess, move);
- if (valid) return 0;
+ if (!valid) continue;
+ if (found) continue;
+ found = 1;
+ *result_move = *move;
}
}
- return 1;
+ if (!found) return 1;
+ return 0;
}
-int moonfish_checkmate(struct moonfish_chess *chess)
+int moonfish_from_san(struct moonfish_chess *chess, struct moonfish_move *move, char *name)
{- if (!moonfish_check(chess)) return 0;
- return moonfish_finished(chess);
+ int count;
+ unsigned char type;
+ unsigned char promotion;
+ int check, capture;
+ int x0, y0;
+ int x1, y1;
+ char *name0, name_array[32], ch;
+ size_t length, i;
+
+ length = strlen(name);
+ if (length >= sizeof name_array) return 1;
+ strcpy(name_array, name);
+ name = name_array;
+
+ /* reverse the string (because it is easier to parse) */
+ for (i = length / 2 ; i < length ; i++)
+ {+ ch = name[i];
+ name[i] = name[length - i - 1];
+ name[length - i - 1] = ch;
+ }
+
+ check = 0;
+ if (*name == '+') check = 2;
+ else if (*name == '+') check = 1;
+ if (check) name++;
+
+ count = 0;
+ for (name0 = name ; *name0 != 0 ; name0++)
+ {+ if (*name0 == '-') continue;
+ if (*name0 == '_') continue;
+ if (*name0 == '0') { count++; continue; }+ if (*name0 == 'O') { count++; continue; }+ if (*name0 == 'o') { count++; continue; }+ count = 0;
+ break;
+ }
+
+ if (count > 0)
+ {+ if (chess->white) y1 = 1;
+ else y1 = 8;
+
+ if (count == 2) x1 = 7;
+ else if (count == 3) x1 = 3;
+ else return 1;
+
+ return moonfish_match_move(chess, move, moonfish_king, 0, 5, y1, x1, y1, check, 0);
+ }
+
+ x0 = 0;
+ y0 = 0;
+ x1 = 0;
+ y1 = 0;
+ check = 0;
+
+ switch (*name++)
+ {+ default:
+ promotion = 0;
+ name--;
+ break;
+ case 'K': case 'k':
+ promotion = moonfish_king;
+ break;
+ case 'Q': case 'q':
+ promotion = moonfish_queen;
+ break;
+ case 'R': case 'r':
+ promotion = moonfish_rook;
+ break;
+ case 'B': case 'b':
+ promotion = moonfish_bishop;
+ break;
+ case 'N': case 'n':
+ promotion = moonfish_knight;
+ break;
+ }
+
+ if (promotion != 0 && *name == '=') name++;
+
+ capture = 0;
+ if (*name >= '1' && *name <= '8') y1 = *name++ - '0';
+ if (*name >= 'a' && *name <= 'h') x1 = *name++ - 'a' + 1;
+ if (*name == 'x') capture = 1, name++;
+ if (*name >= '1' && *name <= '8') y0 = *name++ - '0';
+ if (*name >= 'a' && *name <= 'h') x0 = *name++ - 'a' + 1;
+
+ if (x1 == 0) return 1;
+ if (y1 == 0) return 1;
+
+ switch (*name++)
+ {+ default:
+ type = moonfish_pawn;
+ if (x0 && y0) type = chess->board[x0 + (y0 + 1) * 10] % 16;
+ if (type == 0x0F) return 1;
+ name--;
+ break;
+ case 'K': case 'k':
+ type = moonfish_king;
+ break;
+ case 'Q': case 'q':
+ type = moonfish_queen;
+ break;
+ case 'R': case 'r':
+ type = moonfish_rook;
+ break;
+ case 'B': /* no lowercase here */
+ type = moonfish_bishop;
+ break;
+ case 'N': case 'n':
+ type = moonfish_knight;
+ break;
+ }
+
+ if (*name != 0) return 1;
+
+ return moonfish_match_move(chess, move, type, promotion, x0, y0, x1, y1, check, capture);
}
-int moonfish_stalemate(struct moonfish_chess *chess)
+/* todo: improve this */
+void moonfish_to_fen(struct moonfish_chess *chess, char *fen)
{- if (moonfish_check(chess)) return 0;
- return moonfish_finished(chess);
+ int x, y;
+ unsigned char piece;
+
+ for (y = 7 ; y >= 0 ; y--)
+ {+ for (x = 0 ; x < 8 ; x++)
+ {+ piece = chess->board[(x + 1) + (y + 2) * 10];
+ if (piece == moonfish_empty)
+ {+ *fen++ = '1';
+ continue;
+ }
+
+ switch (piece % 16)
+ {+ default:
+ return;
+ case moonfish_pawn:
+ *fen = 'p';
+ break;
+ case moonfish_knight:
+ *fen = 'n';
+ break;
+ case moonfish_bishop:
+ *fen = 'b';
+ break;
+ case moonfish_rook:
+ *fen = 'r';
+ break;
+ case moonfish_queen:
+ *fen = 'q';
+ break;
+ case moonfish_king:
+ *fen = 'k';
+ break;
+ }
+
+ if (piece / 16 == 1) *fen += 'A' - 'a';
+ fen++;
+ }
+
+ *fen++ = '/';
+ }
+
+ fen[-1] = 0;
+}
+
+void moonfish_to_san(struct moonfish_chess *chess, char *name, struct moonfish_move *move)
+{+ static char names[] = "NBRQK";
+
+ int x, y;
+ struct moonfish_move moves[32];
+ struct moonfish_move *other_move;
+ char file_ambiguity, rank_ambiguity, ambiguity;
+ int to_x, to_y;
+ int from_x, from_y;
+
+ from_x = move->from % 10 - 1;
+ from_y = move->from / 10 - 2;
+
+ to_x = move->to % 10 - 1;
+ to_y = move->to / 10 - 2;
+
+ if (move->piece % 16 == moonfish_pawn)
+ {+ if (from_x != to_x)
+ {+ *name++ = from_x + 'a';
+ *name++ = 'x';
+ }
+
+ *name++ = to_x + 'a';
+ *name++ = to_y + '1';
+
+ if (move->promotion % 16 != moonfish_pawn)
+ {+ *name++ = '=';
+ *name++ = names[move->promotion % 16 - 2];
+ }
+
+ *name = 0;
+
+ return;
+ }
+
+ file_ambiguity = 0;
+ rank_ambiguity = 0;
+ ambiguity = 0;
+
+ for (y = 0 ; y < 8 ; y++)
+ for (x = 0 ; x < 8 ; x++)
+ {+ if ((x + 1) + (y + 2) * 10 == move->from) continue;
+ moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
+
+ for (other_move = moves ; other_move->piece != moonfish_outside ; other_move++)
+ {+ if (other_move->to != move->to) continue;
+ if (other_move->piece != move->piece) continue;
+
+ ambiguity = 1;
+ if (other_move->from % 10 - 1 == from_x) file_ambiguity = 1;
+ if (other_move->from / 10 - 2 == from_y) rank_ambiguity = 1;
+ }
+ }
+
+ *name++ = names[(move->piece & 0xF) - 2];
+
+ if (ambiguity)
+ {+ if (file_ambiguity)
+ {+ if (rank_ambiguity)
+ *name++ = from_x + 'a';
+ *name++ = from_y + '1';
+ }
+ else
+ {+ *name++ = from_x + 'a';
+ }
+ }
+
+ if (move->captured != moonfish_empty)
+ *name++ = 'x';
+
+ *name++ = to_x + 'a';
+ *name++ = to_y + '1';
+
+ *name = 0;
}
#endif
--- a/main.c
+++ b/main.c
@@ -23,6 +23,7 @@
char *end;
#ifndef moonfish_mini
long int long_depth;
+ long int time;
#endif
if (argc > 1)
@@ -56,6 +57,9 @@
wtime = -1;
btime = -1;
depth = -1;
+#ifndef moonfish_mini
+ time = -1;
+#endif
for (;;)
{@@ -113,6 +117,29 @@
depth = long_depth;
}
+ else if (!strcmp(arg, "movetime"))
+ {+ arg = strtok(NULL, "\r\n\t ");
+
+ if (arg == NULL)
+ {+ fprintf(stderr, "%s: malformed 'go movetime' command\n", argv[0]);
+ return 1;
+ }
+
+ errno = 0;
+ time = strtol(arg, &end, 10);
+ if (errno != 0)
+ {+ perror(argv[0]);
+ return 1;
+ }
+ if (*end != 0 || time < 0)
+ {+ fprintf(stderr, "%s: malformed move time in 'go' command\n", argv[0]);
+ return 1;
+ }
+ }
#endif
}
@@ -119,16 +146,17 @@
if (wtime < 0) wtime = 0;
if (btime < 0) btime = 0;
+#ifndef moonfish_mini
if (depth >= 0)
-#ifdef moonfish_mini
- return 1;
-#else
score = moonfish_best_move_depth(analysis, &move, depth);
+ else if (time >= 0)
+ score = moonfish_best_move_time(analysis, &move, &depth, time);
+ else
#endif
- else if (chess.white)
- score = moonfish_best_move_time(analysis, &move, &depth, wtime, btime);
+ if (chess.white)
+ score = moonfish_best_move_clock(analysis, &move, &depth, wtime, btime);
else
- score = moonfish_best_move_time(analysis, &move, &depth, btime, wtime);
+ score = moonfish_best_move_clock(analysis, &move, &depth, btime, wtime);
printf("info depth %d ", depth);if (score >= moonfish_omega || score <= -moonfish_omega)
--- a/makefile
+++ b/makefile
@@ -15,7 +15,7 @@
.PHONY: all clean install uninstall
-all: moonfish play lichess analyse battle ribbon uci-ugi ugi-uci
+all: moonfish play lichess analyse battle ribbon chat uci-ugi ugi-uci
moonfish moonfish.exe moonfish.wasm: moonfish.h chess.c search.c main.c
$(moonfish_cc) -o $@ $(filter %.c,$^)
@@ -26,6 +26,9 @@
lichess: $(tools_src) tools/lichess.c tools/https.c
$(tools_cc) -o $@ $(filter %.c,$^) -ltls -lssl -lcrypto -lcjson
+chat: $(tools_src) tools/chat.c tools/https.c
+ $(tools_cc) -o $@ $(filter %.c,$^) -ltls -lssl -lcrypto
+
ugi-uci: $(ugi_src)
$(tools_cc) -o $@ $(filter %.c,$^)
@@ -42,6 +45,7 @@
install -D -m 755 analyse $(BINDIR)/moonfish-analyse
install -D -m 755 battle $(BINDIR)/moonfish-battle
install -D -m 755 ribbon $(BINDIR)/moonfish-ribbon
+ install -D -m 755 chat $(BINDIR)/moonfish-chat
install -D -m 755 ugi-uci $(BINDIR)/ugi-uci
install -D -m 755 uci-ugi $(BINDIR)/uci-ugi
--- a/moonfish.h
+++ b/moonfish.h
@@ -69,7 +69,8 @@
void moonfish_play(struct moonfish_chess *chess, struct moonfish_move *move);
void moonfish_unplay(struct moonfish_chess *chess, struct moonfish_move *move);
-int moonfish_best_move_time(struct moonfish_analysis *analysis, struct moonfish_move *move, int *depth, long int our_time, long int their_time);
+int moonfish_best_move_time(struct moonfish_analysis *analysis, struct moonfish_move *move, int *depth, long int time);
+int moonfish_best_move_clock(struct moonfish_analysis *analysis, struct moonfish_move *move, int *depth, long int our_time, long int their_time);
int moonfish_countdown(int score);
struct moonfish_analysis *moonfish_analysis(char *argv0);
@@ -87,6 +88,9 @@
int moonfish_fen(struct moonfish_chess *chess, char *fen);
int moonfish_best_move_depth(struct moonfish_analysis *analysis, struct moonfish_move *move, int depth);
+int moonfish_from_san(struct moonfish_chess *chess, struct moonfish_move *move, char *name);
+void moonfish_to_san(struct moonfish_chess *chess, char *name, struct moonfish_move *move);
+void moonfish_to_fen(struct moonfish_chess *chess, char *fen);
int moonfish_finished(struct moonfish_chess *chess);
int moonfish_checkmate(struct moonfish_chess *chess);
--- a/search.c
+++ b/search.c
@@ -347,18 +347,14 @@
#endif
-int moonfish_best_move_time(struct moonfish_analysis *analysis, struct moonfish_move *best_move, int *depth, long int our_time, long int their_time)
+int moonfish_best_move_time(struct moonfish_analysis *analysis, struct moonfish_move *best_move, int *depth, long int time)
{- long int d, t, t0, t1;
+ long int t, t0, t1;
int r;
r = 24 * 2048;
t = -1;
- d = our_time - their_time;
- if (d < 0) d = 0;
- d += our_time / 8;
-
for (;;)
{t0 = moonfish_clock(analysis);
@@ -368,8 +364,8 @@
if (t >= 0) r = (t1 - t0) * 2048 / (t + 1);
t = t1 - t0;
- d -= t;
- if (t * r > d * 2048) break;
+ time -= t;
+ if (t * r > time * 2048) break;
analysis->depth++;
}
@@ -376,4 +372,13 @@
*depth = analysis->depth;
return analysis->score;
+}
+
+int moonfish_best_move_clock(struct moonfish_analysis *analysis, struct moonfish_move *best_move, int *depth, long int our_time, long int their_time)
+{+ long int time;
+ time = our_time - their_time;
+ if (time < 0) time = 0;
+ time += our_time / 8;
+ return moonfish_best_move_time(analysis, best_move, depth, time);
}
--- a/tools/analyse.c
+++ b/tools/analyse.c
@@ -289,91 +289,6 @@
fflush(stdout);
}
-static void moonfish_to_san(struct moonfish_chess *chess, char *name, struct moonfish_move *move)
-{- static char names[] = "NBRQK";
-
- int x, y;
- struct moonfish_move moves[32];
- struct moonfish_move *other_move;
- char file_ambiguity, rank_ambiguity, ambiguity;
- int to_x, to_y;
- int from_x, from_y;
-
- from_x = move->from % 10 - 1;
- from_y = move->from / 10 - 2;
-
- to_x = move->to % 10 - 1;
- to_y = move->to / 10 - 2;
-
- if (move->piece % 16 == moonfish_pawn)
- {- if (from_x != to_x)
- {- *name++ = from_x + 'a';
- *name++ = 'x';
- }
-
- *name++ = to_x + 'a';
- *name++ = to_y + '1';
-
- if (move->promotion % 16 != moonfish_pawn)
- {- *name++ = '=';
- *name++ = names[move->promotion % 16 - 2];
- }
-
- *name = 0;
-
- return;
- }
-
- file_ambiguity = 0;
- rank_ambiguity = 0;
- ambiguity = 0;
-
- for (y = 0 ; y < 8 ; y++)
- for (x = 0 ; x < 8 ; x++)
- {- if ((x + 1) + (y + 2) * 10 == move->from) continue;
- moonfish_moves(chess, moves, (x + 1) + (y + 2) * 10);
-
- for (other_move = moves ; other_move->piece != moonfish_outside ; other_move++)
- {- if (other_move->to != move->to) continue;
- if (other_move->piece != move->piece) continue;
-
- ambiguity = 1;
- if (other_move->from % 10 - 1 == from_x) file_ambiguity = 1;
- if (other_move->from / 10 - 2 == from_y) rank_ambiguity = 1;
- }
- }
-
- *name++ = names[(move->piece & 0xF) - 2];
-
- if (ambiguity)
- {- if (file_ambiguity)
- {- if (rank_ambiguity)
- *name++ = from_x + 'a';
- *name++ = from_y + '1';
- }
- else
- {- *name++ = from_x + 'a';
- }
- }
-
- if (move->captured != moonfish_empty)
- *name++ = 'x';
-
- *name++ = to_x + 'a';
- *name++ = to_y + '1';
-
- *name = 0;
-}
-
static void *moonfish_start(void *data)
{static char line[2048];
--- /dev/null
+++ b/tools/chat.c
@@ -1,0 +1,307 @@
+/* moonfish is licensed under the AGPL (v3 or later) */
+/* copyright 2024 zamfofex */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "../moonfish.h"
+#include "tools.h"
+#include "https.h"
+
+static void moonfish_parse_chat(char *argv0, char *line, char ***command, int *count)
+{+ int length;
+
+ *command = NULL;
+ *count = 0;
+
+ while (*line == ' ') line++;
+ if (*line == 0) return;
+ if (*line == '@')
+ {+ line++;
+ while (*line != ' ' && *line != 0) line++;
+ while (*line == ' ') line++;
+ }
+ if (*line == ':')
+ {+ line++;
+ while (*line != ' ' && *line != 0) line++;
+ while (*line == ' ') line++;
+ }
+
+ while (*line != 0)
+ {+ *command = realloc(*command, sizeof **command * (*count + 1));
+ if (*line == ':')
+ {+ (*command)[*count] = strdup(line + 1);
+ if ((*command)[*count] == NULL)
+ {+ perror(argv0);
+ exit(1);
+ }
+ (*count)++;
+ break;
+ }
+
+ length = 0;
+ while (line[length] != ' ' && line[length] != 0) length++;
+ (*command)[*count] = strndup(line, length);
+ line += length;
+ if ((*command)[*count] == NULL)
+ {+ perror(argv0);
+ exit(1);
+ }
+
+ while (*line == ' ') line++;
+ (*count)++;
+ }
+}
+
+static void moonfish_free_chat(char **command, int count)
+{+ int i;
+ if (count == 0) return;
+ for (i = 0 ; i < count ; i++) free(command[i]);
+ free(command);
+}
+
+static void moonfish_read_chat(char *argv0, struct tls *tls, char *username, char **channel, char **message)
+{+ static int under_count = 0;
+
+ char *line;
+ char **command;
+ int count;
+ int i;
+
+ for (;;)
+ {+ line = moonfish_read_line(argv0, tls);
+ if (line == NULL)
+ {+ fprintf(stderr, "%s: IRC connection closed unexpectedly\n", argv0);
+ exit(1);
+ }
+
+ moonfish_parse_chat(argv0, line, &command, &count);
+ free(line);
+
+ if (count == 0) continue;
+
+ if (!strcmp(command[0], "433"))
+ {+ moonfish_free_chat(command, count);
+
+ under_count++;
+
+ if (under_count > 16)
+ {+ fprintf(stderr, "%s: Tried too many nicknames\n", argv0);
+ exit(1);
+ }
+
+ moonfish_write_text(argv0, tls, "NICK ");
+ moonfish_write_text(argv0, tls, username);
+ for (i = 0 ; i < under_count ; i++) moonfish_write_text(argv0, tls, "_");
+ moonfish_write_text(argv0, tls, "\r\n");
+
+ continue;
+ }
+
+ if (!strcmp(command[0], "PING"))
+ {+ if (count < 2)
+ {+ moonfish_free_chat(command, count);
+ moonfish_write_text(argv0, tls, "PONG\r\n");
+ continue;
+ }
+
+ moonfish_write_text(argv0, tls, "PONG ");
+ moonfish_write_text(argv0, tls, command[1]);
+ moonfish_write_text(argv0, tls, "\r\n");
+ moonfish_free_chat(command, count);
+ continue;
+ }
+
+ if (!strcmp(command[0], "PRIVMSG"))
+ {+ if (count < 3)
+ {+ fprintf(stderr, "%s: Invalid IRC message\n", argv0);
+ exit(1);
+ }
+
+ *channel = strdup(command[1]);
+ if (*channel == NULL)
+ {+ perror(argv0);
+ exit(1);
+ }
+
+ *message = strdup(command[2]);
+ if (*message == NULL)
+ {+ perror(argv0);
+ exit(1);
+ }
+
+ moonfish_free_chat(command, count);
+ break;
+ }
+
+ moonfish_free_chat(command, count);
+ }
+}
+
+static void moonfish_chat(char *argv0, char **command, char **options, char *host, char *port, char *username, char *channels)
+{+ struct tls *tls;
+ char *channel;
+ char *message;
+ struct moonfish_chess chess;
+ struct moonfish_move move;
+ char name[12];
+ FILE *in, *out;
+ char *value;
+ char *names, *name0;
+ char fen[128];
+
+ moonfish_chess(&chess);
+
+ moonfish_spawn(argv0, command, &in, &out);
+ fprintf(in, "uci\n");
+ moonfish_wait(out, "uciok");
+
+ for (;;)
+ {+ value = strchr(*options, '=');
+ if (value == NULL) break;
+ fprintf(in, "setoption name %.*s value %s\n", (int) (value - *options), *options, value + 1);
+ options++;
+ }
+
+ fprintf(in, "isready\n");
+ moonfish_wait(out, "readyok");
+
+ fprintf(in, "ucinewgame\n");
+
+ tls = moonfish_connect(argv0, host, port);
+
+ moonfish_write_text(argv0, tls, "USER ");
+ moonfish_write_text(argv0, tls, username);
+ moonfish_write_text(argv0, tls, " 0 * ");
+ moonfish_write_text(argv0, tls, username);
+ moonfish_write_text(argv0, tls, "\r\nNICK ");
+ moonfish_write_text(argv0, tls, username);
+ moonfish_write_text(argv0, tls, "\r\n");
+
+ moonfish_write_text(argv0, tls, "JOIN ");
+ moonfish_write_text(argv0, tls, channels);
+ moonfish_write_text(argv0, tls, "\r\n");
+
+ names = strdup("");+ if (names == NULL)
+ {+ perror(argv0);
+ exit(1);
+ }
+
+ for (;;)
+ {+ moonfish_read_chat(argv0, tls, username, &channel, &message);
+ if (moonfish_from_san(&chess, &move, message))
+ {+ free(channel);
+ free(message);
+ continue;
+ }
+
+ moonfish_to_uci(name, &move);
+
+ names = realloc(names, strlen(names) + strlen(name) + 2);
+ if (names == NULL)
+ {+ perror(argv0);
+ exit(1);
+ }
+
+ strcat(names, " ");
+ strcat(names, name);
+
+ fprintf(in, "isready\n");
+ moonfish_wait(out, "readyok");
+ fprintf(in, "position startpos moves");
+ fprintf(in, names);
+ fprintf(in, "\n");
+
+ fprintf(in, "isready\n");
+ moonfish_wait(out, "readyok");
+ fprintf(in, "go movetime 5000\n");
+
+ name0 = moonfish_wait(out, "bestmove");
+ names = realloc(names, strlen(names) + strlen(name0) + 2);
+ if (names == NULL)
+ {+ perror(argv0);
+ exit(1);
+ }
+
+ strcat(names, " ");
+ strcat(names, name0);
+
+ moonfish_play(&chess, &move);
+ moonfish_from_uci(&chess, &move, name0);
+ moonfish_to_san(&chess, name, &move);
+ moonfish_play(&chess, &move);
+ moonfish_to_fen(&chess, fen);
+
+ moonfish_write_text(argv0, tls, "PRIVMSG ");
+ moonfish_write_text(argv0, tls, channel);
+ moonfish_write_text(argv0, tls, " :");
+ moonfish_write_text(argv0, tls, name);
+ moonfish_write_text(argv0, tls, "\r\n");
+
+ moonfish_write_text(argv0, tls, "PRIVMSG ");
+ moonfish_write_text(argv0, tls, channel);
+ moonfish_write_text(argv0, tls, " :https://lichess.org/export/fen.gif?fen=");
+ moonfish_write_text(argv0, tls, fen);
+ moonfish_write_text(argv0, tls, "\r\n");
+
+ free(channel);
+ free(message);
+ }
+}
+
+int main(int argc, char **argv)
+{+ static char *format = "<UCI-options> <cmd> <args>...";
+ static struct moonfish_arg args[] =
+ {+ {"N", "host", "<name>", "irc.libera.chat", "network host name (default: 'irc.libera.chat')"},+ {"P", "port", "<port>", "6697", "network port (default: '6697')"},+ {"M", "nick", "<nickname>", "moonfish", "the bot's nickname (default: 'moonfish')"},+ {"C", "channel", "<channels>", "#moonfish", "channels to join (default: '#moonfish')"},+ {NULL, NULL, NULL, NULL, NULL},+ };
+
+ char **options, **command;
+
+ /* todo: validate nickname & channels*/
+ options = moonfish_args(args, format, argc, argv);
+
+ command = options;
+ for (;;)
+ {+ if (*command == NULL) moonfish_usage(args, format, argv[0]);
+ if (strchr(*command, '=') == NULL) break;
+ command++;
+ }
+
+ moonfish_chat(argv[0], command, options, args[0].value, args[1].value, args[2].value, args[3].value);
+ fprintf(stderr, "%s: Unreachable\n", argv[0]);
+ return 1;
+}
--
⑨