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