1 /* Scores and scoring in Xconq.
2    Copyright (C) 1987-1989, 1991-2000 Stanley T. Shebs.
3    Copyright (C) 2005 Eric A. McDonald.
4 
5 Xconq is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.  See the file COPYING.  */
9 
10 /* Scoring in Xconq does not happen by default; instead a game design
11    may include one or more "scorekeepers", which are objects with
12    properties that define how winning and losing will be decided.
13 
14    There are two kinds of functionality in scorekeepers.  One kind is
15    the ability to end a game based on a specified condition, such as
16    "the Star of Satyria enters Noblum".  The other is the accumulation
17    of a numeric score.  A scorekeeper may include both, so it can (for
18    instance) end the game the instant that a player's score reaches
19    100, or 100 more than another player, or whatever.
20 
21    The indepside should not participate in scoring, so the iterations
22    in this file generally iterate only over the "real" sides. */
23 
24 #include "conq.h"
25 #include "kernel.h"
26 
27 typedef struct a_score_record {
28     char *gamename;
29     Obj *sides;
30     Obj *raw;
31     int numturns;
32     Obj *varsets;
33     struct a_score_record *next;
34 } ScoreRecord;
35 
36 /* This is true when the given side should be tested against the given
37    scorekeeper. */
38 
39 #define scorekeeper_applicable(side,sk)  \
40   ((side)->ingame && (side_in_set((side), (sk)->whomask)))
41 
42 /* Iteration over all recorded scores. */
43 
44 #define for_all_score_records(sr)  \
45   for ((sr) = records; (sr) != NULL; (sr) = (sr)->next)
46 
47 static int read_scorefile(void);
48 static int interp_score_record(Obj *form);
49 static char *basic_player_name(Player *player);
50 static void eval_sk_last_side_wins(Scorekeeper *sk);
51 static void eval_sk_last_alliance_wins(Scorekeeper *sk);
52 static void score_variant_desc(ScoreRecord *sr, char *varbuf);
53 
54 /* The head of the list of scorekeepers. */
55 
56 Scorekeeper *scorekeepers;
57 
58 /* The end of the list of scorekeepers. */
59 
60 Scorekeeper *last_scorekeeper;
61 
62 /* The total number of scorekeepers defined. */
63 
64 int numscorekeepers;
65 
66 int nextskid;
67 
68 /* The number of scorekeepers maintaining numeric scores. */
69 
70 int numscores;
71 
72 /* True if any pre-turn scorekeepers are defined. */
73 
74 int any_pre_turn_scores;
75 
76 /* True if any post-turn scorekeepers are defined. */
77 
78 int any_post_turn_scores;
79 
80 /* True if any post-action scorekeepers are defined. */
81 
82 int any_post_action_scores;
83 
84 /* True if any post-event scorekeepers are defined. */
85 
86 int any_post_event_scores;
87 
88 /* True if any turn-specific scorekeepers are defined. */
89 
90 int any_turn_specific_scores;
91 
92 /* The count of sides in the game when the last-side-wins scorekeeper
93    first tested. */
94 
95 static int num_sides_originally;
96 
97 static int only_checking;
98 
99 static int we_have_a_winner;
100 
101 ScoreRecord *records;
102 
103 /* Clear out any possible scorekeepers. */
104 
105 void
init_scorekeepers(void)106 init_scorekeepers(void)
107 {
108     scorekeepers = last_scorekeeper = NULL;
109     numscorekeepers = 0;
110     nextskid = 1;
111     any_pre_turn_scores = FALSE;
112     any_post_turn_scores = FALSE;
113     any_post_action_scores = FALSE;
114     any_post_event_scores = FALSE;
115     any_turn_specific_scores = FALSE;
116 }
117 
118 Scorekeeper *
create_scorekeeper(void)119 create_scorekeeper(void)
120 {
121     Scorekeeper *sk = (Scorekeeper *) xmalloc(sizeof(Scorekeeper));
122 
123     /* Initialize any nonzero fields. */
124     sk->id = nextskid++;
125     sk->when = lispnil;
126     sk->who = lispnil;
127     sk->whomask = ALLSIDES;
128     sk->knownto = lispnil;
129     sk->knowntomask = ALLSIDES;
130     sk->trigger = lispnil;
131     sk->body = lispnil;
132     sk->record = lispnil;
133     sk->notes = lispnil;
134     sk->scorenum = -1;
135     sk->keepscore = TRUE;
136     sk->initial = 0;
137     sk->triggered = FALSE;
138     /* Add the new scorekeeper to the end of the list. */
139     if (last_scorekeeper != NULL) {
140 	last_scorekeeper->next = sk;
141 	last_scorekeeper = sk;
142     } else {
143 	scorekeepers = last_scorekeeper = sk;
144     }
145     ++numscorekeepers;
146     return sk;
147 }
148 
149 Scorekeeper *
find_scorekeeper(int id)150 find_scorekeeper(int id)
151 {
152     Scorekeeper *sk;
153 
154     for_all_scorekeepers(sk) {
155 	if (sk->id == id)
156 	  return sk;
157     }
158     return NULL;
159 }
160 
161 /* Allocate and fill in the initial score records for each side.  This
162    must happen after all scorekeepers have been defined. */
163 
164 void
init_scores(void)165 init_scores(void)
166 {
167     int score;
168     Side *side;
169     Scorekeeper *sk;
170     Obj *savedscores, *when;
171 
172     /* First count and index all the scorekeepers that maintain
173        a numeric score. */
174     numscores = 0;
175     for_all_scorekeepers(sk) {
176     	if (sk->keepscore) {
177     	    sk->scorenum = numscores++;
178     	}
179     }
180     /* Allocate an appropriately-sized scorecard for each side.  Note that a
181        particular position in the scorecard might not apply to all sides. */
182     for_all_sides(side) {
183 	/* Collect any Lisp object that might have been stashed here
184 	   while reading a game module. */
185 	savedscores = side->rawscores;
186 	side->scores = NULL;
187 	if (numscores > 0) {
188 	    side->scores = (short *) xmalloc(numscores * sizeof(short));
189 	    for_all_scorekeepers(sk) {
190 		if (!sk->keepscore)
191 		  continue;
192 		score = sk->initial;
193 		/* Collect a saved score if there is one to collect. */
194 		if (savedscores != NULL
195 		    && savedscores != lispnil
196 		    && numberp(car(savedscores))) {
197 		    score = c_number(car(savedscores));
198 		    savedscores = cdr(savedscores);
199 		}
200 		side->scores[sk->scorenum] = score;
201 	    }
202 	}
203     }
204     /* Some kinds of scorekeepers are expensive to run, so we set flags to
205        indicate directly that we need to make the check. */
206     for_all_scorekeepers(sk) {
207     	when = sk->when;
208     	if (consp(when)) {
209 	    if (cdr(when) != lispnil) {
210 		any_turn_specific_scores = TRUE;
211 	    }
212 	    when = car(when);
213 	}
214 	if (match_keyword(when, K_BEFORE_TURN)) {
215 	    any_pre_turn_scores = TRUE;
216 	}
217 	if (when == lispnil || match_keyword(when, K_AFTER_TURN)) {
218 	    any_post_turn_scores = TRUE;
219 	}
220 	if (match_keyword(when, K_AFTER_ACTION)) {
221 	    any_post_action_scores = TRUE;
222 	}
223 	if (match_keyword(when, K_AFTER_EVENT)) {
224 	    any_post_event_scores = TRUE;
225 	}
226     }
227     /* Compute the number of sides in the game initially.  This is so
228        we don't force the game to end because (for whatever reason)
229        only one side was active from the beginning. */
230     num_sides_originally = 0;
231     for_all_sides(side) {
232 	if (side->ingame)
233 	  ++num_sides_originally;
234     }
235 }
236 
237 /* Generate a sidemask for sides that the scorekeeper should run for.
238    Note that this can be a subset of the 'whomask' for the scorekeeper,
239     since the 'when' condition may limit the sk from being applied to some
240     sides.
241 */
242 
243 SideMask
generate_effective_sk_whomask(Scorekeeper * sk)244 generate_effective_sk_whomask(Scorekeeper *sk)
245 {
246     SideMask ewhomask = NOSIDES;
247     Side *side = NULL;
248     Obj *whenexpr = NULL;
249 
250     assert_error(sk, "Attempted to access a NULL scorekeeper");
251     assert_error(sk->when,
252 		 "Attempted to access a NULL scorekeeper \"when expression\"");
253     /* If the when expression is NIL or not a full expression,
254 	then just return the whomask. */
255     if ((sk->when == lispnil) || !consp(sk->when))
256       return sk->whomask;
257     /* Get the actual when expression.
258        We do not care about the when keyword here. */
259     whenexpr = cdr(sk->when);
260     /* If the when expression is a number,
261 	then return NOSIDES if it is 0 (false), else return the sidemask. */
262     if (numberp(whenexpr)) {
263 	if (c_number(whenexpr))
264 	  ewhomask = sk->whomask;
265 	else
266 	  ewhomask = NOSIDES;
267     }
268     /* Else if the expression is NIL, then return NOSIDES. */
269     else if (lispnil == whenexpr)
270       ewhomask = NOSIDES;
271     /* Else if the expression is a cons,
272 	then evaluate it for each side and build the sidemask accordingly. */
273     else if (consp(whenexpr)) {
274 	/* If the test is a cons, then evaluate it. */
275 	if (consp(car(whenexpr))) {
276 	    for_all_sides(side) {
277 		if (!scorekeeper_applicable(side, sk))
278 		  continue;
279 		if (lispnil != eval_sk_test(side, sk, car(whenexpr)))
280 		  ewhomask = add_side_to_set(side, ewhomask);
281 	    }
282 	}
283 	/* Else if the test is just a number, then assume <= g_turn(). */
284 	/*! \todo Decide based on the when type once we support event and
285 		    action scorekeepers. */
286 	else if (numberp(car(whenexpr))) {
287 	    if (c_number(car(whenexpr)) <= g_turn())
288 	      ewhomask = sk->whomask;
289 	}
290 	/* Else we don't know how to handle the test... */
291 	else {
292 	    run_warning(
293 			"Garbled \"when expression\" for scorekeeper %s",
294 			(sk->title ? sk->title : ""));
295 	    ewhomask = NOSIDES;
296 	}
297     }
298     /* Else if the expression cannot be understood,
299 	then warn and return NOSIDES. */
300     else {
301 	run_warning(
302 		    "Garbled \"when expression\" for scorekeeper %s",
303 		    (sk->title ? sk->title : ""));
304 	ewhomask = NOSIDES;
305     }
306     return ewhomask;
307 }
308 
309 /* Test all the scorekeepers that should be run immediately before any
310    side moves anything. */
311 
312 void
check_pre_turn_scores(void)313 check_pre_turn_scores(void)
314 {
315     Side *side;
316     Scorekeeper *sk;
317 
318     if (any_pre_turn_scores) {
319 	for_all_scorekeepers(sk) {
320 	    if (match_keyword(sk->when, K_BEFORE_TURN)) {
321 		for_all_sides(side) {
322 		    if (scorekeeper_applicable(side, sk)) {
323 			run_scorekeeper(side, sk);
324 		    } else {
325 		    	Dprintf("sk %d not applicable to %s\n",
326 		    		sk->id, side_desig(side));
327 		    }
328 		}
329 	    }
330 	}
331     }
332 }
333 
334 /* Test all the scorekeepers that should be run only at the end of a turn. */
335 
336 void
check_post_turn_scores(void)337 check_post_turn_scores(void)
338 {
339     Side *side;
340     Scorekeeper *sk;
341     Obj *when = lispnil;
342     SideMask ewhomask = NOSIDES;
343 
344     if (any_post_turn_scores) {
345 	for_all_scorekeepers(sk) {
346 	    Dprintf("Checking post-turn scorekeeper %d\n", sk->id);
347 	    ewhomask = NOSIDES;
348 	    /* Parse the when expression. */
349 	    when = sk->when;
350 	    if ((when != lispnil) && consp(when))
351 	      when = car(when);
352 	    /* See if when expression is applicable here,
353 	       and act accordingly. */
354 	    if ((when == lispnil)
355 		|| match_keyword(when, K_AFTER_TURN)) {
356 		ewhomask = generate_effective_sk_whomask(sk);
357 		if (NOSIDES == ewhomask)
358 		  continue;
359 	    	if (symbolp(sk->body)
360 		    && match_keyword(sk->body, K_LAST_SIDE_WINS)) {
361 		    eval_sk_last_side_wins(sk);
362 	    	} else if (symbolp(sk->body)
363 			   && match_keyword(sk->body, K_LAST_ALLIANCE_WINS)) {
364 		    eval_sk_last_alliance_wins(sk);
365 	    	} else {
366 		    for_all_sides(side) {
367 			if (side_in_set(side, ewhomask)) {
368 			    run_scorekeeper(side, sk);
369 			} else {
370 			    Dprintf("sk %d not applicable to %s\n",
371 				    sk->id, side_desig(side));
372 			}
373 		    }
374 		}
375 	    }
376 	}
377     }
378 }
379 
380 void
check_post_action_scores(void)381 check_post_action_scores(void)
382 {
383     Side *side;
384     Scorekeeper *sk;
385 
386     if (any_post_action_scores) {
387 	Dprintf("Checking post-action scorekeepers\n");
388 	for_all_scorekeepers(sk) {
389 	    if (match_keyword(sk->when, K_AFTER_ACTION)) {
390 		if (sk->trigger == lispnil || sk->triggered) {
391 		    for_all_sides(side) {
392 			if (scorekeeper_applicable(side, sk)) {
393 			    run_scorekeeper(side, sk);
394 		        } else {
395 		    	    Dprintf("sk %d not applicable to %s\n",
396 		    		    sk->id, side_desig(side));
397 			}
398 		    }
399 		}
400 	    }
401 	}
402     }
403 }
404 
405 void
check_post_event_scores(void)406 check_post_event_scores(void)
407 {
408     Side *side;
409     Scorekeeper *sk;
410 
411     if (any_post_event_scores) {
412 	Dprintf("Checking post-event scorekeepers\n");
413 	for_all_scorekeepers(sk) {
414 	    if (match_keyword(sk->when, K_AFTER_EVENT)) {
415 		if (sk->trigger == lispnil || sk->triggered) {
416 		    for_all_sides(side) {
417 			if (scorekeeper_applicable(side, sk)) {
418 			    run_scorekeeper(side, sk);
419 		        } else {
420 		    	    Dprintf("sk %d not applicable to %s\n",
421 		    		    sk->id, side_desig(side));
422 			}
423 		    }
424 		}
425 	    }
426 	}
427     }
428 }
429 
430 /* This is what actually does the test and effect of the scorekeeper
431    on the given side.  This can be expensive to run. */
432 
433 void
run_scorekeeper(Side * side,Scorekeeper * sk)434 run_scorekeeper(Side *side, Scorekeeper *sk)
435 {
436     eval_sk_form(side, sk, sk->body);
437 }
438 
439 /* Sum an integer uprop across a given list of utypes. */
440 /* If side is NULL, then all sides should be considered. */
441 
442 Obj *
sum_uprop(Side * side,Obj * form)443 sum_uprop(Side *side, Obj *form)
444 {
445     int u = NONUTYPE;
446     Obj *uprop = lispnil;
447     Side *side2 = NULL;
448     Unit *unit = NULL;
449     int i = 0, usepointval = FALSE, sum = 0;
450     char *upropname = NULL;
451     int (*intgetter)(int) = NULL;
452 
453     /* Sanity Checks. */
454     assert_error(form, "Attempted to access a NULL GDL form");
455     if (lispnil == form) {
456 	run_warning("Bad 'sum-uprop' form encountered.");
457 	return lispnil;
458     }
459     /* Prep data structures. */
460     for_all_unit_types(u)
461       tmp_u_array[u] = FALSE;
462     /* Parse utype(s). */
463     fill_utype_array_from_lisp(tmp_u_array, car(form));
464     /* Retrieve uprop. */
465     if (lispnil == cdr(form)) {
466 	run_warning("Bad 'sum-uprop' form encountered.");
467 	return lispnil;
468     }
469     form = cdr(form);
470     uprop = car(form);
471     if (lispnil == uprop) {
472 	run_warning("Bad 'sum-uprop' form encountered.");
473 	return lispnil;
474     }
475     /*! \todo Generate optional sidemask. */
476     /*! \todo Handle other filters. */
477     /* Parse uprop. */
478     while (symbolp(uprop) && boundp(uprop))
479       uprop = eval_symbol(uprop);
480     if (symbolp(uprop))
481       /*! \todo Add a GC to GDL to handle memory leaks like the following. */
482       uprop = new_string(c_string(uprop));
483     if (stringp(uprop)) {
484 	upropname = c_string(uprop);
485 	/* Handle special cases. */
486 	if (!strcmp(upropname, "point-value"))
487 	  usepointval = TRUE;
488 	/* Handle general cases. */
489 	else {
490 	    for (i = 0; utypedefns[i].name; ++i) {
491 		if (!strcmp(upropname, utypedefns[i].name)) {
492 		    intgetter = utypedefns[i].intgetter;
493 		    if (!intgetter) {
494 			run_warning(
495 "'sum-uprop' encountered non-integer uprop, \"%s\"", upropname);
496 			return lispnil;
497 		    }
498 		    break;
499 		}
500 	    }
501 	    if (!utypedefns[i].name) {
502 		run_warning("No uprop matching name, \"%s\", found",
503 			    upropname);
504 		return lispnil;
505 	    }
506 	}
507     }
508     /* Sum it across relevant units. */
509     for_all_sides(side2) {
510 	/*! \note For now, we only sum across the given side. */
511 	if (side != side2)
512 	  continue;
513 	for_all_side_units(side2, unit) {
514 	    /* Skip any unit type not in the utype array. */
515 	    if (!tmp_u_array[unit->type])
516 	      continue;
517 	    /* Handle special cases. */
518 	    if (usepointval)
519 	      sum += point_value(unit);
520 	    /* Handle general cases. */
521 	    else
522 	      sum += intgetter(unit->type);
523 	}
524     }
525     /*! \todo Add a GC to GDL to handle memory leaks like the following. */
526     return new_number(sum);
527 }
528 
529 /* Return the point value of a particular unit. */
530 
531 int
point_value(Unit * unit)532 point_value(Unit *unit)
533 {
534     /* Incomplete units are always worthless. */
535     if (unit->cp > 0 && !completed(unit))
536       return 0;
537     if (unit_point_value(unit) >= 0)
538       return unit_point_value(unit);
539     return u_point_value(unit->type);
540 }
541 
542 /* Return the sum of a side's units' point values. */
543 
544 int
side_point_value(Side * side)545 side_point_value(Side *side)
546 {
547     Unit *unit;
548 
549     if (!side->point_value_valid) {
550 	side->point_value_cache = 0;
551 	for_all_side_units(side, unit) {
552 	    /* Note that we want to count units that may appear in
553 	       the future. */
554 	    if ((in_play(unit) && completed(unit))
555 		|| (alive(unit) && unit->cp < 0)) {
556 		side->point_value_cache += point_value(unit);
557 	    }
558 	}
559 	side->point_value_valid = TRUE;
560     }
561     return side->point_value_cache;
562 }
563 
564 int
alliance_point_value(Side * side)565 alliance_point_value(Side *side)
566 {
567     int score = 0;
568     Side *side2;
569 
570     for_all_sides(side2) {
571 	if (side2->ingame
572 	    && (side2 == side || trusted_side(side2, side))) {
573 		score += side_point_value(side2);
574 	}
575     }
576     return score;
577 }
578 
579 int
has_allies(Side * side)580 has_allies(Side *side)
581 {
582 	Side *side2;
583 
584 	for_all_sides(side2) {
585 		if (side2->ingame
586 		    && side2 != side
587 		    && trusted_side(side2, side)) {
588 			return TRUE;
589 		}
590 	}
591 	return FALSE;
592 }
593 
594 /* Evaluate a given scorekeeper test form wrt to a given side and
595     scorekeeper. */
596 /* This function walks through the expression tree, and only expands things
597     related to scorekeeper tests. */
598 
599 Obj *
eval_sk_test(Side * side,Scorekeeper * sk,Obj * form)600 eval_sk_test(Side *side, Scorekeeper *sk, Obj *form)
601 {
602     Obj *curobj = lispnil, *nextobj = lispnil, *newform = lispnil;
603     Obj *rest = lispnil;
604     enum keywords code;
605 
606     /* Sanity checks. */
607     assert_error(sk, "No scorekeeper provided for scorekeeper test");
608     assert_error(form,
609 "No scorekeeper test expression provided for scorekeeper test");
610     /* If test form is nil, then return nil. */
611     if (lispnil == form)
612       return lispnil;
613     /* If test form is list... */
614     if (consp(form)) {
615 	/* If action form has a test or action form keyword. */
616 	curobj = car(form);
617 	if (symbolp(curobj) && !boundp(curobj)) {
618 	    code = (enum keywords)keyword_code(c_string(curobj));
619 	    switch (code) {
620 	      /* Sum an uprop over a list of utypes. */
621 	      case K_SUM_UPROP:
622 		return sum_uprop(side, cdr(form));
623 	      /*! \todo Add other interesting cases. */
624 	      default: break;
625 	    }
626 	}
627 	/* Iterate through test form in order. */
628 	curobj = lispnil;
629 	for_all_list(form, rest) {
630 	    nextobj = eval_sk_test(side, sk, car(rest));
631 	    nextobj = cons(nextobj, lispnil);
632 	    if (lispnil != curobj)
633 	      set_cdr(curobj, nextobj);
634 	    else
635 	      newform = nextobj;
636 	    curobj = nextobj;
637 	}
638     }
639     /* If test form is symbol... */
640     else if (symbolp(form)) {
641 	/* If symbol is bound, then substitute and re-test. */
642 	if (boundp(form))
643 	  newform = eval_sk_test(side, sk, eval_symbol(form));
644 	/* Else, try to substitute a scorekeeper keyword. */
645 	code = (enum keywords)keyword_code(c_string(form));
646 	switch (code) {
647 	  /* Current turn number. */
648 	  case K_TURN:
649 	    return new_number(g_turn());
650 	  /* Current score for given side and scorekeeper. */
651 	  case K_SCORE:
652 	    if (!sk->keepscore) {
653 		run_warning(
654 "Attempted to use 'score' with non-numeric scorekeeper");
655 		return lispnil;
656 	    }
657 	    /*! \todo Need GC so these memleaks don't accumulate. */
658 	    return new_number(side->scores[sk->scorenum]);
659 	  /*! \todo Add other interesting cases here. */
660 	  /* No matching keyword, so leave it be. */
661 	  default:
662 	    return form;
663 	}
664     }
665     else
666       newform = form;
667     /* Evaluate new form and return. */
668     return eval(newform);
669 }
670 
671 /* Evaluate a given scorekeeper action form wrt to a given side and
672     scorekeeper. */
673 /* This function walks through the expression tree, and only expands things
674     related to scorekeeper actions. */
675 
676 Obj *
eval_sk_form(Side * side,Scorekeeper * sk,Obj * form)677 eval_sk_form(Side *side, Scorekeeper *sk, Obj *form)
678 {
679     Obj *curobj = lispnil, *nextobj = lispnil, *newform = lispnil;
680     Obj *clauses = lispnil, *clause = lispnil, *rest = lispnil;
681     enum keywords code;
682 
683     /* Sanity checks. */
684     assert_error(sk, "No scorekeeper provided for scorekeeper body");
685     assert_error(form,
686 "No scorekeeper body expression provided for scorekeeper body");
687     /* If action form is nil, then return nil. */
688     if (lispnil == form)
689       return lispnil;
690     /* If action form is list... */
691     if (consp(form)) {
692 	/* If action form has a test or action form keyword. */
693 	curobj = car(form);
694 	if (symbolp(curobj) && !boundp(curobj)) {
695 	    code = (enum keywords)keyword_code(c_string(curobj));
696 	    switch (code) {
697 	      /* Interpret Common Lisp 'if' form. */
698 	      case K_IF:
699 		if (lispnil != eval_sk_test(side, sk, cadr(form)))
700 		  return eval_sk_form(side, sk, caddr(form));
701 		else {
702 		    if (lispnil != cdr(cddr(form)))
703 		      return eval_sk_form(side, sk, cadr(cddr(form)));
704 		}
705 		return lispnil;
706 	      /* Interpret the traditional cond form. */
707 	      case K_COND:
708 		for_all_list(cdr(form), clauses) {
709 		    clause = car(clauses);
710 		    if (lispnil !=
711 			(newform = eval_sk_test(side, sk, car(clause)))) {
712 			for_all_list(cdr(clause), rest)
713 			  newform = eval_sk_form(side, sk, car(rest));
714 			return newform;
715 		    }
716 		}
717 		return lispnil;
718 	      case K_SET_SCORE:
719 		if (!sk->keepscore) {
720 		    run_warning(
721 "Attempted to use 'set-score' with non-numeric scorekeeper");
722 		    return lispnil;
723 		}
724 		curobj = eval_sk_test(side, sk, cadr(form));
725 		if (!numberp(curobj))
726 		  run_warning("Invalid 'set-score' form encountered");
727 		else
728 		  side->scores[sk->scorenum] = c_number(curobj);
729 		return curobj;
730 	      case K_ADD_SCORE:
731 		if (!sk->keepscore) {
732 		    run_warning(
733 "Attempted to use 'add-score' with non-numeric scorekeeper");
734 		    return lispnil;
735 		}
736 		curobj = eval_sk_test(side, sk, cadr(form));
737 		if (!numberp(curobj))
738 		  run_warning("Invalid 'add-score' form encountered");
739 		else
740 		  side->scores[sk->scorenum] += c_number(curobj);
741 		return curobj;
742 	      /*! \todo Add other interesting cases. */
743 	      default: break;
744 	    }
745 	}
746 	/* Iterate through action form in order. */
747 	curobj = lispnil;
748 	for_all_list(form, rest) {
749 	    nextobj = eval_sk_form(side, sk, car(rest));
750 	    nextobj = cons(nextobj, lispnil);
751 	    if (lispnil != curobj)
752 	      set_cdr(curobj, nextobj);
753 	    else
754 	      newform = nextobj;
755 	    curobj = nextobj;
756 	}
757     }
758     /* If test form is symbol... */
759     else if (symbolp(form)) {
760 	/* If symbol is bound, then substitute and re-eval. */
761 	if (boundp(form))
762 	  newform = eval_sk_form(side, sk, eval_symbol(form));
763 	/* Else, try to substitute a scorekeeper keyword. */
764 	code = (enum keywords)keyword_code(c_string(form));
765 	switch (code) {
766 	  case K_LAST_SIDE_WINS:
767 	    eval_sk_last_side_wins(sk);
768 	    break;
769 	  case K_LAST_ALLIANCE_WINS:
770 	    eval_sk_last_alliance_wins(sk);
771 	    break;
772 	  case K_WIN:
773 	    if (side->ingame && !only_checking)
774 	      side_wins(side, sk->id);
775 	    break;
776 	  case K_LOSE:
777 	    if (side->ingame && !only_checking)
778 	      side_loses(side, NULL, sk->id);
779 	    break;
780 	  case K_END:
781 	    if (!only_checking)
782 	      all_sides_draw();
783 	    break;
784 	  /*! \todo Add other interesting cases here. */
785 	  /* No matching keyword, so leave it be. */
786 	  default:
787 	    return form;
788 	}
789     }
790     else
791       newform = form;
792     return eval(newform);
793 }
794 
795 /* Test whether a single side is still alive in the game. */
796 
797 static void
eval_sk_last_side_wins(Scorekeeper * sk)798 eval_sk_last_side_wins(Scorekeeper *sk)
799 {
800     Side *side2, *winner = NULL;
801     int numleft = 0, points;
802 
803     /* This is only meaningful in games with at least two sides. */
804     if (num_sides_originally < 2)
805       return;
806     for_all_sides(side2) {
807 	/* The independent side should not be counted among the sides
808 	   left (unless it has somebody running it), otherwise the
809 	   "but I already won!" player has to scour the map looking for
810 	   remaining inert independents. Even if someone is running the
811 	   indepside, it may be reasonable to exclude it from scorekeeping. */
812 	if (inactive_indepside(side2))
813 	  continue;
814 	/* Exclude any side that is not in the scorekeeper's sidemask. */
815 	if (!side_in_set(side2, sk->whomask))
816 	  continue;
817 	if (side2->ingame) {
818 	    points = side_point_value(side2);
819 	    Dprintf("%s has %d points worth of units\n",
820 		    side_desig(side2), points);
821 	    if (points == 0) {
822 		if (!only_checking)
823 		  side_loses(side2, NULL, sk->id);
824 	    } else {
825 		++numleft;
826 		/* Take note of the possible winner. */
827 		winner = side2;
828 	    }
829 	}
830     }
831     if (numleft == 1) {
832 	we_have_a_winner = TRUE;
833 	if (!only_checking)
834 	  side_wins(winner, sk->id);
835     }
836 }
837 
838 /* Test whether a single alliance (a group of trusting sides) is all
839    that remains in the game. */
840 
841 static void
eval_sk_last_alliance_wins(Scorekeeper * sk)842 eval_sk_last_alliance_wins(Scorekeeper *sk)
843 {
844     Side *side2, *side3, *winner = NULL;
845     int numleft = 0, sidepoints[MAXSIDES+1], alliancepoints[MAXSIDES+1];
846     int alliancewins;
847 
848     /* This is only meaningful in games with at least two sides. */
849     if (num_sides_originally < 2)
850       return;
851     for_all_sides(side2) {
852 	sidepoints[side2->id] = alliancepoints[side2->id] = 0;
853 	if (side2->ingame) {
854 	    sidepoints[side2->id] = side_point_value(side2);
855 	    Dprintf("%s has %d points worth of units\n",
856 		    side_desig(side2), sidepoints[side2->id]);
857 	}
858     }
859     /* Sum up to get points for each alliance. */
860     for_all_sides(side2) {
861 	if (side2->ingame) {
862 	    for_all_sides(side3) {
863 		if (side2 == side3 || trusted_side(side2, side3)) {
864 		    alliancepoints[side2->id] += sidepoints[side3->id];
865 		}
866 	    }
867 	}
868     }
869     /* Make all the sides belonging to point-less alliances lose now,
870        and look for a non-losing side. */
871     for_all_sides(side2) {
872 	if (inactive_indepside(side2))
873 	  continue;
874 	/* Exclude any side that is not in the scorekeeper's sidemask. */
875 	if (!side_in_set(side2, sk->whomask))
876 	  continue;
877 	if (side2->ingame) {
878 	    if (alliancepoints[side2->id] == 0) {
879 		if (!only_checking)
880 		  side_loses(side2, NULL, sk->id);
881 	    } else {
882 		++numleft;
883 		/* We still need to pick a single side, we'll take
884 		   care of all its buddies shortly. */
885 		winner = side2;
886 	    }
887 	}
888     }
889     /* It may happen that all sides lose (for instance if the last two
890        units die in a duel). */
891     if (numleft == 0)
892       return;
893     /* See if the non-losers all belong to a single alliance. */
894     alliancewins = TRUE;
895     for_all_sides(side2) {
896 	if (inactive_indepside(side2))
897 	  continue;
898 	/* Exclude any side that is not in the scorekeeper's sidemask. */
899 	if (!side_in_set(side2, sk->whomask))
900 	  continue;
901 	if (side2->ingame
902 	    && side2 != indepside
903 	    && side2 != winner
904 	    && !trusted_side(winner, side2)) {
905 	    alliancewins = FALSE;
906 	    break;
907 	}
908     }
909     /* If so, everybody in the alliance wins equally. */
910     if (alliancewins) {
911 	we_have_a_winner = TRUE;
912 	for_all_sides(side2) {
913 	    if (inactive_indepside(side2))
914 	      continue;
915 	    /* Exclude any side that is not in the scorekeeper's sidemask. */
916 	    if (!side_in_set(side2, sk->whomask))
917 	      continue;
918 	    if (side2->ingame
919 		&& (side2 == winner || trusted_side(winner, side2))) {
920 		if (!only_checking)
921 		  side_wins(side2, sk->id);
922 	    }
923 	}
924     }
925 }
926 
927 /* Implement the effects of a side winning. */
928 
929 void
side_wins(Side * side,int why)930 side_wins(Side *side, int why)
931 {
932     /* Nothing happens to the side's units or people. */
933     side->status = 1;
934     remove_side_from_game(side);
935     /* Record the event after the side is removed, so we don't get infinite
936        recursion if there is an event-triggered scorekeeper. */
937     record_event(H_SIDE_WON, ALLSIDES, side_number(side), why);
938 }
939 
940 /* Implement the effects of a side losing. */
941 
942 void
side_loses(Side * side,Side * side2,int why)943 side_loses(Side *side, Side *side2, int why)
944 {
945     int x, y, s, s2, ux, uy, changed;
946     Unit *unit;
947     Side *side3;
948 
949     /* We can't lose twice. */
950     if (!side->ingame)
951         return;
952     /* The independents cannot lose by definition even if they
953     are down to zero points (unless played by a human). */
954     if (side == indepside && !side_has_display(side))
955       return;
956     /* These should not happen, but a stupid AI might try, so just
957        and clear the mistaken side. */
958     if (side == side2) {
959 	run_warning("losing to self, ignoring side");
960 	side2 = NULL;
961     }
962     if (side2 != NULL && !side2->ingame) {
963 	run_warning("losing to side not in game, ignoring side");
964 	side2 = NULL;
965     }
966     /* If there's a controlling side, it gets everything. */
967     if (side->controlled_by != NULL && side->controlled_by->ingame) {
968 	side2 = side->controlled_by;
969     }
970     if (side2 != NULL) {
971 	/* Dispose of all of a side's units. */
972 	for_all_units(unit) {
973 	    if (unit->side == side) {
974 		if (in_play(unit)) {
975 		    ux = unit->x;  uy = unit->y;
976 		    change_unit_side(unit, side2, H_SIDE_LOST, NULL);
977 		    /* Everybody gets to see this change. */
978 		    all_see_cell(ux, uy);
979 		} else {
980 		    /* Even out-of-play units need to have their side set. */
981 		    set_unit_side(unit, side2);
982 		}
983 	    }
984 	}
985 	/* The people also change sides. */
986 	if (people_sides_defined() || control_sides_defined()) {
987 	    s = side_number(side);
988 	    s2 = side_number(side2);
989 	    for_all_cells(x, y) {
990 		changed = FALSE;
991 		if (people_sides_defined() && people_side_at(x, y) == s) {
992 		    set_people_side_at(x, y, s2);
993 		    changed = TRUE;
994 		}
995 		if (control_sides_defined() && control_side_at(x, y) == s) {
996 		    set_control_side_at(x, y, s2);
997 		    changed = TRUE;
998 		}
999 		if (changed)
1000 		  all_see_cell(x, y);
1001 	    }
1002 	}
1003 	/* Reset view coverage everywhere. */
1004 	if (!side->see_all) {
1005 	    for_all_cells(x, y) {
1006 		set_cover(side, x, y, 0);
1007 	    }
1008 	}
1009     }
1010 
1011     /* Advanced units (cities) should not disappear - make them independent. */
1012     else if (indepside->ingame) {
1013 	for_all_units(unit) {
1014 	    if (unit->side == side
1015 	        && u_advanced(unit->type)) {
1016 		if (in_play(unit)) {
1017 		    ux = unit->x;
1018 		    uy = unit->y;
1019 		    change_unit_side(unit, indepside, H_SIDE_LOST, NULL);
1020 		    /* Everybody gets to see this change. */
1021 		    all_see_cell(ux, uy);
1022 		} else {
1023 		    /* Even out-of-play units need to have their side set. */
1024 		    set_unit_side(unit, indepside);
1025 		}
1026 	    }
1027 	}
1028     }
1029 
1030     /* Add the mark of shame itself. */
1031     side->status = -1;
1032     remove_side_from_game(side);
1033     /* Record the event after the side is removed, so we don't get infinite
1034        recursion if there is an event-triggered scorekeeper. */
1035     record_event(H_SIDE_LOST, ALLSIDES, side_number(side), why);
1036     /* As a special case, look at post-turn scorekeepers to see if
1037        sides should end their turns early because the game is over. */
1038     only_checking = TRUE;
1039     check_post_turn_scores();
1040     only_checking = FALSE;
1041     if (we_have_a_winner) {
1042 	for_all_sides(side3) {
1043 	    if (side3->ingame) {
1044 		finish_turn(side3);
1045 	    }
1046 	}
1047     }
1048     /* When the remaining sides' turns are finished, the end-of-turn
1049        processing will commence, and it includes the real run of
1050        post-turn scorekeeper checking. */
1051 }
1052 
1053 /* Implement a draw.  Note that unlike winning or losing, the draw
1054    applies to all sides currently in the game. */
1055 
1056 void
all_sides_draw(void)1057 all_sides_draw(void)
1058 {
1059     SideMask drew;
1060     Side *side;
1061 
1062     drew = NOSIDES;
1063     for_all_sides(side) {
1064     	if (side->ingame) {
1065 	    side->status = 0;
1066 	    remove_side_from_game(side);
1067 	    drew = add_side_to_set(side, drew);
1068 	}
1069     }
1070     /* Now that all sides are out, safe to record events to that effect
1071        (no possibility of triggering post-event scorekeepers). */
1072     for_all_sides(side) {
1073     	if (side_in_set(side, drew)) {
1074 	    record_event(H_SIDE_WITHDREW, ALLSIDES, side_number(side));
1075 	}
1076     }
1077 }
1078 
1079 /* (should move this to nlang.c?) */
1080 static char *
basic_player_name(Player * player)1081 basic_player_name(Player *player)
1082 {
1083     char *playername;
1084 
1085     playername = "";
1086     if (player) {
1087 	if (!empty_string(player->name))
1088 	  playername = player->name;
1089 	else if (!empty_string(player->displayname))
1090 	  playername = player->displayname;
1091 	else if (!empty_string(player->aitypename))
1092 	  playername = player->aitypename;
1093     }
1094     return playername;
1095 }
1096 
1097 /* Record the outcome of the game into the scorefile. */
1098 
1099 void
record_into_scorefile(void)1100 record_into_scorefile(void)
1101 {
1102     int i;
1103     int any_advantage_variation = FALSE, adv, adv2, advantage;
1104     char *filename, *mversion, *playername, *varname;
1105     Variant *variants, *var;
1106     FILE *fp;
1107     Side *side;
1108 
1109     /* (should make following code into a separate routine) */
1110     adv = -1;
1111     for_all_sides(side) {
1112 	adv2 = actual_advantage(side);
1113 	if (adv < 1)
1114 	  adv = adv2;
1115 	if (adv != adv2) {
1116 	    any_advantage_variation = TRUE;
1117 	    break;
1118 	}
1119     }
1120     filename = SCOREFILE;
1121     if (!empty_string(g_scorefile_name()))
1122       filename = g_scorefile_name();
1123     fp = open_scorefile_for_writing(filename);
1124     if (fp == NULL) {
1125 	run_warning("%s cannot be opened for writing, will not record score",
1126 		    filename);
1127 	/* (should provide some sort of retry here) */
1128     } else {
1129 	fprintf(fp, "(g %s",
1130 		/* (should record this for comparison when displaying scores) */
1131 		escaped_symbol((mainmodule->origmodulename
1132 				? mainmodule->origmodulename
1133 				: mainmodule->name)));
1134 	/* Record the module's version if defined. */
1135 	mversion = (mainmodule->origversion
1136 		    ? mainmodule->origversion
1137 		    : mainmodule->version);
1138 	if (!empty_string(mversion))
1139 	  fprintf(fp, " (ve \"%s\")", mversion);
1140 	/* Record all the choices of variant. */
1141 	variants = (mainmodule->origvariants
1142 		    ? mainmodule->origvariants
1143 		    : mainmodule->variants);
1144 	if (variants) {
1145 	    fprintf(fp, " (v");
1146 	    for (i = 0; variants[i].id != lispnil; ++i) {
1147 		var = &(variants[i]);
1148 		varname = c_string(var->id);
1149 		fprintf(fp, " (");
1150 		/* Encode the common variants by number, for
1151 		   compactness.  Note that once assigned, these
1152 		   numbers can never change. */
1153 		switch (keyword_code(varname)) {
1154 		  case K_WORLD_SEEN:
1155 		    fprintf(fp, "1 %d", var->intvalue);
1156 		    break;
1157 		  case K_SEE_ALL:
1158 		    fprintf(fp, "2 %d", var->intvalue);
1159 		    break;
1160 		  case K_SEQUENTIAL:
1161 		    fprintf(fp, "3 %d", var->intvalue);
1162 		    break;
1163 		  case K_PEOPLE:
1164 		    fprintf(fp, "4 %d", var->intvalue);
1165 		    break;
1166 		  case K_ECONOMY:
1167 		    fprintf(fp, "5 %d", var->intvalue);
1168 		    break;
1169 		  case K_SUPPLY:
1170 		    fprintf(fp, "6 %d", var->intvalue);
1171 		    break;
1172 		  case K_WORLD_SIZE:
1173 		    fprintf(fp, "11 %d %d %d",
1174 			    area.width, area.height, world.circumference);
1175 		    break;
1176 		  case K_REAL_TIME:
1177 		    fprintf(fp, "12 %d %d %d",
1178 			    g_rt_for_game(),
1179 			    g_rt_per_side(),
1180 			    g_rt_per_turn());
1181 		    break;
1182 		  default:
1183 		    fprintf(fp, "%s", escaped_symbol(varname));
1184 		    if (var->hasintvalue) {
1185 			fprintf(fp, " %d", var->intvalue);
1186 		    }
1187 		    /* Variants with unknown types of data end up getting
1188 		       recorded as just "(<var-name>)", which is OK. */
1189 		}
1190 		fprintf(fp, ")");
1191 	    }
1192 	    fprintf(fp, ")");
1193 	}
1194 	fprintf(fp, " (t %d)", g_turn());
1195 	/* End of the first line of a score record. */
1196 	fprintf(fp, "\n");
1197 	/* Record all the participants and how they fared. */
1198 	fprintf(fp, " (s");
1199 	for_all_sides(side) {
1200 	    /* Only record info about sides that actually participated. */
1201 	    if (side->everingame) {
1202 		playername = basic_player_name(side->player);
1203 		/* If a side has no player, don't record it. */
1204 		if (empty_string(playername))
1205 		  continue;
1206 		fprintf(fp, " (");
1207 		fprintf(fp, "%s", escaped_symbol(playername));
1208 		fprintf(fp, " %s",
1209 			(side_won(side)
1210 			 ? "won"
1211 			 : (side_lost(side)
1212 			    ? "lost"
1213 			    : "drew")));
1214 		/* (should write info about ai helping human players) */
1215 		if (any_advantage_variation) {
1216 		    advantage = actual_advantage(side);
1217 		    if (advantage > 1)
1218 		      fprintf(fp, " (a %d)", advantage);
1219 		}
1220 		if (numscores > 0) {
1221 		    fprintf(fp, " (sc");
1222 		    for (i = 0; i < numscores; ++i) {
1223 			fprintf(fp, " %d", side->scores[i]);
1224 		    }
1225 		    fprintf(fp, ")");
1226 		}
1227 		fprintf(fp, ")");
1228 	    }
1229 	}
1230 	fprintf(fp, ")");
1231 	/* (should record other useful info about game, such as
1232            date(s) played) */
1233 	fprintf(fp, ")\n");
1234 	close_scorefile_for_writing(fp);
1235     }
1236 }
1237 
1238 static int
read_scorefile(void)1239 read_scorefile(void)
1240 {
1241     int startlineno = 1, endlineno = 1;
1242     int numrecs;
1243     char *filename;
1244     Obj *form;
1245     FILE *fp;
1246     ScoreRecord *sr;
1247 
1248     filename = SCOREFILE;
1249     if (!empty_string(g_scorefile_name()))
1250       filename = g_scorefile_name();
1251     fp = open_scorefile_for_reading(filename);
1252     if ((NULL == fp) && !strcmp(filename, SCOREFILE))
1253       fp = open_scorefile_for_reading(OLD_SCOREFILE);
1254     if (fp != NULL) {
1255         /* Note that we clear all existing score records.  This is because
1256 	   the score file may be shared and thus getting multiple updates. */
1257 	/* (should free any existing records) */
1258 	records = NULL;
1259 	numrecs = 0;
1260 	/* Read everything in the file. */
1261 	while ((form = read_form(fp, &startlineno, &endlineno)) != lispeof) {
1262 	    if (interp_score_record(form)) {
1263 		++numrecs;
1264 	    }
1265 	}
1266 	fclose(fp);
1267 	Dprintf("%d score records read.\n", numrecs);
1268 	for_all_score_records(sr) {
1269 	    Dprintf("%s\n", sr->gamename);
1270 	}
1271 	return TRUE;
1272     }
1273     return FALSE;
1274 }
1275 
1276 static int
interp_score_record(Obj * form)1277 interp_score_record(Obj *form)
1278 {
1279     char *propname;
1280     Obj *props, *prop;
1281     ScoreRecord *sr;
1282 
1283     if (consp(form)
1284 	&& symbolp(car(form))
1285 	&& strcmp(c_string(car(form)), "g") == 0
1286 	&& (stringp(cadr(form)) || symbolp(cadr(form)))) {
1287 	sr = (ScoreRecord *) xmalloc(sizeof(ScoreRecord));
1288 	sr->gamename = c_string(cadr(form));
1289 	sr->sides = lispnil;
1290 	sr->varsets = lispnil;
1291 	for_all_list(cddr(form), props) {
1292 	    prop = car(props);
1293 	    if (symbolp(car(prop))) {
1294 		propname = c_string(car(prop));
1295 		if (strcmp(propname, "s") == 0) {
1296 		    sr->sides = cdr(prop);
1297 		} else if (strcmp(propname, "t") == 0) {
1298 		    sr->numturns = c_number(cadr(prop));
1299 		} else if (strcmp(propname, "v") == 0) {
1300 		    sr->varsets = cdr(prop);
1301 		} else if (strcmp(propname, "ve") == 0) {
1302 		    /* (should do something with module version) */
1303 		} else {
1304 		    run_warning("Score record prop name `%s' not recognized, ignoring",
1305 				propname);
1306 		}
1307 	    }
1308 	}
1309 	sr->raw = form;
1310 	/* Add the record to the beginning of the list.  This is so displays list
1311 	   the most recent game first. */
1312 	sr->next = records;
1313 	records = sr;
1314 	return TRUE;
1315     } else {
1316 	run_warning("Garbage in scorefile, ignoring");
1317     }
1318     return FALSE;
1319 }
1320 
1321 /* Collect scorefile contents and format them.  This routine starts from
1322    a fresh set of records each time, and allocates a new formatting
1323    buffer each time, so should be called sparingly. */
1324 
1325 char *
get_scores(Side * side)1326 get_scores(Side *side)
1327 {
1328     int wins, losses, draws, plays, allplays;
1329     char *buf, sdadvbuf[20], varbuf[BUFSIZE];
1330     char *thisgame, *thisgametitle, *playername, *sdnamestr, *sdfatestr;
1331     Obj *sds, *sd, *sdname, *sdfate, *more, *more1;
1332     ScoreRecord *sr;
1333 
1334     thisgame = (mainmodule->origmodulename
1335 		? mainmodule->origmodulename
1336 		: mainmodule->name);
1337     if (thisgame == NULL)
1338       return "???";
1339     read_scorefile();
1340     if (records == NULL)
1341       return "No scores available.\n";
1342     buf = (char *)xmalloc(5000);
1343     thisgametitle = thisgame;
1344     if (!empty_string(mainmodule->title))
1345       thisgametitle = mainmodule->title;
1346     sprintf(buf, "Scores for %s", thisgametitle);
1347     strcat(buf, ":\n");
1348     playername = NULL;
1349     if (side != NULL) {
1350 	playername = basic_player_name(side->player);
1351     }
1352     if (1 /* summarize */) {
1353 	wins = losses = draws = plays = 0;
1354 	allplays = 0;
1355 	for_all_score_records(sr) {
1356 	    if (!empty_string(sr->gamename)
1357 		&& strcmp(thisgame, sr->gamename) == 0) {
1358 		if (playername != NULL) {
1359 		    /* Scan all the sides listed as having played in
1360                        the game. */
1361 		    for_all_list(sr->sides, sds) {
1362 			sd = car(sds);
1363 			if (numberp(car(sd)))
1364 			  sd = cdr(sd);
1365 			sdname = car(sd);
1366 			sdnamestr = ((symbolp(sdname) || stringp(sdname))
1367 				     ? c_string(sdname) : (char *)"");
1368 			sdfate = cadr(sd);
1369 			sdfatestr = (symbolp(sdfate) ? c_string(sdfate) : (char *)"");
1370 			if (sdnamestr != NULL
1371 			    && strcmp(playername, sdnamestr) == 0) {
1372 			    if (strcmp("won", sdfatestr) == 0) {
1373 				++wins;
1374 			    } else if (strcmp("lost", sdfatestr) == 0) {
1375 				++losses;
1376 			    } else if (strcmp("drew", sdfatestr) == 0) {
1377 				++draws;
1378 			    }
1379 			    ++plays;
1380 			    /* Only count the first appearance of the
1381 			       player in the list; multiple
1382 			       appearances can occur, but are likely
1383 			       to be test games (two players with same
1384 			       username open on the same X screen, for
1385 			       instance) */
1386 			    break;
1387 			}
1388 			/* (should study scores also?) */
1389 		    }
1390 		}
1391 		++allplays;
1392 	    }
1393 	}
1394 	/* (should go to nlang.c) */
1395 	tprintf(buf, "You (%s) won %d, lost %d, and drew %d of %d game%s played.\n",
1396 		playername, wins, losses, draws, plays, (plays == 1 ? "" : "s"));
1397 	if (allplays != plays)
1398 	  tprintf(buf, "Altogether, this game has been played %d time%s.\n",
1399 		  allplays, (allplays == 1 ? "" : "s"));
1400 	tprintf(buf, "\n\n");
1401     }
1402     /* List all the games explicitly. */
1403     if (1 /* complete listing */) {
1404 	tprintf(buf, "Listing of games played:\n");
1405 	for_all_score_records(sr) {
1406 	    if (!empty_string(sr->gamename)
1407 		&& strcmp(thisgame, sr->gamename) == 0) {
1408 		if (playername == NULL
1409 		    || 1 /* matching playername */) {
1410 		    for_all_list(sr->sides, sds) {
1411 			sd = car(sds);
1412 			if (numberp(car(sd)))
1413 			  sd = cdr(sd);
1414 			sdname = car(sd);
1415 			sdnamestr = ((symbolp(sdname) || stringp(sdname))
1416 				     ? c_string(sdname) : (char *)"?");
1417 			sdfate = cadr(sd);
1418 			sdfatestr = (symbolp(sdfate) ? c_string(sdfate) : (char *)"?");
1419 			strcpy(sdadvbuf, "");
1420 			for_all_list(cddr(sd), more) {
1421 			    more1 = car(more);
1422 			    Dprintf("entry is");
1423 			    Dprintlisp(more1);
1424 			    Dprintf("\n");
1425 			    if (consp(more1)
1426 				&& symbolp(car(more1))
1427 				&& strcmp(c_string(car(more1)), "a") == 0
1428 				&& numberp(cadr(more1))) {
1429 				sprintf(sdadvbuf, " +%d",
1430 					c_number(cadr(more1)));
1431 				break;
1432 			    }
1433 			}
1434 			if (sds != sr->sides)
1435 			  tprintf(buf, ", ");
1436 			tprintf(buf, "%s%s %s",
1437 				(!empty_string(sdnamestr)
1438 				 ? sdnamestr : "(no player)"),
1439 				sdadvbuf, sdfatestr);
1440 		    }
1441 		    if (sr->numturns > 0)
1442 		      tprintf(buf, " (in %d turn%s)",
1443 			      sr->numturns, (sr->numturns != 1 ? "s" : ""));
1444 		    score_variant_desc(sr, varbuf);
1445 		    if (!empty_string(varbuf)) {
1446 			strcat(buf, " (variants");
1447 			strcat(buf, varbuf);
1448 			strcat(buf, ")");
1449 		    }
1450 		    tprintf(buf, "\n");
1451 		    if (strlen(buf) > 4500)
1452 		      break;
1453 		}
1454 	    }
1455 	}
1456     }
1457     return buf;
1458 }
1459 
1460 /* Given a score record, report any variants that were specified and
1461    different from the current module's defaults. */
1462 
1463 static void
score_variant_desc(ScoreRecord * sr,char * varbuf)1464 score_variant_desc(ScoreRecord *sr, char *varbuf)
1465 {
1466     int i, hasdflt, dfltval, val;
1467     Obj *restvarset, *varset, *varsetname, *rslt;
1468     Variant *variants, *tmpvar, *var;
1469 
1470     varbuf[0] = '\0';
1471     if (sr->varsets != lispnil) {
1472 	variants = (mainmodule->origvariants
1473 		    ? mainmodule->origvariants
1474 		    : mainmodule->variants);
1475 	for_all_list(sr->varsets, restvarset) {
1476 	    varset = car(restvarset);
1477 	    if (consp(varset)) {
1478 		if (numberp(car(varset))) {
1479 		    /* Get the names of the standard variants from
1480                        their numbers. */
1481 		    switch (c_number(car(varset))) {
1482 		      case 1:
1483 			varsetname = intern_symbol(keyword_name(K_WORLD_SEEN));
1484 			break;
1485 		      case 2:
1486 			varsetname = intern_symbol(keyword_name(K_SEE_ALL));
1487 			break;
1488 		      case 3:
1489 			varsetname = intern_symbol(keyword_name(K_SEQUENTIAL));
1490 			break;
1491 		      case 4:
1492 			varsetname = intern_symbol(keyword_name(K_PEOPLE));
1493 			break;
1494 		      case 5:
1495 			varsetname = intern_symbol(keyword_name(K_ECONOMY));
1496 			break;
1497 		      case 6:
1498 			varsetname = intern_symbol(keyword_name(K_SUPPLY));
1499 			break;
1500 		      case 11:
1501 			varsetname = intern_symbol(keyword_name(K_WORLD_SIZE));
1502 			break;
1503 		      case 12:
1504 			varsetname = intern_symbol(keyword_name(K_REAL_TIME));
1505 			break;
1506 		      default:
1507 			break;
1508 		    }
1509 		} else {
1510 		    varsetname = car(varset);
1511 		}
1512 		var = NULL;
1513 		if (variants != NULL) {
1514 		    for (i = 0; variants[i].id != lispnil; ++i) {
1515 			tmpvar = &(variants[i]);
1516 			if (tmpvar->id == varsetname) {
1517 			    var = tmpvar;
1518 			    break;
1519 			}
1520 		    }
1521 		}
1522 		hasdflt = dfltval = FALSE;
1523 		/* Don't complain if variant not found, might have been
1524 		   removed from current module. */
1525 		if (var != NULL) {
1526 		    rslt = eval(var->dflt);
1527 		    if (numberp(rslt))
1528 		      dfltval = c_number(rslt);
1529 		    hasdflt = TRUE;
1530 		}
1531 		val = 54321;
1532 		/* The numeric value is the second element in the variant
1533 		   setting. */
1534 		if (numberp(cadr(varset)) && cddr(varset) == lispnil)
1535 		  val = c_number(cadr(varset));
1536 		/* Handle variants with several values. */
1537 		if (keyword_code(c_string(varsetname)) == K_WORLD_SIZE) {
1538 		    if (!hasdflt || !equal(rslt, cdr(varset)))
1539 		      tprintf(varbuf, " %s=%dx%dW%d", c_string(varsetname),
1540 			      c_number(cadr(varset)), c_number(caddr(varset)),
1541 			      c_number(cadr(cddr(varset))));
1542 		} else if (keyword_code(c_string(varsetname)) == K_REAL_TIME) {
1543 		    if (!hasdflt || !equal(rslt, cdr(varset)))
1544 		      tprintf(varbuf, " %s=%d,%d,%d", c_string(varsetname),
1545 			      c_number(cadr(varset)), c_number(caddr(varset)),
1546 			      c_number(cadr(cddr(varset))));
1547 		} else {
1548 		    if (!hasdflt || val != dfltval)
1549 		      tprintf(varbuf, " %s=%d", c_string(varsetname), val);
1550 		}
1551 	    }
1552 	}
1553     }
1554 }
1555 
1556