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