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
38 static 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
53 static int lastline;
54 static 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 */
62 void
63 cursinit(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 */
87 void
88 cursfini(void)
89 {
90
91 move(BSZ + 4, 0);
92 clrtoeol();
93 refresh();
94 echo();
95 endwin();
96 }
97
98 /*
99 * Initialize board display.
100 */
101 void
102 bdisp_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 */
132 void
133 bdwho(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 */
170 void
171 bdisp(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 */
204 void
205 bdump(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 */
242 void
243 dislog(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
261 void
262 ask(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
273 int
274 get_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
291 bool
292 get_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 */
340 int
341 get_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