xref: /openbsd-src/games/atc/input.c (revision 2317ec67b0fa8cbb4cdee40097f28e1a0c3dd5ef)
1 /*	$OpenBSD: input.c,v 1.13 2015/12/31 16:50:29 mestre Exp $	*/
2 /*	$NetBSD: input.c,v 1.4 1995/04/27 21:22:24 mycroft Exp $	*/
3 
4 /*-
5  * Copyright (c) 1990, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Ed James.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 /*
37  * Copyright (c) 1987 by Ed James, UC Berkeley.  All rights reserved.
38  *
39  * Copy permission is hereby granted provided that this notice is
40  * retained on all partial or complete copies.
41  *
42  * For more info on this and all of my stuff, mail edjames@berkeley.edu.
43  */
44 
45 #include <ctype.h>
46 #include <math.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <termios.h>
50 
51 #include "def.h"
52 #include "extern.h"
53 
54 #define MAXRULES	6
55 #define MAXDEPTH	15
56 
57 #define RETTOKEN	'\n'
58 #define REDRAWTOKEN	'\014'	/* CTRL(L) */
59 #define HELPTOKEN	'?'
60 #define ALPHATOKEN	256
61 #define NUMTOKEN	257
62 
63 typedef struct {
64 	int		token;
65 	int		to_state;
66 	const char	*str;
67 	const char	*(*func)(char);
68 } RULE;
69 
70 typedef struct {
71 	int	num_rules;
72 	RULE	*rule;
73 } STATE;
74 
75 typedef struct {
76 	char	str[20];
77 	int	state;
78 	int	rule;
79 	int	ch;
80 	int	pos;
81 } STACK;
82 
83 #define T_RULE		stack[level].rule
84 #define T_STATE		stack[level].state
85 #define T_STR		stack[level].str
86 #define T_POS		stack[level].pos
87 #define	T_CH		stack[level].ch
88 
89 #define NUMELS(a)	(sizeof (a) / sizeof (*(a)))
90 
91 #define NUMSTATES	NUMELS(st)
92 
93 RULE	state0[] = {	{ ALPHATOKEN,	1,	"%c:",		setplane},
94 			{ RETTOKEN,	-1,	"",		NULL	},
95 			{ HELPTOKEN,	12,	" [a-z]<ret>",	NULL	}},
96 	state1[] = {	{ 't',		2,	" turn",	turn	},
97 			{ 'a',		3,	" altitude:",	NULL	},
98 			{ 'c',		4,	" circle",	circle	},
99 			{ 'm',		7,	" mark",	mark	},
100 			{ 'u',		7,	" unmark",	unmark	},
101 			{ 'i',		7,	" ignore",	ignore	},
102 			{ HELPTOKEN,	12,	" tacmui",	NULL	}},
103 	state2[] = {	{ 'l',		6,	" left",	left	},
104 			{ 'r',		6,	" right",	right	},
105 			{ 'L',		4,	" left 90",	Left	},
106 			{ 'R',		4,	" right 90",	Right	},
107 			{ 't',		11,	" towards",	NULL	},
108 			{ 'w',		4,	" to 0",	to_dir	},
109 			{ 'e',		4,	" to 45",	to_dir	},
110 			{ 'd',		4,	" to 90",	to_dir	},
111 			{ 'c',		4,	" to 135",	to_dir	},
112 			{ 'x',		4,	" to 180",	to_dir	},
113 			{ 'z',		4,	" to 225",	to_dir	},
114 			{ 'a',		4,	" to 270",	to_dir	},
115 			{ 'q',		4,	" to 315",	to_dir	},
116 			{ HELPTOKEN,	12,	" lrLRt<dir>",	NULL	}},
117 	state3[] = {	{ '+',		10,	" climb",	climb	},
118 			{ 'c',		10,	" climb",	climb	},
119 			{ '-',		10,	" descend",	descend	},
120 			{ 'd',		10,	" descend",	descend	},
121 			{ NUMTOKEN,	7,	" %c000 feet",	setalt	},
122 			{ HELPTOKEN,	12,	" +-cd[0-9]",	NULL	}},
123 	state4[] = {	{ '@',		9,	" at",		NULL	},
124 			{ 'a',		9,	" at",		NULL	},
125 			{ RETTOKEN,	-1,	"",		NULL	},
126 			{ HELPTOKEN,	12,	" @a<ret>",	NULL	}},
127 	state5[] = {	{ NUMTOKEN,	7,	"%c",		delayb	},
128 			{ HELPTOKEN,	12,	" [0-9]",	NULL	}},
129 	state6[] = {	{ '@',		9,	" at",		NULL	},
130 			{ 'a',		9,	" at",		NULL	},
131 			{ 'w',		4,	" 0",		rel_dir	},
132 			{ 'e',		4,	" 45",		rel_dir	},
133 			{ 'd',		4,	" 90",		rel_dir	},
134 			{ 'c',		4,	" 135",		rel_dir	},
135 			{ 'x',		4,	" 180",		rel_dir	},
136 			{ 'z',		4,	" 225",		rel_dir	},
137 			{ 'a',		4,	" 270",		rel_dir	},
138 			{ 'q',		4,	" 315",		rel_dir	},
139 			{ RETTOKEN,	-1,	"",		NULL	},
140 			{ HELPTOKEN,	12,	" @a<dir><ret>",NULL	}},
141 	state7[] = {	{ RETTOKEN,	-1,	"",		NULL	},
142 			{ HELPTOKEN,	12,	" <ret>",	NULL	}},
143 	state8[] = {	{ NUMTOKEN,	4,	"%c",		benum	},
144 			{ HELPTOKEN,	12,	" [0-9]",	NULL	}},
145 	state9[] = {	{ 'b',		5,	" beacon #",	NULL	},
146 			{ '*',		5,	" beacon #",	NULL	},
147 			{ HELPTOKEN,	12,	" b*",		NULL	}},
148 	state10[] = {	{ NUMTOKEN,	7,	" %c000 ft",	setrelalt},
149 			{ HELPTOKEN,	12,	" [0-9]",	NULL	}},
150 	state11[] = {	{ 'b',		8,	" beacon #",	beacon	},
151 			{ '*',		8,	" beacon #",	beacon	},
152 			{ 'e',		8,	" exit #",	ex_it	},
153 			{ 'a',		8,	" airport #",	airport	},
154 			{ HELPTOKEN,	12,	" b*ea",	NULL	}},
155 	state12[] = {	{ -1,		-1,	"",		NULL	}};
156 
157 #define DEF_STATE(s)	{ NUMELS(s),	(s)	}
158 
159 STATE	st[] = {
160 	DEF_STATE(state0), DEF_STATE(state1), DEF_STATE(state2),
161 	DEF_STATE(state3), DEF_STATE(state4), DEF_STATE(state5),
162 	DEF_STATE(state6), DEF_STATE(state7), DEF_STATE(state8),
163 	DEF_STATE(state9), DEF_STATE(state10), DEF_STATE(state11),
164 	DEF_STATE(state12)
165 };
166 
167 PLANE	p;
168 STACK	stack[MAXDEPTH];
169 int	level;
170 int	tval;
171 int	dest_type, dest_no, dir;
172 
173 int
pop(void)174 pop(void)
175 {
176 	if (level == 0)
177 		return (-1);
178 	level--;
179 
180 	ioclrtoeol(T_POS);
181 
182 	strlcpy(T_STR, "", sizeof T_STR);
183 	T_RULE = -1;
184 	T_CH = -1;
185 	return (0);
186 }
187 
188 void
rezero(void)189 rezero(void)
190 {
191 	iomove(0);
192 
193 	level = 0;
194 	T_STATE = 0;
195 	T_RULE = -1;
196 	T_CH = -1;
197 	T_POS = 0;
198 	strlcpy(T_STR, "", sizeof T_STR);
199 }
200 
201 void
push(int ruleno,int ch)202 push(int ruleno, int ch)
203 {
204 	int	newstate, newpos;
205 
206 	(void)snprintf(T_STR, sizeof T_STR, st[T_STATE].rule[ruleno].str, tval);
207 	T_RULE = ruleno;
208 	T_CH = ch;
209 	newstate = st[T_STATE].rule[ruleno].to_state;
210 	newpos = T_POS + strlen(T_STR);
211 
212 	ioaddstr(T_POS, T_STR);
213 
214 	if (level == 0)
215 		ioclrtobot();
216 	level++;
217 	T_STATE = newstate;
218 	T_POS = newpos;
219 	T_RULE = -1;
220 	strlcpy(T_STR, "", sizeof T_STR);
221 }
222 
223 int
getcommand(void)224 getcommand(void)
225 {
226 	int	c, i, done;
227 	const char	*s, *(*func)(char);
228 	PLANE	*pp;
229 
230 	rezero();
231 
232 	do {
233 		c = gettoken();
234 		if (c == tty_new.c_cc[VERASE]) {
235 			if (pop() < 0)
236 				noise();
237 		} else if (c == tty_new.c_cc[VKILL]) {
238 			while (pop() >= 0)
239 				;
240 		} else {
241 			done = 0;
242 			for (i = 0; i < st[T_STATE].num_rules; i++) {
243 				if (st[T_STATE].rule[i].token == c ||
244 				    st[T_STATE].rule[i].token == tval) {
245 					push(i, (c >= ALPHATOKEN) ? tval : c);
246 					done = 1;
247 					break;
248 				}
249 			}
250 			if (!done)
251 				noise();
252 		}
253 	} while (T_STATE != -1);
254 
255 	if (level == 1)
256 		return (1);	/* forced update */
257 
258 	dest_type = T_NODEST;
259 
260 	for (i = 0; i < level; i++) {
261 		func = st[stack[i].state].rule[stack[i].rule].func;
262 		if (func != NULL)
263 			if ((s = (*func)(stack[i].ch)) != NULL) {
264 				ioerror(stack[i].pos, strlen(stack[i].str), s);
265 				return (-1);
266 			}
267 	}
268 
269 	pp = findplane(p.plane_no);
270 	if (pp->new_altitude != p.new_altitude)
271 		pp->new_altitude = p.new_altitude;
272 	else if (pp->status != p.status)
273 		pp->status = p.status;
274 	else {
275 		pp->new_dir = p.new_dir;
276 		pp->delayd = p.delayd;
277 		pp->delayd_no = p.delayd_no;
278 	}
279 	return (0);
280 }
281 
282 void
noise(void)283 noise(void)
284 {
285 	if (makenoise)
286 		putchar('\07');
287 	fflush(stdout);
288 }
289 
290 int
gettoken(void)291 gettoken(void)
292 {
293 	while ((tval = getAChar()) == REDRAWTOKEN)
294 	{
295 		redraw();
296 	}
297 
298 	if (isdigit(tval))
299 		return (NUMTOKEN);
300 	else if (isalpha(tval))
301 		return (ALPHATOKEN);
302 	else
303 		return (tval);
304 }
305 
306 const char	*
setplane(char c)307 setplane(char c)
308 {
309 	PLANE	*pp;
310 
311 	pp = findplane(number(c));
312 	if (pp == NULL)
313 		return ("Unknown Plane");
314 	memcpy(&p, pp, sizeof (p));
315 	p.delayd = 0;
316 	return (NULL);
317 }
318 
319 const char	*
turn(char c)320 turn(char c)
321 {
322 	if (p.altitude == 0)
323 		return ("Planes at airports may not change direction");
324 	return (NULL);
325 }
326 
327 const char	*
circle(char c)328 circle(char c)
329 {
330 	if (p.altitude == 0)
331 		return ("Planes cannot circle on the ground");
332 	p.new_dir = MAXDIR;
333 	return (NULL);
334 }
335 
336 const char	*
left(char c)337 left(char c)
338 {
339 	dir = D_LEFT;
340 	p.new_dir = p.dir - 1;
341 	if (p.new_dir < 0)
342 		p.new_dir += MAXDIR;
343 	return (NULL);
344 }
345 
346 const char	*
right(char c)347 right(char c)
348 {
349 	dir = D_RIGHT;
350 	p.new_dir = p.dir + 1;
351 	if (p.new_dir >= MAXDIR)
352 		p.new_dir -= MAXDIR;
353 	return (NULL);
354 }
355 
356 const char	*
Left(char c)357 Left(char c)
358 {
359 	p.new_dir = p.dir - 2;
360 	if (p.new_dir < 0)
361 		p.new_dir += MAXDIR;
362 	return (NULL);
363 }
364 
365 const char	*
Right(char c)366 Right(char c)
367 {
368 	p.new_dir = p.dir + 2;
369 	if (p.new_dir >= MAXDIR)
370 		p.new_dir -= MAXDIR;
371 	return (NULL);
372 }
373 
374 const char	*
delayb(char c)375 delayb(char c)
376 {
377 	int	xdiff, ydiff;
378 
379 	c -= '0';
380 
381 	if (c >= sp->num_beacons)
382 		return ("Unknown beacon");
383 	xdiff = sp->beacon[(int)c].x - p.xpos;
384 	xdiff = SGN(xdiff);
385 	ydiff = sp->beacon[(int)c].y - p.ypos;
386 	ydiff = SGN(ydiff);
387 	if (xdiff != displacement[p.dir].dx || ydiff != displacement[p.dir].dy)
388 		return ("Beacon is not in flight path");
389 	if (xdiff != 0 && ydiff !=0)
390 		if (abs(sp->beacon[(int)c].x - p.xpos) !=
391 		    abs(sp->beacon[(int)c].y - p.ypos))
392 			return ("Beacon is not in flight path");
393 	p.delayd = 1;
394 	p.delayd_no = c;
395 
396 	if (dest_type != T_NODEST) {
397 		switch (dest_type) {
398 		case T_BEACON:
399 			xdiff = sp->beacon[dest_no].x - sp->beacon[(int)c].x;
400 			ydiff = sp->beacon[dest_no].y - sp->beacon[(int)c].y;
401 			break;
402 		case T_EXIT:
403 			xdiff = sp->exit[dest_no].x - sp->beacon[(int)c].x;
404 			ydiff = sp->exit[dest_no].y - sp->beacon[(int)c].y;
405 			break;
406 		case T_AIRPORT:
407 			xdiff = sp->airport[dest_no].x - sp->beacon[(int)c].x;
408 			ydiff = sp->airport[dest_no].y - sp->beacon[(int)c].y;
409 			break;
410 		default:
411 			return ("Bad case in delayb!  Get help!");
412 			break;
413 		}
414 		if (xdiff == 0 && ydiff == 0)
415 			return ("Would already be there");
416 		p.new_dir = DIR_FROM_DXDY(xdiff, ydiff);
417 		if (p.new_dir == p.dir)
418 			return ("Already going in that direction");
419 	}
420 	return (NULL);
421 }
422 
423 const char	*
beacon(char c)424 beacon(char c)
425 {
426 	dest_type = T_BEACON;
427 	return (NULL);
428 }
429 
430 const char	*
ex_it(char c)431 ex_it(char c)
432 {
433 	dest_type = T_EXIT;
434 	return (NULL);
435 }
436 
437 const char	*
airport(char c)438 airport(char c)
439 {
440 	dest_type = T_AIRPORT;
441 	return (NULL);
442 }
443 
444 const char	*
climb(char c)445 climb(char c)
446 {
447 	dir = D_UP;
448 	return (NULL);
449 }
450 
451 const char	*
descend(char c)452 descend(char c)
453 {
454 	dir = D_DOWN;
455 	return (NULL);
456 }
457 
458 const char	*
setalt(char c)459 setalt(char c)
460 {
461 	if ((p.altitude == c - '0') && (p.new_altitude == p.altitude))
462 		return ("Already at that altitude");
463 	if (p.new_altitude == c - '0')
464 		return ("Already going to that altitude");
465 	p.new_altitude = c - '0';
466 	return (NULL);
467 }
468 
469 const char	*
setrelalt(char c)470 setrelalt(char c)
471 {
472 	int new_altitude;
473 
474 	if (c == 0)
475 		return ("altitude not changed");
476 
477 	switch (dir) {
478 	case D_UP:
479 		new_altitude = p.altitude + c - '0';
480 		break;
481 	case D_DOWN:
482 		new_altitude = p.altitude - (c - '0');
483 		break;
484 	default:
485 		return ("Unknown case in setrelalt!  Get help!");
486 		break;
487 	}
488 	if (new_altitude < 0)
489 		return ("Altitude would be too low");
490 	else if (new_altitude > 9)
491 		return ("Altitude would be too high");
492 	else if (new_altitude == p.new_altitude)
493 		return ("Already going to that altitude");
494 
495 	p.new_altitude = new_altitude;
496 	return (NULL);
497 }
498 
499 const char	*
benum(char c)500 benum(char c)
501 {
502 	dest_no = c -= '0';
503 
504 	switch (dest_type) {
505 	case T_BEACON:
506 		if (c >= sp->num_beacons)
507 			return ("Unknown beacon");
508 		p.new_dir = DIR_FROM_DXDY(sp->beacon[(int)c].x - p.xpos,
509 			sp->beacon[(int)c].y - p.ypos);
510 		break;
511 	case T_EXIT:
512 		if (c >= sp->num_exits)
513 			return ("Unknown exit");
514 		p.new_dir = DIR_FROM_DXDY(sp->exit[(int)c].x - p.xpos,
515 			sp->exit[(int)c].y - p.ypos);
516 		break;
517 	case T_AIRPORT:
518 		if (c >= sp->num_airports)
519 			return ("Unknown airport");
520 		p.new_dir = DIR_FROM_DXDY(sp->airport[(int)c].x - p.xpos,
521 			sp->airport[(int)c].y - p.ypos);
522 		break;
523 	default:
524 		return ("Unknown case in benum!  Get help!");
525 		break;
526 	}
527 	return (NULL);
528 }
529 
530 const char	*
to_dir(char c)531 to_dir(char c)
532 {
533 	p.new_dir = dir_no(c);
534 	return (NULL);
535 }
536 
537 const char	*
rel_dir(char c)538 rel_dir(char c)
539 {
540 	int	angle;
541 
542 	angle = dir_no(c);
543 	switch (dir) {
544 	case D_LEFT:
545 		p.new_dir = p.dir - angle;
546 		if (p.new_dir < 0)
547 			p.new_dir += MAXDIR;
548 		break;
549 	case D_RIGHT:
550 		p.new_dir = p.dir + angle;
551 		if (p.new_dir >= MAXDIR)
552 			p.new_dir -= MAXDIR;
553 		break;
554 	default:
555 		return ("Bizarre direction in rel_dir!  Get help!");
556 		break;
557 	}
558 	return (NULL);
559 }
560 
561 const char	*
mark(char c)562 mark(char c)
563 {
564 	if (p.altitude == 0)
565 		return ("Cannot mark planes on the ground");
566 	if (p.status == S_MARKED)
567 		return ("Already marked");
568 	p.status = S_MARKED;
569 	return (NULL);
570 }
571 
572 const char	*
unmark(char c)573 unmark(char c)
574 {
575 	if (p.altitude == 0)
576 		return ("Cannot unmark planes on the ground");
577 	if (p.status == S_UNMARKED)
578 		return ("Already unmarked");
579 	p.status = S_UNMARKED;
580 	return (NULL);
581 }
582 
583 const char	*
ignore(char c)584 ignore(char c)
585 {
586 	if (p.altitude == 0)
587 		return ("Cannot ignore planes on the ground");
588 	if (p.status == S_IGNORED)
589 		return ("Already ignored");
590 	p.status = S_IGNORED;
591 	return (NULL);
592 }
593 
594 int
dir_no(char ch)595 dir_no(char ch)
596 {
597 	int	dir;
598 
599 	switch (ch) {
600 	case 'w':	dir = 0;	break;
601 	case 'e':	dir = 1;	break;
602 	case 'd':	dir = 2;	break;
603 	case 'c':	dir = 3;	break;
604 	case 'x':	dir = 4;	break;
605 	case 'z':	dir = 5;	break;
606 	case 'a':	dir = 6;	break;
607 	case 'q':	dir = 7;	break;
608 	default:
609 		dir = -1;
610 		fprintf(stderr, "bad character in dir_no\n");
611 		break;
612 	}
613 	return (dir);
614 }
615