1 /* $NetBSD: bdisp.c,v 1.55 2022/05/29 17:01:42 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.55 2022/05/29 17:01:42 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 48 static int lastline; 49 static 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 */ 59 void 60 cursinit(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 */ 82 void 83 cursfini(void) 84 { 85 86 move(BSZ + 4, 0); 87 clrtoeol(); 88 refresh(); 89 echo(); 90 endwin(); 91 } 92 93 /* 94 * Initialize board display. 95 */ 96 void 97 bdisp_init(void) 98 { 99 100 /* top and bottom borders */ 101 for (int col = 1; col <= BSZ; col++) { 102 mvaddch(scr_y(BSZ + 1), scr_x(col), letters[col]); 103 mvaddch(scr_y(0), scr_x(col), letters[col]); 104 } 105 106 /* left and right edges */ 107 for (int row = BSZ; row >= 1; row--) { 108 mvprintw(scr_y(row), 0, "%2d", row); 109 mvprintw(scr_y(row), scr_x(BSZ) + 2, "%d", row); 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 */ 121 void 122 bdwho(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 151 static bool 152 should_highlight(spot_index s) 153 { 154 155 if (game.nmoves > 0 && game.moves[game.nmoves - 1] == s) 156 return true; 157 if (game.win_spot != 0) 158 for (int off = 0; off < 5; off++) 159 if (s == game.win_spot + off * dd[game.win_dir]) 160 return true; 161 return false; 162 } 163 164 /* 165 * Update the board display after a move. 166 */ 167 void 168 bdisp(void) 169 { 170 struct spotstr *sp; 171 172 for (int row = BSZ + 1; --row > 0; ) { 173 for (int col = 1; col <= BSZ; col++) { 174 sp = &board[PT(col, row)]; 175 char c; 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(row), scr_x(col)); 187 if (should_highlight(PT(col, row))) { 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 */ 202 void 203 bdump(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 row = BSZ + 1; --row > 0; ) { 212 fprintf(fp, "%2d ", row); /* left edge */ 213 214 for (int col = 1; col <= BSZ; col++) { 215 sp = &board[PT(col, row)]; 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 229 fprintf(fp, "%d\n", row); /* right edge */ 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 */ 240 void 241 dislog(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 */ 257 void 258 ask(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 268 int 269 get_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 282 bool 283 get_line(char *buf, int size, void (*on_change)(const char *)) 284 { 285 char *cp, *end; 286 int c; 287 288 cp = buf; 289 end = buf + size - 1; /* save room for the '\0' */ 290 while ((c = getchar()) != EOF && c != '\n' && c != '\r') { 291 if (!interactive && cp < end) { 292 *cp++ = c; 293 continue; 294 } 295 if (!interactive) 296 errx(EXIT_FAILURE, "line too long"); 297 298 switch (c) { 299 case 0x0c: /* ^L */ 300 wrefresh(curscr); 301 continue; 302 case 0x15: /* ^U */ 303 case 0x18: /* ^X */ 304 for (; cp > buf; cp--) 305 addstr("\b \b"); 306 break; 307 case '\b': 308 case 0x7f: /* DEL */ 309 if (cp == buf) 310 continue; 311 cp--; 312 addstr("\b \b"); 313 break; 314 default: 315 if (cp < end) { 316 *cp++ = c; 317 addch(c); 318 } else 319 beep(); 320 } 321 if (on_change != NULL) { 322 *cp = '\0'; 323 on_change(buf); 324 } 325 refresh(); 326 } 327 *cp = '\0'; 328 return c != EOF; 329 } 330 331 static bool 332 get_coord_mouse(int *x, int *y) 333 { 334 MEVENT ev; 335 336 if (getmouse(&ev) == OK && 337 (ev.bstate & (BUTTON1_RELEASED | BUTTON1_CLICKED)) != 0 && 338 ev.y >= scr_y(BSZ) && ev.y <= scr_y(1) && 339 ev.x >= scr_x(1) && ev.x <= scr_x(BSZ) && 340 (ev.x - scr_x(1)) % (scr_x(2) - scr_x(1)) == 0) { 341 *x = 1 + (ev.x - scr_x(1)) / (scr_x(2) - scr_x(1)); 342 *y = 1 + (scr_y(1) - ev.y) / (scr_y(1) - scr_y(2)); 343 return true; 344 } 345 return false; 346 } 347 348 /* 349 * Ask the user for the coordinate of a move, or return RESIGN or SAVE. 350 * 351 * Based on Eric S. Raymond's modifications to the battleship (bs) user 352 * interface. 353 */ 354 spot_index 355 get_coord(void) 356 { 357 int x = game.user_x, y = game.user_y; 358 359 move(scr_y(y), scr_x(x)); 360 refresh(); 361 for (;;) { 362 mvprintw(BSZ + 3, 6, "(%c %d) ", letters[x], y); 363 move(scr_y(y), scr_x(x)); 364 365 int ch = getch(); 366 switch (ch) { 367 case 'k': 368 case '8': 369 case KEY_UP: 370 y++; 371 break; 372 case 'j': 373 case '2': 374 case KEY_DOWN: 375 y--; 376 break; 377 case 'h': 378 case '4': 379 case KEY_LEFT: 380 x--; 381 break; 382 case 'l': 383 case '6': 384 case KEY_RIGHT: 385 x++; 386 break; 387 case 'y': 388 case '7': 389 case KEY_A1: 390 x--; 391 y++; 392 break; 393 case 'b': 394 case '1': 395 case KEY_C1: 396 x--; 397 y--; 398 break; 399 case 'u': 400 case '9': 401 case KEY_A3: 402 x++; 403 y++; 404 break; 405 case 'n': 406 case '3': 407 case KEY_C3: 408 x++; 409 y--; 410 break; 411 case 'K': 412 y += 5; 413 break; 414 case 'J': 415 y -= 5; 416 break; 417 case 'H': 418 x -= 5; 419 break; 420 case 'L': 421 x += 5; 422 break; 423 case 'Y': 424 x -= 5; 425 y += 5; 426 break; 427 case 'B': 428 x -= 5; 429 y -= 5; 430 break; 431 case 'U': 432 x += 5; 433 y += 5; 434 break; 435 case 'N': 436 x += 5; 437 y -= 5; 438 break; 439 case 0x0c: /* ^L */ 440 (void)clearok(stdscr, true); 441 (void)refresh(); 442 break; 443 case KEY_MOUSE: 444 if (get_coord_mouse(&x, &y)) 445 goto selected; 446 beep(); 447 break; 448 case 'Q': 449 case 'q': 450 return RESIGN; 451 case 'S': 452 case 's': 453 return SAVE; 454 case ' ': 455 case '\r': 456 selected: 457 (void)mvhline(BSZ + 3, 6, ' ', 6); 458 game.user_x = x; 459 game.user_y = y; 460 return PT(x, y); 461 } 462 463 x = 1 + (x + BSZ - 1) % BSZ; 464 y = 1 + (y + BSZ - 1) % BSZ; 465 } 466 } 467