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