xref: /openbsd/games/monop/execute.c (revision 09467b48)
1 /*	$OpenBSD: execute.c,v 1.15 2019/06/28 13:32:52 deraadt Exp $	*/
2 /*	$NetBSD: execute.c,v 1.3 1995/03/23 08:34:38 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/stat.h>
34 
35 #include <err.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 
41 #include "monop.ext"
42 
43 typedef	struct stat	STAT;
44 typedef	struct tm	TIME;
45 
46 static char	buf[257];
47 
48 static bool	new_play;	/* set if move on to new player		*/
49 
50 static void	show_move(void);
51 
52 /*
53  *	This routine takes user input and puts it in buf
54  */
55 void
56 getbuf(void)
57 {
58 	char	*sp;
59 	int	tmpin, i;
60 
61 	i = 1;
62 	sp = buf;
63 	while (((tmpin = getchar()) != '\n') && (i < (int)sizeof(buf)) &&
64 	    (tmpin != EOF)) {
65 		*sp++ = tmpin;
66 		i++;
67 	}
68 	if (tmpin == EOF) {
69 		printf("user closed input stream, quitting...\n");
70                 exit(0);
71 	}
72 	*sp = '\0';
73 }
74 /*
75  *	This routine executes the given command by index number
76  */
77 void
78 execute(int com_num)
79 {
80 	new_play = FALSE;	/* new_play is true if fixing	*/
81 	(*func[com_num])();
82 	notify();
83 	force_morg();
84 	if (new_play)
85 		next_play();
86 	else if (num_doub)
87 		printf("%s rolled doubles.  Goes again\n", cur_p->name);
88 }
89 /*
90  *	This routine moves a piece around.
91  */
92 void
93 do_move(void)
94 {
95 	int	r1, r2;
96 	bool	was_jail;
97 
98 	new_play = was_jail = FALSE;
99 	printf("roll is %d, %d\n", r1 = roll(1, 6), r2 = roll(1, 6));
100 	if (cur_p->loc == JAIL) {
101 		was_jail++;
102 		if (!move_jail(r1, r2)) {
103 			new_play++;
104 			goto ret;
105 		}
106 	}
107 	else {
108 		if (r1 == r2 && ++num_doub == 3) {
109 			printf("That's 3 doubles.  You go to jail\n");
110 			goto_jail();
111 			new_play++;
112 			goto ret;
113 		}
114 		move(r1 + r2);
115 	}
116 	if (r1 != r2 || was_jail)
117 		new_play++;
118 ret:
119 	return;
120 }
121 /*
122  *	This routine moves a normal move
123  */
124 void
125 move(int rl)
126 {
127 	int	old_loc;
128 
129 	old_loc = cur_p->loc;
130 	cur_p->loc = (cur_p->loc + rl) % N_SQRS;
131 	if (cur_p->loc < old_loc && rl > 0) {
132 		cur_p->money += 200;
133 		printf("You pass %s and get $200\n", board[0].name);
134 	}
135 	show_move();
136 }
137 /*
138  *	This routine shows the results of a move
139  */
140 static void
141 show_move(void)
142 {
143 	SQUARE	*sqp;
144 
145 	sqp = &board[(int)cur_p->loc];
146 	printf("That puts you on %s\n", sqp->name);
147 	switch (sqp->type) {
148 	case SAFE:
149 		printf("That is a safe place\n");
150 		break;
151 	case CC:
152 		cc();
153 		break;
154 	case CHANCE:
155 		chance();
156 		break;
157 	case INC_TAX:
158 		inc_tax();
159 		break;
160 	case GOTO_J:
161 		goto_jail();
162 		break;
163 	case LUX_TAX:
164 		lux_tax();
165 		break;
166 	case PRPTY:
167 	case RR:
168 	case UTIL:
169 		if (sqp->owner < 0) {
170 			printf("That would cost $%d\n", sqp->cost);
171 			if (getyn("Do you want to buy? ") == 0) {
172 				buy(player, sqp);
173 				cur_p->money -= sqp->cost;
174 			}
175 			else if (num_play > 2)
176 				bid();
177 		}
178 		else if (sqp->owner == player)
179 			printf("You own it.\n");
180 		else
181 			rent(sqp);
182 	}
183 }
184 
185 
186 #define MONOP_TAG "monop(6) save file"
187 /*
188  *	This routine saves the current game for use at a later date
189  */
190 void
191 save(void)
192 {
193 	int i, j;
194 	time_t t;
195 	struct stat sb;
196 	char *sp;
197 	FILE *outf;
198 
199 	printf("Which file do you wish to save it in? ");
200 	getbuf();
201 
202 	/*
203 	 * check for existing files, and confirm overwrite if needed
204 	 */
205 	if (stat(buf, &sb) == 0
206 	    && getyn("File exists.  Do you wish to overwrite? ") > 0)
207 		return;
208 
209 	umask(022);
210 	if ((outf = fopen(buf, "w")) == NULL) {
211 		warn("%s", buf);
212 		return;
213 	}
214 	printf("\"%s\" ", buf);
215 	time(&t);			/* get current time		*/
216 	fprintf(outf, "%s\n", MONOP_TAG);
217 	fprintf(outf, "# %s", ctime(&t));	/* ctime() has \n */
218 	fprintf(outf, "%d %d %d\n", num_play, player, num_doub);
219 	for (i = 0; i < num_play; i++)
220 		fprintf(outf, "%s\n", name_list[i]);
221 	for (i = 0; i < num_play; i++)
222 		fprintf(outf, "%d %d %d %d\n", play[i].money, play[i].loc,
223 		    play[i].num_gojf, play[i].in_jail);
224 	/* Deck status */
225 	for (i = 0; i < 2; i++) {
226 		fprintf(outf, "%d %d %d\n", (int)(deck[i].num_cards),
227 		    (int)(deck[i].top_card), (int)(deck[i].gojf_used));
228 		for (j = 0; j < deck[i].num_cards; j++)
229 			fprintf(outf, "%ld ", (long)(deck[i].offsets[j]));
230 		fprintf(outf, "\n");
231 	}
232 	/* Ownership */
233 	for (i = 0; i < N_SQRS; i++) {
234 		if (board[i].owner >= 0) {
235 			if (board[i].type == PRPTY)
236 				fprintf(outf, "%d %d %d %d\n", i, board[i].owner,
237 				    board[i].desc->morg, board[i].desc->houses);
238 			else if (board[i].type == RR || board[i].type == UTIL)
239 				fprintf(outf, "%d %d %d 0\n", i, board[i].owner,
240 				    board[i].desc->morg);
241 		}
242 	}
243 	fclose(outf);
244 
245 	strlcpy(buf, ctime(&t), sizeof buf);
246 	for (sp = buf; *sp != '\n'; sp++)
247 		continue;
248 	*sp = '\0';
249 	printf("[%s]\n", buf);
250 }
251 /*
252  * If we are restoring during a game, try not to leak memory.
253  */
254 void
255 game_restore(void)
256 {
257 	int i;
258 
259 	free(play);
260 	for (i = 0; i < num_play; i++)
261 		free(name_list[i]);
262 	restore();
263 }
264 /*
265  *	This routine restores an old game from a file
266  */
267 void
268 restore(void)
269 {
270 	printf("Which file do you wish to restore from? ");
271 	getbuf();
272 	if (rest_f(buf) == FALSE) {
273 		printf("Restore failed\n");
274 		exit(1);
275 	}
276 }
277 /*
278  *	This does the actual restoring.  It returns TRUE if the
279  *	backup was successful, else FALSE.
280  */
281 int
282 rest_f(char *file)
283 {
284 	char *sp;
285 	int  i, j, num;
286 	FILE *inf;
287 	char *st, *a, *b;
288 	size_t len;
289 	STAT sbuf;
290 	int  t1;
291 	short t2, t3, t4;
292 	long tl;
293 
294 	printf("\"%s\" ", file);
295 	if (stat(file, &sbuf) == -1) {		/* get file stats	*/
296 		warn("%s", file);
297 		return(FALSE);
298 	}
299 	if ((inf = fopen(file, "r")) == NULL) {
300 		warn("%s", file);
301 		return(FALSE);
302 	}
303 
304 	num = 1;
305 	st = fgetln(inf, &len);
306 	if (st == NULL || len != strlen(MONOP_TAG) + 1 ||
307 	    strncmp(st, MONOP_TAG, strlen(MONOP_TAG))) {
308 badness:
309 		warnx("%s line %d", file, num);
310 		fclose(inf);
311 		return(FALSE);
312 	}
313 	num++;
314 	if (fgetln(inf, &len) == NULL)
315 		goto badness;
316 	num++;
317 	if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
318 		goto badness;
319 	st[len - 1] = '\0';
320 	if (sscanf(st, "%d %d %d", &num_play, &player, &num_doub) != 3 ||
321 	    num_play > MAX_PL || num_play < 1 ||
322 	    player < 0 || player >= num_play ||
323 	    num_doub < 0 || num_doub > 2)
324 		goto badness;
325 	if ((play = calloc(num_play, sizeof(PLAY))) == NULL)
326 		err(1, NULL);
327 	cur_p = play + player;
328 	/* Names */
329 	for (i = 0; i < num_play; i++) {
330 		num++;
331 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
332 			goto badness;
333 		st[len - 1] = '\0';
334 		if ((name_list[i] = play[i].name = strdup(st)) == NULL)
335 			err(1, NULL);
336 	}
337 	if ((name_list[i++] = strdup("done")) == NULL)
338 		err(1, NULL);
339 	name_list[i] = NULL;
340 	/* Money, location, GOJF cards, turns in jail */
341 	for (i = 0; i < num_play; i++) {
342 		num++;
343 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
344 			goto badness;
345 		st[len - 1] = '\0';
346 		if (sscanf(st, "%d %hd %hd %hd", &(play[i].money), &t2,
347 		    &t3, &t4) != 4 ||
348 		    t2 < 0 || t2 > N_SQRS || t3 < 0 || t3 > 2 ||
349 		    (t2 != JAIL && t4 != 0) || t4 < 0 || t4 > 3)
350 			goto badness;
351 		play[i].loc = t2;
352 		play[i].num_gojf = t3;
353 		play[i].in_jail  = t4;
354 	}
355 	/* Deck status; init_decks() must have been called. */
356 	for (i = 0; i < 2; i++) {
357 		num++;
358 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
359 			goto badness;
360 		st[len - 1] = '\0';
361 		if (sscanf(st, "%d %d %hd", &t1, &j, &t2) != 3 ||
362 		    j > t1 || t1 != deck[i].num_cards || j < 0 ||
363 		    (t2 != FALSE && t2 != TRUE))
364 			goto badness;
365 		deck[i].top_card = j;
366 		deck[i].gojf_used = t2;
367 		num++;
368 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
369 			goto badness;
370 		st[len - 1] = '\0';
371 		a = st;
372 		for (j = 0; j < deck[i].num_cards; j++) {
373 			if ((tl = strtol(a, &b, 10)) < 0 || tl >= 0x7FFFFFFF ||
374 			    b == a)
375 			    goto badness;
376 			deck[i].offsets[j] = tl;
377 			b = a;
378 		}
379 		/* Ignore anything trailing */
380 	}
381 	trading = FALSE;
382 	while ((st = fgetln(inf, &len)) != NULL) {
383 		num++;
384 		if (st[len - 1] != '\n')
385 			goto badness;
386 		st[len - 1] = '\0';
387 		/* Location, owner, mortgaged, nhouses */
388 		if (sscanf(st, "%d %hd %hd %hd", &t1, &t2, &t3, &t4) != 4 ||
389 		    t1 < 0 || t1 >= N_SQRS || (board[t1].type != PRPTY &&
390 		    board[t1].type != RR && board[t1].type != UTIL) ||
391 		    t2 < 0 || t2 >= num_play ||
392 		    (t3 != TRUE && t3 != FALSE) ||
393 		    t4 < 0 || t4 > 5 || (t4 > 0 && t3 == TRUE))
394 			goto badness;
395 		add_list(t2, &(play[t2].own_list), t1);
396 		/* No properties on mortgaged lots */
397 		if (t3 && t4)
398 			goto badness;
399 		board[t1].owner = t2;
400 		(board[t1].desc)->morg = t3;
401 		(board[t1].desc)->houses = t4;
402 		/* XXX Should check that number of houses per property are all
403 		 * within 1 in each monopoly
404 		 */
405 	}
406 	fclose(inf);
407 	/* Check total hotel and house count */
408 	t1 = j = 0;
409 	for (i = 0; i < N_SQRS; i++) {
410 		if (board[i].type == PRPTY) {
411 			if ((board[i].desc)->houses == 5)
412 				j++;
413 			else
414 				t1 += (board[i].desc)->houses;
415 		}
416 	}
417 	if (t1 > N_HOUSE || j > N_HOTEL) {
418 		warnx("too many buildings");
419 		return(FALSE);
420 	}
421 	/* Check GOJF cards */
422 	t1 = 0;
423 	for (i = 0; i < num_play; i++)
424 		t1 += play[i].num_gojf;
425 	for (i = 0; i < 2; i++)
426 		t1 -= (deck[i].gojf_used == TRUE);
427 	if (t1 != 0) {
428 		warnx("can't figure out the Get-out-of-jail-free cards");
429 		return(FALSE);
430 	}
431 
432 	strlcpy(buf, ctime(&sbuf.st_mtime), sizeof buf);
433 	for (sp = buf; *sp != '\n'; sp++)
434 		continue;
435 	*sp = '\0';
436 	printf("[%s]\n", buf);
437 	return(TRUE);
438 }
439