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