bdisp.c revision 1.37
1/*	$NetBSD: bdisp.c,v 1.37 2022/05/21 09:25:51 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.37 2022/05/21 09:25:51 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	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/*
55 * Initialize screen display.
56 */
57void
58cursinit(void)
59{
60
61	if (initscr() == NULL) {
62		errx(EXIT_FAILURE, "Couldn't initialize screen");
63	}
64	if ((LINES < SCRNH) || (COLS < SCRNW)) {
65		errx(EXIT_FAILURE, "Screen too small (need %dx%d)",
66		    SCRNW, SCRNH);
67	}
68	keypad(stdscr, true);
69	nonl();
70	noecho();
71	cbreak();
72	leaveok(stdscr, false);
73
74#if 0 /* no mouse support in netbsd curses yet */
75	mousemask(BUTTON1_CLICKED, NULL);
76#endif
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 + 1, "#  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
151/*
152 * Update the board display after a move.
153 */
154void
155bdisp(void)
156{
157	int c;
158	struct spotstr *sp;
159
160	for (int j = BSZ + 1; --j > 0; ) {
161		for (int i = 1; i < BSZ + 1; i++) {
162			sp = &board[i + j * (BSZ + 1)];
163			if (debug > 1 && sp->s_occ == EMPTY) {
164				if ((sp->s_flags & IFLAGALL) != 0)
165					c = '+';
166				else if ((sp->s_flags & CFLAGALL) != 0)
167					c = '-';
168				else
169					c = '.';
170			} else
171				c = pcolor[sp->s_occ];
172
173			move(scr_y(j), scr_x(i));
174			if (movenum > 1 && movelog[movenum - 2] == PT(i, j)) {
175				attron(A_BOLD);
176				addch(c);
177				attroff(A_BOLD);
178			} else
179				addch(c);
180		}
181	}
182	refresh();
183}
184
185#ifdef DEBUG
186/*
187 * Dump board display to a file.
188 */
189void
190bdump(FILE *fp)
191{
192	int c;
193	struct spotstr *sp;
194
195	/* top border */
196	fprintf(fp, "   A B C D E F G H J K L M N O P Q R S T\n");
197
198	for (int j = BSZ + 1; --j > 0; ) {
199		/* left edge */
200		fprintf(fp, "%2d ", j);
201		for (int i = 1; i < BSZ + 1; i++) {
202			sp = &board[i + j * (BSZ + 1)];
203			if (debug > 1 && sp->s_occ == EMPTY) {
204				if ((sp->s_flags & IFLAGALL) != 0)
205					c = '+';
206				else if ((sp->s_flags & CFLAGALL) != 0)
207					c = '-';
208				else
209					c = '.';
210			} else
211				c = pcolor[sp->s_occ];
212			putc(c, fp);
213			putc(' ', fp);
214		}
215		/* right edge */
216		fprintf(fp, "%d\n", j);
217	}
218
219	/* bottom border */
220	fprintf(fp, "   A B C D E F G H J K L M N O P Q R S T\n");
221}
222#endif /* DEBUG */
223
224/*
225 * Display a transcript entry
226 */
227void
228dislog(const char *str)
229{
230
231	if (++lastline >= SCRNH - 1) {
232		/* move 'em up */
233		lastline = 1;
234	}
235	mvaddnstr(lastline, TRANSCRIPT_COL, str, SCRNW - TRANSCRIPT_COL - 1);
236	clrtoeol();
237	move(lastline + 1, TRANSCRIPT_COL);
238	clrtoeol();
239}
240
241/*
242 * Display a question.
243 */
244
245void
246ask(const char *str)
247{
248	int len = (int)strlen(str);
249
250	mvaddstr(BSZ + 4, 0, str);
251	clrtoeol();
252	move(BSZ + 4, len);
253	refresh();
254}
255
256int
257get_key(const char *allowed)
258{
259	int ch;
260
261	for (;;) {
262		ch = getch();
263		if (allowed != NULL &&
264		    ch != '\0' && strchr(allowed, ch) == NULL) {
265			beep();
266			refresh();
267			continue;
268		}
269		break;
270	}
271	return ch;
272}
273
274bool
275get_line(char *buf, int size)
276{
277	char *cp, *end;
278	int c;
279
280	c = 0;
281	cp = buf;
282	end = buf + size - 1;	/* save room for the '\0' */
283	while (cp < end && (c = getchar()) != EOF && c != '\n' && c != '\r') {
284		*cp++ = c;
285		if (interactive) {
286			switch (c) {
287			case 0x0c: /* ^L */
288				wrefresh(curscr);
289				cp--;
290				continue;
291			case 0x15: /* ^U */
292			case 0x18: /* ^X */
293				while (cp > buf) {
294					cp--;
295					addch('\b');
296				}
297				clrtoeol();
298				break;
299			case '\b':
300			case 0x7f: /* DEL */
301				if (cp == buf + 1) {
302					cp--;
303					continue;
304				}
305				cp -= 2;
306				addch('\b');
307				c = ' ';
308				/* FALLTHROUGH */
309			default:
310				addch(c);
311			}
312			refresh();
313		}
314	}
315	*cp = '\0';
316	return c != EOF;
317}
318
319/*
320 * Decent (n)curses interface for the game, based on Eric S. Raymond's
321 * modifications to the battleship (bs) user interface.
322 */
323int
324get_coord(void)
325{
326	/* XXX: These coordinates are 0-based, all others are 1-based. */
327	static int curx = BSZ / 2;
328	static int cury = BSZ / 2;
329	int ny, nx, ch;
330
331	move(scr_y(cury + 1), scr_x(curx + 1));
332	refresh();
333	nx = curx;
334	ny = cury;
335	for (;;) {
336		mvprintw(BSZ + 3, 6, "(%c %d) ",
337		    letters[curx + 1], cury + 1);
338		move(scr_y(cury + 1), scr_x(curx + 1));
339
340		ch = getch();
341		switch (ch) {
342		case 'k':
343		case '8':
344		case KEY_UP:
345			nx = curx;
346			ny = cury + 1;
347			break;
348		case 'j':
349		case '2':
350		case KEY_DOWN:
351			nx = curx;
352			ny = BSZ + cury - 1;
353			break;
354		case 'h':
355		case '4':
356		case KEY_LEFT:
357			nx = BSZ + curx - 1;
358			ny = cury;
359			break;
360		case 'l':
361		case '6':
362		case KEY_RIGHT:
363			nx = curx + 1;
364			ny = cury;
365			break;
366		case 'y':
367		case '7':
368		case KEY_A1:
369			nx = BSZ + curx - 1;
370			ny = cury + 1;
371			break;
372		case 'b':
373		case '1':
374		case KEY_C1:
375			nx = BSZ + curx - 1;
376			ny = BSZ + cury - 1;
377			break;
378		case 'u':
379		case '9':
380		case KEY_A3:
381			nx = curx + 1;
382			ny = cury + 1;
383			break;
384		case 'n':
385		case '3':
386		case KEY_C3:
387			nx = curx + 1;
388			ny = BSZ + cury - 1;
389			break;
390		case 'K':
391			nx = curx;
392			ny = cury + 5;
393			break;
394		case 'J':
395			nx = curx;
396			ny = BSZ + cury - 5;
397			break;
398		case 'H':
399			nx = BSZ + curx - 5;
400			ny = cury;
401			break;
402		case 'L':
403			nx = curx + 5;
404			ny = cury;
405			break;
406		case 'Y':
407			nx = BSZ + curx - 5;
408			ny = cury + 5;
409			break;
410		case 'B':
411			nx = BSZ + curx - 5;
412			ny = BSZ + cury - 5;
413			break;
414		case 'U':
415			nx = curx + 5;
416			ny = cury + 5;
417			break;
418		case 'N':
419			nx = curx + 5;
420			ny = BSZ + cury - 5;
421			break;
422		case '\f':
423			nx = curx;
424			ny = cury;
425			(void)clearok(stdscr, true);
426			(void)refresh();
427			break;
428#if 0 /* notyet */
429		case KEY_MOUSE:
430		{
431			MEVENT	myevent;
432
433			getmouse(&myevent);
434			if (myevent.y >= 1 && myevent.y <= BSZ + 1 &&
435			    myevent.x >= 3 && myevent.x <= 2 * BSZ + 1) {
436				curx = (myevent.x - 3) / 2;
437				cury = BSZ - myevent.y;
438				return PT(curx,cury);
439			} else {
440				beep();
441			}
442		}
443		break;
444#endif /* 0 */
445		case 'Q':
446		case 'q':
447			return RESIGN;
448		case 'S':
449		case 's':
450			return SAVE;
451		case ' ':
452		case '\r':
453			(void)mvaddstr(BSZ + 3, 6, "      ");
454			return PT(curx + 1, cury + 1);
455		}
456
457		curx = nx % BSZ;
458		cury = ny % BSZ;
459	}
460}
461