bdisp.c revision 1.40 1 /* $NetBSD: bdisp.c,v 1.40 2022/05/21 12:29:34 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.40 2022/05/21 12:29:34 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
48 static int lastline;
49 static 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 */
57 void
58 cursinit(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 */
82 void
83 cursfini(void)
84 {
85
86 move(BSZ + 4, 0);
87 clrtoeol();
88 refresh();
89 echo();
90 endwin();
91 }
92
93 /*
94 * Initialize board display.
95 */
96 void
97 bdisp_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 */
121 void
122 bdwho(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 */
154 void
155 bdisp(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 */
189 void
190 bdump(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 */
227 void
228 dislog(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
245 void
246 ask(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
256 int
257 get_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
274 bool
275 get_line(char *buf, int size, void (*on_change)(const char *))
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 ((c = getchar()) != EOF && c != '\n' && c != '\r') {
284 if (!interactive && cp < end) {
285 *cp++ = c;
286 continue;
287 }
288 if (!interactive)
289 errx(EXIT_FAILURE, "line too long");
290
291 switch (c) {
292 case 0x0c: /* ^L */
293 wrefresh(curscr);
294 continue;
295 case 0x15: /* ^U */
296 case 0x18: /* ^X */
297 for (; cp > buf; cp--)
298 addstr("\b \b");
299 break;
300 case '\b':
301 case 0x7f: /* DEL */
302 if (cp == buf)
303 continue;
304 cp--;
305 addstr("\b \b");
306 break;
307 default:
308 if (cp < end) {
309 *cp++ = c;
310 addch(c);
311 } else
312 beep();
313 }
314 if (on_change != NULL) {
315 *cp = '\0';
316 on_change(buf);
317 }
318 refresh();
319 }
320 *cp = '\0';
321 return c != EOF;
322 }
323
324 /*
325 * Decent (n)curses interface for the game, based on Eric S. Raymond's
326 * modifications to the battleship (bs) user interface.
327 */
328 int
329 get_coord(void)
330 {
331 /* XXX: These coordinates are 0-based, all others are 1-based. */
332 static int curx = BSZ / 2;
333 static int cury = BSZ / 2;
334 int ny, nx, ch;
335
336 move(scr_y(cury + 1), scr_x(curx + 1));
337 refresh();
338 nx = curx;
339 ny = cury;
340 for (;;) {
341 mvprintw(BSZ + 3, 6, "(%c %d) ",
342 letters[curx + 1], cury + 1);
343 move(scr_y(cury + 1), scr_x(curx + 1));
344
345 ch = getch();
346 switch (ch) {
347 case 'k':
348 case '8':
349 case KEY_UP:
350 nx = curx;
351 ny = cury + 1;
352 break;
353 case 'j':
354 case '2':
355 case KEY_DOWN:
356 nx = curx;
357 ny = BSZ + cury - 1;
358 break;
359 case 'h':
360 case '4':
361 case KEY_LEFT:
362 nx = BSZ + curx - 1;
363 ny = cury;
364 break;
365 case 'l':
366 case '6':
367 case KEY_RIGHT:
368 nx = curx + 1;
369 ny = cury;
370 break;
371 case 'y':
372 case '7':
373 case KEY_A1:
374 nx = BSZ + curx - 1;
375 ny = cury + 1;
376 break;
377 case 'b':
378 case '1':
379 case KEY_C1:
380 nx = BSZ + curx - 1;
381 ny = BSZ + cury - 1;
382 break;
383 case 'u':
384 case '9':
385 case KEY_A3:
386 nx = curx + 1;
387 ny = cury + 1;
388 break;
389 case 'n':
390 case '3':
391 case KEY_C3:
392 nx = curx + 1;
393 ny = BSZ + cury - 1;
394 break;
395 case 'K':
396 nx = curx;
397 ny = cury + 5;
398 break;
399 case 'J':
400 nx = curx;
401 ny = BSZ + cury - 5;
402 break;
403 case 'H':
404 nx = BSZ + curx - 5;
405 ny = cury;
406 break;
407 case 'L':
408 nx = curx + 5;
409 ny = cury;
410 break;
411 case 'Y':
412 nx = BSZ + curx - 5;
413 ny = cury + 5;
414 break;
415 case 'B':
416 nx = BSZ + curx - 5;
417 ny = BSZ + cury - 5;
418 break;
419 case 'U':
420 nx = curx + 5;
421 ny = cury + 5;
422 break;
423 case 'N':
424 nx = curx + 5;
425 ny = BSZ + cury - 5;
426 break;
427 case '\f':
428 nx = curx;
429 ny = cury;
430 (void)clearok(stdscr, true);
431 (void)refresh();
432 break;
433 #if 0 /* notyet */
434 case KEY_MOUSE:
435 {
436 MEVENT myevent;
437
438 getmouse(&myevent);
439 if (myevent.y >= 1 && myevent.y <= BSZ + 1 &&
440 myevent.x >= 3 && myevent.x <= 2 * BSZ + 1) {
441 curx = (myevent.x - 3) / 2;
442 cury = BSZ - myevent.y;
443 return PT(curx,cury);
444 } else {
445 beep();
446 }
447 }
448 break;
449 #endif /* 0 */
450 case 'Q':
451 case 'q':
452 return RESIGN;
453 case 'S':
454 case 's':
455 return SAVE;
456 case ' ':
457 case '\r':
458 (void)mvaddstr(BSZ + 3, 6, " ");
459 return PT(curx + 1, cury + 1);
460 }
461
462 curx = nx % BSZ;
463 cury = ny % BSZ;
464 }
465 }
466