juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

commit 3528f3271236ebbbf4fce5f5f2612df62d8579b6
parent 0b40051a1a09805653363e4b6ab3176759f00312
Author: Francisco Armando Rosmino <franciscorosmino@rosmino.com.ar>
Date:   Sat,  3 Oct 2026 12:57:19 -0300

Jugar otra al terminar una partida local

Al final de la partida, 'a' (o tocar la linea en el cuadro) arma otra con las
mismas opciones, asientos y nombres, con otro reparto del mismo mapa y otros
dados (app_play devuelve 1 y app_play_again rearma la sesion). Solo partidas
locales; en GUI, TUI y web.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mcatan/core/ctrl.h | 1+
Mcatan/core/strings.h | 1+
Mcatan/gfx/screen.c | 12+++++++-----
Mcatan/linux/app.c | 43++++++++++++++++++++++++++++++++++---------
Mcatan/linux/app.h | 3++-
Mcatan/linux/main.c | 4++--
Mcatan/linux/tui.c | 10++++++++--
7 files changed, 55 insertions(+), 19 deletions(-)

diff --git a/catan/core/ctrl.h b/catan/core/ctrl.h @@ -44,6 +44,7 @@ typedef struct { int modal; /* dialogo de la app encima (salir, etc): 0 ninguno */ const char *modal_text; bool can_free; /* al terminar se puede seguir en juego libre (partida local) */ + bool can_again; /* al terminar: jugar otra con las mismas opciones y otro reparto (local) */ bool touch; /* pantalla tactil sin teclado: botones para lo que solo tenia tecla */ const char *preview; /* no NULL: vista previa del tablero antes de crear la partida (titulo); abajo van los botones PK_APP 20 mapa anterior, 21 otro reparto, diff --git a/catan/core/strings.h b/catan/core/strings.h @@ -69,6 +69,7 @@ X(S_TURN_OF, "Turno de", "Turn:") \ X(S_WINS, "gana la partida", "wins the game") \ X(S_GO_FREE, "[c] seguir jugando (libre) · Enter salir", "[c] keep playing (free) · Enter quit") \ + X(S_AGAIN, "[a] jugar otra (mismas opciones, otro reparto)", "[a] play again (same settings, new layout)") \ X(S_WON_FREE, "ganó · juego libre", "won · free play") \ X(S_ROLLED, "tiró", "rolled") \ X(S_BUILT, "construyó", "built") \ diff --git a/catan/gfx/screen.c b/catan/gfx/screen.c @@ -260,7 +260,7 @@ static void modal_text(Surface *s, int bx, int by, int bw, int bh, const char *l static void modal_over(Surface *s, const Game *g, const UiInfo *ui, int bx, int by, int bw, int bh) { - int w = bw * 4 / 5, h = 40 + g->o.np * 16 + (ui->can_free ? 16 : 0); + int w = bw * 4 / 5, h = 40 + g->o.np * 16 + (ui->can_free ? 16 : 0) + (ui->can_again ? 16 : 0); int x = bx + (bw - w) / 2, y = by + (bh - h) / 2; box(s, x, y, w, h, C_PANEL, C_ACCENT); char buf[64] = ""; @@ -277,7 +277,8 @@ static void modal_over(Surface *s, const Game *g, const UiInfo *ui, int bx, int gfx_text(s, &font_bold, x + w - 60, ly - 1, v, C_ACCENT); ly += 16; } - if (ui->can_free) center_text(s, &font_normal, x + 4, ly + 2, w - 8, tr(S_GO_FREE), C_OK); + if (ui->can_free) { center_text(s, &font_normal, x + 4, ly + 2, w - 8, tr(S_GO_FREE), C_OK); ly += 16; } + if (ui->can_again) center_text(s, &font_normal, x + 4, ly + 2, w - 8, tr(S_AGAIN), C_ACCENT); } static void modal_help(Surface *s, int bx, int by, int bw, int bh) @@ -319,10 +320,11 @@ static void modals(Surface *s, const Game *g, const Ctrl *c, const UiInfo *ui, i hit_add(hits, bx, by, bw, bh, PK_ITEM, 0); } else if (c->mode == CM_OVER || g->phase == PH_OVER) modal_over(s, g, ui, bx, by, bw, bh); if (c->mode == CM_OVER || g->phase == PH_OVER) { /* tocar el cuadro: volver (9); la ultima linea: juego libre (10) */ - int w = bw * 4 / 5, h = 40 + g->o.np * 16 + (ui->can_free ? 16 : 0); - int x = bx + (bw - w) / 2, y = by + (bh - h) / 2; + int w = bw * 4 / 5, h = 40 + g->o.np * 16 + (ui->can_free ? 16 : 0) + (ui->can_again ? 16 : 0); + int x = bx + (bw - w) / 2, y = by + (bh - h) / 2, ly = y + h - 2; hit_add(hits, x, y, w, h, PK_APP, 9); - if (ui->can_free) hit_add(hits, x, y + h - 18, w, 18, PK_APP, 10); + if (ui->can_again) { hit_add(hits, x, ly - 16, w, 16, PK_APP, 14); ly -= 16; } + if (ui->can_free) hit_add(hits, x, ly - 16, w, 16, PK_APP, 10); } if (ui->show_help) modal_help(s, bx, by, bw, bh); if (ui->modal == 2) { /* menu de la app (tactil): ayuda, salir */ diff --git a/catan/linux/app.c b/catan/linux/app.c @@ -267,7 +267,27 @@ static bool picking(const Ctrl *c) return c->mode == CM_VERT || c->mode == CM_EDGE || c->mode == CM_HEX || c->mode == CM_MENU || c->mode == CM_CARDS; } -void app_play(Frontend *fe, Session *s, Settings *st) +/* "Jugar otra": las mismas opciones, asientos y nombres de la partida local que + * termino, con otro reparto del tablero (y otros dados). */ +void app_play_again(Session *s, Settings *st) +{ + Opts o = s->g->o; + uint8_t kinds[MAXP]; + char names[MAXP][20]; + for (int i = 0; i < MAXP; i++) { + kinds[i] = s->host.kind[i] == SEAT_REMOTE ? SEAT_LOCAL : s->host.kind[i]; + snprintf(names[i], sizeof names[i], "%s", s->names[i] ? s->names[i] : ""); + } + Rng r; + rng_seed(&r, fresh_seed(), 0x3A9); + if (!o.beginner) o.bseed = 1 + rng_below(&r, 0x7FFFFFFE); /* otro reparto del mismo mapa */ + sess_close(s); + sess_new_local(s, &o, fresh_seed(), kinds, names); + s->handoff = st->handoff; + sess_set_bots(s, st->bot_hard); +} + +int app_play(Frontend *fe, Session *s, Settings *st) { Ctrl c; ctrl_init(&c, s->local_mask, s->handoff); @@ -299,13 +319,14 @@ void app_play(Frontend *fe, Session *s, Settings *st) ui.show_help = help; ui.touch = fe->touch; ui.can_free = s->local && !s->net && s->save_path[0] && s->g->winner >= 0; + ui.can_again = s->local && !s->net && s->g->phase == PH_OVER; fe->draw_game(fe, s->g, &c, &ui); int fds[16]; int nf = sess_fds(s, fds, 16); InEvent ev; if (!fe->wait(fe, sess_timeout(s), fds, nf, &ev)) continue; - if (ev.type == EV_QUIT) return; + if (ev.type == EV_QUIT) return 0; Move m; if (ev.type == EV_WHEEL) { const Hit *h = hit_find(&fe->hits, ev.x, ev.y); @@ -326,7 +347,7 @@ void app_play(Frontend *fe, Session *s, Settings *st) if (ev.type == EV_CLICK && !modal && !help) { /* botones de la pantalla tactil: valen como teclas */ const Hit *h = hit_find(&fe->hits, ev.x, ev.y); if (h && h->kind == PK_APP) { - int k = h->id == 7 ? K_BACK : h->id == 9 ? K_OK : h->id == 10 ? 'c' : 0; + int k = h->id == 7 ? K_BACK : h->id == 9 ? K_OK : h->id == 10 ? 'c' : h->id == 14 ? 'a' : 0; if (k) { ev.type = EV_KEY; ev.key = k; typing = false; } } } @@ -366,13 +387,13 @@ void app_play(Frontend *fe, Session *s, Settings *st) if (ev.type == EV_KEY) { int k = ev.key; if (modal) { - if (modal == 1 && (k == 's' || k == 'S' || k == 'y' || k == 'Y' || k == K_OK)) return; + if (modal == 1 && (k == 's' || k == 'S' || k == 'y' || k == 'Y' || k == K_OK)) return 0; modal = 0; continue; } if (help) { help = false; continue; } if (k == 'q' || k == 'Q') { - if (s->g->phase == PH_OVER) return; + if (s->g->phase == PH_OVER) return 0; modal = 1; continue; } @@ -383,7 +404,8 @@ void app_play(Frontend *fe, Session *s, Settings *st) ctrl_init(&c, s->local_mask, s->handoff); continue; } - if (s->g->phase == PH_OVER && (k == K_OK || k == K_BACK)) return; + if (s->g->phase == PH_OVER && (k == 'a' || k == 'A') && ui.can_again) return 1; /* jugar otra */ + if (s->g->phase == PH_OVER && (k == K_OK || k == K_BACK)) return 0; if (k == '?') { help = true; continue; } if (k == 'L') { @@ -405,7 +427,7 @@ void app_play(Frontend *fe, Session *s, Settings *st) if (modal) { const Hit *h = hit_find(&fe->hits, ev.x, ev.y); int id = h && h->kind == PK_APP ? h->id : -1; - if (modal == 1 && id == 8) return; /* "si" del dialogo de salir */ + if (modal == 1 && id == 8) return 0; /* "si" del dialogo de salir */ if (modal == 2 && id == 1) { modal = 0; help = true; continue; } /* menu de la app (tactil) */ if (modal == 2 && id == 0) { modal = 1; continue; } modal = 0; @@ -725,14 +747,17 @@ void app_menu(Frontend *fe, Settings *st) static Session s; switch (r) { case M_NEW: - if (new_game(fe, st, &s)) { app_play(fe, &s, st); sess_close(&s); } + if (new_game(fe, st, &s)) { + while (app_play(fe, &s, st) == 1) app_play_again(&s, st); + sess_close(&s); + } break; case M_CONTINUE: { char err[128] = ""; if (have_save && sess_resume(&s, last, err, sizeof err)) { s.handoff = st->handoff; sess_set_bots(&s, st->bot_hard); - app_play(fe, &s, st); + while (app_play(fe, &s, st) == 1) app_play_again(&s, st); sess_close(&s); } break; diff --git a/catan/linux/app.h b/catan/linux/app.h @@ -27,7 +27,8 @@ int app_sim(int n, uint64_t seed0, int players, bool v2p); extern int g_sim_scenario, g_sim_map; int app_duel(int n, uint64_t seed0, int players, bool v2p, const SearchCfg *cfg); void app_menu(Frontend *fe, Settings *st); -void app_play(Frontend *fe, Session *s, Settings *st); +int app_play(Frontend *fe, Session *s, Settings *st); /* 1: "jugar otra" (ver app_play_again) */ +void app_play_again(Session *s, Settings *st); /* misma partida local, otro reparto y otros dados */ int run_form(Frontend *fe, Form *f, void (*on_change)(Form *, void *), void *ctx); uint64_t fresh_seed(void); diff --git a/catan/linux/main.c b/catan/linux/main.c @@ -117,7 +117,7 @@ int main(int argc, char **argv) sess_new_local(&s, &o, fresh_seed(), kinds, names); sess_set_bots(&s, st.bot_hard); s.handoff = st.handoff; - app_play(fe, &s, &st); + while (app_play(fe, &s, &st) == 1) app_play_again(&s, &st); sess_close(&s); } else if (!strcmp(cmd, "resume")) { char path[512], err[160] = ""; @@ -126,7 +126,7 @@ int main(int argc, char **argv) if (!sess_resume(&s, path, err, sizeof err)) { fe->close(fe); fprintf(stderr, "%s: %s\n", path, err); return 1; } sess_set_bots(&s, st.bot_hard); s.handoff = st.handoff; - app_play(fe, &s, &st); + while (app_play(fe, &s, &st) == 1) app_play_again(&s, &st); sess_close(&s); } else if (!strcmp(cmd, "online")) { online_menu(fe, &st, nargs ? args[0] : st.server); diff --git a/catan/linux/tui.c b/catan/linux/tui.c @@ -404,7 +404,7 @@ static void draw_cards_modal(Frontend *f, const Game *g, const Ctrl *c, int bx, static void draw_over(const Game *g, const UiInfo *ui, int bx, int by, int bw, int bh) { - int w = 48, h = g->o.np + 6 + (ui->can_free ? 2 : 0); + int w = 48, h = g->o.np + 6 + (ui->can_free ? 2 : 0) + (ui->can_again ? 1 : 0); int x = bx + (bw - w) / 2, y = by + (bh - h) / 2; tk_box(y, x, h, w, tr(S_P_OVER), C_ACCENT); char buf[96]; @@ -421,9 +421,15 @@ static void draw_over(const Game *g, const UiInfo *ui, int bx, int by, int bw, i tk_att(255, 236, p == g->winner ? A_BOLD : 0); tk_put(y + 4 + p, x + 6, buf, w - 8); } + int ly = y + 5 + g->o.np; if (ui->can_free) { tk_att(tk_xterm(C_OK), 236, 0); - tk_put(y + 5 + g->o.np, x + (w - tk_ulen(tr(S_GO_FREE))) / 2, tr(S_GO_FREE), w - 4); + tk_put(ly, x + (w - tk_ulen(tr(S_GO_FREE))) / 2, tr(S_GO_FREE), w - 4); + ly++; + } + if (ui->can_again) { + tk_att(tk_xterm(C_ACCENT), 236, 0); + tk_put(ly, x + (w - tk_ulen(tr(S_AGAIN))) / 2, tr(S_AGAIN), w - 4); } }