xref: /openbsd/games/monop/execute.c (revision 8529ddd3)
1 /*	$OpenBSD: execute.c,v 1.10 2014/12/08 21:11:02 tedu 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	"monop.ext"
34 #include	<sys/types.h>
35 #include	<sys/stat.h>
36 #include	<sys/time.h>
37 #include	<err.h>
38 #include 	<fcntl.h>
39 #include	<stdlib.h>
40 #include	<unistd.h>
41 
42 typedef	struct stat	STAT;
43 typedef	struct tm	TIME;
44 
45 static char	buf[257];
46 
47 static bool	new_play;	/* set if move on to new player		*/
48 
49 static void	show_move(void);
50 
51 /*
52  *	This routine takes user input and puts it in buf
53  */
54 void
55 getbuf()
56 {
57 	char	*sp;
58 	int	tmpin, i;
59 
60 	i = 1;
61 	sp = buf;
62 	while (((tmpin = getchar()) != '\n') && (i < (int)sizeof(buf)) &&
63 	    (tmpin != EOF)) {
64 		*sp++ = tmpin;
65 		i++;
66 	}
67 	if (tmpin == EOF) {
68 		printf("user closed input stream, quitting...\n");
69                 exit(0);
70 	}
71 	*sp = '\0';
72 }
73 /*
74  *	This routine executes the given command by index number
75  */
76 void
77 execute(com_num)
78 	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()
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(rl)
126 	int	rl;
127 {
128 	int	old_loc;
129 
130 	old_loc = cur_p->loc;
131 	cur_p->loc = (cur_p->loc + rl) % N_SQRS;
132 	if (cur_p->loc < old_loc && rl > 0) {
133 		cur_p->money += 200;
134 		printf("You pass %s and get $200\n", board[0].name);
135 	}
136 	show_move();
137 }
138 /*
139  *	This routine shows the results of a move
140  */
141 static void
142 show_move()
143 {
144 	SQUARE	*sqp;
145 
146 	sqp = &board[(int)cur_p->loc];
147 	printf("That puts you on %s\n", sqp->name);
148 	switch (sqp->type) {
149 	case SAFE:
150 		printf("That is a safe place\n");
151 		break;
152 	case CC:
153 		cc();
154 		break;
155 	case CHANCE:
156 		chance();
157 		break;
158 	case INC_TAX:
159 		inc_tax();
160 		break;
161 	case GOTO_J:
162 		goto_jail();
163 		break;
164 	case LUX_TAX:
165 		lux_tax();
166 		break;
167 	case PRPTY:
168 	case RR:
169 	case UTIL:
170 		if (sqp->owner < 0) {
171 			printf("That would cost $%d\n", sqp->cost);
172 			if (getyn("Do you want to buy? ") == 0) {
173 				buy(player, sqp);
174 				cur_p->money -= sqp->cost;
175 			}
176 			else if (num_play > 2)
177 				bid();
178 		}
179 		else if (sqp->owner == player)
180 			printf("You own it.\n");
181 		else
182 			rent(sqp);
183 	}
184 }
185 
186 
187 #define MONOP_TAG "monop(6) save file"
188 /*
189  *	This routine saves the current game for use at a later date
190  */
191 void
192 save()
193 {
194 	int i, j;
195 	time_t t;
196 	struct stat sb;
197 	char *sp;
198 	FILE *outf;
199 
200 	printf("Which file do you wish to save it in? ");
201 	getbuf();
202 
203 	/*
204 	 * check for existing files, and confirm overwrite if needed
205 	 */
206 	if (stat(buf, &sb) == 0
207 	    && getyn("File exists.  Do you wish to overwrite? ") > 0)
208 		return;
209 
210 	umask(022);
211 	if ((outf = fopen(buf, "w")) == NULL) {
212 		warn("%s", buf);
213 		return;
214 	}
215 	printf("\"%s\" ", buf);
216 	time(&t);			/* get current time		*/
217 	fprintf(outf, "%s\n", MONOP_TAG);
218 	fprintf(outf, "# %s", ctime(&t));	/* ctime() has \n */
219 	fprintf(outf, "%d %d %d\n", num_play, player, num_doub);
220 	for (i = 0; i < num_play; i++)
221 		fprintf(outf, "%s\n", name_list[i]);
222 	for (i = 0; i < num_play; i++)
223 		fprintf(outf, "%d %d %d %d\n", play[i].money, play[i].loc,
224 		    play[i].num_gojf, play[i].in_jail);
225 	/* Deck status */
226 	for (i = 0; i < 2; i++) {
227 		fprintf(outf, "%d %d %d\n", (int)(deck[i].num_cards),
228 		    (int)(deck[i].top_card), (int)(deck[i].gojf_used));
229 		for (j = 0; j < deck[i].num_cards; j++)
230 			fprintf(outf, "%ld ", (long)(deck[i].offsets[j]));
231 		fprintf(outf, "\n");
232 	}
233 	/* Ownership */
234 	for (i = 0; i < N_SQRS; i++) {
235 		if (board[i].owner >= 0) {
236 			if (board[i].type == PRPTY)
237 				fprintf(outf, "%d %d %d %d\n", i, board[i].owner,
238 				    board[i].desc->morg, board[i].desc->houses);
239 			else if (board[i].type == RR || board[i].type == UTIL)
240 				fprintf(outf, "%d %d %d 0\n", i, board[i].owner,
241 				    board[i].desc->morg);
242 		}
243 	}
244 	fclose(outf);
245 
246 	strlcpy(buf, ctime(&t), sizeof buf);
247 	for (sp = buf; *sp != '\n'; sp++)
248 		continue;
249 	*sp = '\0';
250 	printf("[%s]\n", buf);
251 }
252 /*
253  * If we are restoring during a game, try not to leak memory.
254  */
255 void
256 game_restore()
257 {
258 	int i;
259 
260 	free(play);
261 	for (i = 0; i < num_play; i++)
262 		free(name_list[i]);
263 	restore();
264 }
265 /*
266  *	This routine restores an old game from a file
267  */
268 void
269 restore()
270 {
271 	printf("Which file do you wish to restore from? ");
272 	getbuf();
273 	if (rest_f(buf) == FALSE) {
274 		printf("Restore failed\n");
275 		exit(1);
276 	}
277 }
278 /*
279  *	This does the actual restoring.  It returns TRUE if the
280  *	backup was successful, else FALSE.
281  */
282 int
283 rest_f(file)
284 	char	*file;
285 {
286 	char *sp;
287 	int  i, j, num;
288 	FILE *inf;
289 	char *st, *a, *b;
290 	size_t len;
291 	STAT sbuf;
292 	int  t1;
293 	short t2, t3, t4;
294 	long tl;
295 
296 	printf("\"%s\" ", file);
297 	if (stat(file, &sbuf) < 0) {		/* get file stats	*/
298 		warn("%s", file);
299 		return(FALSE);
300 	}
301 	if ((inf = fopen(file, "r")) == NULL) {
302 		warn("%s", file);
303 		return(FALSE);
304 	}
305 
306 	num = 1;
307 	st = fgetln(inf, &len);
308 	if (st == NULL || len != strlen(MONOP_TAG) + 1 ||
309 	    strncmp(st, MONOP_TAG, strlen(MONOP_TAG))) {
310 badness:
311 		warnx("%s line %d", file, num);
312 		fclose(inf);
313 		return(FALSE);
314 	}
315 	num++;
316 	if (fgetln(inf, &len) == NULL)
317 		goto badness;
318 	num++;
319 	if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
320 		goto badness;
321 	st[len - 1] = '\0';
322 	if (sscanf(st, "%d %d %d", &num_play, &player, &num_doub) != 3 ||
323 	    num_play > MAX_PL || num_play < 1 ||
324 	    player < 0 || player >= num_play ||
325 	    num_doub < 0 || num_doub > 2)
326 		goto badness;
327 	if ((play = (PLAY *)calloc(num_play, sizeof(PLAY))) == NULL)
328 		err(1, NULL);
329 	cur_p = play + player;
330 	/* Names */
331 	for (i = 0; i < num_play; i++) {
332 		num++;
333 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
334 			goto badness;
335 		st[len - 1] = '\0';
336 		if ((name_list[i] = play[i].name = strdup(st)) == NULL)
337 			err(1, NULL);
338 	}
339 	if ((name_list[i++] = strdup("done")) == NULL)
340 		err(1, NULL);
341 	name_list[i] = NULL;
342 	/* Money, location, GOJF cards, turns in jail */
343 	for (i = 0; i < num_play; i++) {
344 		num++;
345 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
346 			goto badness;
347 		st[len - 1] = '\0';
348 		if (sscanf(st, "%d %hd %hd %hd", &(play[i].money), &t2,
349 		    &t3, &t4) != 4 ||
350 		    t2 < 0 || t2 > N_SQRS || t3 < 0 || t3 > 2 ||
351 		    (t2 != JAIL && t4 != 0) || t4 < 0 || t4 > 3)
352 			goto badness;
353 		play[i].loc = t2;
354 		play[i].num_gojf = t3;
355 		play[i].in_jail  = t4;
356 	}
357 	/* Deck status; init_decks() must have been called. */
358 	for (i = 0; i < 2; i++) {
359 		num++;
360 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
361 			goto badness;
362 		st[len - 1] = '\0';
363 		if (sscanf(st, "%d %d %hd", &t1, &j, &t2) != 3 ||
364 		    j > t1 || t1 != deck[i].num_cards || j < 0 ||
365 		    (t2 != FALSE && t2 != TRUE))
366 			goto badness;
367 		deck[i].top_card = j;
368 		deck[i].gojf_used = t2;
369 		num++;
370 		if ((st = fgetln(inf, &len)) == NULL || st[len - 1] != '\n')
371 			goto badness;
372 		st[len - 1] = '\0';
373 		a = st;
374 		for (j = 0; j < deck[i].num_cards; j++) {
375 			if ((tl = strtol(a, &b, 10)) < 0 || tl >= 0x7FFFFFFF ||
376 			    b == a)
377 			    goto badness;
378 			deck[i].offsets[j] = tl;
379 			b = a;
380 		}
381 		/* Ignore anything trailing */
382 	}
383 	trading = FALSE;
384 	while ((st = fgetln(inf, &len)) != NULL) {
385 		num++;
386 		if (st[len - 1] != '\n')
387 			goto badness;
388 		st[len - 1] = '\0';
389 		/* Location, owner, mortgaged, nhouses */
390 		if (sscanf(st, "%d %hd %hd %hd", &t1, &t2, &t3, &t4) != 4 ||
391 		    t1 < 0 || t1 >= N_SQRS || (board[t1].type != PRPTY &&
392 		    board[t1].type != RR && board[t1].type != UTIL) ||
393 		    t2 < 0 || t2 >= num_play ||
394 		    (t3 != TRUE && t3 != FALSE) ||
395 		    t4 < 0 || t4 > 5 || (t4 > 0 && t3 == TRUE))
396 			goto badness;
397 		add_list(t2, &(play[t2].own_list), t1);
398 		/* No properties on mortgaged lots */
399 		if (t3 && t4)
400 			goto badness;
401 		board[t1].owner = t2;
402 		(board[t1].desc)->morg = t3;
403 		(board[t1].desc)->houses = t4;
404 		/* XXX Should check that number of houses per property are all
405 		 * within 1 in each monopoly
406 		 */
407 	}
408 	fclose(inf);
409 	/* Check total hotel and house count */
410 	t1 = j = 0;
411 	for (i = 0; i < N_SQRS; i++) {
412 		if (board[i].type == PRPTY) {
413 			if ((board[i].desc)->houses == 5)
414 				j++;
415 			else
416 				t1 += (board[i].desc)->houses;
417 		}
418 	}
419 	if (t1 > N_HOUSE || j > N_HOTEL) {
420 		warnx("too many buildings");
421 		return(FALSE);
422 	}
423 	/* Check GOJF cards */
424 	t1 = 0;
425 	for (i = 0; i < num_play; i++)
426 		t1 += play[i].num_gojf;
427 	for (i = 0; i < 2; i++)
428 		t1 -= (deck[i].gojf_used == TRUE);
429 	if (t1 != 0) {
430 		warnx("can't figure out the Get-out-of-jail-free cards");
431 		return(FALSE);
432 	}
433 
434 	strlcpy(buf, ctime(&sbuf.st_mtime), sizeof buf);
435 	for (sp = buf; *sp != '\n'; sp++)
436 		continue;
437 	*sp = '\0';
438 	printf("[%s]\n", buf);
439 	return(TRUE);
440 }
441