bdisp.c revision 1.43
1/*	$NetBSD: bdisp.c,v 1.43 2022/05/22 08:12:15 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.43 2022/05/22 08:12:15 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#if 0 /* no mouse support in netbsd curses yet */
77	mousemask(BUTTON1_CLICKED, NULL);
78#endif
79}
80
81/*
82 * Restore screen display.
83 */
84void
85cursfini(void)
86{
87
88	move(BSZ + 4, 0);
89	clrtoeol();
90	refresh();
91	echo();
92	endwin();
93}
94
95/*
96 * Initialize board display.
97 */
98void
99bdisp_init(void)
100{
101
102	/* top and bottom borders */
103	for (int i = 1; i < BSZ + 1; i++) {
104		mvaddch(scr_y(BSZ + 1), scr_x(i), letters[i]);
105		mvaddch(scr_y(0), scr_x(i), letters[i]);
106	}
107
108	/* left and right edges */
109	for (int j = BSZ + 1; --j > 0; ) {
110		mvprintw(scr_y(j), 0, "%2d", j);
111		mvprintw(scr_y(j), scr_x(BSZ) + 2, "%d", j);
112	}
113
114	bdwho();
115	mvaddstr(0, TRANSCRIPT_COL, "  #  black  white");
116	lastline = 0;
117	bdisp();
118}
119
120/*
121 * Update who is playing whom.
122 */
123void
124bdwho(void)
125{
126	int bw = (int)strlen(plyr[BLACK]);
127	int ww = (int)strlen(plyr[WHITE]);
128	int available = 3 + (1 + scr_x(BSZ) - scr_x(1)) + 3;
129	int fixed = (int)sizeof("BLACK/ (*) vs. WHITE/ (O)") - 1;
130	int total = fixed + bw + ww;
131	int x;
132
133	if (total <= available)
134		x = (available - total) / 2;
135	else {
136		int remaining = available - fixed;
137		int half = remaining / 2;
138
139		if (bw <= half)
140			ww = remaining - bw;
141		else if (ww <= half)
142			bw = remaining - ww;
143		else
144			bw = half, ww = remaining - half;
145		x = 0;
146	}
147
148	mvhline(BSZ + 2, 0, ' ', available);
149	mvprintw(BSZ + 2, x, "BLACK/%.*s (*) vs. WHITE/%.*s (O)",
150	    bw, plyr[BLACK], ww, plyr[WHITE]);
151}
152
153/*
154 * Update the board display after a move.
155 */
156void
157bdisp(void)
158{
159	int c;
160	struct spotstr *sp;
161
162	for (int j = BSZ + 1; --j > 0; ) {
163		for (int i = 1; i < BSZ + 1; i++) {
164			sp = &board[i + j * (BSZ + 1)];
165			if (debug > 1 && sp->s_occ == EMPTY) {
166				if ((sp->s_flags & IFLAGALL) != 0)
167					c = '+';
168				else if ((sp->s_flags & CFLAGALL) != 0)
169					c = '-';
170				else
171					c = '.';
172			} else
173				c = pcolor[sp->s_occ];
174
175			move(scr_y(j), scr_x(i));
176			if (movenum > 1 && movelog[movenum - 2] == PT(i, j)) {
177				attron(A_BOLD);
178				addch(c);
179				attroff(A_BOLD);
180			} else
181				addch(c);
182		}
183	}
184	refresh();
185}
186
187#ifdef DEBUG
188/*
189 * Dump board display to a file.
190 */
191void
192bdump(FILE *fp)
193{
194	int c;
195	struct spotstr *sp;
196
197	/* top border */
198	fprintf(fp, "   A B C D E F G H J K L M N O P Q R S T\n");
199
200	for (int j = BSZ + 1; --j > 0; ) {
201		/* left edge */
202		fprintf(fp, "%2d ", j);
203		for (int i = 1; i < BSZ + 1; i++) {
204			sp = &board[i + j * (BSZ + 1)];
205			if (debug > 1 && sp->s_occ == EMPTY) {
206				if ((sp->s_flags & IFLAGALL) != 0)
207					c = '+';
208				else if ((sp->s_flags & CFLAGALL) != 0)
209					c = '-';
210				else
211					c = '.';
212			} else
213				c = pcolor[sp->s_occ];
214			putc(c, fp);
215			putc(' ', fp);
216		}
217		/* right edge */
218		fprintf(fp, "%d\n", j);
219	}
220
221	/* bottom border */
222	fprintf(fp, "   A B C D E F G H J K L M N O P Q R S T\n");
223}
224#endif /* DEBUG */
225
226/*
227 * Display a transcript entry
228 */
229void
230dislog(const char *str)
231{
232
233	if (++lastline >= SCRNH - 1) {
234		/* move 'em up */
235		lastline = 1;
236	}
237	mvaddnstr(lastline, TRANSCRIPT_COL, str, SCRNW - TRANSCRIPT_COL - 1);
238	clrtoeol();
239	move(lastline + 1, TRANSCRIPT_COL);
240	clrtoeol();
241}
242
243/*
244 * Display a question.
245 */
246
247void
248ask(const char *str)
249{
250	int len = (int)strlen(str);
251
252	mvaddstr(BSZ + 4, 0, str);
253	clrtoeol();
254	move(BSZ + 4, len);
255	refresh();
256}
257
258int
259get_key(const char *allowed)
260{
261	int ch;
262
263	for (;;) {
264		ch = getch();
265		if (allowed != NULL &&
266		    ch != '\0' && strchr(allowed, ch) == NULL) {
267			beep();
268			refresh();
269			continue;
270		}
271		break;
272	}
273	return ch;
274}
275
276bool
277get_line(char *buf, int size, void (*on_change)(const char *))
278{
279	char *cp, *end;
280	int c;
281
282	c = 0;
283	cp = buf;
284	end = buf + size - 1;	/* save room for the '\0' */
285	while ((c = getchar()) != EOF && c != '\n' && c != '\r') {
286		if (!interactive && cp < end) {
287			*cp++ = c;
288			continue;
289		}
290		if (!interactive)
291			errx(EXIT_FAILURE, "line too long");
292
293		switch (c) {
294		case 0x0c:	/* ^L */
295			wrefresh(curscr);
296			continue;
297		case 0x15:	/* ^U */
298		case 0x18:	/* ^X */
299			for (; cp > buf; cp--)
300				addstr("\b \b");
301			break;
302		case '\b':
303		case 0x7f:	/* DEL */
304			if (cp == buf)
305				continue;
306			cp--;
307			addstr("\b \b");
308			break;
309		default:
310			if (cp < end) {
311				*cp++ = c;
312				addch(c);
313			} else
314				beep();
315		}
316		if (on_change != NULL) {
317			*cp = '\0';
318			on_change(buf);
319		}
320		refresh();
321	}
322	*cp = '\0';
323	return c != EOF;
324}
325
326/*
327 * Ask the user for the coordinate of a move, or return RESIGN or SAVE.
328 *
329 * Based on Eric S. Raymond's modifications to the battleship (bs) user
330 * interface.
331 */
332int
333get_coord(void)
334{
335	/* XXX: These coordinates are 0-based, all others are 1-based. */
336	static int curx = BSZ / 2;
337	static int cury = BSZ / 2;
338	int ny, nx, ch;
339
340	move(scr_y(cury + 1), scr_x(curx + 1));
341	refresh();
342	nx = curx;
343	ny = cury;
344	for (;;) {
345		mvprintw(BSZ + 3, 6, "(%c %d) ",
346		    letters[curx + 1], cury + 1);
347		move(scr_y(cury + 1), scr_x(curx + 1));
348
349		ch = getch();
350		switch (ch) {
351		case 'k':
352		case '8':
353		case KEY_UP:
354			nx = curx;
355			ny = cury + 1;
356			break;
357		case 'j':
358		case '2':
359		case KEY_DOWN:
360			nx = curx;
361			ny = BSZ + cury - 1;
362			break;
363		case 'h':
364		case '4':
365		case KEY_LEFT:
366			nx = BSZ + curx - 1;
367			ny = cury;
368			break;
369		case 'l':
370		case '6':
371		case KEY_RIGHT:
372			nx = curx + 1;
373			ny = cury;
374			break;
375		case 'y':
376		case '7':
377		case KEY_A1:
378			nx = BSZ + curx - 1;
379			ny = cury + 1;
380			break;
381		case 'b':
382		case '1':
383		case KEY_C1:
384			nx = BSZ + curx - 1;
385			ny = BSZ + cury - 1;
386			break;
387		case 'u':
388		case '9':
389		case KEY_A3:
390			nx = curx + 1;
391			ny = cury + 1;
392			break;
393		case 'n':
394		case '3':
395		case KEY_C3:
396			nx = curx + 1;
397			ny = BSZ + cury - 1;
398			break;
399		case 'K':
400			nx = curx;
401			ny = cury + 5;
402			break;
403		case 'J':
404			nx = curx;
405			ny = BSZ + cury - 5;
406			break;
407		case 'H':
408			nx = BSZ + curx - 5;
409			ny = cury;
410			break;
411		case 'L':
412			nx = curx + 5;
413			ny = cury;
414			break;
415		case 'Y':
416			nx = BSZ + curx - 5;
417			ny = cury + 5;
418			break;
419		case 'B':
420			nx = BSZ + curx - 5;
421			ny = BSZ + cury - 5;
422			break;
423		case 'U':
424			nx = curx + 5;
425			ny = cury + 5;
426			break;
427		case 'N':
428			nx = curx + 5;
429			ny = BSZ + cury - 5;
430			break;
431		case '\f':
432			nx = curx;
433			ny = cury;
434			(void)clearok(stdscr, true);
435			(void)refresh();
436			break;
437#if 0 /* notyet */
438		case KEY_MOUSE:
439		{
440			MEVENT	myevent;
441
442			getmouse(&myevent);
443			if (myevent.y >= 1 && myevent.y <= BSZ + 1 &&
444			    myevent.x >= 3 && myevent.x <= 2 * BSZ + 1) {
445				curx = (myevent.x - 3) / 2;
446				cury = BSZ - myevent.y;
447				return PT(curx,cury);
448			} else {
449				beep();
450			}
451		}
452		break;
453#endif /* 0 */
454		case 'Q':
455		case 'q':
456			return RESIGN;
457		case 'S':
458		case 's':
459			return SAVE;
460		case ' ':
461		case '\r':
462			(void)mvaddstr(BSZ + 3, 6, "      ");
463			return PT(curx + 1, cury + 1);
464		}
465
466		curx = nx % BSZ;
467		cury = ny % BSZ;
468	}
469}
470