|
|
1.1 root 1: /* atc: simulate an air traffic controller at work */
2:
3: #define NRAND 1
4: #define die nrand
5:
6: #include <stdio.h>
7: #include <sys/ttyio.h>
8: #include <sys/param.h>
9: #include <sys/timeb.h>
10: #include <ctype.h>
11: #include <signal.h>
12: #include <pwd.h>
13: #include "ahdr.h"
14:
15: #define SYNTAX "Usage: atc [-s=<seed>] [-a=<airspace>] [-t=<time>]\n"
16:
17: /* size of terminal capability buffer */
18: #define TCSIZE 1024
19:
20: int cmdx; /* command located using same cursor positioning as screen */
21: int cmdy; /* usually will be at 24 lines down */
22:
23: char screen[MAXWIDTH][MAXHEIGHT]; /* contents of screen when no planes */
24:
25: char *flowfile = 0; /* no default flow patterns */
26: char *airfile = AIRFILE;
27: char *airspace = {"Apple1"}; /* default airspace is as on Apple */
28: char *protofile = 0; /* no default protocol file */
29: FILE *proto; /* stays open all the time if protocol */
30: char *moviefile = 0; /* if in movie mode, here's the file */
31: FILE *movie; /* and the file descriptor */
32: int nextmovie; /* next time for a moviefile command */
33: char *mcommand; /* next movie command */
34: int width,height; /* these values set in the airspace file */
35: char *bigname = 0; /* megastatistics file */
36: FILE *bigstatsfile; /* megastatistics stream */
37:
38: struct flow fpath[MAXPATHS];
39: int maxflow;
40: int npaths;
41:
42: int nentry = 0; /* number of entries into the board */
43: struct pstruct entry[EMAX];
44:
45: int nairport = 0;
46: struct pstruct airport[AMAX];
47:
48: int nnavaid = 0;
49: struct pstruct navaid[NMAX];
50:
51: int nplanes = PMAX;
52: struct astruct plane[PMAX];
53:
54: int initseed,seed,game_time,initgame_time,last_update;
55: time_t start_time; /* real time in seconds that game started */
56:
57: time_t get_time();
58: struct passwd *getpwuid();
59: FILE *popen();
60:
61: #define BUFSIZE 80 /* buffer for message */
62:
63: int remain;
64:
65: int bound = 0; /* number of boundary errors */
66: int system = 0; /* system errors */
67: int crash = 0; /* crashed - out of fuel */
68: int fuel = 0; /* out of fuel on runway - tow truck */
69: int goaround = 0; /* missed an approach */
70: int icmds = 0; /* number of cmds issued (including deferred) */
71: int cmds = 0; /* number of cmds actually executed */
72:
73: int fuelused = 0;
74:
75: int inaptot=0,outaptot=0; /* denominators for random entries/exits */
76: int inawtot=0,outawtot=0; /* for airports and airways */
77:
78: char tcbuf[TCSIZE];
79: char cmbuf[100]; /* buffer for cursor motion strings */
80: char clbuf[100]; /* clear screen sequence */
81:
82: int rubout = 0;
83:
84: catchint(n)
85: int n;
86: {
87: rubout = 1;
88: signal (n, catchint);
89: }
90:
91: struct sgttyb tty_setting;
92: short tty_flags;
93:
94: main(argc,argv)
95: int argc;
96: char **argv;
97: { register char *arg;
98:
99: char *ttype;
100: char *getenv();
101:
102: ttype = getenv("TERM");
103: if (ttype == NULL || *ttype == '\0' || tgetent (tcbuf, ttype) <= 0) {
104: fprintf (stderr, "cannot determine terminal type\n");
105: exit (1);
106: }
107:
108: /* get cursor motion and clear screen code into cmbuf and clbuf */
109: {
110: char *p;
111: p = cmbuf;
112: tgetstr ("cm", &p);
113: p = clbuf;
114: tgetstr ("cl", &p);
115: }
116:
117: ioctl(0, TIOCGETP, &tty_setting);
118: tty_flags = tty_setting.sg_flags;
119: seed = 0;
120: for (arg = *++argv; --argc; arg = *++argv) /* read args 1 at a time */
121: { if (arg[0] == '-')
122: switch(arg[1])
123: { case 's':
124: if (arg[2] != '=') err(SYNTAX);
125: seed = atoi(&arg[3]);
126: break;
127: case 'S':
128: if (arg[2] != '=') err(SYNTAX);
129: bigname = &arg[3];
130: break;
131: case 'a':
132: if (arg[2] != '=') err(SYNTAX);
133: airspace = &arg[3];
134: break;
135: case 'u':
136: if (arg[2] != '=') err(SYNTAX);
137: airfile = &arg[3];
138: break;
139: case 't':
140: if (arg[2] != '=') err(SYNTAX);
141: initgame_time = game_time = 59 + 60 * atoi(&arg[3]);
142: break;
143: case 'f':
144: if (arg[2] != '=') err(SYNTAX);
145: flowfile = &arg[3];
146: break;
147: case 'p': /* run a protocol */
148: if (arg[2] != '=') err(SYNTAX);
149: protofile = &arg[3];
150: break;
151: case 'm': /* movie mode */
152: if (arg[2] != '=') err(SYNTAX);
153: moviefile = &arg[3];
154: break;
155: default :
156: err("Bad arg: %s\n",arg);
157: }
158: }
159: init(); /* set up flight plans, initialize screen */
160: while (command()); /* get commands from user */
161: done(1);
162: exit(0);
163: }
164:
165: #define CMDSIZE 40
166:
167: int inter; /* non-zero if an alarm went off */
168: intp() /* process interrupt from alarm */
169: { inter++;
170: }
171:
172:
173: command()
174: { time_t now;
175: char cmd[CMDSIZE];
176: char *c,utype,*d;
177: int i,j;
178:
179: for (utype = i = *(c=cmd) = 0; utype != RETURN; )
180: {
181: if (moviefile) game_time--; /* 1 sec at a time? */
182: else
183: { now = get_time();
184: game_time = initgame_time - (now - start_time);
185: }
186: if (remain == 0) return(0);
187: if (game_time < 0)
188: { report(OUTOFTIME,0,0);
189: return(0);
190: }
191: if (i == 0) /* preserve partial command */
192: { cursor(cmdx,cmdy);
193: printf("<%.2d> COMMAND: ",game_time/60);
194: }
195: if (last_update - game_time >= UPDATE)
196: { update();
197: if (c != cmd) {BEEP;} /* cmd may be obsolete */
198: }
199: while (moviefile && game_time <= nextmovie)
200: { if (bigname) pcmd(bigstatsfile,mcommand);
201: if (protofile) pcmd(proto,mcommand);
202: i = parse(mcommand);
203: if (!i) return i;
204: getmcmd();
205: }
206: if (moviefile) continue; /* no longer accept inputs */
207: cursor(cmdx+7,cmdy);
208: for (j=0; j<i; j++) putchar(cmd[j]);
209: signal(SIGALRM,intp);/* catch the alarm if it goes off */
210: inter = 0; /* set to >0 if the alarm goes off */
211: if (rubout) return 0; /* user really wants to quit */
212: alarm(TIMEOUT); /* read for TIMEOUT sec unless he types */
213: utype = getchar() & 0177;
214: alarm(0);
215: if (inter > 0) continue; /* didn't type anything */
216: delmsg(cmdx,cmdy-1); /* erase previous echo if he types */
217: cursor(cmdx+7,cmdy);
218: if (i != 2 || (*(c-1) != '*' && *(c-1) != ':'))
219: if (*cmd != '!' || i == 0) /* don't shift CDL filename */
220: if (*cmd != '@'|| i == 0) /* don't shift location descrip */
221: if (utype >= 'a' && utype <= 'z')
222: utype = utype - 'a' + 'A';
223: if (utype == INSMODE) utype = RETURN;
224: if ((*c++ = utype) != RETURN)
225: { if (++i >= CMDSIZE) c--;
226: else
227: switch(utype) /* echo something reasonable */
228: {
229: #ifdef ANNARBOR
230: case MIPAGE:/* check cursor controls */
231: putchar(*--c = 'N');
232: putchar(*++c = 'W');
233: c++; i++;
234: break;
235: case HOME:
236: putchar(*--c = 'N');
237: c++;
238: break;
239: case PLPAGE:
240: putchar(*--c = 'N');
241: putchar(*++c = 'E');
242: c++; i++;
243: break;
244: case MISRCH:
245: putchar(*--c = 'W');
246: c++;
247: break;
248: case UPARROW:
249: putchar(*--c = '0');
250: c++;
251: break;
252: case PLSRCH:
253: putchar(*--c = 'E');
254: c++;
255: break;
256: case LEFTARROW:
257: putchar(*--c = 'S');
258: putchar(*++c = 'W');
259: c++; i++;
260: break;
261: case DOWNARROW:
262: putchar(*--c = 'S');
263: c++;
264: break;
265: case RIGHTARROW:
266: putchar(*--c = 'S');
267: putchar(*++c = 'E');
268: c++; i++;
269: break;
270: case ':': /* like a * */
271: putchar('*');
272: break;
273: #endif
274: case BS:
275: if (i<=0) break;
276: if (i==1) { i = 0; break; }
277: putchar(BS);
278: c--; c--; i--; i--;
279: break;
280: default: putchar(utype); break;
281: }
282: }
283: }
284: *c = 0;
285: if (bigname) pcmd(bigstatsfile,cmd);
286: if (protofile) pcmd(proto,cmd);
287: return(parse(cmd));
288: }
289:
290: pcmd(file,cmd) /* protocol a command */
291: FILE *file;
292: char *cmd;
293: { register char *s;
294:
295: if (file == proto)
296: fprintf(file,"%5d: ",game_time);
297: else fprintf(file,"%5d: ",initgame_time - game_time);
298: for (s = cmd; *s && *s != RETURN; s++) fputc(*s,file);
299: fputc('\n',file);
300: }
301:
302: parse(cmd) /* command is terminated by RETURN */
303: register char *cmd;
304: { time_t now;
305: register int i;
306: register struct astruct *pl;
307: char dest;
308: char *fullcmd;
309: char ubuf[10];
310:
311: fullcmd = cmd;
312: switch(*cmd) /* parse 1 letter at a time */
313: { case ' ': /* time advance */
314: sprintf (ubuf, "%d", UPDATE);
315: echo("%s\" - ROGER",ubuf);
316: now = get_time();
317: start_time = last_update - UPDATE - 1 - initgame_time + now;
318: break;
319: case '$':
320: return(0);
321: case '!': /* read a file of clearance directive lists */
322: readcdl(++cmd);
323: break;
324: case '@': /* get a monitoring command */
325: getmon(++cmd);
326: break;
327: default: /* should be airplane id */
328: /* ASSUMPTION HERE: aircraft are A-Z */
329: if (*cmd < 'A' || *cmd > 'Z')
330: { echo("%s - UNABLE",fullcmd);
331: BEEP;
332: break;
333: }
334: for (pl = &plane[i=0]; i < nplanes; i++,pl++)
335: if (pl->a_id == *cmd) break;
336: if (i == nplanes)
337: { echo("%s - NOT ACTIVE",fullcmd);
338: BEEP;
339: break;
340: }
341:
342: if (cmd[1] == '?') /* o.k. to cancel plans */
343: { if (pl->a_active != ACTIVE &&
344: pl->a_active != WAITING)
345: { echo("%s - NOT ACTIVE",fullcmd);
346: BEEP;
347: break;
348: }
349: }
350: else if (cmd[1] == RETURN) /* info request only */
351: { if (pl->a_active != ACTIVE &&
352: pl->a_active != APPROACHING &&
353: pl->a_active != WAITING)
354: { echo("%s - NOT ACTIVE",fullcmd);
355: BEEP;
356: break;
357: }
358: }
359: else
360: { if (pl->a_active != ACTIVE && pl->a_active != WAITING)
361: { echo("%s - NOT ACTIVE",fullcmd);
362: BEEP;
363: break;
364: }
365: if (pl->a_active==ACTIVE && pl->a_nextz == 0 && cmd[1]!=RETURN)
366: { echo("%s - LANDING",fullcmd);
367: BEEP;
368: break;
369: }
370: }
371: switch(*++cmd) /* parse second char */
372: { case RETURN: /* info request */
373: strip(pl,cmdy+1,VERBOSE);
374: break;
375: case 'A': /* change altitude */
376: if (*++cmd < '0' || *cmd > '5')
377: { echo("%s - UNABLE",fullcmd);
378: BEEP;
379: }
380: else
381: { pl->a_nextz = *cmd - '0';
382: echo("%s - ROGER",fullcmd);
383: cmds++;
384: icmds++;
385: }
386: break;
387: case 'H': /* hold at beacon */
388: pl->a_hold = 1;
389: pl->a_clear = 0;
390: echo("%s - ROGER",fullcmd);
391: cmds++;
392: icmds++;
393: break;
394: case 'L': /* left turn */
395: rdturn(pl,'L',++cmd,fullcmd);
396: break;
397: case 'R': /* right turn */
398: rdturn(pl,'R',++cmd,fullcmd);
399: break;
400: case 'T': /* shortest turn */
401: rdturn(pl,'T',++cmd,fullcmd);
402: break;
403: case '*': /* clearance at navaid */
404: case ':':
405: dest = *++cmd;
406: for (i=0; i<nentry; i++) /* vector to entry? */
407: if (dest == entry[i].p_sym) break;
408: if (pl->a_hold == 1) pl->a_hold = 0;
409: if (i<nentry)
410: { echo("%s - ROGER",fullcmd);
411: pl->a_clear = dest;
412: cmds++;
413: icmds++;
414: break;
415: }
416: for (i=0; i<nairport; i++)
417: if (dest == airport[i].p_sym) break;
418: if (i<nairport)
419: { echo("%s - ROGER",fullcmd);
420: pl->a_clear = dest;
421: cmds++;
422: icmds++;
423: break;
424: }
425: echo("%s - UNABLE",fullcmd);
426: BEEP;
427: break;
428: case '?': /* delete delayed commands */
429: ddelay(pl);
430: echo("%s - ROGER",fullcmd);
431: cmds++;
432: icmds++;
433: break;
434: default: /* airport id? - maybe cursor motions? */
435: echo("%s - UNABLE",fullcmd);
436: BEEP;
437: break;
438: }
439: }
440: return(1);
441: }
442:
443: getmon(cmd) /* get monitor command from command line */
444: register char *cmd;
445: { int x,y;
446: register char *start;
447:
448: start = cmd;
449: if (getloc(cmd,&x,&y) == 0) /* where did you say? */
450: { echo("%s - BAD LOC SPEC",cmd);
451: BEEP;
452: return;
453: }
454: while (*cmd) /* pick up each command that happens here */
455: { while (*cmd && *cmd != ',') cmd++;
456: if (*cmd == 0) return; /* read last command */
457: putlist(start,x,y,++cmd); /* put the next command on a list */
458: }
459: }
460:
461: ddelay(pl) /* delete any delayed commands from this plane's list */
462: struct astruct *pl;
463: { struct list *l,*next;
464:
465: if (pl->a_plan == 0) return;
466: for (l = pl->a_plan; l; l = next)
467: { next = l->l_next;
468: free(l);
469: }
470: pl->a_plan = 0;
471: }
472:
473: putlist(start,x,y,cmd) /* put some commands on the list for a plane */
474: char *start;
475: int x,y;
476: char *cmd;
477: { register struct list *l;
478: register struct astruct *p;
479: register char *c,*d;
480: int i;
481:
482: d = cmd;
483: for (i = 0; cmd[i] != ',' && cmd[i] != 0 && cmd[i] != RETURN; i++)
484: if (i != 2 || (cmd[i-1] != '*' && cmd[i-1] != ':'))
485: if (cmd[i] >= 'a' && cmd[i] <= 'z') cmd[i] += 'A' - 'a';
486: for (p = &plane[i = 0]; i < nplanes; i++,p++)
487: if (p->a_id == *cmd) break;
488: if (i == nplanes)
489: { echo("BAD PLANE SPEC: %s",cmd);
490: BEEP;
491: return;
492: }
493: l = p->a_plan; /* go to the CDL for this plane */
494: if (p->a_plan == 0)
495: l = p->a_plan = (struct list *) malloc(sizeof (struct list));
496: else
497: { for (l = p->a_plan; l->l_next != 0; l = l->l_next); /* eol */
498: l->l_next = (struct list *) malloc(sizeof (struct list));
499: l = l->l_next;
500: }
501: l->l_next = 0;
502: l->l_x = x;
503: l->l_y = y;
504: c = l->l_cmd;
505: while (*cmd != ',' && *cmd != 0 && *cmd != RETURN)
506: *c++ = *cmd++;
507: *c++ = RETURN;
508: *c = 0;
509: for (c = d; *c; c++) if (*c == RETURN) *c = 0; /* return damages dspla*/
510: echo("%s - WILCO",d);
511: icmds++;
512: /* copy the verbatim direction used by the guy */
513: for (c = start, d = l->l_loc; *c != ','; *d++ = *c++);
514: *d = 0;
515: }
516:
517:
518: getloc(cmd,px,py) /* get location description from a monitor command */
519: char *cmd;
520: int *px,*py;
521: { register char l,*m; /* location */
522: register int i;
523: register struct pstruct *p;
524: int dx,dy;
525: char buf[300];
526:
527: l = *(m = cmd);
528: p = 0;
529: for (i = 0; i < nentry; i++)
530: if (entry[i].p_sym == l) p = &entry[i];
531: for (i = 0; i < nairport; i++)
532: if (airport[i].p_sym == l) p = &airport[i];
533: for (i = 0; i < nnavaid; i++)
534: if (navaid[i].p_sym == l) p = &navaid[i];
535: if (p == 0) return 0;
536: *px = p->p_x;
537: *py = p->p_y;
538: m++;
539: while (*m != ',') /* read through the other directions */
540: { dx = dy = 0;
541: switch(*m) /* NSEW */
542: { case 'n': dy = -1; break;
543: case 's': dy = 1; break;
544: case 'e': dx = 1; break;
545: case 'w': dx = -1; break;
546: default:
547: sprintf(buf,"Got %c instead of n,s,e,w",*m);
548: msg(cmdx,cmdy+3,buf);
549: return 0;
550: }
551: if (*++m == 'w') { dx = -1; m++;} /* NW or SW */
552: else if (*m == 'e') { dx = 1; m++;} /* NE or SE */
553: if (*m < '0' || *m > '9')
554: { sprintf(buf,"Got %c instead of digit",*m);
555: msg(cmdx,cmdy+3,buf);
556: return 0;
557: }
558: for (i = 0; *m >= '0' && *m <= '9'; m++)
559: i = 10 * i + *m - '0';
560: *px += i * dx;
561: *py += i * dy;
562: }
563: return 1;
564: }
565:
566: readcdl(fname) /* fname points at CDL filename, \r-terminated */
567: char *fname;
568: { FILE *cdlfile;
569: char *p;
570:
571: while (*fname == ' ') fname++; /* skip initial blanks */
572: for (p = fname; *p && *p != RETURN; p++); /* remove newline */
573: *p = 0;
574: if ((cdlfile = fopen(fname,"r")) == NULL)
575: { echo("Cannot open %s",fname);
576: return;
577: }
578: echo("Reading CDL file...",0);
579: fclose(cdlfile);
580: }
581:
582:
583: rdturn(pl,dir,cmd,fullcmd)
584: struct astruct *pl;
585: char dir;
586: char *cmd,*fullcmd;
587: { int i,ok,newx,newy,turncount;
588:
589: if (pl->a_nextz == 0)
590: { echo("%s - WRONG WAY",fullcmd);
591: BEEP;
592: return;
593: }
594: ok = 1; /* check whether this is a legal command */
595: newx = newy = 0;
596: for (i=0; i<2; i++) /* look at next 2 chars */
597: switch(cmd[i])
598: { case RETURN:
599: if (i == 0)
600: { echo("%s - WHICH WAY?",fullcmd);
601: BEEP;
602: return;
603: }
604: break;
605: case '0':
606: if (i == 1) /* can't be second char */
607: { ok = 0;
608: break;
609: }
610: pl->a_dxnew = 0;
611: pl->a_dynew = 0;
612: pl->a_turn = 0;
613: pl->a_clear = 0;
614: pl->a_hold = 0;
615: echo("%s - ROGER",fullcmd);
616: cmds++;
617: icmds++;
618: return;
619: case 'N':
620: newy = -1;
621: break;
622: case 'S':
623: newy = 1;
624: break;
625: case 'E':
626: newx = 1;
627: break;
628: case 'W':
629: newx = -1;
630: break;
631: default: /* numbers (e.g.) no longer acceptable */
632: ok = 0;
633: break;
634: }
635: if (ok)
636: { echo("%s - ROGER",fullcmd);
637: cmds++;
638: icmds++;
639: pl->a_turn = dir; /* will replace 'T' with 'L' or 'R' */
640: pl->a_dxnew = newx; pl->a_dynew = newy;
641: pl->a_hold = pl->a_clear = 0;
642: if (dir == 'T')
643: { newx = pl->a_dx; /* start with this direction */
644: newy = pl->a_dy; /* and turn until done */
645: for (turncount = 1; turncount < 9; turncount++)
646: if (turn(&newx,&newy,'L',pl->a_dxnew,pl->a_dynew))
647: break;
648: if (turncount <= 4) pl->a_turn = 'L';
649: else pl->a_turn = 'R';
650: }
651: }
652: else
653: { echo("%s - UNABLE",fullcmd);
654: BEEP;
655: }
656: }
657:
658: echo(format,cmd)
659: char *format,*cmd;
660: { char *c;
661: char mbuf[BUFSIZE];
662:
663: for (c=cmd; *c; c++) if (*c == RETURN) *c = 0;
664: sprintf(mbuf,format,cmd);
665: msg(cmdx,cmdy-1,mbuf);
666: }
667:
668: int parity = 0; /* props move every other update */
669:
670: update()
671: { parity = 1 - parity;
672: last_update -= UPDATE; /* must average out to every UPDATE secs */
673: moveplanes();
674: printplans();
675: if (bigname) activity();
676: }
677:
678: activity() /* number of active planes to megastats file */
679: { register int i,active,waiting,approaching,planned;
680: struct astruct *p;
681:
682: for (i=active=waiting=approaching=planned=0; i < nplanes; i++)
683: { p = &plane[i];
684: switch(p->a_active)
685: { case ACTIVE:
686: active++;
687: break;
688: case WAITING:
689: waiting++;
690: break;
691: case APPROACHING:
692: approaching++;
693: break;
694: }
695: if (p->a_plan && p->a_active != DONE) planned++;
696: }
697: fprintf(bigstatsfile,"%5d: %d active, %d approaching, %d waiting. ",
698: initgame_time - game_time, active,approaching,waiting);
699: fprintf(bigstatsfile,"%d planned.\n",planned);
700: for (i = 0; i < nplanes; i++)
701: { int a;
702:
703: p = &plane[i];
704: a = p->a_active;
705: if (a == ACTIVE || a == APPROACHING || a == WAITING)
706: { p->a_load += active;
707: p->a_ticks++;
708: p->a_planned += planned;
709: }
710: if (a == APPROACHING || a == WAITING)
711: { p->a_aprev += active;
712: p->a_prev++;
713: p->a_pprev += planned;
714: p->a_pplanned = (p->a_plan? YES : NO);
715: }
716: }
717: }
718:
719: moveplanes()
720: { register struct astruct *pl;
721: register int i,j;
722: register int nextx,nexty;
723: int ax,ay,oldx,oldy;
724: struct pstruct *dst;
725:
726: for (pl = &plane[i = 0]; i < PMAX; i++, pl++)
727: { if (parity && pl->a_type == PROP)
728: { pl->a_lastz = pl->a_z;
729: if (pl->a_active == LANDING) pl->a_active = DONE;
730: continue;
731: }
732: switch(pl->a_active)
733: { case INACTIVE:
734: if (initgame_time - game_time + FPTHRESH>= pl->a_stime
735: && !pl->a_sair)
736: pl->a_active = APPROACHING;
737:
738: case APPROACHING:
739: if (initgame_time - game_time>= pl->a_stime)
740: { pl->a_active = ACTIVE;
741: if (pl->a_sair) pl->a_active = WAITING;
742: nextx = pl->a_x;
743: nexty = pl->a_y;
744: }
745: break;
746:
747: case LANDING: /* to allow for conflicts */
748: pl->a_active = DONE;
749: break;
750:
751: case ACTIVE:
752: pl->a_dist++; /* increment distance traveled */
753: nextx = pl->a_x + pl->a_dx;
754: nexty = pl->a_y + pl->a_dy;
755: if (nextx < 0 || nexty < 0 ||
756: nextx >= width || nexty >= height)
757: { leavescreen(pl);
758: break;
759: }
760:
761: /* turning? */
762: if (pl->a_turn) /* turn 1 tick in right direction */
763: if (turn(&pl->a_dx,&pl->a_dy,
764: pl->a_turn,pl->a_dxnew,pl->a_dynew))
765: pl->a_turn = pl->a_dxnew = pl->a_dynew = 0;
766:
767:
768: if (pl->a_dair) /* landing? */
769: { dst = &airport[pl->a_dest];
770: ax = dst->p_x;
771: ay = dst->p_y;
772: if (ax == nextx && ay == nexty)
773: if (pl->a_z == 0 &&
774: pl->a_dx == dst->p_dx &&
775: pl->a_dy == dst->p_dy)
776: { pl->a_active = LANDING; /* landed */
777: erase(pl->a_x,pl->a_y);
778: pl->a_x = nextx;
779: pl->a_y = nexty;
780: remain--;
781: }
782: else if (pl->a_nextz == 0) /* flyby */
783: { pl->a_nextz = 1; /* down too late*/
784: goaround++;
785: report(GOAROUND,pl);
786: }
787: }
788:
789: /* changing altitude? */
790: if (pl->a_z > pl->a_nextz)
791: pl->a_lastz = pl->a_z--;
792: else if (pl->a_z < pl->a_nextz)
793: pl->a_lastz = pl->a_z++;
794: else pl->a_lastz = pl->a_z;
795:
796: /* out of fuel? */
797: if (--pl->a_fuel <= 0)
798: { crash++;
799: report(CRASH,pl);
800: pl->a_active = DONE;
801: remain--;
802: erase(pl->a_x,pl->a_y);
803: }
804: if (pl->a_type == JET) fuelused += 5;
805: else fuelused += 1;
806: break;
807:
808: case WAITING: /* waiting for takeoff */
809: if (--pl->a_fuel <= 0) /* out of fuel on ground? */
810: { fuel++;
811: if (bigname) freport(pl);
812: report(FUEL,pl);
813: pl->a_active = DONE;
814: remain--;
815: }
816: if (pl->a_nextz != 0)
817: { pl->a_z++; /* take off */
818: pl->a_active = ACTIVE;
819: pl->a_dist++;
820: }
821: nextx = pl->a_x + pl->a_dx;
822: nexty = pl->a_y + pl->a_dy;
823:
824: /* turning? */
825: if (pl->a_turn) /* turn 1 tick in right direction */
826: if (turn(&pl->a_dx,&pl->a_dy,
827: pl->a_turn,pl->a_dxnew,pl->a_dynew))
828: pl->a_turn = pl->a_dxnew = pl->a_dynew = 0;
829:
830: if (pl->a_type == JET) fuelused += 5;
831: else fuelused += 1;
832: break;
833:
834: case DONE: /* off the screen */
835: break;
836: }
837: if (pl->a_active != ACTIVE) continue;
838: oldx = pl->a_x;
839: oldy = pl->a_y;
840: pl->a_x = nextx;
841: pl->a_y = nexty;
842: cursor(nextx,nexty);
843: printf("%c%c",pl->a_id,(pl->a_z>9? '^' : pl->a_z+'0'));
844: erase(oldx,oldy);
845: if (pl->a_hold || pl->a_clear)
846: for (j = 0; j < nnavaid; j++) /* at a navaid? */
847: if (screen[nextx][nexty] == navaid[j].p_sym)
848: { donavaid(pl); /* right navaid? */
849: break;
850: }
851: }
852: conflict();
853: delays(); /* execute any applicable delayed commands */
854: }
855:
856: freport(pl) /* report to big stats file about tow truck */
857: struct astruct *pl;
858: {
859: fprintf(bigstatsfile,"%5d: %c out of fuel on runway.\n",
860: initgame_time - game_time,pl->a_id);
861: }
862:
863: delays() /* execute any applicable delayed actions */
864: { register struct astruct *p;
865: register int i;
866: register struct list *l,*last;
867:
868: /* for each plane, if it is active and if it has a command pending,
869: * and if it is at the right place for the command, then execute it.
870: */
871: for (i = 0; i < PMAX; i++)
872: { p = &plane[i];
873: if (p->a_active != ACTIVE) continue;
874: if (p->a_plan == 0) continue;
875: last = 0;
876: for (l = p->a_plan; l; )
877: { if (p->a_x != l->l_x || p->a_y != l->l_y)
878: {
879: last = l;
880: l = l->l_next;
881: }
882: else /* it matched */
883: {
884: parse(l->l_cmd); /* execute the command */
885:
886: /* now delete the command from the list */
887: if (last == 0) /* it was the first one */
888: { p->a_plan = l->l_next;
889: free(l);
890: l = p->a_plan;
891: /* last stays at 0 */
892: }
893: else
894: { last->l_next = l->l_next;
895: free(l);
896: l = last->l_next;
897: /* last stays where it was */
898: }
899: }
900: }
901: }
902: }
903:
904: donavaid(pl) /* take appropriate action at navaid */
905: struct astruct *pl; /* the plane has either been vectored or told to hold */
906: { register struct pstruct *d;
907: register int i;
908:
909: if (pl->a_hold)
910: { pl->a_hold = 2; /* will be holding now */
911: pl->a_turn = 'L'; /* and turning continuously to the left */
912: pl->a_dxnew = pl->a_dx; /* until it reaches this heading agn*/
913: pl->a_dynew = pl->a_dy;
914: }
915: if (pl->a_clear)
916: { pl->a_turn = pl->a_dxnew = pl->a_dynew = pl->a_hold = 0;
917: /* see what we're vectoring to */
918: for (d = &entry[i=0]; i < nentry; i++,d++)
919: if (d->p_sym == pl->a_clear) break;
920: if (i != nentry) /* yes, it was a navaid */
921: { pl->a_dx = -d->p_dx; /* opposite of entry heading*/
922: pl->a_dy = -d->p_dy;
923: pl->a_clear = 0;
924: return;
925: }
926: for (d = &airport[i=0]; i < nairport; i++,d++)
927: if (d->p_sym == pl->a_clear) break;
928: if (i == nairport) return; /* program bug - cleared to ?? */
929: pl->a_dx = d->p_dx; /* assume landing heading for airport */
930: pl->a_dy = d->p_dy;
931: pl->a_clear = 0;
932:
933: if (pl->a_hold == 1)
934: pl->a_hold = pl->a_dxnew = pl->a_dynew = 0;
935: else if (pl->a_hold == 0)
936: pl->a_dxnew = pl->a_dynew = 0;
937: return;
938: }
939: }
940:
941: turn(newx,newy,direc,dx,dy) /* messy - enumeration of all cases */
942: int *newx,*newy; /* returned values of new heading */
943: char direc;
944: int dx,dy;
945: { int x,y;
946:
947: x = *newx;
948: y = *newy;
949: switch(direc)
950: { case 'L': /* cases would be more efficient, but this is clearer*/
951: if (x== -1 && y== -1) {*newx= -1; *newy= 0; break;}
952: if (x== -1 && y== 0) {*newx= -1; *newy= 1; break;}
953: if (x== -1 && y== 1) {*newx= 0; *newy= 1; break;}
954: if (x== 0 && y== -1) {*newx= -1; *newy= -1; break;}
955: if (x== 0 && y== 1) {*newx= 1; *newy= 1; break;}
956: if (x== 1 && y== -1) {*newx= 0; *newy= -1; break;}
957: if (x== 1 && y== 0) {*newx= 1; *newy= -1; break;}
958: if (x== 1 && y== 1) {*newx= 1; *newy= 0; break;}
959: break;
960: case 'R':
961: if (x== -1 && y== -1) {*newx= 0; *newy= -1; break;}
962: if (x== -1 && y== 0) {*newx= -1; *newy= -1; break;}
963: if (x== -1 && y== 1) {*newx= -1; *newy= 0; break;}
964: if (x== 0 && y== -1) {*newx= 1; *newy= -1; break;}
965: if (x== 0 && y== 1) {*newx= -1; *newy= 1; break;}
966: if (x== 1 && y== -1) {*newx= 1; *newy= 0; break;}
967: if (x== 1 && y== 0) {*newx= 1; *newy= 1; break;}
968: if (x== 1 && y== 1) {*newx= 0; *newy= 1; break;}
969: break;
970: }
971: if (*newx == dx && *newy == dy) return(1); /* finished turn */
972: else return(0);
973: }
974:
975:
976: report(error,plane1,plane2)
977: int error;
978: struct astruct *plane1,*plane2;
979: { char mbuf[BUFSIZE];
980: int i;
981:
982: putchar(BELL);
983: switch(error)
984: { case FUEL:
985: sprintf(mbuf,"Tow truck called for %c (%c)",
986: plane1->a_id,airport[plane1->a_start].p_sym);
987: break;
988: case CRASH:
989: sprintf(mbuf,"PLANE %c OUT OF GAS",plane1->a_id);
990: break;
991: case BOUND:
992: sprintf(mbuf,"Boundary error - %c",plane1->a_id);
993: break;
994: case SYSTEM:
995: sprintf(mbuf,"%c AND %c: SYSTEM ERROR",
996: plane1->a_id, plane2->a_id);
997: break;
998: case GOAROUND:
999: sprintf(mbuf,"%c missed approach",plane1->a_id);
1000: break;
1001: case OUTOFTIME:
1002: sprintf(mbuf,"Out of time: %d remain",remain);
1003: break;
1004: }
1005: msg(cmdx,cmdy+2,mbuf);
1006: }
1007:
1008:
1009:
1010: leavescreen(pl)
1011: struct astruct *pl;
1012: { struct pstruct *e;
1013:
1014: pl->a_active = DONE;
1015: pl->a_dist++;
1016: erase(pl->a_x,pl->a_y);
1017: remain--;
1018: e = &entry[pl->a_dest];
1019: if (pl->a_z == 5 && !pl->a_dair && /* leaving at non-airport, 5K' */
1020: screen[pl->a_x][pl->a_y] == e->p_sym && /* right exit */
1021: pl->a_dx == -e->p_dx && pl->a_dy == -e->p_dy && /* right direc */
1022: pl->a_turn == 0) /* and not turning */
1023: return(0);
1024: bound++;
1025: if (bigname)
1026: { fprintf(bigstatsfile,"%5d: Boundary error - plane %c ",
1027: initgame_time - game_time,pl->a_id);
1028: fprintf(bigstatsfile,"left at %d000' ",pl->a_z);
1029: if (pl->a_nextz > pl->a_z)
1030: fprintf(bigstatsfile,"climbing to %d000'",pl->a_nextz);
1031: if (pl->a_nextz < pl->a_z)
1032: fprintf(bigstatsfile,"descending to %d000'",pl->a_nextz);
1033: fprintf(bigstatsfile,
1034: "\n\tPosition: (%d,%d) Bearing: ",pl->a_x,pl->a_y);
1035: if (pl->a_dy < 0) fprintf(bigstatsfile,"N");
1036: else if (pl->a_dy > 0) fprintf(bigstatsfile,"S");
1037: if (pl->a_dx > 0) fprintf(bigstatsfile,"E");
1038: else if (pl->a_dx < 0) fprintf(bigstatsfile,"W");
1039: if (pl->a_turn)
1040: { fprintf(bigstatsfile," turning %c to ",pl->a_turn);
1041: if (pl->a_dynew < 0) fprintf(bigstatsfile,"N");
1042: else if (pl->a_dynew > 0) fprintf(bigstatsfile,"S");
1043: if (pl->a_dxnew > 0) fprintf(bigstatsfile,"E");
1044: else if (pl->a_dxnew < 0) fprintf(bigstatsfile,"W");
1045: }
1046: fprintf(bigstatsfile,".\n");
1047: }
1048: report(BOUND,pl);
1049: return(0);
1050: }
1051:
1052: conflict() /* check each pair of planes for problems */
1053: { register struct astruct *q,*p;
1054: register int i,j,k;
1055: register int miss;
1056:
1057: for (p = &plane[k=1]; k < nplanes; k++,p++)
1058: { if (p->a_active != ACTIVE && p->a_active != LANDING) continue;
1059: for (q = &plane[i=0]; i < k; i++,q++)
1060: { if (q->a_active != ACTIVE) continue;
1061: if (q->a_z != p->a_z && q->a_z != p->a_lastz &&
1062: q->a_lastz != p->a_z && q->a_lastz != p->a_lastz)
1063: continue;
1064: miss = abs(q->a_x - p->a_x);
1065: j = abs(q->a_y - p->a_y);
1066: if (j>miss) miss=j;
1067: if (miss < 3) /* too close - system error */
1068: { system++;
1069: if (bigname) bigsyserr(p,q);
1070: report(SYSTEM,p,q);
1071: break;
1072: }
1073: }
1074: }
1075: }
1076:
1077: bigsyserr(p,q) /* spew sys err info into the big stats file */
1078: struct astruct *p,*q;
1079: {
1080: fprintf(bigstatsfile,
1081: "%5d: System error between %c (%d,%d,%d000) and %c (%d,%d,%d000)\n",
1082: initgame_time - game_time,p->a_id,p->a_x,p->a_y,p->a_z,
1083: q->a_id,q->a_x,q->a_y,q->a_z);
1084: }
1085:
1086: erase(x,y)
1087: int x,y;
1088: { register struct astruct *p;
1089: register int i;
1090:
1091: if (x<0 || y < 0) return;
1092: if (x>=width || y>=height) return;
1093: cursor(x,y);
1094: printf("%c ",screen[x][y]);
1095: for (p = &plane[i=0]; i < nplanes; i++,p++)
1096: if (p->a_active == ACTIVE && x == p->a_x && y == p->a_y)
1097: { cursor(x,y);
1098: printf("%c%c",p->a_id,(p->a_z>9?'^':p->a_z+'0'));
1099: }
1100: }
1101:
1102:
1103: int msglen[MAXHEIGHT]; /* length of message string on this row */
1104:
1105: init()
1106: { int i,j;
1107: char c,mbuf[BUFSIZE];
1108: int pcompare();
1109: FILE *statfile;
1110: struct passwd *pw;
1111: time_t now;
1112: char *p,*ct;
1113: extern char *ctime();
1114: int nearlies;
1115:
1116: if ((statfile = fopen(STATSFILE,"a")) != NULL)
1117: { pw = getpwuid(getuid());
1118: now = get_time();
1119: ct = ctime(&now);
1120: for (p= &ct[4]; p <= &ct[16]; p++) fputc(*p,statfile);
1121: fprintf(statfile,"%8s\n",pw->pw_name);
1122: fclose(statfile);
1123: }
1124:
1125: /* go into raw mode, no echo */
1126: if (!moviefile)
1127: { ioctl(0, TIOCGETP, &tty_setting);
1128: tty_flags = tty_setting.sg_flags;
1129: #ifdef CBREAK
1130: tty_setting.sg_flags |= CBREAK;
1131: tty_setting.sg_flags &= ~CRMOD;
1132: #else
1133: tty_setting.sg_flags |= RAW;
1134: #endif
1135: tty_setting.sg_flags &= ~ECHO;
1136: ioctl(0, TIOCSETP, &tty_setting);
1137: if (signal (SIGINT, SIG_IGN) != SIG_IGN)
1138: signal (SIGINT, catchint);
1139: if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
1140: signal (SIGQUIT, catchint);
1141: }
1142:
1143: for (i = 0; i < MAXHEIGHT; i++)
1144: msglen[i] = 0;
1145:
1146: for (i=0; i<MAXWIDTH; i++) for (j=0; j<MAXHEIGHT; j++)
1147: screen[i][j] = '.';
1148:
1149: if (moviefile)
1150: { if ((movie = fopen(moviefile,"r")) == NULL)
1151: err("Cannot open %s for reading.\n",moviefile);
1152: minit(); /* initialize from movie file */
1153: }
1154:
1155: if (seed == 0) seed = get_time();
1156: initseed = seed;
1157: #ifdef vax
1158: srand(seed);
1159: #else
1160: srandom(seed); /* initialize random number generator */
1161: #endif
1162:
1163: clearscreen();
1164: readspace();
1165:
1166:
1167: cmdx = width; /* start command just to right of display */
1168: cmdy = height-4; /* and a ways from the bottom */
1169: display(); /* initial display */
1170: for (i=0; i<nairport; i++)
1171: { sprintf(mbuf,"%c : %c%c",
1172: screen[airport[i].p_x][airport[i].p_y],
1173: (j = airport[i].p_dy) == 1 ? 'S' : j == -1 ? 'N' : ' ',
1174: (j = airport[i].p_dx) == 1 ? 'E' : j == -1 ? 'W' : ' ');
1175: msg(cmdx,cmdy-2-i,mbuf);
1176: }
1177:
1178: cdltop.c_next = 0; /* initially no clearance directive lists */
1179: cdltop.c_list = 0;
1180:
1181: while (game_time < 16*60 || game_time > 99*60+59) /*get time from user */
1182: { char buf[100];
1183: char *p;
1184: cursor(cmdx,cmdy);
1185: printf("< >");
1186: cursor(cmdx,cmdy);
1187:
1188: /* move the cursor one space to the right */
1189: p = buf;
1190: tgetstr ("nd", &p);
1191: fwrite (buf, sizeof (*buf), p-buf, stdout);
1192:
1193: game_time = getdigit()*10; /* get the time as 2 digits */
1194: game_time += getdigit();
1195: initgame_time = game_time = game_time*60+59;
1196: }
1197:
1198: if (protofile) /* keeping a protocol of the whole game */
1199: { if ((proto = fopen(protofile,"w")) == NULL)
1200: err("Cannot open %s for writing.\n",protofile);
1201: protostats(proto);
1202: }
1203:
1204: if (bigname) /* megastatistics requested */
1205: { if ((bigstatsfile = fopen(bigname,"w")) == NULL)
1206: err("Cannot open %s for writing.\n",bigname);
1207: protostats(bigstatsfile);
1208: if (protofile)
1209: fprintf(bigstatsfile,"Protocol from %s\n",protofile);
1210: }
1211:
1212: last_update = initgame_time+UPDATE; /* force early update */
1213: if (moviefile) last_update += OFFSET;
1214:
1215: for (i=nearlies=0; i<nplanes; i++) /* construct 26 flight plans */
1216: { plane[i].a_id = 'A'+i;
1217: flightplan(&plane[i],0);
1218: if (plane[i].a_stime < FPTHRESH) nearlies++;
1219: }
1220: for (i=nearlies; i < NEARLY; i++) /* ensure 2 or so planes in 1st minute */
1221: flightplan(&plane[die(nplanes)],1);
1222: qsort(plane,nplanes,sizeof (struct astruct),pcompare);
1223: remain = nplanes;
1224: if (bigname)
1225: for (i = 0; i < nplanes; i++)
1226: strip(&plane[i],-1,TERSE);
1227:
1228: for (i=0; i<nairport; i++) /* erase the approaches */
1229: delmsg(cmdx,cmdy-2-i);
1230: start_time = get_time();
1231: }
1232:
1233: protostats(proto) /* bunch of statistics for protocols and stuff */
1234: FILE *proto;
1235: { char wd[100]; /* result of a "pwd" command */
1236: FILE *pwd;
1237: char *s,*t,*u;
1238: int c;
1239:
1240: if ((pwd = popen("pwd","r")) == NULL)
1241: err("Cannot open a pipe to pwd.\n");
1242: for (s = wd; (c = getc(pwd)) != EOF;)
1243: if (c != '\n') *s++ = c;
1244: t = s; /* here's where to put the filename */
1245: *s = 0;
1246: fclose(pwd);
1247: if (*airfile != '/') /* relative airspace file */
1248: { s = t;
1249: *s++ = '/';
1250: for (u = airfile; *s++ = *u++; );
1251: fprintf(proto,"Airspace file: %s\n",wd);
1252: }
1253: else fprintf(proto,"Airspace file: %s\n",airfile);
1254: fprintf(proto,"Airspace: %s\n",airspace);
1255: if (flowfile)
1256: { if (*flowfile != '/') /* relative flowfile */
1257: { s = t;
1258: *s++ = '/';
1259: for (u = flowfile; *s++ = *u++; );
1260: fprintf(proto,"Flow file: %s\n",wd);
1261: }
1262: else fprintf(proto,"Flow file: %s\n",flowfile);
1263: }
1264: else fprintf(proto,"Flow file: \n");
1265: fprintf(proto,"Seed: %d\n",seed);
1266: fprintf(proto,"Time: %d seconds\n",game_time);
1267: }
1268:
1269: minit() /* initialize the game from a protocol file */
1270: { /* the file is open on stream "movie" */
1271: char buffer[80];
1272: static char airfilebuf[80];
1273: static char airbuf[80];
1274: static char flowbuf[80];
1275: char *s,*t;
1276:
1277: if (fgets(buffer,80,movie) == 0) /* airspace file line */
1278: err("Unexpected EOF on movie file.\n");
1279: for (s = buffer; *s != ':' && *s; s++); /* scan to colon */
1280: if (*s == 0) err("Bad airspace file line.\n");
1281: s++; s++; /* skip colon and space */
1282: for (t = airfilebuf; (*t++ = *s++) != '\n';); /* copy airspace file */
1283: *--t = 0;
1284: airfile = airfilebuf; /* and dummy the pointer */
1285: if (fgets(buffer,80,movie) == 0) /* airspace line */
1286: err("Unexpected EOF on movie file.\n");
1287: for (s = buffer; *s != ':' && *s; s++); /* scan to colon */
1288: if (*s == 0) err("Bad airspace line.\n");
1289: s++; s++; /* skip colon and space */
1290: for (t = airbuf; (*t++ = *s++) != '\n';); /* copy airspace name */
1291: *--t = 0;
1292: airspace = airbuf; /* and dummy the pointer */
1293: if (fgets(buffer,80,movie) == 0) /* flowfile line */
1294: err("Unexpected EOF on movie file.\n");
1295: for (s = buffer; *s != ':' && *s; s++); /* scan to colon */
1296: if (*s == 0) err("Bad flowfile line.\n");
1297: s++; s++; /* skip colon and space */
1298: if (*s == '\n') flowfile = 0;
1299: else
1300: { for (t = flowbuf; (*t++ = *s++) != '\n';); /* copy flowfile */
1301: *--t = 0;
1302: flowfile = flowbuf; /* and dummy the pointer */
1303: }
1304: if (fgets(buffer,80,movie) == 0) /* seed */
1305: err("Unexpected EOF on movie file.\n");
1306: for (s = buffer; *s != ':' && *s; s++); /* scan to colon */
1307: if (*s == 0) err("Bad seed line.\n");
1308: s++; s++; /* skip colon and space */
1309: seed = atoi(s);
1310: if (fgets(buffer,80,movie) == 0) /* time line */
1311: err("Unexpected EOF on movie file.\n");
1312: for (s = buffer; *s != ':' && *s; s++); /* scan to colon */
1313: if (*s == 0) err("Bad time line.\n");
1314: s++; s++; /* skip colon and space */
1315: initgame_time = game_time = atoi(s);
1316: getmcmd(); /* get the first command from movie mode */
1317: }
1318:
1319: getmcmd() /* read a command and store time and command */
1320: { static char buff[80];
1321: char *s;
1322:
1323: if (fgets(buff,80,movie) == 0)
1324: { mcommand = "";
1325: nextmovie = -20;
1326: fclose(movie);
1327: return;
1328: }
1329: for (s = buff; *s == ' '; s++); /* skip initial blanks */
1330: nextmovie = atoi(s);
1331: while (*s++ != ' '); /* skip to the command */
1332: mcommand = s; /* and save it in a global */
1333: for (; *s; s++) if (*s == '\n') *s = RETURN;
1334: }
1335:
1336: pcompare(p1,p2) /* compare start times for sorting the plane list */
1337: struct astruct *p1,*p2;
1338: { if ((p1)->a_stime < (p2)->a_stime) return(-1);
1339: else if ((p1)->a_stime == (p2)->a_stime) return(0);
1340: else return(1);
1341: }
1342:
1343:
1344: printplans() /* flight plans for active and near-active planes */
1345: { register int ctr; /* can only keep cmdy-2 planes on the screen */
1346: register struct astruct *p;
1347: register int i;
1348:
1349: for (i = ctr = 0, p = &plane[0]; i < nplanes && ctr < cmdy - 2; i++,p++)
1350: if (p->a_active == APPROACHING || p->a_active == WAITING)
1351: strip(p,ctr++,TERSE);
1352: delmsg(cmdx,ctr++); /* blank line between approaching and active*/
1353: for (p = &plane[i=0]; i < nplanes && ctr < cmdy - 2; i++,p++)
1354: if (p->a_active == ACTIVE)
1355: strip(p,ctr++,TERSE);
1356: while (ctr < cmdy - 2) delmsg(cmdx,ctr++); /* delete any msg on line*/
1357: }
1358:
1359: strip(p,y,outstyle) /* display flight strip for this plane on cmd row y */
1360: register struct astruct *p;
1361: int y,outstyle;
1362: { char mbuf[80]; /* plan message to appear */
1363: int pfuel;
1364: struct list *l;
1365: char *s;
1366:
1367: if (p->a_active == WAITING) sprintf(mbuf," ");
1368: else if (p->a_active == APPROACHING)
1369: sprintf(mbuf,"%d ",
1370: (p->a_stime - initgame_time + game_time + 14) / 15);
1371: else mbuf[0] = 0;
1372: sprintf(&mbuf[strlen(mbuf)],"%c%c %c->%c ",
1373: p->a_id,
1374: (p->a_type? 'j': 'p'),
1375: screen[p->a_x][p->a_y],
1376: (p->a_dair? airport[p->a_dest].p_sym :
1377: entry[p->a_dest].p_sym));
1378: sprintf(&mbuf[strlen(mbuf)],"%d%c%c ",
1379: p->a_z,
1380: (p->a_nextz > p->a_z ? '^' :
1381: p->a_nextz < p->a_z ? 'v' : ' '),
1382: (p->a_nextz != p->a_z ? p->a_nextz + '0' : ' '));
1383: if (p->a_dy>0) sprintf(&mbuf[strlen(mbuf)],"S");
1384: else if (p->a_dy<0) sprintf(&mbuf[strlen(mbuf)],"N");
1385: if (p->a_dx>0) sprintf(&mbuf[strlen(mbuf)],"E");
1386: else if (p->a_dx<0) sprintf(&mbuf[strlen(mbuf)],"W");
1387: if (p->a_dy == 0) sprintf(&mbuf[strlen(mbuf)]," ");
1388: switch(p->a_hold)
1389: { case 2: sprintf(&mbuf[strlen(mbuf)]," h "); break;
1390: case 1: sprintf(&mbuf[strlen(mbuf)]," * "); break;
1391: case 0: sprintf(&mbuf[strlen(mbuf)]," %c ",p->a_turn-'A'+'a');
1392: break;
1393: }
1394: if (p->a_clear)
1395: sprintf(&mbuf[strlen(mbuf)],"* %c",p->a_clear);
1396: if (p->a_dynew>0) sprintf(&mbuf[strlen(mbuf)],"S");
1397: else if (p->a_dynew<0) sprintf(&mbuf[strlen(mbuf)],"N");
1398: if (p->a_dxnew>0) sprintf(&mbuf[strlen(mbuf)],"E");
1399: else if (p->a_dxnew<0) sprintf(&mbuf[strlen(mbuf)],"W");
1400: if (p->a_dynew == 0) sprintf(&mbuf[strlen(mbuf)]," ");
1401: pfuel = (p->a_type == JET? p->a_fuel/4 : p->a_fuel/2);
1402: sprintf(&mbuf[strlen(mbuf)]," %c",pfuel>=10?'+':pfuel+'0');
1403: if (p->a_plan)
1404: sprintf(&mbuf[strlen(mbuf)]," P");
1405: if (y < 0) /* flight strip to file, not screen */
1406: { if (bigname)
1407: { int st;
1408:
1409: st = p->a_stime;
1410: if (p->a_type == JET)
1411: { st = st / 15;
1412: st = st * 15;
1413: }
1414: else
1415: { st = (st + 45) / 30;
1416: st = (st * 30) - 15;
1417: }
1418: if (st < FIRST_TIME) st = FIRST_TIME;
1419: fprintf(bigstatsfile,"%5d: ",st);
1420: fprintf(bigstatsfile,"%s\n",mbuf);
1421: }
1422: }
1423: else
1424: { msg(cmdx,y,mbuf); /* display the string */
1425: if (outstyle == VERBOSE && p->a_plan)
1426: { for (l = p->a_plan; l; l = l->l_next)
1427: { sprintf(mbuf,"%s %s",l->l_loc,l->l_cmd);
1428: for (s = mbuf; *s; s++) if (*s == RETURN) *s = 0;
1429: msg(cmdx,++y,mbuf);
1430: }
1431: }
1432: while (++y < MAXHEIGHT && msglen[y]) msg(cmdx,y,"");/* clr old plans*/
1433: }
1434: }
1435:
1436: msg(x,y,string) /* put up a message at cursor position x,y */
1437: int x,y;
1438: char *string;
1439: { int i,l;
1440:
1441: cursor(x,y);
1442: printf("%s",string);
1443: l = strlen(string);
1444: for (i=l; i<msglen[y]; i++) putchar(' ');
1445: msglen[y] = l;
1446: }
1447:
1448: delmsg(x,y) /* delete any message starting at cursor posn x,y */
1449: int x,y;
1450: { int i;
1451:
1452: if (msglen[y] <= 0) return;
1453: cursor(x,y);
1454: for (i=0; i<msglen[y]; i++) putchar(' ');
1455: msglen[y] = 0;
1456: }
1457:
1458:
1459: flightplan(p,early)
1460: struct astruct *p;
1461: int early; /* 1 if this plane should be rescheduled for early arrival */
1462: { int i,r,cum;
1463: struct astruct *q;
1464: struct pstruct *loc;
1465:
1466: p->a_type = die(2);
1467: /* starting time: no planes in last 15 minutes; pretend interval
1468: * extends negatively from 0 by FUDGE seconds to make it more likely
1469: * that there will be planes available soon. On the other hand, no
1470: * plane can come on for the first FIRST_TIME seconds.
1471: */
1472: p->a_stime = die(FUDGE + game_time - 15*60) - FUDGE;
1473: if (early) p->a_stime = die(FPTHRESH);
1474: if (p->a_stime <FIRST_TIME) p->a_stime = FIRST_TIME;
1475: if (flowfile) /* flows have been specified */
1476: { r = die(maxflow);
1477: for (i = cum = 0; i < npaths; i++)
1478: { cum += fpath[i].f_freq;
1479: if (cum >= r) break;
1480: }
1481: if (i >= npaths) i = 0; /* shouldn't ever do this */
1482: p->a_sair = fpath[i].f_fair;
1483: p->a_dair = fpath[i].f_tair;
1484: p->a_hold = (p->a_dair && !p->a_sair? 1 : 0);
1485: p->a_start = fpath[i].f_from;
1486: p->a_dest = fpath[i].f_to;
1487:
1488: if (p->a_sair) loc = &airport[p->a_start];
1489: else loc = &entry[p->a_start];
1490: }
1491: else
1492: { p->a_sair = (die(inaptot+inawtot) <= inaptot? 1 : 0);
1493: p->a_dair = (die(outaptot+outawtot) <= outaptot? 1 : 0);
1494:
1495: p->a_hold = (p->a_dair? 1 : 0);
1496: if (p->a_sair)
1497: { p->a_start =pick(die(inaptot),&airport[0],nairport,1);
1498: loc = &airport[p->a_start];
1499: p->a_hold = 0;
1500: if (p->a_dair) p->a_dest = pick(die(inaptot),
1501: &airport[0],nairport,0);
1502: else p->a_dest = pick(die(inawtot),
1503: &entry[0],nentry,0);
1504: }
1505: else
1506: { p->a_start = pick(die(inawtot),&entry[0],nentry,0);
1507: loc = &entry[p->a_start];
1508: if (p->a_dair) p->a_dest = pick(die(inaptot),
1509: &airport[0],nairport,0);
1510: else do p->a_dest = pick(die(inawtot),
1511: &entry[0],nentry,0);
1512: while (p->a_dest == p->a_start);
1513: }
1514: }
1515: p->a_active = INACTIVE;
1516: p->a_z = (p->a_sair? 0 : 6);
1517: if (!p->a_sair) /* separate planes by 1000' altitude */
1518: for (q = &plane[0]; q != &plane[PMAX]; q++)
1519: { if (q == p && !early) break;
1520: if (q == p) continue;
1521: if (p->a_start == q->a_start)
1522: if (abs(q->a_stime - p->a_stime) < SAFETY)
1523: if (q->a_z >= p->a_z)
1524: p->a_z = q->a_z + 1;
1525: }
1526: if (flowfile && p->a_z < NALT)
1527: { p->a_opt = fpath[i].f_dist[p->a_z];
1528: p->a_cmds = fpath[i].f_cmds;
1529: }
1530: p->a_prev = p->a_pprev = p->a_aprev = 0;
1531: p->a_x = loc->p_x;
1532: p->a_y = loc->p_y;
1533: p->a_dx = loc->p_dx;
1534: p->a_dy = loc->p_dy;
1535: p->a_dxnew = p->a_dynew = 0;
1536: p->a_lastz = p->a_nextz = p->a_z;
1537: p->a_turn = 0;
1538: p->a_fuel = (p->a_type == JET ? JETFUEL : PROPFUEL);
1539: p->a_clear = 0; /* not cleared for any approach */
1540: p->a_plan = 0; /* no monitor points on the flight plan */
1541: p->a_load = p->a_ticks = p->a_planned = p->a_dist = 0;
1542: }
1543:
1544: pick(draw,point,num,in)
1545: int draw;
1546: struct pstruct *point;
1547: int num,in;
1548: { register int i,cum;
1549:
1550: for (i = cum = 0; i<num; i++,point++)
1551: { if (in) cum += point->p_inprop;
1552: else cum += point->p_outprop;
1553: if (cum >= draw) return(i);
1554: }
1555: return(0); /* should never get here */
1556: }
1557:
1558: #if !NRAND
1559: die(n) /* throw an n-sided die (sides 0 thru n-1) */
1560: int n;
1561: { int r;
1562:
1563: #ifdef vax
1564: r = rand() >> 10; /* rightmost bits are not very random */
1565: #else
1566: r = random(); /* rightmost bits are not very random */
1567: #endif
1568: return(r % n); /* enough resolution to make this o.k. */
1569: }
1570: #endif
1571:
1572: getdigit() /* keep reading chars from terminal until get a digit */
1573: { int c; /* returns digit - '0' */
1574:
1575: do {
1576: if (rubout) return 9;
1577: c = getchar() & 0177;
1578: } while (c < '0' || c > '9');
1579: putchar(c);
1580: return(c-'0');
1581: }
1582:
1583: time_t
1584: get_time() /* returns time in seconds since 1970 */
1585: {
1586: time_t t;
1587:
1588: time(&t);
1589: return(t); /* seconds since 1970 */
1590: }
1591:
1592:
1593: display() /* put up initial screen */
1594: { int i,j;
1595:
1596: for (j=0; j<height; j++)
1597: { cursor(0,j); /* general enough to extend to graphics */
1598: for (i=0; i<width; i++)
1599: { putchar(screen[i][j]);
1600: putchar(' ');
1601: }
1602: }
1603: }
1604:
1605:
1606: cursor(x,y) /* move to location x,y on WIDTHxHEIGHT screen */
1607: char x,y; /* this is in terms of screen, i.e. with x's 2 chars wide */
1608: {
1609: poscur(2*x,y);
1610: }
1611:
1612:
1613: poscur(x,y) /* position cursor at this absolute location on screen */
1614: char x,y;
1615: {
1616: char *tgoto();
1617: printf ("%s", tgoto (cmbuf, x, y));
1618: }
1619:
1620:
1621: clearscreen()
1622: {
1623: printf ("%s", clbuf);
1624: }
1625:
1626: char *
1627: plural(n) /* returns "s" unless n=1 */
1628: int n;
1629: { static char s[2] = "s";
1630:
1631: if (n != 1) return(s);
1632: else return("");
1633: }
1634:
1635: done(flag) /* clean up */
1636: int flag;
1637: { FILE *statfile;
1638: struct passwd *pw;
1639: time_t now;
1640: char *c,*ct;
1641: extern char *ctime();
1642:
1643: if (!moviefile)
1644: { tty_setting.sg_flags = tty_flags;/* put terminal back the way it was*/
1645: ioctl(0, TIOCSETP, &tty_setting);
1646: }
1647: if (flag) cursor(LEFT,height+1);
1648:
1649: if ((statfile = fopen(STATSFILE,"a")) != NULL)
1650: { pw = getpwuid(getuid());
1651: now = get_time();
1652: ct = ctime(&now);
1653: for (c= &ct[4]; c <= &ct[16]; c++) fputc(*c,statfile);
1654: if (!flag) fprintf(statfile,"%8s: aborted\n",pw->pw_name);
1655: else
1656: { fprintf(statfile,"%8s: %2d %3d %2d %2d %3d %5d %3d %2d",
1657: pw->pw_name,crash,system,fuel,bound,goaround,
1658: fuelused,cmds,remain);
1659: fprintf(statfile,". %d %2d %s\n",
1660: initseed,initgame_time/60,airspace);
1661: fclose(statfile);
1662: }
1663: }
1664: if (!flag) return;
1665: ttystats(stdout);
1666: if (bigname)
1667: { int i;
1668: float load,ticks,plans,route,d,stripped;
1669:
1670: for (i = 0; i < nplanes; i++)
1671: { load = plane[i].a_load;
1672: ticks = plane[i].a_ticks;
1673: plans = plane[i].a_planned;
1674: route = plane[i].a_opt;
1675: stripped = plane[i].a_prev;
1676: d = plane[i].a_dist;
1677: fprintf(bigstatsfile,
1678: "%c: %2d tks, %2d/%2d=%1.2f, %d, %.2f a/tk, %.2f p/tk, %.2f a/ptk, %.2f p/ptk, %c, %s\n",
1679: plane[i].a_id,plane[i].a_ticks,
1680: plane[i].a_dist,
1681: plane[i].a_opt,
1682: d/route,
1683: plane[i].a_cmds,
1684: load/ticks,
1685: plans/ticks,
1686: plane[i].a_aprev/stripped,
1687: plane[i].a_pprev/stripped,
1688: plane[i].a_sair? 'W' : 'A',
1689: plane[i].a_pplanned? "+P" : "-P"
1690: );
1691: }
1692: }
1693: if (bigname) ttystats(bigstatsfile);
1694: if (protofile) fclose(proto);
1695: if (bigname) fclose(bigstatsfile);
1696: }
1697:
1698: ttystats(file)
1699: FILE *file;
1700: { char *eol;
1701:
1702: if (file == bigstatsfile) eol = "\n";
1703: else {
1704: clearscreen();
1705: eol = "\n\r";
1706: }
1707: if (crash) fprintf(file,"%d plane%s out of fuel, crashed%s",
1708: crash,plural(crash),eol);
1709: if (system) fprintf(file,"%d system error%s%s",
1710: system,plural(system),eol);
1711: if (fuel) fprintf(file,"%d tow truck%s called%s",
1712: fuel,plural(fuel),eol);
1713: if (bound) fprintf(file,"%d boundary error%s%s",
1714: bound,plural(bound),eol);
1715: if (goaround) fprintf(file,"%d go-around%s%s",
1716: goaround,plural(goaround),eol);
1717: if (crash+system+fuel+bound+remain == 0)
1718: { fprintf(file,"GREAT JOB!! Have you considered a career in ATC?\n");
1719: fprintf(file,"Now try %d minutes...\n",initgame_time/60-5);
1720: }
1721: fprintf(file,"Fuel used: %d\n",fuelused);
1722: fprintf(file,"Commands issued (including deferred): %d\n",icmds);
1723: fprintf(file,"Commands executed: %d\n",cmds);
1724: fprintf(file,"Airspace: %s; seed: %d; time=%d; %d remaining.%s",
1725: airspace,initseed,initgame_time/60,remain,eol);
1726: if (file == bigstatsfile) fprintf(file,"\f");
1727: }
1728:
1729: err(msg,arg1,arg2)
1730: char *msg;
1731: { fprintf(stderr,msg,arg1,arg2);
1732: done(0);
1733: exit(1);
1734: }
1735:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.