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