Home | History | Annotate | Line # | Download | only in gomoku
      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