bdisp.c revision 1.45 1 /* $NetBSD: bdisp.c,v 1.45 2022/05/22 12:42:54 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.45 2022/05/22 12:42:54 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
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, " # 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 * Ask the user for the coordinate of a move, or return RESIGN or SAVE.
328 *
329 * Based on Eric S. Raymond's modifications to the battleship (bs) user
330 * interface.
331 */
332 int
333 get_coord(void)
334 {
335 static int x = 1 + (BSZ - 1) / 2;
336 static int y = 1 + (BSZ - 1) / 2;
337
338 move(scr_y(y), scr_x(x));
339 refresh();
340 for (;;) {
341 mvprintw(BSZ + 3, 6, "(%c %d) ", letters[x], y);
342 move(scr_y(y), scr_x(x));
343
344 int ch = getch();
345 switch (ch) {
346 case 'k':
347 case '8':
348 case KEY_UP:
349 y++;
350 break;
351 case 'j':
352 case '2':
353 case KEY_DOWN:
354 y--;
355 break;
356 case 'h':
357 case '4':
358 case KEY_LEFT:
359 x--;
360 break;
361 case 'l':
362 case '6':
363 case KEY_RIGHT:
364 x++;
365 break;
366 case 'y':
367 case '7':
368 case KEY_A1:
369 x--;
370 y++;
371 break;
372 case 'b':
373 case '1':
374 case KEY_C1:
375 x--;
376 y--;
377 break;
378 case 'u':
379 case '9':
380 case KEY_A3:
381 x++;
382 y++;
383 break;
384 case 'n':
385 case '3':
386 case KEY_C3:
387 x++;
388 y--;
389 break;
390 case 'K':
391 y += 5;
392 break;
393 case 'J':
394 y -= 5;
395 break;
396 case 'H':
397 x -= 5;
398 break;
399 case 'L':
400 x += 5;
401 break;
402 case 'Y':
403 x -= 5;
404 y += 5;
405 break;
406 case 'B':
407 x -= 5;
408 y -= 5;
409 break;
410 case 'U':
411 x += 5;
412 y += 5;
413 break;
414 case 'N':
415 x += 5;
416 y -= 5;
417 break;
418 case 0x0c: /* ^L */
419 (void)clearok(stdscr, true);
420 (void)refresh();
421 break;
422 #if 0 /* notyet */
423 case KEY_MOUSE:
424 {
425 MEVENT myevent;
426
427 getmouse(&myevent);
428 /* XXX: 'y <= BSZ + 1' should probably be '<'. */
429 /* TODO: use scr_x and scr_y. */
430 if (myevent.y >= 1 && myevent.y <= BSZ + 1 &&
431 myevent.x >= 3 && myevent.x <= 2 * BSZ + 1) {
432 curx0 = (myevent.x - 3) / 2;
433 cury0 = BSZ - myevent.y;
434 return PT(curx0,cury0);
435 } else {
436 beep();
437 }
438 }
439 break;
440 #endif
441 case 'Q':
442 case 'q':
443 return RESIGN;
444 case 'S':
445 case 's':
446 return SAVE;
447 case ' ':
448 case '\r':
449 (void)mvhline(BSZ + 3, 6, ' ', 6);
450 return PT(x, y);
451 }
452
453 x = 1 + (x + BSZ - 1) % BSZ;
454 y = 1 + (y + BSZ - 1) % BSZ;
455 }
456 }
457