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

ctrl.c (6821B)


      1 /* ctrl.c - maquina de estados de la interaccion con el tablero. */
      2 #include <string.h>
      3 #include "ctrl.h"
      4 
      5 static void msg(Ctrl *c, const char *m, bool err)
      6 {
      7     str_put(c->msg, sizeof c->msg, 0, m ? m : "");
      8     c->msg_err = err;
      9 }
     10 
     11 void ctrl_init(Ctrl *c, uint8_t local, bool flipped)
     12 {
     13     memset(c, 0, sizeof *c);
     14     c->local = local;
     15     c->flipped = flipped;
     16     c->sel = -1;
     17     c->cursor = flipped ? SQ(4, 6) : SQ(4, 1);
     18 }
     19 
     20 bool ctrl_my_turn(const Ctrl *c)
     21 {
     22     return c->g && c->g->result == R_NONE && ((c->local >> c->g->pos.side) & 1);
     23 }
     24 
     25 static void deselect(Ctrl *c)
     26 {
     27     c->sel = -1;
     28     c->ntargets = 0;
     29     if (c->mode == CM_PROMO) c->mode = CM_BOARD;
     30 }
     31 
     32 static void select_sq(Ctrl *c, int sq)
     33 {
     34     MoveList l;
     35     pos_legal(&c->g->pos, &l);
     36     c->ntargets = 0;
     37     for (int i = 0; i < l.n && c->ntargets < 32; i++)
     38         if (MFROM(l.m[i]) == sq) {
     39             /* las coronaciones se muestran una vez por casilla */
     40             bool dup = false;
     41             for (int k = 0; k < c->ntargets; k++) if (MTO(c->targets[k]) == MTO(l.m[i])) dup = true;
     42             if (!dup) c->targets[c->ntargets++] = l.m[i];
     43         }
     44     c->sel = c->ntargets ? sq : -1;
     45 }
     46 
     47 void ctrl_sync(Ctrl *c, const Game *g)
     48 {
     49     c->g = g;
     50     if (c->sel >= 0) {
     51         int s = c->sel;
     52         if (!ctrl_my_turn(c)) deselect(c);
     53         else select_sq(c, s);
     54     }
     55     if (!ctrl_my_turn(c) && c->mode != CM_ENTRY) c->mode = CM_BOARD;
     56 }
     57 
     58 void ctrl_error(Ctrl *c, int err) { msg(c, err_text(err), true); }
     59 
     60 bool ctrl_is_target(const Ctrl *c, int sq)
     61 {
     62     for (int i = 0; i < c->ntargets; i++) if (MTO(c->targets[i]) == sq) return true;
     63     return false;
     64 }
     65 
     66 int ctrl_last_move(const Ctrl *c, int *from, int *to)
     67 {
     68     if (!c->g || !c->g->nply) return 0;
     69     Move m = c->g->mv[c->g->nply - 1];
     70     *from = MFROM(m);
     71     *to = MTO(m);
     72     return 1;
     73 }
     74 
     75 const char *ctrl_prompt(const Ctrl *c)
     76 {
     77     if (!c->g) return "";
     78     if (c->mode == CM_ENTRY) return tr(S_P_ENTRY);
     79     if (c->g->result != R_NONE) return tr(S_P_OVER);
     80     if (!ctrl_my_turn(c)) return tr(S_P_WAIT);
     81     if (c->mode == CM_PROMO) return tr(S_P_PROMO);
     82     return c->sel >= 0 ? tr(S_P_TARGET) : tr(S_P_SELECT);
     83 }
     84 
     85 static bool emit_move(Ctrl *c, Move m, Act *out)
     86 {
     87     memset(out, 0, sizeof *out);
     88     out->type = ACT_MOVE;
     89     out->c = (int8_t)c->g->pos.side;
     90     out->m = m;
     91     deselect(c);
     92     msg(c, "", false);
     93     return true;
     94 }
     95 
     96 /* eleccion de destino: si es coronacion, primero se elige la pieza */
     97 static bool choose_target(Ctrl *c, int to, Act *out)
     98 {
     99     for (int i = 0; i < c->ntargets; i++) {
    100         Move m = c->targets[i];
    101         if (MTO(m) != to) continue;
    102         if (MIS_PROMO(m)) {
    103             int from = MFROM(m), capflag = MIS_CAPTURE(m) ? 4 : 0;
    104             for (int k = 0; k < 4; k++) c->promo[k] = MOVE(from, to, MF_PROMO + 3 - k + capflag);   /* Q R B N */
    105             c->promo_cur = 0;
    106             c->mode = CM_PROMO;
    107             return false;
    108         }
    109         return emit_move(c, m, out);
    110     }
    111     return false;
    112 }
    113 
    114 static bool press_square(Ctrl *c, int sq, Act *out)
    115 {
    116     if (!ctrl_my_turn(c)) { if (c->g && c->g->result == R_NONE) msg(c, err_text(E_TURN), true); return false; }
    117     uint8_t pc = c->g->pos.sq[sq];
    118     if (c->sel >= 0) {
    119         if (sq == c->sel) { deselect(c); return false; }
    120         if (ctrl_is_target(c, sq)) return choose_target(c, sq, out);
    121     }
    122     if (pc && PCOLOR(pc) == c->g->pos.side) {
    123         select_sq(c, sq);
    124         if (c->sel < 0) msg(c, tr(S_NO_MOVES), true);
    125         else msg(c, "", false);
    126         return false;
    127     }
    128     if (c->sel >= 0) msg(c, err_text(E_ILLEGAL), true);
    129     return false;
    130 }
    131 
    132 /* Tab: siguiente pieza propia con jugadas, o siguiente destino si hay una elegida */
    133 static void tab(Ctrl *c)
    134 {
    135     if (!ctrl_my_turn(c)) return;
    136     if (c->sel >= 0 && c->ntargets) {
    137         int cur = -1;
    138         for (int i = 0; i < c->ntargets; i++) if (MTO(c->targets[i]) == c->cursor) cur = i;
    139         c->cursor = MTO(c->targets[(cur + 1) % c->ntargets]);
    140         return;
    141     }
    142     MoveList l;
    143     pos_legal(&c->g->pos, &l);
    144     for (int k = 1; k <= 64; k++) {
    145         int s = (c->cursor + k) & 63;
    146         for (int i = 0; i < l.n; i++) if (MFROM(l.m[i]) == s) { c->cursor = s; return; }
    147     }
    148 }
    149 
    150 static const char PROMO_ES[] = "dtac", PROMO_EN[] = "qrbn";
    151 
    152 bool ctrl_key(Ctrl *c, int key, Act *out)
    153 {
    154     if (!c->g) return false;
    155     if (c->mode == CM_ENTRY) {
    156         int n = (int)strlen(c->entry);
    157         if (key == K_BACK) { c->mode = CM_BOARD; c->entry[0] = 0; return false; }
    158         if (key == K_BKSP) { if (n) c->entry[n - 1] = 0; return false; }
    159         if (key == K_OK) {
    160             c->mode = CM_BOARD;
    161             Move m = ctrl_my_turn(c) ? move_from_san(&c->g->pos, c->entry) : MOVE_NONE;
    162             c->entry[0] = 0;
    163             if (!ctrl_my_turn(c)) { msg(c, err_text(E_TURN), true); return false; }
    164             if (!m) { msg(c, err_text(E_ILLEGAL), true); return false; }
    165             return emit_move(c, m, out);
    166         }
    167         if (key > ' ' && key < 127 && n < (int)sizeof c->entry - 1) { c->entry[n] = (char)key; c->entry[n + 1] = 0; }
    168         return false;
    169     }
    170     if (c->mode == CM_PROMO) {
    171         const char *keys = g_lang ? PROMO_EN : PROMO_ES;
    172         const char *k = key < 128 && key > 0 ? strchr(keys, key | 0x20) : 0;
    173         if (k && *k) return emit_move(c, c->promo[k - keys], out);
    174         switch (key) {
    175         case K_LEFT: case K_UP: c->promo_cur = (c->promo_cur + 3) & 3; return false;
    176         case K_RIGHT: case K_DOWN: case K_TAB: c->promo_cur = (c->promo_cur + 1) & 3; return false;
    177         case K_OK: case ' ': return emit_move(c, c->promo[c->promo_cur], out);
    178         case K_BACK: c->mode = CM_BOARD; return false;
    179         }
    180         return false;
    181     }
    182     int f = FILE_OF(c->cursor), r = RANK_OF(c->cursor);
    183     int dir = c->flipped ? -1 : 1;
    184     switch (key) {
    185     case K_UP: case 'k': r += dir; break;
    186     case K_DOWN: case 'j': r -= dir; break;
    187     case K_LEFT: case 'h': f -= dir; break;
    188     case K_RIGHT: case 'l': f += dir; break;
    189     case K_OK: case ' ': return press_square(c, c->cursor, out);
    190     case K_BACK: if (c->sel >= 0) deselect(c); return false;
    191     case K_TAB: tab(c); return false;
    192     case ':': case '/':
    193         if (ctrl_my_turn(c)) { c->mode = CM_ENTRY; c->entry[0] = 0; }
    194         return false;
    195     default: return false;
    196     }
    197     if (f < 0) f = 0;
    198     if (f > 7) f = 7;
    199     if (r < 0) r = 0;
    200     if (r > 7) r = 7;
    201     c->cursor = SQ(f, r);
    202     return false;
    203 }
    204 
    205 bool ctrl_click(Ctrl *c, int kind, int id, Act *out)
    206 {
    207     if (kind == HIT_PROMO && c->mode == CM_PROMO && id >= 0 && id < 4) return emit_move(c, c->promo[id], out);
    208     if (kind == HIT_SQUARE && id >= 0 && id < 64) {
    209         if (c->mode == CM_PROMO) c->mode = CM_BOARD;
    210         c->cursor = id;
    211         return press_square(c, id, out);
    212     }
    213     return false;
    214 }