]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - monop/execute.c
Remove last traces of old save/load code. We no longer need a private
[bsdgames-darwin.git] / monop / execute.c
1 /* $NetBSD: execute.c,v 1.16 2008/02/24 01:41:14 dholland Exp $ */
2
3 /*
4 * Copyright (c) 1980, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)execute.c 8.1 (Berkeley) 5/31/93";
36 #else
37 __RCSID("$NetBSD: execute.c,v 1.16 2008/02/24 01:41:14 dholland Exp $");
38 #endif
39 #endif /* not lint */
40
41 #include "monop.ext"
42 #include <fcntl.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <limits.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/time.h>
49 #include <time.h>
50 #include <errno.h>
51
52 #define MIN_FORMAT_VERSION 1
53 #define CUR_FORMAT_VERSION 1
54 #define MAX_FORMAT_VERSION 1
55
56 typedef struct stat STAT;
57 typedef struct tm TIME;
58
59 static char buf[257];
60
61 static bool new_play; /* set if move on to new player */
62
63 static void show_move(void);
64
65 static void restore_reset(void);
66 static int restore_parseline(char *txt);
67 static int restore_toplevel_attr(const char *attribute, char *txt);
68 static int restore_player_attr(const char *attribute, char *txt);
69 static int restore_deck_attr(const char *attribute, char *txt);
70 static int restore_square_attr(const char *attribute, char *txt);
71 static int getnum(const char *what, char *txt, int min, int max, int *ret);
72 static int getnum_withbrace(const char *what, char *txt, int min, int max,
73 int *ret);
74
75 /*
76 * This routine executes the given command by index number
77 */
78 void
79 execute(com_num)
80 int com_num;
81 {
82 new_play = FALSE; /* new_play is true if fixing */
83 (*func[com_num])();
84 notify();
85 force_morg();
86 if (new_play)
87 next_play();
88 else if (num_doub)
89 printf("%s rolled doubles. Goes again\n", cur_p->name);
90 }
91
92 /*
93 * This routine moves a piece around.
94 */
95 void
96 do_move()
97 {
98 int r1, r2;
99 bool was_jail;
100
101 new_play = was_jail = FALSE;
102 printf("roll is %d, %d\n", r1=roll(1, 6), r2=roll(1, 6));
103 if (cur_p->loc == JAIL) {
104 was_jail++;
105 if (!move_jail(r1, r2)) {
106 new_play++;
107 goto ret;
108 }
109 }
110 else {
111 if (r1 == r2 && ++num_doub == 3) {
112 printf("That's 3 doubles. You go to jail\n");
113 goto_jail();
114 new_play++;
115 goto ret;
116 }
117 move(r1+r2);
118 }
119 if (r1 != r2 || was_jail)
120 new_play++;
121 ret:
122 return;
123 }
124
125 /*
126 * This routine moves a normal move
127 */
128 void
129 move(rl)
130 int rl;
131 {
132 int old_loc;
133
134 old_loc = cur_p->loc;
135 cur_p->loc = (cur_p->loc + rl) % N_SQRS;
136 if (cur_p->loc < old_loc && rl > 0) {
137 cur_p->money += 200;
138 printf("You pass %s and get $200\n", board[0].name);
139 }
140 show_move();
141 }
142
143 /*
144 * This routine shows the results of a move
145 */
146 static void
147 show_move()
148 {
149 SQUARE *sqp;
150
151 sqp = &board[cur_p->loc];
152 printf("That puts you on %s\n", sqp->name);
153 switch (sqp->type) {
154 case SAFE:
155 printf("That is a safe place\n");
156 break;
157 case CC:
158 cc();
159 break;
160 case CHANCE:
161 chance();
162 break;
163 case INC_TAX:
164 inc_tax();
165 break;
166 case GOTO_J:
167 goto_jail();
168 break;
169 case LUX_TAX:
170 lux_tax();
171 break;
172 case PRPTY:
173 case RR:
174 case UTIL:
175 if (sqp->owner < 0) {
176 printf("That would cost $%d\n", sqp->cost);
177 if (getyn("Do you want to buy? ") == 0) {
178 buy(player, sqp);
179 cur_p->money -= sqp->cost;
180 }
181 else if (num_play > 2)
182 bid();
183 }
184 else if (sqp->owner == player)
185 printf("You own it.\n");
186 else
187 rent(sqp);
188 }
189 }
190
191 /*
192 * Reset the game state.
193 */
194 static void
195 reset_game(void)
196 {
197 int i;
198
199 for (i = 0; i < N_SQRS; i++) {
200 board[i].owner = -1;
201 if (board[i].type == PRPTY) {
202 board[i].desc->morg = 0;
203 board[i].desc->houses = 0;
204 } else if (board[i].type == RR || board[i].type == UTIL) {
205 board[i].desc->morg = 0;
206 }
207 }
208
209 for (i = 0; i < 2; i++) {
210 deck[i].top_card = 0;
211 deck[i].gojf_used = FALSE;
212 }
213
214 if (play) {
215 for (i = 0; i < num_play; i++) {
216 free(play[i].name);
217 play[i].name = NULL;
218 }
219 free(play);
220 play = NULL;
221 }
222
223 for (i = 0; i < MAX_PL+2; i++) {
224 name_list[i] = NULL;
225 }
226
227 cur_p = NULL;
228 num_play = 0;
229 player = 0;
230 num_doub = 0;
231 fixing = FALSE;
232 trading = FALSE;
233 told_em = FALSE;
234 spec = FALSE;
235 }
236
237
238 /*
239 * This routine saves the current game for use at a later date
240 */
241 void
242 save()
243 {
244 char *sp;
245 FILE *outf;
246 time_t t;
247 struct stat sb;
248 int i, j;
249
250 printf("Which file do you wish to save it in? ");
251 fgets(buf, sizeof(buf), stdin);
252 if (feof(stdin))
253 return;
254 sp = strchr(buf, '\n');
255 if (sp)
256 *sp = '\0';
257
258 /*
259 * check for existing files, and confirm overwrite if needed
260 */
261
262 if (stat(buf, &sb) == 0
263 && getyn("File exists. Do you wish to overwrite? ") > 0)
264 return;
265
266 outf = fopen(buf, "w");
267 if (outf == NULL) {
268 warn("%s", buf);
269 return;
270 }
271 printf("\"%s\" ", buf);
272 time(&t); /* get current time */
273
274 /* Header */
275 fprintf(outf, "NetBSD monop format v%d\n", CUR_FORMAT_VERSION);
276 fprintf(outf, "time %s", ctime(&t)); /* ctime includes a \n */
277 fprintf(outf, "numplayers %d\n", num_play);
278 fprintf(outf, "currentplayer %d\n", player);
279 fprintf(outf, "doubles %d\n", num_doub);
280
281 /* Players */
282 for (i = 0; i < num_play; i++) {
283 fprintf(outf, "player %d {\n", i);
284 fprintf(outf, " name %s\n", name_list[i]);
285 fprintf(outf, " money %d\n", play[i].money);
286 fprintf(outf, " loc %d\n", play[i].loc);
287 fprintf(outf, " num_gojf %d\n", play[i].num_gojf);
288 fprintf(outf, " in_jail %d\n", play[i].in_jail);
289 fprintf(outf, "}\n");
290 }
291
292 /* Decks */
293 for (i = 0; i < 2; i++) {
294 fprintf(outf, "deck %d {\n", i);
295 fprintf(outf, " numcards %d\n", deck[i].num_cards);
296 fprintf(outf, " topcard %d\n", deck[i].top_card);
297 fprintf(outf, " gojf_used %d\n", deck[i].gojf_used);
298 fprintf(outf, " offsets");
299 for (j = 0; j < deck[i].num_cards; j++)
300 fprintf(outf, " %ld", (long)(deck[i].offsets[j]));
301 fprintf(outf, "\n");
302 fprintf(outf, "}\n");
303 }
304
305 /* Board */
306 for (i = 0; i < N_SQRS; i++) {
307 fprintf(outf, "square %d {\n", i);
308 fprintf(outf, "owner %d\n", board[i].owner);
309 if (board[i].owner < 0) {
310 /* nothing */
311 } else if (board[i].type == PRPTY) {
312 fprintf(outf, "morg %d\n", board[i].desc->morg);
313 fprintf(outf, "houses %d\n", board[i].desc->houses);
314 } else if (board[i].type == RR || board[i].type == UTIL) {
315 fprintf(outf, "morg %d\n", board[i].desc->morg);
316 }
317 fprintf(outf, "}\n");
318 }
319 if (ferror(outf) || fflush(outf))
320 warnx("write error");
321 fclose(outf);
322
323 strcpy(buf, ctime(&t));
324 for (sp = buf; *sp != '\n'; sp++)
325 continue;
326 *sp = '\0';
327 printf("[%s]\n", buf);
328 }
329
330 /*
331 * This routine restores an old game from a file
332 */
333 void
334 restore()
335 {
336 char *sp;
337
338 printf("Which file do you wish to restore from? ");
339 fgets(buf, sizeof(buf), stdin);
340 if (feof(stdin))
341 return;
342 sp = strchr(buf, '\n');
343 if (sp)
344 *sp = '\0';
345 rest_f(buf);
346 }
347
348 /*
349 * This does the actual restoring. It returns TRUE if the
350 * backup was successful, else false.
351 */
352 int
353 rest_f(file)
354 const char *file;
355 {
356 char *sp;
357 FILE *inf;
358 char xbuf[80];
359 STAT sbuf;
360 char readbuf[512];
361
362 inf = fopen(file, "r");
363 if (inf == NULL) {
364 warn("%s", file);
365 return FALSE;
366 }
367 printf("\"%s\" ", file);
368 if (fstat(fileno(inf), &sbuf) < 0) {
369 err(1, "%s: fstat", file);
370 }
371
372 /* Clear the game state to prevent brokenness on misordered files. */
373 reset_game();
374
375 /* Reset the parser */
376 restore_reset();
377
378 /* Note: can't use buf[], file might point at it. (Lame...) */
379 while (fgets(readbuf, sizeof(readbuf), inf)) {
380 /*
381 * The input buffer is long enough to handle anything
382 * that's supposed to be in the output buffer, so if
383 * we get a partial line, complain.
384 */
385 sp = strchr(readbuf, '\n');
386 if (sp == NULL) {
387 printf("file is corrupt: long lines.\n");
388 break;
389 }
390 *sp = '\0';
391
392 if (restore_parseline(readbuf)) {
393 break;
394 }
395 }
396
397 if (ferror(inf))
398 warnx("%s: read error", file);
399 fclose(inf);
400
401 name_list[num_play] = "done";
402
403 /*
404 * We could at this point crosscheck the following:
405 * - there are only two GOJF cards floating around
406 * - total number of houses and hotels does not exceed maximums
407 * - no props are both built and mortgaged
408 * but for now we don't.
409 */
410
411 strcpy(xbuf, ctime(&sbuf.st_mtime));
412 for (sp = xbuf; *sp != '\n'; sp++)
413 continue;
414 *sp = '\0';
415 printf("[%s]\n", xbuf);
416 return TRUE;
417 }
418
419 /*
420 * State of the restore parser
421 */
422 static int restore_version;
423 static enum {
424 RI_NONE,
425 RI_PLAYER,
426 RI_DECK,
427 RI_SQUARE,
428 } restore_item;
429 static int restore_itemnum;
430
431 /*
432 * Reset the restore parser
433 */
434 static void
435 restore_reset(void)
436 {
437 restore_version = -1;
438 restore_item = RI_NONE;
439 restore_itemnum = -1;
440 }
441
442 /*
443 * Handle one line of the save file
444 */
445 static int
446 restore_parseline(char *txt)
447 {
448 char *attribute;
449 char *s;
450
451 if (restore_version < 0) {
452 /* Haven't seen the header yet. Demand it right away. */
453 if (!strncmp(txt, "NetBSD monop format v", 21)) {
454 return getnum("format version", txt+21,
455 MIN_FORMAT_VERSION,
456 MAX_FORMAT_VERSION,
457 &restore_version);
458 }
459 printf("file is not a monop save file.\n");
460 return -1;
461 }
462
463 /* Check for lines that are right braces. */
464 if (!strcmp(txt, "}")) {
465 if (restore_item == RI_NONE) {
466 printf("mismatched close brace.\n");
467 return -1;
468 }
469 restore_item = RI_NONE;
470 restore_itemnum = -1;
471 return 0;
472 }
473
474 /* Any other line must begin with a word, which is the attribute. */
475 s = txt;
476 while (*s==' ')
477 s++;
478 attribute = s;
479 s = strchr(attribute, ' ');
480 if (s == NULL) {
481 printf("file is corrupt: attribute %s lacks value.\n",
482 attribute);
483 return -1;
484 }
485 *(s++) = '\0';
486 while (*s==' ')
487 s++;
488 /* keep the remaining text for further handling */
489 txt = s;
490
491 switch (restore_item) {
492 case RI_NONE:
493 /* toplevel attributes */
494 return restore_toplevel_attr(attribute, txt);
495
496 case RI_PLAYER:
497 /* player attributes */
498 return restore_player_attr(attribute, txt);
499
500 case RI_DECK:
501 /* deck attributes */
502 return restore_deck_attr(attribute, txt);
503
504 case RI_SQUARE:
505 /* board square attributes */
506 return restore_square_attr(attribute, txt);
507 }
508 /* NOTREACHED */
509 printf("internal logic error\n");
510 return -1;
511 }
512
513 static int
514 restore_toplevel_attr(const char *attribute, char *txt)
515 {
516 if (!strcmp(attribute, "time")) {
517 /* nothing */
518 } else if (!strcmp(attribute, "numplayers")) {
519 if (getnum("numplayers", txt, 2, MAX_PL, &num_play) < 0) {
520 return -1;
521 }
522 if (play != NULL) {
523 printf("numplayers: multiple settings\n");
524 return -1;
525 }
526 play = calloc(num_play, sizeof(play[0]));
527 if (play == NULL) {
528 err(1, "calloc");
529 }
530 } else if (!strcmp(attribute, "currentplayer")) {
531 if (getnum("currentplayer", txt, 0, num_play-1, &player) < 0) {
532 return -1;
533 }
534 if (play == NULL) {
535 printf("currentplayer: before numplayers\n");
536 return -1;
537 }
538 cur_p = &play[player];
539 } else if (!strcmp(attribute, "doubles")) {
540 if (getnum("doubles", txt, 0, 2, &num_doub) < 0) {
541 return -1;
542 }
543 } else if (!strcmp(attribute, "player")) {
544 if (getnum_withbrace("player", txt, 0, num_play-1,
545 &restore_itemnum) < 0) {
546 return -1;
547 }
548 restore_item = RI_PLAYER;
549 } else if (!strcmp(attribute, "deck")) {
550 if (getnum_withbrace("deck", txt, 0, 1,
551 &restore_itemnum) < 0) {
552 return -1;
553 }
554 restore_item = RI_DECK;
555 } else if (!strcmp(attribute, "square")) {
556 if (getnum_withbrace("square", txt, 0, N_SQRS-1,
557 &restore_itemnum) < 0) {
558 return -1;
559 }
560 restore_item = RI_SQUARE;
561 } else {
562 printf("unknown attribute %s\n", attribute);
563 return -1;
564 }
565 return 0;
566 }
567
568 static int
569 restore_player_attr(const char *attribute, char *txt)
570 {
571 PLAY *pp;
572 int tmp;
573
574 if (play == NULL) {
575 printf("player came before numplayers.\n");
576 return -1;
577 }
578 pp = &play[restore_itemnum];
579
580 if (!strcmp(attribute, "name")) {
581 if (pp->name != NULL) {
582 printf("player has multiple names.\n");
583 return -1;
584 }
585 /* XXX should really systematize the max name length */
586 if (strlen(txt) > 256) {
587 txt[256] = 0;
588 }
589 pp->name = strdup(txt);
590 if (pp->name == NULL)
591 err(1, "strdup");
592 name_list[restore_itemnum] = pp->name;
593 } else if (!strcmp(attribute, "money")) {
594 if (getnum(attribute, txt, 0, INT_MAX, &pp->money) < 0) {
595 return -1;
596 }
597 } else if (!strcmp(attribute, "loc")) {
598 /* note: not N_SQRS-1 */
599 if (getnum(attribute, txt, 0, N_SQRS, &tmp) < 0) {
600 return -1;
601 }
602 pp->loc = tmp;
603 } else if (!strcmp(attribute, "num_gojf")) {
604 if (getnum(attribute, txt, 0, 2, &tmp) < 0) {
605 return -1;
606 }
607 pp->num_gojf = tmp;
608 } else if (!strcmp(attribute, "in_jail")) {
609 if (getnum(attribute, txt, 0, 3, &tmp) < 0) {
610 return -1;
611 }
612 pp->in_jail = tmp;
613 if (pp->in_jail > 0 && pp->loc != JAIL) {
614 printf("player escaped from jail?\n");
615 return -1;
616 }
617 } else {
618 printf("unknown attribute %s\n", attribute);
619 return -1;
620 }
621 return 0;
622 }
623
624 static int
625 restore_deck_attr(const char *attribute, char *txt)
626 {
627 int tmp, j;
628 char *s;
629 DECK *dp;
630
631 dp = &deck[restore_itemnum];
632
633 if (!strcmp(attribute, "numcards")) {
634 if (getnum(attribute, txt, dp->num_cards, dp->num_cards,
635 &tmp) < 0) {
636 return -1;
637 }
638 } else if (!strcmp(attribute, "topcard")) {
639 if (getnum(attribute, txt, 0, dp->num_cards,
640 &dp->top_card) < 0) {
641 return -1;
642 }
643 } else if (!strcmp(attribute, "gojf_used")) {
644 if (getnum(attribute, txt, 0, 1, &tmp) < 0) {
645 return -1;
646 }
647 dp->gojf_used = tmp;
648 } else if (!strcmp(attribute, "offsets")) {
649 errno = 0;
650 s = txt;
651 for (j = 0; j<dp->num_cards; j++) {
652 dp->offsets[j] = strtol(s, &s, 10);
653 }
654 if (errno) {
655 printf("offsets: invalid values\n");
656 return -1;
657 }
658 } else {
659 printf("unknown attribute %s\n", attribute);
660 return -1;
661 }
662 return 0;
663 }
664
665 static int
666 restore_square_attr(const char *attribute, char *txt)
667 {
668 SQUARE *sp = &board[restore_itemnum];
669 int tmp;
670
671 if (!strcmp(attribute, "owner")) {
672 if (getnum(attribute, txt, -1, num_play-1, &tmp) < 0) {
673 return -1;
674 }
675 sp->owner = tmp;
676 if (tmp >= 0)
677 add_list(tmp, &play[tmp].own_list, restore_itemnum);
678 } else if (!strcmp(attribute, "morg")) {
679 if (sp->type != PRPTY && sp->type != RR && sp->type != UTIL) {
680 printf("unownable property is mortgaged.\n");
681 return -1;
682 }
683 if (getnum(attribute, txt, 0, 1, &tmp) < 0) {
684 return -1;
685 }
686 sp->desc->morg = tmp;
687 } else if (!strcmp(attribute, "houses")) {
688 if (sp->type != PRPTY) {
689 printf("unbuildable property has houses.\n");
690 return -1;
691 }
692 if (getnum(attribute, txt, 0, 5, &tmp) < 0) {
693 return -1;
694 }
695 sp->desc->houses = tmp;
696 } else {
697 printf("unknown attribute %s\n", attribute);
698 return -1;
699 }
700 return 0;
701 }
702
703 static int
704 getnum(const char *what, char *txt, int min, int max, int *ret)
705 {
706 char *s;
707 long l;
708
709 errno = 0;
710 l = strtol(txt, &s, 10);
711 if (errno || strlen(s)>0) {
712 printf("%s: not a number.\n", what);
713 return -1;
714 }
715 if (l < min || l > max) {
716 printf("%s: out of range.\n", what);
717 }
718 *ret = l;
719 return 0;
720 }
721
722 static int
723 getnum_withbrace(const char *what, char *txt, int min, int max, int *ret)
724 {
725 char *s;
726 s = strchr(txt, ' ');
727 if (s == NULL) {
728 printf("%s: expected open brace\n", what);
729 return -1;
730 }
731 *(s++) = '\0';
732 while (*s == ' ')
733 s++;
734 if (*s != '{') {
735 printf("%s: expected open brace\n", what);
736 return -1;
737 }
738 if (s[1] != 0) {
739 printf("%s: garbage after open brace\n", what);
740 return -1;
741 }
742 return getnum(what, txt, min, max, ret);
743 }