diff options
| author | info@mode42.com <info@mode42.com> | 2026-08-09 11:54:17 +0000 |
|---|---|---|
| committer | info@mode42.com <info@mode42.com> | 2026-08-09 11:54:17 +0000 |
| commit | a989bd16c40e04bba5a5404b0ef0cd22f19d2707 (patch) | |
| tree | 604a0439c7d8c022cbd483d62bdc2cfc1c8d18f8 /plugins/entertain/chess_cmd.c | |
| parent | a72e5ea7558e5172c08d397d6f1e0e0f9273e694 (diff) | |
now including entertain/chess
Diffstat (limited to 'plugins/entertain/chess_cmd.c')
| -rw-r--r-- | plugins/entertain/chess_cmd.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/plugins/entertain/chess_cmd.c b/plugins/entertain/chess_cmd.c new file mode 100644 index 0000000..3e57223 --- /dev/null +++ b/plugins/entertain/chess_cmd.c @@ -0,0 +1,277 @@ +/* + * Entertain plugin — chess_cmd.c + * /chess command handler (join/leave/list/move/resign/draw/clear/status/board). + */ +#include "entertain_chess.h" +#include "hybbx/session.h" +#include "hybbx/service.h" +#include "hybbx/command.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++; + } + return *a == *b; +} + +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]; + (void)service; + + if (!hybbx_session_logged_in(session)) { + hybbx_session_write_line(session, "Log in to play chess."); + return HYBBX_ERR_DENIED; + } + + 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); + return HYBBX_OK; + } + + /* ── status ────────────────────────────────────────────── */ + if (ieq(sub, "status")) { + snprintf(line, sizeof(line), "Chess: %u max games", chess_max_games()); + hybbx_session_write_line(session, line); + return HYBBX_OK; + } + + /* ── list ──────────────────────────────────────────────── */ + if (ieq(sub, "list") || ieq(sub, "ls")) { + unsigned found = 0; + hybbx_session_write_line(session, "Chess games:"); + for (unsigned 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"; + 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."); + 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 = 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); + hybbx_session_write_line(session, line); + hybbx_session_write_line(session, "Waiting for Black to /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; + } + } + chess_game_t *g = NULL; + if (id) { + 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; } + } else { + g = chess_find_open_game(); + if (!g) { hybbx_session_write_line(session, "No open games. /chess new"); return HYBBX_ERR_NOT_FOUND; } + } + chess_join_game(g, session, hybbx_session_username(session)); + snprintf(line, sizeof(line), "Joined %s as Black. Game on!", g->game_id); + hybbx_session_write_line(session, line); + chess_send_board(g, g->white_session); + chess_send_board(g, session); + return HYBBX_OK; + } + + /* ── watch ─────────────────────────────────────────────── */ + if (ieq(sub, "watch") || ieq(sub, "spectate")) { + const char *id = (cmd->argc >= 2) ? cmd->argv[1] : NULL; + chess_game_t *g = NULL; + if (id) { + g = chess_find_game_by_id(id); + } else { + for (unsigned i = 0; i < chess_max_games(); i++) { + chess_game_t *t = chess_get_game(i); + if (t && t->black_session) { 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); + 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++) { + chess_game_t *g = chess_get_game(i); + if (g && chess_is_spectator(g, session)) { + chess_remove_spectator(g, session); + hybbx_session_write_line(session, "Stopped watching."); + return HYBBX_OK; + } + } + hybbx_session_write_line(session, "Not watching any game."); + return HYBBX_ERR_NOT_FOUND; + } + + /* ── board ─────────────────────────────────────────────── */ + if (ieq(sub, "board") || ieq(sub, "show")) { + for (unsigned 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))) { + chess_send_board(g, session); + return HYBBX_OK; + } + } + 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; + } + + /* ── 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; } + } + if (!game) { hybbx_session_write_line(session, "Not in a game."); return HYBBX_ERR_NOT_FOUND; } + 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); + 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; } + } + 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."); + return HYBBX_OK; + } + + hybbx_session_write_line(session, "Unknown chess command. /chess help"); + return HYBBX_ERR_NOT_FOUND; +} |
