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