bdisp.c revision 1.50
1/* $NetBSD: bdisp.c,v 1.50 2022/05/28 20:54:31 rillig Exp $ */ 2 3/* 4 * Copyright (c) 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Ralph Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include <sys/cdefs.h> 36/* @(#)bdisp.c 8.2 (Berkeley) 5/3/95 */ 37__RCSID("$NetBSD: bdisp.c,v 1.50 2022/05/28 20:54:31 rillig Exp $"); 38 39#include <curses.h> 40#include <string.h> 41#include <stdlib.h> 42#include <err.h> 43#include "gomoku.h" 44 45#define SCRNH 24 /* assume 24 lines for the moment */ 46#define SCRNW 80 /* assume 80 chars for the moment */ 47 48static int lastline; 49static const char pcolor[] = "*O.?"; 50 51#define scr_y(by) (1 + (BSZ - 1) - ((by) - 1)) 52#define scr_x(bx) (3 + 2 * ((bx) - 1)) 53 54#define TRANSCRIPT_COL (3 + (2 * BSZ - 1) + 3 + 3) 55 56/* 57 * Initialize screen display. 58 */ 59void 60cursinit(void) 61{ 62 63 if (initscr() == NULL) 64 errx(EXIT_FAILURE, "Couldn't initialize screen"); 65 66 if (LINES < SCRNH || COLS < SCRNW) 67 errx(EXIT_FAILURE, "Screen too small (need %dx%d)", 68 SCRNW, SCRNH); 69 70 keypad(stdscr, true); 71 nonl(); 72 noecho(); 73 cbreak(); 74 leaveok(stdscr, false); 75 76 mousemask(BUTTON1_CLICKED, NULL); 77} 78 79/* 80 * Restore screen display. 81 */ 82void 83cursfini(void) 84{ 85 86 move(BSZ + 4, 0); 87 clrtoeol(); 88 refresh(); 89 echo(); 90 endwin(); 91} 92 93/* 94 * Initialize board display. 95 */ 96void 97bdisp_init(void) 98{ 99 100 /* top and bottom borders */ 101 for (int i = 1; i < BSZ + 1; i++) { 102 mvaddch(scr_y(BSZ + 1), scr_x(i), letters[i]); 103 mvaddch(scr_y(0), scr_x(i), letters[i]); 104 } 105 106 /* left and right edges */ 107 for (int j = BSZ + 1; --j > 0; ) { 108 mvprintw(scr_y(j), 0, "%2d", j); 109 mvprintw(scr_y(j), scr_x(BSZ) + 2, "%d", j); 110 } 111 112 bdwho(); 113 mvaddstr(0, TRANSCRIPT_COL, " # black white"); 114 lastline = 0; 115 bdisp(); 116} 117 118/* 119 * Update who is playing whom. 120 */ 121void 122bdwho(void) 123{ 124 int bw = (int)strlen(plyr[BLACK]); 125 int ww = (int)strlen(plyr[WHITE]); 126 int available = 3 + (1 + scr_x(BSZ) - scr_x(1)) + 3; 127 int fixed = (int)sizeof("BLACK/ (*) vs. WHITE/ (O)") - 1; 128 int total = fixed + bw + ww; 129 int x; 130 131 if (total <= available) 132 x = (available - total) / 2; 133 else { 134 int remaining = available - fixed; 135 int half = remaining / 2; 136 137 if (bw <= half) 138 ww = remaining - bw; 139 else if (ww <= half) 140 bw = remaining - ww; 141 else 142 bw = half, ww = remaining - half; 143 x = 0; 144 } 145 146 mvhline(BSZ + 2, 0, ' ', available); 147 mvprintw(BSZ + 2, x, "BLACK/%.*s (*) vs. WHITE/%.*s (O)", 148 bw, plyr[BLACK], ww, plyr[WHITE]); 149} 150 151static bool 152should_highlight(int s) 153{ 154 155 if (game.nmoves > 0 && game.moves[game.nmoves - 1] == s) 156 return true; 157 if (game.winning_spot != 0) 158 for (int i = 0; i < 5; i++) 159 if (s == game.winning_spot + i * dd[game.winning_dir]) 160 return true; 161 return false; 162} 163 164/* 165 * Update the board display after a move. 166 */ 167void 168bdisp(void) 169{ 170 int c; 171 struct spotstr *sp; 172 173 for (int j = BSZ + 1; --j > 0; ) { 174 for (int i = 1; i < BSZ + 1; i++) { 175 sp = &board[i + j * (BSZ + 1)]; 176 if (debug > 1 && sp->s_occ == EMPTY) { 177 if ((sp->s_flags & IFLAGALL) != 0) 178 c = '+'; 179 else if ((sp->s_flags & CFLAGALL) != 0) 180 c = '-'; 181 else 182 c = '.'; 183 } else 184 c = pcolor[sp->s_occ]; 185 186 move(scr_y(j), scr_x(i)); 187 if (should_highlight(PT(i, j))) { 188 attron(A_BOLD); 189 addch(c); 190 attroff(A_BOLD); 191 } else 192 addch(c); 193 } 194 } 195 refresh(); 196} 197 198#ifdef DEBUG 199/* 200 * Dump board display to a file. 201 */ 202void 203bdump(FILE *fp) 204{ 205 int c; 206 struct spotstr *sp; 207 208 /* top border */ 209 fprintf(fp, " A B C D E F G H J K L M N O P Q R S T\n"); 210 211 for (int j = BSZ + 1; --j > 0; ) { 212 /* left edge */ 213 fprintf(fp, "%2d ", j); 214 for (int i = 1; i < BSZ + 1; i++) { 215 sp = &board[i + j * (BSZ + 1)]; 216 if (debug > 1 && sp->s_occ == EMPTY) { 217 if ((sp->s_flags & IFLAGALL) != 0) 218 c = '+'; 219 else if ((sp->s_flags & CFLAGALL) != 0) 220 c = '-'; 221 else 222 c = '.'; 223 } else 224 c = pcolor[sp->s_occ]; 225 putc(c, fp); 226 putc(' ', fp); 227 } 228 /* right edge */ 229 fprintf(fp, "%d\n", j); 230 } 231 232 /* bottom border */ 233 fprintf(fp, " A B C D E F G H J K L M N O P Q R S T\n"); 234} 235#endif /* DEBUG */ 236 237/* 238 * Display a transcript entry 239 */ 240void 241dislog(const char *str) 242{ 243 244 if (++lastline >= SCRNH - 1) { 245 /* move 'em up */ 246 lastline = 1; 247 } 248 mvaddnstr(lastline, TRANSCRIPT_COL, str, SCRNW - TRANSCRIPT_COL - 1); 249 clrtoeol(); 250 move(lastline + 1, TRANSCRIPT_COL); 251 clrtoeol(); 252} 253 254/* 255 * Display a question. 256 */ 257void 258ask(const char *str) 259{ 260 int len = (int)strlen(str); 261 262 mvaddstr(BSZ + 4, 0, str); 263 clrtoeol(); 264 move(BSZ + 4, len); 265 refresh(); 266} 267 268int 269get_key(const char *allowed) 270{ 271 272 for (;;) { 273 int ch = getch(); 274 if (allowed == NULL || ch == '\0' || 275 strchr(allowed, ch) != NULL) 276 return ch; 277 beep(); 278 refresh(); 279 } 280} 281 282bool 283get_line(char *buf, int size, void (*on_change)(const char *)) 284{ 285 char *cp, *end; 286 int c; 287 288 c = 0; 289 cp = buf; 290 end = buf + size - 1; /* save room for the '\0' */ 291 while ((c = getchar()) != EOF && c != '\n' && c != '\r') { 292 if (!interactive && cp < end) { 293 *cp++ = c; 294 continue; 295 } 296 if (!interactive) 297 errx(EXIT_FAILURE, "line too long"); 298 299 switch (c) { 300 case 0x0c: /* ^L */ 301 wrefresh(curscr); 302 continue; 303 case 0x15: /* ^U */ 304 case 0x18: /* ^X */ 305 for (; cp > buf; cp--) 306 addstr("\b \b"); 307 break; 308 case '\b': 309 case 0x7f: /* DEL */ 310 if (cp == buf) 311 continue; 312 cp--; 313 addstr("\b \b"); 314 break; 315 default: 316 if (cp < end) { 317 *cp++ = c; 318 addch(c); 319 } else 320 beep(); 321 } 322 if (on_change != NULL) { 323 *cp = '\0'; 324 on_change(buf); 325 } 326 refresh(); 327 } 328 *cp = '\0'; 329 return c != EOF; 330} 331 332static bool 333get_coord_mouse(int *x, int *y) 334{ 335 MEVENT ev; 336 337 if (getmouse(&ev) == OK && 338 (ev.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)) != 0 && 339 ev.y >= scr_y(BSZ) && ev.y <= scr_y(1) && 340 ev.x >= scr_x(1) && ev.x <= scr_x(BSZ) && 341 (ev.x - scr_x(1)) % (scr_x(2) - scr_x(1)) == 0) { 342 *x = 1 + (ev.x - scr_x(1)) / (scr_x(2) - scr_x(1)); 343 *y = 1 + (scr_y(1) - ev.y) / (scr_y(1) - scr_y(2)); 344 return true; 345 } 346 return false; 347} 348 349/* 350 * Ask the user for the coordinate of a move, or return RESIGN or SAVE. 351 * 352 * Based on Eric S. Raymond's modifications to the battleship (bs) user 353 * interface. 354 */ 355int 356get_coord(void) 357{ 358 static int x = 1 + (BSZ - 1) / 2; 359 static int y = 1 + (BSZ - 1) / 2; 360 361 move(scr_y(y), scr_x(x)); 362 refresh(); 363 for (;;) { 364 mvprintw(BSZ + 3, 6, "(%c %d) ", letters[x], y); 365 move(scr_y(y), scr_x(x)); 366 367 int ch = getch(); 368 switch (ch) { 369 case 'k': 370 case '8': 371 case KEY_UP: 372 y++; 373 break; 374 case 'j': 375 case '2': 376 case KEY_DOWN: 377 y--; 378 break; 379 case 'h': 380 case '4': 381 case KEY_LEFT: 382 x--; 383 break; 384 case 'l': 385 case '6': 386 case KEY_RIGHT: 387 x++; 388 break; 389 case 'y': 390 case '7': 391 case KEY_A1: 392 x--; 393 y++; 394 break; 395 case 'b': 396 case '1': 397 case KEY_C1: 398 x--; 399 y--; 400 break; 401 case 'u': 402 case '9': 403 case KEY_A3: 404 x++; 405 y++; 406 break; 407 case 'n': 408 case '3': 409 case KEY_C3: 410 x++; 411 y--; 412 break; 413 case 'K': 414 y += 5; 415 break; 416 case 'J': 417 y -= 5; 418 break; 419 case 'H': 420 x -= 5; 421 break; 422 case 'L': 423 x += 5; 424 break; 425 case 'Y': 426 x -= 5; 427 y += 5; 428 break; 429 case 'B': 430 x -= 5; 431 y -= 5; 432 break; 433 case 'U': 434 x += 5; 435 y += 5; 436 break; 437 case 'N': 438 x += 5; 439 y -= 5; 440 break; 441 case 0x0c: /* ^L */ 442 (void)clearok(stdscr, true); 443 (void)refresh(); 444 break; 445 case KEY_MOUSE: 446 if (get_coord_mouse(&x, &y)) 447 return PT(x, y); 448 beep(); 449 break; 450 case 'Q': 451 case 'q': 452 return RESIGN; 453 case 'S': 454 case 's': 455 return SAVE; 456 case ' ': 457 case '\r': 458 (void)mvhline(BSZ + 3, 6, ' ', 6); 459 return PT(x, y); 460 } 461 462 x = 1 + (x + BSZ - 1) % BSZ; 463 y = 1 + (y + BSZ - 1) % BSZ; 464 } 465} 466