screen.c (18848B)
1 /* screen.c - pantalla del ajedrez en pixeles: layout ancho (PC 640x480) y 2 * compacto (PicoCalc 320x320). Registra las zonas clickeables en un HitList. */ 3 #include <string.h> 4 #include "chess_gfx.h" 5 6 const Theme THEMES[NTHEMES] = { 7 { "classic", { RGB(215, 175, 135), RGB(135, 135, 95), RGB(0, 135, 255), RGB(215, 0, 0), RGB(95, 175, 95), RGB(135, 175, 175) }, { 180, 101, 33, 160, 71, 109 } }, 8 { "ocean", { RGB(175, 215, 255), RGB(0, 95, 175), RGB(255, 215, 0), RGB(255, 0, 0), RGB(0, 255, 135), RGB(95, 255, 255) }, { 153, 25, 220, 196, 48, 87 } }, 9 { "night", { RGB(88, 88, 88), RGB(38, 38, 38), RGB(0, 95, 135), RGB(135, 0, 0), RGB(0, 95, 0), RGB(0, 95, 95) }, { 240, 235, 24, 88, 22, 23 } }, 10 { "contrast", { RGB(255, 255, 255), RGB(0, 0, 0), RGB(0, 0, 255), RGB(255, 0, 0), RGB(0, 255, 0), RGB(0, 255, 255) }, { 231, 16, 21, 196, 46, 51 } }, 11 { "colorblind", { RGB(255, 175, 95), RGB(0, 95, 175), RGB(255, 255, 0), RGB(255, 95, 0), RGB(95, 175, 255), RGB(135, 215, 255) }, { 215, 25, 226, 202, 75, 117 } }, 12 { "sunset", { RGB(255, 175, 135), RGB(135, 95, 175), RGB(0, 135, 255), RGB(215, 0, 0), RGB(0, 175, 95), RGB(135, 175, 175) }, { 216, 97, 33, 160, 35, 109 } }, 13 }; 14 15 #define C_WPIECE RGB(242, 240, 230) 16 #define C_WLINE RGB(24, 22, 20) 17 #define C_BPIECE RGB(36, 34, 38) 18 #define C_BLINE RGB(205, 205, 200) 19 #define C_BG RGB(28, 32, 40) 20 21 static void cat(char *b, int n, const char *s) { int p = (int)strlen(b); str_put(b, n, p, s); } 22 static void cati(char *b, int n, long v) { int p = (int)strlen(b); str_int(b, n, p, v); } 23 24 void fmt_clock(int32_t ms, char *buf, int n) 25 { 26 buf[0] = 0; 27 if (ms < 0) ms = 0; 28 int32_t s = (ms + 999) / 1000; 29 if (s >= 86400) { 30 cati(buf, n, s / 86400); cat(buf, n, "d "); 31 if ((s % 86400) / 3600 < 10) cat(buf, n, "0"); 32 cati(buf, n, (s % 86400) / 3600); cat(buf, n, "h"); 33 return; 34 } 35 if (s >= 3600) { cati(buf, n, s / 3600); cat(buf, n, ":"); s %= 3600; if (s / 60 < 10) cat(buf, n, "0"); } 36 cati(buf, n, s / 60); 37 cat(buf, n, s % 60 < 10 ? ":0" : ":"); 38 cati(buf, n, s % 60); 39 } 40 41 void captured_pieces(const Game *g, int out[2][7]) 42 { 43 int start[2][7], now[2][7]; 44 memset(start, 0, sizeof start); 45 memset(now, 0, sizeof now); 46 for (int s = 0; s < 64; s++) { 47 if (g->start.sq[s]) start[PCOLOR(g->start.sq[s])][PTYPE(g->start.sq[s])]++; 48 if (g->pos.sq[s]) now[PCOLOR(g->pos.sq[s])][PTYPE(g->pos.sq[s])]++; 49 } 50 for (int c = 0; c < 2; c++) 51 for (int t = 0; t < 7; t++) out[c][t] = start[c][t] > now[c][t] ? start[c][t] - now[c][t] : 0; 52 } 53 54 /* ------------------------------------------------------------ piezas */ 55 56 static const PieceSet *set_for(int sq) 57 { 58 const PieceSet *best = &PIECES[NPIECESETS - 1]; 59 for (int i = 0; i < NPIECESETS; i++) 60 if (PIECES[i].size <= sq - 2 && PIECES[i].size > best->size) best = &PIECES[i]; 61 return best; 62 } 63 64 static void draw_piece(Surface *s, const PieceSet *ps, int x, int y, int pc) 65 { 66 int t = PTYPE(pc), size = ps->size; 67 if (!t) return; 68 const uint8_t *img = ps->img[t]; 69 Color body = PCOLOR(pc) == WHITE ? C_WPIECE : C_BPIECE, line = PCOLOR(pc) == WHITE ? C_WLINE : C_BLINE; 70 int y0 = y < s->cy0 ? s->cy0 - y : 0, y1 = y + size > s->cy1 ? s->cy1 - y : size; 71 for (int j = y0; j < y1; j++) { 72 int i = 0; 73 while (i < size) { 74 int k = j * size + i; 75 int v = (img[k >> 2] >> ((k & 3) * 2)) & 3; 76 int run = 1; 77 while (i + run < size) { 78 int k2 = k + run; 79 if (((img[k2 >> 2] >> ((k2 & 3) * 2)) & 3) != v) break; 80 run++; 81 } 82 if (v) gfx_rect(s, x + i, y + j, run, 1, v == 1 ? body : line); 83 i += run; 84 } 85 } 86 } 87 88 /* ------------------------------------------------------------ tablero */ 89 90 static void sq_xy(const View *v, int sq, int bx, int by, int ss, int *x, int *y) 91 { 92 int f = FILE_OF(sq), r = RANK_OF(sq); 93 if (v->c && v->c->flipped) { f = 7 - f; r = 7 - r; } 94 *x = bx + f * ss; 95 *y = by + (7 - r) * ss; 96 } 97 98 static void board(Surface *s, const View *v, int bx, int by, int ss, HitList *hits) 99 { 100 const Theme *th = &THEMES[v->theme % NTHEMES]; 101 const Game *g = v->g; 102 const Ctrl *c = v->c; 103 int lf = -1, lt = -1; 104 if (g->nply) { lf = MFROM(g->mv[g->nply - 1]); lt = MTO(g->mv[g->nply - 1]); } 105 int check_sq = g->result == R_NONE && pos_in_check(&g->pos) ? g->pos.king[g->pos.side] : -1; 106 if (g->result == R_WHITE || g->result == R_BLACK) 107 if (g->reason == RS_MATE) check_sq = g->pos.king[g->pos.side]; 108 const PieceSet *ps = set_for(ss); 109 int off = (ss - ps->size) / 2; 110 for (int sq = 0; sq < 64; sq++) { 111 int x, y; 112 sq_xy(v, sq, bx, by, ss, &x, &y); 113 bool light = (FILE_OF(sq) + RANK_OF(sq)) & 1; 114 Color bg = light ? th->c[TH_LIGHT] : th->c[TH_DARK]; 115 if (sq == lf || sq == lt) bg = th->c[TH_PATH]; 116 if (c && sq == c->sel) bg = th->c[TH_FROM]; 117 if (sq == check_sq) bg = th->c[TH_CAPTURE]; 118 gfx_rect(s, x, y, ss, ss, bg); 119 if (c && ctrl_is_target(c, sq)) { 120 if (g->pos.sq[sq] || (c->sel >= 0 && PTYPE(g->pos.sq[c->sel]) == PAWN && sq == g->pos.ep)) { 121 gfx_ring(s, x + ss / 2, y + ss / 2, ss / 2 - 1, ss > 40 ? 4 : 3, th->c[TH_CAPTURE]); 122 } else gfx_disc(s, x + ss / 2, y + ss / 2, ss / 7 + 1, th->c[TH_TARGET]); 123 } 124 draw_piece(s, ps, x + off, y + off, g->pos.sq[sq]); 125 if (c && sq == c->cursor && c->g && c->g->result == R_NONE) { 126 gfx_frame(s, x, y, ss, ss, C_CURSOR); 127 gfx_frame(s, x + 1, y + 1, ss - 2, ss - 2, C_CURSOR); 128 if (ss > 40) gfx_frame(s, x + 2, y + 2, ss - 4, ss - 4, C_OUTLINE); 129 } 130 hit_add(hits, x, y, ss, ss, HIT_SQUARE, sq); 131 } 132 /* coordenadas en los bordes: abajo-izquierda siempre es oscura, arriba-izquierda clara */ 133 bool fl = c && c->flipped; 134 for (int i = 0; i < 8; i++) { 135 char fc[2] = { (char)('a' + (fl ? 7 - i : i)), 0 }, rc[2] = { (char)('8' - (fl ? 7 - i : i)), 0 }; 136 gfx_text(s, &font_small, bx + i * ss + ss - 7, by + 8 * ss - 9, fc, (i & 1) ? th->c[TH_DARK] : th->c[TH_LIGHT]); 137 gfx_text(s, &font_small, bx + 2, by + i * ss + 2, rc, (i & 1) ? th->c[TH_LIGHT] : th->c[TH_DARK]); 138 } 139 /* coronacion: cuatro piezas al lado del destino */ 140 if (c && c->mode == CM_PROMO) { 141 int to = MTO(c->promo[0]), x, y; 142 sq_xy(v, to, bx, by, ss, &x, &y); 143 int col = g->pos.side; 144 int dy = y < by + 4 * ss ? ss : -ss; 145 gfx_frame(s, x - 2, (dy > 0 ? y : y - 3 * ss) - 2, ss + 4, 4 * ss + 4, C_ACCENT); 146 for (int k = 0; k < 4; k++) { 147 int yy = y + k * dy; 148 gfx_rect(s, x, yy, ss, ss, k == c->promo_cur ? C_ACCENT : C_PANEL2); 149 draw_piece(s, ps, x + off, yy + off, PIECE(col, MPROMO(c->promo[k]))); 150 hit_add(hits, x, yy, ss, ss, HIT_PROMO, k); 151 } 152 } 153 } 154 155 /* ------------------------------------------------------------ textos */ 156 157 static void status_line(const View *v, char *buf, int n) 158 { 159 const Game *g = v->g; 160 buf[0] = 0; 161 if (g->result == R_DRAW) { cat(buf, n, tr(S_DRAW)); cat(buf, n, " · "); cat(buf, n, reason_text(g->reason)); } 162 else if (g->result != R_NONE) { 163 cat(buf, n, color_name(g->result == R_WHITE ? WHITE : BLACK)); 164 cat(buf, n, " "); 165 cat(buf, n, tr(S_WINS)); 166 cat(buf, n, " · "); 167 cat(buf, n, reason_text(g->reason)); 168 } else { 169 cat(buf, n, g_lang ? "" : "Juegan "); 170 cat(buf, n, v->names[g->pos.side] && *v->names[g->pos.side] ? v->names[g->pos.side] : color_name(g->pos.side)); 171 if (g_lang) cat(buf, n, " to move"); 172 if (pos_in_check(&g->pos)) { cat(buf, n, " · "); cat(buf, n, tr(S_CHECK)); } 173 } 174 } 175 176 /* SAN de las ultimas jugadas: se reproduce la partida hasta ahi */ 177 static int recent_san(const Game *g, int first, char san[][12], int max) 178 { 179 Pos p = g->start; 180 int k = 0; 181 for (int i = 0; i < g->nply; i++) { 182 if (i >= first && k < max) move_san(&p, g->mv[i], san[k++], 12); 183 Undo u; 184 pos_make(&p, g->mv[i], &u); 185 } 186 return k; 187 } 188 189 static void player_box(Surface *s, const View *v, int color, int x, int y, int w, bool compact) 190 { 191 const Game *g = v->g; 192 bool active = g->result == R_NONE && g->pos.side == color; 193 gfx_box(s, x, y, w, compact ? 14 : 22, active ? C_PANEL2 : C_PANEL, active ? C_ACCENT : C_DIM); 194 int px = x + 4; 195 gfx_disc(s, px + 4, y + (compact ? 7 : 11), compact ? 3 : 5, color == WHITE ? C_WPIECE : C_BPIECE); 196 gfx_ring(s, px + 4, y + (compact ? 7 : 11), compact ? 3 : 5, 1, C_DIM); 197 const char *name = v->names[color] && *v->names[color] ? v->names[color] : color_name(color); 198 int cw = 0; 199 if (v->has_clock) { 200 char clk[16]; 201 fmt_clock(v->clock[color], clk, sizeof clk); 202 const Font *f = compact ? &font_normal : &font_bold; 203 cw = gfx_text_w(f, clk) + 6; 204 Color cc = v->clock[color] < 20000 && active ? C_ERR : active ? C_ACCENT : C_TEXT; 205 gfx_text(s, f, x + w - cw, y + (compact ? 2 : 5), clk, cc); 206 } 207 gfx_text_fit(s, compact ? &font_small : &font_normal, px + 12, y + (compact ? 3 : 5), w - 20 - cw, name, active ? C_WHITE : C_TEXT); 208 } 209 210 static void eval_bar_v(Surface *s, const View *v, int x, int y, int w, int h) 211 { 212 int cp = v->eval; 213 if (cp > 800) cp = 800; 214 if (cp < -800) cp = -800; 215 int white_h = h / 2 + cp * (h / 2) / 800; 216 if (MATE_IN(v->eval)) white_h = v->eval > 0 ? h : 0; 217 bool fl = v->c && v->c->flipped; 218 gfx_rect(s, x, y, w, h, C_BPIECE); 219 if (fl) gfx_rect(s, x, y, w, white_h, C_WPIECE); 220 else gfx_rect(s, x, y + h - white_h, w, white_h, C_WPIECE); 221 gfx_frame(s, x, y, w, h, C_DIM); 222 } 223 224 static void eval_text(const View *v, char *buf, int n) 225 { 226 buf[0] = 0; 227 if (MATE_IN(v->eval)) { cat(buf, n, v->eval > 0 ? "+M" : "-M"); cati(buf, n, (MATE - (v->eval > 0 ? v->eval : -v->eval) + 1) / 2); return; } 228 cat(buf, n, v->eval >= 0 ? "+" : "-"); 229 int a = v->eval >= 0 ? v->eval : -v->eval; 230 cati(buf, n, a / 100); 231 cat(buf, n, "."); 232 if (a % 100 < 10) cat(buf, n, "0"); 233 cati(buf, n, a % 100); 234 } 235 236 static void captured_row(Surface *s, int x, int y, int w, const int cap[7], int color) 237 { 238 static const char SYM[] = " PNBRQK"; 239 char buf[48] = ""; 240 for (int t = QUEEN; t >= PAWN; t--) 241 for (int k = 0; k < cap[t] && (int)strlen(buf) < 40; k++) { char c1[2] = { SYM[t], 0 }; cat(buf, sizeof buf, c1); } 242 gfx_text_fit(s, &font_small, x, y, w, buf, color == WHITE ? C_WPIECE : C_DIM); 243 } 244 245 static void help_overlay(Surface *s, int x, int y, int w, int h) 246 { 247 gfx_box(s, x, y, w, h, C_PANEL, C_ACCENT); 248 const char *t = tr(S_HELP_KEYS); 249 int ly = y + 6; 250 char line[96]; 251 while (*t && ly < y + h - 10) { 252 int k = 0; 253 while (*t && !(t[0] == ' ' && t[1] == '\xc2')) { if (k < (int)sizeof line - 1) line[k++] = *t; t++; } 254 line[k] = 0; 255 if (*t) t += 4; /* " · " */ 256 while (*t == ' ') t++; 257 gfx_text_fit(s, &font_normal, x + 8, ly, w - 16, line, C_TEXT); 258 ly += 13; 259 } 260 } 261 262 static void modal(Surface *s, const View *v, int bx, int by, int bw, int bh) 263 { 264 int w = bw * 5 / 6, h = v->modal2 ? 48 : 34; 265 int x = bx + (bw - w) / 2, y = by + (bh - h) / 2; 266 gfx_box(s, x, y, w, h, C_PANEL, C_ACCENT); 267 gfx_text_center(s, &font_bold, x + 4, y + 10, w - 8, v->modal, C_ACCENT); 268 if (v->modal2) gfx_text_center(s, &font_normal, x + 4, y + 28, w - 8, v->modal2, C_TEXT); 269 } 270 271 /* ------------------------------------------------------------ layouts */ 272 273 static void prompt_line(Surface *s, const View *v, int x, int y, int w, const Font *f) 274 { 275 const Ctrl *c = v->hide_prompt ? 0 : v->c; 276 char buf[128] = ""; 277 if (c && c->mode == CM_ENTRY) { cat(buf, sizeof buf, tr(S_P_ENTRY)); cat(buf, sizeof buf, " "); cat(buf, sizeof buf, c->entry); cat(buf, sizeof buf, "_"); } 278 else if (c && c->msg[0]) { gfx_text_fit(s, f, x, y, w, c->msg, c->msg_err ? C_ERR : C_OK); return; } 279 else if (c) { 280 cat(buf, sizeof buf, ctrl_prompt(c)); 281 if (c->g && c->g->result == R_NONE && !ctrl_my_turn(c)) { 282 int side = c->g->pos.side; 283 cat(buf, sizeof buf, " "); 284 cat(buf, sizeof buf, v->names[side] && *v->names[side] ? v->names[side] : color_name(side)); 285 } 286 } 287 gfx_text_fit(s, f, x, y, w, buf, C_ACCENT); 288 } 289 290 static void screen_wide(Surface *s, const View *v, HitList *hits) 291 { 292 const Game *g = v->g; 293 int ss = 56, bx = 8, by = 8; 294 gfx_rect(s, 0, 0, s->w, s->h, C_BG); 295 board(s, v, bx, by, ss, hits); 296 int px = bx + 8 * ss + 10, pw = s->w - px - 6; 297 bool fl = v->c && v->c->flipped; 298 int top = fl ? WHITE : BLACK, bot = top ^ 1; 299 int cap[2][7]; 300 captured_pieces(g, cap); 301 player_box(s, v, top, px, by, pw, false); 302 captured_row(s, px + 2, by + 25, pw - 4, cap[bot], bot); /* lo que le capturo el de arriba */ 303 int y = by + 40; 304 char buf[160]; 305 status_line(v, buf, sizeof buf); 306 gfx_text_fit(s, &font_bold, px, y, pw, buf, g->result != R_NONE ? C_ACCENT : C_TEXT); 307 y += 15; 308 if (v->status && *v->status) { gfx_text_fit(s, &font_small, px, y, pw, v->status, C_DIM); y += 11; } 309 if (v->opening) { gfx_text_fit(s, &font_small, px, y, pw, v->opening, C_OK); y += 11; } 310 if (v->show_eval && v->eval_valid) { 311 char e[16]; 312 eval_text(v, e, sizeof e); 313 gfx_text(s, &font_small, px, y + 1, e, C_TEXT); 314 int bw = pw - 40; 315 int wx = bw * (v->eval > 800 ? 800 : v->eval < -800 ? -800 : v->eval) / 1600 + bw / 2; 316 if (MATE_IN(v->eval)) wx = v->eval > 0 ? bw : 0; 317 gfx_rect(s, px + 38, y, bw, 9, C_BPIECE); 318 gfx_rect(s, px + 38, y, wx, 9, C_WPIECE); 319 gfx_frame(s, px + 38, y, bw, 9, C_DIM); 320 y += 13; 321 } 322 if (v->banner) { 323 gfx_box(s, px, y, pw, 30, C_PANEL2, C_ACCENT); 324 gfx_text_fit(s, &font_normal, px + 4, y + 3, pw - 8, v->banner, C_ACCENT); 325 y += 34; 326 } 327 /* jugadas: las ultimas que entran, de a dos por renglon */ 328 int lh = 12, list_bot = s->h - 64 - 20; 329 int rows = (list_bot - y - 14) / lh; 330 if (rows < 1) rows = 1; 331 gfx_text(s, &font_small, px, y, tr(S_MOVES), C_DIM); 332 y += 12; 333 int first_side = g->start.side; 334 int nrows_total = (g->nply + first_side + 1) / 2; 335 int first_row = nrows_total > rows ? nrows_total - rows : 0; 336 int first_ply = first_row * 2 - first_side; 337 if (first_ply < 0) first_ply = 0; 338 char san[128][12]; 339 int ns = recent_san(g, first_ply, san, 128); 340 int ply = first_ply; 341 for (int r = first_row; r < nrows_total && ply < g->nply; r++) { 342 char num[12] = ""; 343 cati(num, sizeof num, g->start.fullmove + r); 344 cat(num, sizeof num, "."); 345 gfx_text(s, &font_small, px, y, num, C_DIM); 346 for (int col = 0; col < 2; col++) { 347 if (r == 0 && col == 0 && first_side == BLACK) { gfx_text(s, &font_small, px + 30, y, "...", C_DIM); continue; } 348 if (ply >= g->nply) break; 349 int idx = ply - first_ply; 350 bool last = ply == g->nply - 1 || ply == v->view_ply - 1; 351 if (idx < ns) gfx_text(s, &font_normal, px + 30 + col * 64, y - 1, san[idx], last ? C_ACCENT : C_TEXT); 352 ply++; 353 } 354 y += lh; 355 } 356 captured_row(s, px + 2, s->h - 64 - 12, pw - 4, cap[top], top); 357 player_box(s, v, bot, px, s->h - 64, pw, false); 358 gfx_text_fit(s, &font_small, px, s->h - 38, pw, g_lang ? "? help m menu" : "? ayuda m menú", C_DIM); 359 prompt_line(s, v, bx, by + 8 * ss + 4, 8 * ss, &font_normal); 360 if (v->show_help) help_overlay(s, bx + 20, by + 40, 8 * ss - 40, 8 * ss - 80); 361 if (v->modal) modal(s, v, bx, by, 8 * ss, 8 * ss); 362 } 363 364 static void screen_compact(Surface *s, const View *v, HitList *hits) 365 { 366 const Game *g = v->g; 367 int ss = 36, bx = 0, by = 16; 368 gfx_rect(s, 0, 0, s->w, s->h, C_BG); 369 bool fl = v->c && v->c->flipped; 370 int top = fl ? WHITE : BLACK, bot = top ^ 1; 371 player_box(s, v, top, 0, 1, 160, true); 372 player_box(s, v, bot, 160, 1, 160, true); 373 board(s, v, bx, by, ss, hits); 374 /* columna derecha: evaluacion y capturadas */ 375 int cx = bx + 8 * ss + 2, cw = s->w - cx - 2; 376 if (v->show_eval && v->eval_valid) eval_bar_v(s, v, cx + cw - 8, by, 8, 8 * ss); 377 int cap[2][7]; 378 captured_pieces(g, cap); 379 static const char SYM[] = " PNBRQK"; 380 for (int side = 0; side < 2; side++) { 381 int y = side == 0 ? by + 2 : by + 8 * ss - 60; 382 int who = side == 0 ? bot : top; /* arriba: lo que perdio el de abajo */ 383 for (int t = QUEEN; t >= PAWN; t--) 384 if (cap[who][t]) { 385 char b[6] = { SYM[t], 0 }; 386 if (cap[who][t] > 1) cati(b, sizeof b, cap[who][t]); 387 gfx_text(s, &font_small, cx, y, b, who == WHITE ? C_WPIECE : C_DIM); 388 y += 10; 389 } 390 } 391 char buf[160]; 392 int ly = by + 8 * ss + 1; 393 if (!v->hide_prompt && v->c && (v->c->msg[0] || v->c->mode == CM_ENTRY || v->c->mode == CM_PROMO)) prompt_line(s, v, 2, ly, s->w - 4, &font_small); 394 else if (v->banner) gfx_text_fit(s, &font_small, 2, ly, s->w - 4, v->banner, C_ACCENT); 395 else { 396 status_line(v, buf, sizeof buf); 397 if (v->status && *v->status && g->result == R_NONE) { cat(buf, sizeof buf, " · "); cat(buf, sizeof buf, v->status); } 398 else if (v->opening && g->result == R_NONE) { cat(buf, sizeof buf, " · "); cat(buf, sizeof buf, v->opening); } 399 gfx_text_fit(s, &font_small, 2, ly, s->w - 4, buf, g->result != R_NONE ? C_ACCENT : C_TEXT); 400 } 401 if (v->show_help) help_overlay(s, 8, by + 8, s->w - 16, 8 * ss - 16); 402 if (v->modal) modal(s, v, 0, by, s->w, 8 * ss); 403 } 404 405 void chess_screen(Surface *s, const View *v, HitList *hits) 406 { 407 if (hits) hits->n = 0; 408 if (v->layout == LAYOUT_COMPACT) screen_compact(s, v, hits); 409 else screen_wide(s, v, hits); 410 } 411 412 /* ------------------------------------------------------------ menus */ 413 414 /* fondo de tablero y una fila de piezas como logo */ 415 int chess_form_backdrop(Surface *s, bool compact) 416 { 417 const Theme *th = &THEMES[0]; 418 int W = s->w, H = s->h, ss = compact ? 20 : 40; 419 for (int y = 0; y * ss < H; y++) 420 for (int x = 0; x * ss < W; x++) 421 gfx_rect(s, x * ss, y * ss, ss, ss, ((x + y) & 1) ? gfx_dim(th->c[TH_DARK]) : gfx_dim(gfx_dim(th->c[TH_LIGHT]))); 422 const PieceSet *ps = &PIECES[NPIECESETS - 1]; 423 static const uint8_t ROW[] = { ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK }; 424 int n = compact ? 5 : 8, pw = ps->size + 2; 425 int x0 = (W - n * pw) / 2; 426 for (int i = 0; i < n; i++) draw_piece(s, ps, x0 + i * pw, compact ? 2 : 8, PIECE(i & 1, ROW[compact ? i + 2 : i])); 427 return compact ? ps->size + 6 : ps->size + 20; 428 }