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 }