summaryrefslogtreecommitdiff
path: root/plugins/entertain/chess_cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/entertain/chess_cmd.c')
-rw-r--r--plugins/entertain/chess_cmd.c500
1 files changed, 349 insertions, 151 deletions
diff --git a/plugins/entertain/chess_cmd.c b/plugins/entertain/chess_cmd.c
index 3e57223..fdfcfed 100644
--- a/plugins/entertain/chess_cmd.c
+++ b/plugins/entertain/chess_cmd.c
@@ -1,39 +1,201 @@
/*
* Entertain plugin — chess_cmd.c
- * /chess command handler (join/leave/list/move/resign/draw/clear/status/board).
+ * /chess · /play · /mv command handler.
*/
#include "entertain_chess.h"
#include "hybbx/session.h"
#include "hybbx/service.h"
#include "hybbx/command.h"
+#include <ctype.h>
#include <stdio.h>
#include <string.h>
-#include <strings.h>
-/* Forward declaration */
hybbx_result_t entertain_cmd_chess(struct hybbx_service *service,
struct hybbx_session *session,
const struct hybbx_parsed_command *cmd);
static int ieq(const char *a, const char *b)
{
- if (!a || !b) return 0;
- while (*a && *b) {
- char ca = (*a >= 'A' && *a <= 'Z') ? *a + 32 : *a;
- char cb = (*b >= 'A' && *b <= 'Z') ? *b + 32 : *b;
- if (ca != cb) return 0;
- a++; b++;
+ if (a == NULL || b == NULL) {
+ return 0;
+ }
+ while (*a != '\0' && *b != '\0') {
+ char ca = (*a >= 'A' && *a <= 'Z') ? (char)(*a + 32) : *a;
+ char cb = (*b >= 'A' && *b <= 'Z') ? (char)(*b + 32) : *b;
+
+ if (ca != cb) {
+ return 0;
+ }
+ a++;
+ b++;
}
return *a == *b;
}
+static void chess_help(hybbx_session_t *session)
+{
+ char line[96];
+
+ hybbx_session_write_line(session, "Chess (Entertain area):");
+ hybbx_session_write_line(session, " /chess new Create game (you = White)");
+ hybbx_session_write_line(session, " /chess join [id] Join as Black");
+ hybbx_session_write_line(session, " /chess watch [id] Spectate");
+ hybbx_session_write_line(session, " /chess board Show your board");
+ hybbx_session_write_line(session, " /chess move e2 e4 Move (or e2e4)");
+ hybbx_session_write_line(session, " /mv e2 e4 Move alias");
+ hybbx_session_write_line(session, " /chess move e7 e8 q Promote (q/r/b/n)");
+ hybbx_session_write_line(session, " /chess resign Resign");
+ hybbx_session_write_line(session, " /chess draw Offer / accept draw");
+ hybbx_session_write_line(session, " /chess list List games");
+ hybbx_session_write_line(session, " /chess leave Stop spectating");
+ hybbx_session_write_line(session, " /chess clear Free finished game slots");
+ hybbx_session_write_line(session, " /play … Alias for /chess");
+ snprintf(line, sizeof(line),
+ "Notes: no castling/en passant. Spectator refresh %us.",
+ CHESS_REFRESH_SEC);
+ hybbx_session_write_line(session, line);
+}
+
+static void announce_result(chess_game_t *g)
+{
+ char line[128];
+ const char *res;
+ unsigned j;
+
+ if (g == NULL || g->result == CHESS_RESULT_NONE) {
+ return;
+ }
+ switch (g->result) {
+ case CHESS_WHITE_WINS:
+ res = "White wins";
+ break;
+ case CHESS_BLACK_WINS:
+ res = "Black wins";
+ break;
+ case CHESS_DRAW_STALEMATE:
+ res = "Stalemate";
+ break;
+ case CHESS_DRAW_AGREEMENT:
+ res = "Draw agreed";
+ break;
+ default:
+ res = "Game over";
+ break;
+ }
+ snprintf(line, sizeof(line), "[%s] %s", g->game_id, res);
+ if (g->white_session != NULL) {
+ hybbx_session_write_line(g->white_session, line);
+ }
+ if (g->black_session != NULL) {
+ hybbx_session_write_line(g->black_session, line);
+ }
+ for (j = 0; j < g->spectator_count; j++) {
+ hybbx_session_write_line(g->spectators[j], line);
+ }
+}
+
+static int parse_move_args(const hybbx_parsed_command_t *cmd, unsigned start,
+ char *from, size_t from_len, char *to, size_t to_len,
+ char *promo)
+{
+ const char *a;
+ const char *b;
+
+ *promo = 0;
+ if (cmd == NULL || from == NULL || to == NULL || from_len < 3 || to_len < 3) {
+ return 0;
+ }
+
+ if (cmd->argc < start + 1) {
+ return 0;
+ }
+ a = cmd->argv[start];
+ if (a == NULL) {
+ return 0;
+ }
+
+ /* Compact: e2e4 or e7e8q */
+ if (strlen(a) >= 4 && cmd->argc == start + 1) {
+ if (!isalpha((unsigned char)a[0]) || !isdigit((unsigned char)a[1]) ||
+ !isalpha((unsigned char)a[2]) || !isdigit((unsigned char)a[3])) {
+ return 0;
+ }
+ from[0] = (char)tolower((unsigned char)a[0]);
+ from[1] = a[1];
+ from[2] = '\0';
+ to[0] = (char)tolower((unsigned char)a[2]);
+ to[1] = a[3];
+ to[2] = '\0';
+ if (a[4] != '\0') {
+ *promo = a[4];
+ }
+ return 1;
+ }
+
+ if (cmd->argc < start + 2) {
+ return 0;
+ }
+ b = cmd->argv[start + 1];
+ if (b == NULL) {
+ return 0;
+ }
+ snprintf(from, from_len, "%s", a);
+ snprintf(to, to_len, "%s", b);
+ if (cmd->argc >= start + 3 && cmd->argv[start + 2] != NULL &&
+ cmd->argv[start + 2][0] != '\0') {
+ *promo = cmd->argv[start + 2][0];
+ }
+ return 1;
+}
+
+static hybbx_result_t do_move(hybbx_session_t *session,
+ const hybbx_parsed_command_t *cmd,
+ unsigned arg_start)
+{
+ chess_game_t *game;
+ char from[8];
+ char to[8];
+ char promo = 0;
+ char err[64];
+ char line[96];
+
+ if (!parse_move_args(cmd, arg_start, from, sizeof(from), to, sizeof(to),
+ &promo)) {
+ hybbx_session_write_line(session,
+ "Usage: /chess move e2 e4 | /mv e2 e4 | /mv e2e4");
+ return HYBBX_ERR_INVALID;
+ }
+
+ game = chess_find_player_game(session);
+ if (game == NULL || game->result != CHESS_RESULT_NONE) {
+ hybbx_session_write_line(session, "No active game. /chess new or /chess join");
+ return HYBBX_ERR_DENIED;
+ }
+ if (chess_player_color(game, session) != game->turn) {
+ hybbx_session_write_line(session, "Not your turn.");
+ return HYBBX_ERR_DENIED;
+ }
+ if (!chess_make_move(game, from, to, promo, err, sizeof(err))) {
+ snprintf(line, sizeof(line), "Illegal: %s", err);
+ hybbx_session_write_line(session, line);
+ return HYBBX_ERR_INVALID;
+ }
+ chess_broadcast_board(game);
+ if (game->result != CHESS_RESULT_NONE) {
+ announce_result(game);
+ }
+ return HYBBX_OK;
+}
+
hybbx_result_t entertain_cmd_chess(struct hybbx_service *service,
struct hybbx_session *session,
const struct hybbx_parsed_command *cmd)
{
const char *sub;
- char line[128];
+ char line[160];
+ int mv_shortcut = 0;
+
(void)service;
if (!hybbx_session_logged_in(session)) {
@@ -41,134 +203,168 @@ hybbx_result_t entertain_cmd_chess(struct hybbx_service *service,
return HYBBX_ERR_DENIED;
}
- sub = (cmd->argc >= 1) ? cmd->argv[0] : "help";
+ /* /mv … → treat as move with args at argv[0..] */
+ if (cmd->verb != NULL && ieq(cmd->verb, "mv")) {
+ mv_shortcut = 1;
+ sub = "move";
+ } else {
+ sub = (cmd->argc >= 1) ? cmd->argv[0] : "help";
+ }
- /* ── help ──────────────────────────────────────────────── */
if (ieq(sub, "help") || ieq(sub, "?")) {
- hybbx_session_write_line(session, "Chess commands:");
- hybbx_session_write_line(session, " /chess new - Create game (you are White)");
- hybbx_session_write_line(session, " /chess join [id] - Join game as Black");
- hybbx_session_write_line(session, " /chess watch [id] - Spectate");
- hybbx_session_write_line(session, " /chess board - Show board");
- hybbx_session_write_line(session, " /chess move e2 e4 - Move (alias: /mv e2 e4)");
- hybbx_session_write_line(session, " /chess resign - Resign");
- hybbx_session_write_line(session, " /chess draw - Offer draw");
- hybbx_session_write_line(session, " /chess list - List games + boards");
- hybbx_session_write_line(session, " /chess leave - Stop watching");
- hybbx_session_write_line(session, " /chess clear - Clear game history");
- snprintf(line, sizeof(line), "Board refreshes every %ds for all.", CHESS_REFRESH_SEC);
- hybbx_session_write_line(session, line);
+ chess_help(session);
return HYBBX_OK;
}
- /* ── status ────────────────────────────────────────────── */
if (ieq(sub, "status")) {
- snprintf(line, sizeof(line), "Chess: %u max games", chess_max_games());
+ snprintf(line, sizeof(line), "Chess: max %u concurrent games",
+ chess_max_games());
hybbx_session_write_line(session, line);
return HYBBX_OK;
}
- /* ── list ──────────────────────────────────────────────── */
if (ieq(sub, "list") || ieq(sub, "ls")) {
unsigned found = 0;
+ unsigned i;
+
hybbx_session_write_line(session, "Chess games:");
- for (unsigned i = 0; i < chess_max_games(); i++) {
+ for (i = 0; i < chess_max_games(); i++) {
chess_game_t *g = chess_get_game(i);
- if (!g) continue;
- const char *st = (g->result != CHESS_RESULT_NONE) ? "finished" :
- (!g->black_session) ? "waiting" : "playing";
+ const char *st;
+
+ if (g == NULL) {
+ continue;
+ }
+ if (g->result != CHESS_RESULT_NONE) {
+ st = "finished";
+ } else if (g->black_session == NULL) {
+ st = "waiting";
+ } else {
+ st = "playing";
+ }
snprintf(line, sizeof(line), " [%s] %s(W) vs %s(B) %s move %u",
g->game_id, g->white_name,
g->black_session ? g->black_name : "...",
st, g->move_number);
hybbx_session_write_line(session, line);
- chess_send_board(g, session);
- if (g->spectator_count > 0) {
- int n = snprintf(line, sizeof(line), " Spectators (%u): ", g->spectator_count);
- for (unsigned j = 0; j < g->spectator_count; j++) {
- const char *nm = hybbx_session_display_name(g->spectators[j]);
- if (j > 0) n += snprintf(line+n, sizeof(line)-(size_t)n, ", ");
- n += snprintf(line+n, sizeof(line)-(size_t)n, "%s", nm ? nm : "?");
- }
- hybbx_session_write_line(session, line);
- }
- hybbx_session_write_line(session, "");
found++;
}
- if (!found) hybbx_session_write_line(session, " No active games.");
+ if (!found) {
+ hybbx_session_write_line(session, " No active games.");
+ }
return HYBBX_OK;
}
- /* ── new ───────────────────────────────────────────────── */
if (ieq(sub, "new") || ieq(sub, "create")) {
- for (unsigned i = 0; i < chess_max_games(); i++) {
- chess_game_t *g = chess_get_game(i);
- if (g && chess_is_player(g, session)) {
- hybbx_session_write_line(session, "Already in a game. /chess resign first.");
- return HYBBX_ERR_DENIED;
- }
+ chess_game_t *g;
+
+ if (chess_find_player_game(session) != NULL) {
+ hybbx_session_write_line(session,
+ "Already in a game. /chess resign or wait for finish + /chess clear");
+ return HYBBX_ERR_DENIED;
}
- chess_game_t *g = chess_create_game(session, hybbx_session_username(session));
- if (!g) { hybbx_session_write_line(session, "No free game slots."); return HYBBX_ERR_DENIED; }
- snprintf(line, sizeof(line), "Game %s created. You are White.", g->game_id);
+ g = chess_create_game(session, hybbx_session_username(session));
+ if (g == NULL) {
+ hybbx_session_write_line(session,
+ "No free game slots. /chess clear to free finished games.");
+ return HYBBX_ERR_DENIED;
+ }
+ snprintf(line, sizeof(line), "Game %s created. You are White.",
+ g->game_id);
hybbx_session_write_line(session, line);
- hybbx_session_write_line(session, "Waiting for Black to /chess join...");
+ hybbx_session_write_line(session, "Waiting for Black: /chess join");
chess_send_board(g, session);
return HYBBX_OK;
}
- /* ── join ──────────────────────────────────────────────── */
if (ieq(sub, "join")) {
- const char *id = (cmd->argc >= 2) ? cmd->argv[1] : NULL;
- for (unsigned i = 0; i < chess_max_games(); i++) {
- chess_game_t *g = chess_get_game(i);
- if (g && chess_is_player(g, session)) {
- hybbx_session_write_line(session, "Already in a game. /chess resign first.");
- return HYBBX_ERR_DENIED;
- }
+ const char *id = (!mv_shortcut && cmd->argc >= 2) ? cmd->argv[1] : NULL;
+ chess_game_t *g;
+
+ if (chess_find_player_game(session) != NULL) {
+ hybbx_session_write_line(session, "Already in a game.");
+ return HYBBX_ERR_DENIED;
}
- chess_game_t *g = NULL;
- if (id) {
+ if (id != NULL) {
g = chess_find_game_by_id(id);
- if (!g || !g->active) { hybbx_session_write_line(session, "Game not found."); return HYBBX_ERR_NOT_FOUND; }
- if (g->black_session) { hybbx_session_write_line(session, "Game is full."); return HYBBX_ERR_DENIED; }
+ if (g == NULL || !g->active) {
+ hybbx_session_write_line(session, "Game not found.");
+ return HYBBX_ERR_NOT_FOUND;
+ }
+ if (g->black_session != NULL) {
+ hybbx_session_write_line(session, "Game is full.");
+ return HYBBX_ERR_DENIED;
+ }
+ if (g->result != CHESS_RESULT_NONE) {
+ hybbx_session_write_line(session, "Game already finished.");
+ return HYBBX_ERR_DENIED;
+ }
} else {
g = chess_find_open_game();
- if (!g) { hybbx_session_write_line(session, "No open games. /chess new"); return HYBBX_ERR_NOT_FOUND; }
+ if (g == NULL) {
+ hybbx_session_write_line(session, "No open games. /chess new");
+ return HYBBX_ERR_NOT_FOUND;
+ }
+ }
+ if (!chess_join_game(g, session, hybbx_session_username(session))) {
+ hybbx_session_write_line(session, "Cannot join that game.");
+ return HYBBX_ERR_DENIED;
}
- chess_join_game(g, session, hybbx_session_username(session));
- snprintf(line, sizeof(line), "Joined %s as Black. Game on!", g->game_id);
+ snprintf(line, sizeof(line), "Joined %s as Black. White to move.",
+ g->game_id);
hybbx_session_write_line(session, line);
- chess_send_board(g, g->white_session);
- chess_send_board(g, session);
+ if (g->white_session != NULL) {
+ snprintf(line, sizeof(line), "[%s] %s joined as Black.",
+ g->game_id, g->black_name);
+ hybbx_session_write_line(g->white_session, line);
+ }
+ chess_broadcast_board(g);
return HYBBX_OK;
}
- /* ── watch ─────────────────────────────────────────────── */
if (ieq(sub, "watch") || ieq(sub, "spectate")) {
- const char *id = (cmd->argc >= 2) ? cmd->argv[1] : NULL;
+ const char *id = (!mv_shortcut && cmd->argc >= 2) ? cmd->argv[1] : NULL;
chess_game_t *g = NULL;
- if (id) {
+
+ if (id != NULL) {
g = chess_find_game_by_id(id);
} else {
- for (unsigned i = 0; i < chess_max_games(); i++) {
+ unsigned i;
+
+ for (i = 0; i < chess_max_games(); i++) {
chess_game_t *t = chess_get_game(i);
- if (t && t->black_session) { g = t; break; }
+
+ if (t != NULL && t->black_session != NULL &&
+ t->result == CHESS_RESULT_NONE) {
+ g = t;
+ break;
+ }
}
}
- if (!g || !g->active) { hybbx_session_write_line(session, "No game to watch."); return HYBBX_ERR_NOT_FOUND; }
- if (!chess_add_spectator(g, session)) { hybbx_session_write_line(session, "Spectator list full."); return HYBBX_ERR_DENIED; }
- snprintf(line, sizeof(line), "Watching %s. Board refreshes every %ds.", g->game_id, CHESS_REFRESH_SEC);
+ if (g == NULL || !g->active) {
+ hybbx_session_write_line(session, "No game to watch.");
+ return HYBBX_ERR_NOT_FOUND;
+ }
+ if (!chess_add_spectator(g, session)) {
+ hybbx_session_write_line(session,
+ "Cannot watch (player or spectator list full).");
+ return HYBBX_ERR_DENIED;
+ }
+ snprintf(line, sizeof(line),
+ "Watching %s. Board refreshes every %us for spectators.",
+ g->game_id, CHESS_REFRESH_SEC);
hybbx_session_write_line(session, line);
chess_send_board(g, session);
return HYBBX_OK;
}
- /* ── leave ─────────────────────────────────────────────── */
if (ieq(sub, "leave") || ieq(sub, "unwatch")) {
- for (unsigned i = 0; i < chess_max_games(); i++) {
+ unsigned i;
+
+ for (i = 0; i < chess_max_games(); i++) {
chess_game_t *g = chess_get_game(i);
- if (g && chess_is_spectator(g, session)) {
+
+ if (g != NULL && chess_is_spectator(g, session)) {
chess_remove_spectator(g, session);
hybbx_session_write_line(session, "Stopped watching.");
return HYBBX_OK;
@@ -178,97 +374,99 @@ hybbx_result_t entertain_cmd_chess(struct hybbx_service *service,
return HYBBX_ERR_NOT_FOUND;
}
- /* ── board ─────────────────────────────────────────────── */
if (ieq(sub, "board") || ieq(sub, "show")) {
- for (unsigned i = 0; i < chess_max_games(); i++) {
+ unsigned i;
+
+ for (i = 0; i < chess_max_games(); i++) {
chess_game_t *g = chess_get_game(i);
- if (g && (chess_is_player(g, session) || chess_is_spectator(g, session))) {
+
+ if (g != NULL &&
+ (chess_is_player(g, session) || chess_is_spectator(g, session))) {
chess_send_board(g, session);
return HYBBX_OK;
}
}
- hybbx_session_write_line(session, "Not in a game. /chess new, join, or watch.");
+ hybbx_session_write_line(session,
+ "Not in a game. /chess new, join, or watch.");
return HYBBX_ERR_NOT_FOUND;
}
- /* ── move ──────────────────────────────────────────────── */
- if (ieq(sub, "move") || ieq(sub, "mv") || ieq(sub, "m")) {
- if (cmd->argc < 3) { hybbx_session_write_line(session, "Usage: /chess move e2 e4"); return HYBBX_ERR_INVALID; }
- chess_game_t *game = NULL;
- for (unsigned i = 0; i < chess_max_games(); i++) {
- chess_game_t *g = chess_get_game(i);
- if (g && g->result == CHESS_RESULT_NONE && chess_is_player(g, session)) {
- if (chess_player_color(g, session) == g->turn) { game = g; break; }
- }
- }
- if (!game) { hybbx_session_write_line(session, "Not your turn or no active game."); return HYBBX_ERR_DENIED; }
- char err[64];
- if (!chess_make_move(game, cmd->argv[1], cmd->argv[2], 0, err, sizeof(err))) {
- snprintf(line, sizeof(line), "Illegal: %s", err);
- hybbx_session_write_line(session, line);
- return HYBBX_ERR_INVALID;
- }
- /* Broadcast to all */
- if (game->white_session) chess_send_board(game, game->white_session);
- if (game->black_session) chess_send_board(game, game->black_session);
- for (unsigned j = 0; j < game->spectator_count; j++)
- chess_send_board(game, game->spectators[j]);
- /* Game over? */
- if (game->result != CHESS_RESULT_NONE) {
- const char *res;
- switch (game->result) {
- case CHESS_WHITE_WINS: res = "White wins!"; break;
- case CHESS_BLACK_WINS: res = "Black wins!"; break;
- case CHESS_DRAW_STALEMATE: res = "Stalemate!"; break;
- case CHESS_DRAW_AGREEMENT: res = "Draw!"; break;
- default: res = "Game over."; break;
- }
- snprintf(line, sizeof(line), "[%s] %s", game->game_id, res);
- if (game->white_session) hybbx_session_write_line(game->white_session, line);
- if (game->black_session) hybbx_session_write_line(game->black_session, line);
- for (unsigned j = 0; j < game->spectator_count; j++)
- hybbx_session_write_line(game->spectators[j], line);
- }
- return HYBBX_OK;
+ if (ieq(sub, "move") || ieq(sub, "m")) {
+ return do_move(session, cmd, mv_shortcut ? 0u : 1u);
}
- /* ── resign ────────────────────────────────────────────── */
if (ieq(sub, "resign")) {
- chess_game_t *game = NULL;
- for (unsigned i = 0; i < chess_max_games(); i++) {
- chess_game_t *g = chess_get_game(i);
- if (g && chess_is_player(g, session)) { game = g; break; }
+ chess_game_t *game = chess_find_player_game(session);
+ const char *who;
+
+ if (game == NULL) {
+ hybbx_session_write_line(session, "Not in a game.");
+ return HYBBX_ERR_NOT_FOUND;
+ }
+ if (game->result != CHESS_RESULT_NONE) {
+ hybbx_session_write_line(session, "Game already finished.");
+ return HYBBX_ERR_DENIED;
}
- if (!game) { hybbx_session_write_line(session, "Not in a game."); return HYBBX_ERR_NOT_FOUND; }
+ who = hybbx_session_username(session);
chess_resign(game, session);
- snprintf(line, sizeof(line), "[%s] %s resigned.", game->game_id, hybbx_session_username(session));
- if (game->white_session) hybbx_session_write_line(game->white_session, line);
- if (game->black_session) hybbx_session_write_line(game->black_session, line);
- for (unsigned j = 0; j < game->spectator_count; j++)
- hybbx_session_write_line(game->spectators[j], line);
+ snprintf(line, sizeof(line), "[%s] %s resigned.", game->game_id,
+ who != NULL ? who : "?");
+ if (game->white_session != NULL) {
+ hybbx_session_write_line(game->white_session, line);
+ }
+ if (game->black_session != NULL) {
+ hybbx_session_write_line(game->black_session, line);
+ }
+ {
+ unsigned j;
+
+ for (j = 0; j < game->spectator_count; j++) {
+ hybbx_session_write_line(game->spectators[j], line);
+ }
+ }
+ announce_result(game);
return HYBBX_OK;
}
- /* ── draw ──────────────────────────────────────────────── */
if (ieq(sub, "draw")) {
- chess_game_t *game = NULL;
- for (unsigned i = 0; i < chess_max_games(); i++) {
- chess_game_t *g = chess_get_game(i);
- if (g && chess_is_player(g, session)) { game = g; break; }
+ chess_game_t *game = chess_find_player_game(session);
+ struct hybbx_session *other;
+ int agreed;
+
+ if (game == NULL) {
+ hybbx_session_write_line(session, "Not in a game.");
+ return HYBBX_ERR_NOT_FOUND;
+ }
+ if (game->result != CHESS_RESULT_NONE) {
+ hybbx_session_write_line(session, "Game already finished.");
+ return HYBBX_ERR_DENIED;
+ }
+ agreed = chess_offer_draw(game, session);
+ if (agreed) {
+ announce_result(game);
+ chess_broadcast_board(game);
+ return HYBBX_OK;
+ }
+ hybbx_session_write_line(session, "Draw offered. Opponent: /chess draw");
+ other = (game->white_session == session) ? game->black_session
+ : game->white_session;
+ if (other != NULL) {
+ snprintf(line, sizeof(line),
+ "[%s] Draw offered by %s — /chess draw to accept",
+ game->game_id,
+ hybbx_session_username(session)
+ ? hybbx_session_username(session)
+ : "?");
+ hybbx_session_write_line(other, line);
}
- if (!game) { hybbx_session_write_line(session, "Not in a game."); return HYBBX_ERR_NOT_FOUND; }
- chess_offer_draw(game, session);
- snprintf(line, sizeof(line), "[%s] Draw agreed.", game->game_id);
- if (game->white_session) hybbx_session_write_line(game->white_session, line);
- if (game->black_session) hybbx_session_write_line(game->black_session, line);
- for (unsigned j = 0; j < game->spectator_count; j++)
- hybbx_session_write_line(game->spectators[j], line);
return HYBBX_OK;
}
- /* ── clear ─────────────────────────────────────────────── */
if (ieq(sub, "clear") || ieq(sub, "reset")) {
- hybbx_session_write_line(session, "Chess has no persistent history to clear.");
+ unsigned n = chess_clear_finished();
+
+ snprintf(line, sizeof(line), "Cleared %u finished game(s).", n);
+ hybbx_session_write_line(session, line);
return HYBBX_OK;
}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com