Joe Well's patch for variable-sized worlds.
[vms-empire.git] / compmove.c
1 /* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */
2
3 /*
4  *    Copyright (C) 1987, 1988 Chuck Simmons
5  * 
6  * See the file COPYING, distributed with empire, for restriction
7  * and warranty information.
8  */
9
10 /*
11 compmove.c -- Make a move for the computer. 
12
13 For each move the user wants us to make, we do the following:
14
15     1)  Handle city production;
16     2)  Move computer's pieces;
17     3)  Check to see if the game is over.
18 */
19
20 #ifdef SYSV
21 #include <string.h>
22 #else
23 #include <strings.h>
24 #endif
25
26 #include <curses.h>
27 #include "empire.h"
28 #include "extern.h"
29
30 static view_map_t emap[MAP_SIZE]; /* pruned explore map */
31
32 int load_army(piece_info_t *obj);
33 int lake( long loc );
34 int overproduced( city_info_t *cityp, int *city_count );
35 int nearby_load( piece_info_t *obj, long loc );
36 int nearby_count( long loc );
37 void move_objective(piece_info_t *obj,path_map_t pathmap[],long new_loc,char *adj_list);
38
39 void
40 comp_move (nmoves) 
41 int nmoves;
42 {
43         void do_cities(), do_pieces(), check_endgame();
44
45         int i;
46         piece_info_t *obj;
47
48         /* Update our view of the world. */
49         
50         for (i = 0; i < NUM_OBJECTS; i++)
51         for (obj = comp_obj[i]; obj != NULL; obj = obj->piece_link.next)
52                 scan (comp_map, obj->loc); /* refresh comp's view of world */
53
54         for (i = 1; i <= nmoves; i++) { /* for each move we get... */
55                 comment ("Thinking...",0,0,0,0,0,0,0,0);
56
57                 (void) memcpy (emap, comp_map, MAP_SIZE * sizeof (view_map_t));
58                 vmap_prune_explore_locs (emap);
59         
60                 do_cities (); /* handle city production */
61                 do_pieces (); /* move pieces */
62                 
63                 if (save_movie) save_movie_screen ();
64                 check_endgame (); /* see if game is over */
65
66                 topini ();
67                 (void) refresh ();
68         }
69 }
70
71 /*
72 Handle city production.  First, we set production for new cities.
73 Then we produce new pieces.  After producing a piece, we will see
74 if we should change our production.
75
76 Our goals for city production are first, not to change production
77 while something is in the process of being built.  Second, we attempt
78 to have enough city producing armies on a continent to counter any
79 threat on the continent, and to adequately explore and control the
80 continent.  Third, we attempt to always have at least one transport
81 producer.  Fourth, we attempt to maintain a good ratio between the
82 number of producers we have of each type of piece.  Fifth, we never
83 build carriers, as we don't have a good strategy for moving these.
84 */
85
86 void
87 do_cities () {
88         void comp_prod();
89         
90         int i;
91         int is_lake;
92
93         for (i = 0; i < NUM_CITY; i++) /* new production */
94         if (city[i].owner == COMP) {
95                 scan (comp_map, city[i].loc);
96
97                 if (city[i].prod == NOPIECE)
98                         comp_prod (&city[i], lake (city[i].loc));
99         }
100         for (i = 0; i < NUM_CITY; i++) /* produce and change */
101         if (city[i].owner == COMP) {
102                 is_lake = lake (city[i].loc);
103                 if (city[i].work++ >= (long)piece_attr[(int)city[i].prod].build_time) {
104                         produce (&city[i]);
105                         comp_prod (&city[i], is_lake);
106                 }
107                 /* don't produce ships in lakes */
108                 else if (city[i].prod > FIGHTER && city[i].prod != SATELLITE && is_lake)
109                         comp_prod (&city[i], is_lake);
110         }
111 }
112                         
113 /*
114 Define ratios for numbers of cities we want producing each object.
115
116 Early in the game, we want to produce armies and transports for
117 rapid expansion.  After a while, we add fighters and pt boats
118 for exploration.  Later, we add ships of all kinds for control of
119 the sea.
120 */
121                                  /* A    F    P    S    D    T    C    B   Z*/
122 static int ratio1[NUM_OBJECTS] = { 60,   0,  10,   0,   0,  20,   0,   0,  0};
123 static int ratio2[NUM_OBJECTS] = { 90,  10,  10,  10,  10,  40,   0,   0,  0};
124 static int ratio3[NUM_OBJECTS] = {120,  20,  20,  10,  10,  60,  10,  10,  0};
125 static int ratio4[NUM_OBJECTS] = {150,  30,  30,  20,  20,  70,  10,  10,  0};
126 static int *ratio;
127
128 /*
129 Set city production if necessary.
130
131 The algorithm below contains three parts:
132
133 1)  Defend continents we own.
134
135 2)  Produce a TT and a Satellite.
136
137 3)  Meet the ratio requirements given above.
138 */
139
140 void
141 comp_prod (cityp, is_lake)
142 city_info_t *cityp;
143 int is_lake;
144 {
145         void comp_set_prod(), comp_set_needed();
146         
147         int city_count[NUM_OBJECTS]; /* # of cities producing each piece */
148         int cont_map[MAP_SIZE];
149         int total_cities;
150         long i;
151         int comp_ac;
152         city_info_t *p;
153         int need_count, interest;
154         scan_counts_t counts;
155
156         /* Make sure we have army producers for current continent. */
157         
158         /* map out city's continent */
159         vmap_cont (cont_map, comp_map, cityp->loc, '.');
160
161         /* count items of interest on the continent */
162         counts = vmap_cont_scan (cont_map, comp_map);
163         comp_ac = 0; /* no army producing computer cities */
164         
165         for (i = 0; i < MAP_SIZE; i++)
166         if (cont_map[i]) { /* for each cell of continent */
167                 if (comp_map[i].contents == 'X') {
168                         p = find_city (i);
169                         ASSERT (p != NULL && p->owner == COMP);
170                         if (p->prod == ARMY) comp_ac += 1;
171                 }
172         }
173         /* see if anything of interest is on continent */
174         interest = (counts.unexplored || counts.user_cities
175                  || counts.user_objects[ARMY]
176                  || counts.unowned_cities);
177         
178         /* we want one more army producer than enemy has cities */
179         /* and one more if anything of interest on cont */
180         need_count = counts.user_cities - comp_ac + interest;
181         if (counts.user_cities) need_count += 1;
182         
183         if (need_count > 0) { /* need an army producer? */
184                 comp_set_prod (cityp, ARMY);
185                 return;
186         }
187         
188         /* Produce armies in new cities if there is a city to attack. */
189         if (counts.user_cities && cityp->prod == NOPIECE) {
190                 comp_set_prod (cityp, ARMY);
191                 return;
192         }
193
194         /* Produce a TT and SAT if we don't have one. */
195         
196         /* count # of cities producing each piece */
197         for (i = 0; i < NUM_OBJECTS; i++)
198                 city_count[i] = 0;
199                 
200         total_cities = 0;
201                 
202         for (i = 0; i < NUM_CITY; i++)
203         if (city[i].owner == COMP && city[i].prod != NOPIECE) {
204                 city_count[(int)city[i].prod] += 1;
205                 total_cities += 1;
206         }
207         if (total_cities <= 10) ratio = ratio1;
208         else if (total_cities <= 20) ratio = ratio2;
209         else if (total_cities <= 30) ratio = ratio3;
210         else ratio = ratio4;
211                 
212         /* if we have one army producer, and this is it, return */
213         if (city_count[ARMY] == 1 && cityp->prod == ARMY) return;
214         
215         /* first available non-lake becomes a tt producer */
216         if (city_count[TRANSPORT] == 0) {
217                 if (!is_lake) {
218                         comp_set_prod (cityp, TRANSPORT);
219                         return;
220                 }
221                 /* if we have one army producer that is not on a lake, */
222                 /* produce armies here instead */
223                 if (city_count[ARMY] == 1) {
224                         for (i = 0; i < NUM_CITY; i++)
225                         if (city[i].owner == COMP && city[i].prod == ARMY) break;
226                 
227                         if (!lake (city[i].loc)) {
228                                 comp_set_prod (cityp, ARMY);
229                                 return;
230                         }
231                 }
232         }
233 #if 0
234         /* Now we need a SATELLITE. */
235         if (cityp->prod == NOPIECE && city_count[SATELLITE] == 0) {
236                 comp_set_prod (cityp, SATELLITE);
237                 return;
238         }
239         if (cityp->prod == SATELLITE) return;
240         /* "The satellites are out tonight." -- Lori Anderson */
241 #endif
242         /* don't change prod from armies if something on continent */
243         if (cityp->prod == ARMY && interest) return;
244                         
245         /* Produce armies in new cities if there is a city to attack. */
246         if (counts.unowned_cities && cityp->prod == NOPIECE) {
247                 comp_set_prod (cityp, ARMY);
248                 return;
249         }
250
251         /* Set production to item most needed.  Continents with one
252         city and nothing interesting may not produce armies.  We
253         set production for unset cities, and change production for
254         cities that produce objects for which we have many city producers.
255         Ship producers on lakes also get there production changed. */
256         
257         interest = (counts.comp_cities != 1 || interest);
258         
259         if (cityp->prod == NOPIECE
260             || (cityp->prod == ARMY && counts.comp_cities == 1)
261             || overproduced (cityp, city_count)
262             || (cityp->prod > FIGHTER && is_lake) )
263                 comp_set_needed (cityp, city_count, interest, is_lake);
264 }
265         
266 /*
267 Set production for a computer city to a given type.  Don't
268 reset production if it is already correct.
269 */
270
271 void
272 comp_set_prod (cityp, type)
273 city_info_t *cityp;
274 int type;
275 {
276         if (cityp->prod == type) return;
277         
278         pdebug ("Changing city prod at %d from %d to %d\n",loc_disp(cityp->loc), cityp->prod, type,0,0,0,0,0);
279         cityp->prod = type;
280         cityp->work = -(piece_attr[type].build_time / 5);
281 }
282
283 /*
284 See if a city is producing an object which is being overproduced.
285 */
286
287 int
288 overproduced (cityp, city_count)
289 city_info_t *cityp;
290 int *city_count;
291 {
292         int i;
293
294         for (i = 0; i < NUM_OBJECTS; i++) {
295                 /* return true if changing production would improve balance */
296                 if (i != cityp->prod
297                  && ((city_count[(int)cityp->prod] - 1) * ratio[i]
298                    > (city_count[i] + 1) * ratio[(int)cityp->prod]))
299                 return (TRUE);
300         }
301         return (FALSE);
302 }
303
304 /*
305 See if one type of production is needed more than another type.
306 Return the most-needed type of production.
307 */
308
309 int
310 need_more (city_count, prod1, prod2)
311 int *city_count;
312 int prod1, prod2;
313 {
314         if (city_count[prod1] * ratio[prod2]
315                  <= city_count[prod2] * ratio[prod1])
316         return (prod1);
317
318         else return (prod2);
319 }
320
321 /*
322 Figure out the most needed type of production.  We are passed
323 a flag telling us if armies are ok to produce.
324 */
325
326 void
327 comp_set_needed (cityp, city_count, army_ok, is_lake)
328 city_info_t *cityp;
329 int *city_count;
330 int army_ok;
331 int is_lake;
332 {
333         int best_prod;
334         int prod;
335
336         if (!army_ok) city_count[ARMY] = INFINITY;
337
338         if (is_lake) { /* choose fighter or army */
339                 comp_set_prod (cityp, need_more (city_count, ARMY, FIGHTER));
340                 return;
341         }
342         /* don't choose fighter */
343         city_count[FIGHTER] = INFINITY;
344         
345         best_prod = ARMY; /* default */
346         for (prod = 0; prod < NUM_OBJECTS; prod++) {
347                 best_prod = need_more (city_count, best_prod, prod);
348         }
349         comp_set_prod (cityp, best_prod);
350 }
351
352 /*
353 See if a city is on a lake.  We define a lake to be a body of
354 water (where cities are considered to be water) that does not
355 touch either an attackable city or unexplored territory.
356
357 Be careful, because we use the 'emap'.  This predicts whether
358 unexplored territory will be land or water.  The prediction should
359 be helpful, because small bodies of water that enclose unexplored
360 territory will appear as solid water.  Big bodies of water should
361 have unexplored territory on the edges.
362 */
363
364 int
365 lake (loc)
366 long loc;
367 {
368         int cont_map[MAP_SIZE];
369         scan_counts_t counts;
370
371         vmap_cont (cont_map, emap, loc, '+'); /* map lake */
372         counts = vmap_cont_scan (cont_map, emap);
373
374         return !(counts.unowned_cities || counts.user_cities || counts.unexplored);
375 }
376
377 /*
378 Move all computer pieces.
379 */
380
381 static view_map_t amap[MAP_SIZE]; /* temp view map */
382 static path_map_t path_map[MAP_SIZE];
383
384 void
385 do_pieces () { /* move pieces */
386         void cpiece_move();
387
388         int i;
389         piece_info_t *obj, *next_obj;
390
391         for (i = 0; i < NUM_OBJECTS; i++) { /* loop through obj lists */
392                 for (obj = comp_obj[move_order[i]]; obj != NULL;
393                     obj = next_obj) { /* loop through objs in list */
394                         next_obj = obj->piece_link.next;
395                         cpiece_move (obj); /* yup; move the object */
396                 }
397         }
398 }
399
400 /*
401 Move a piece.  We loop until all the moves of a piece are made.  Within
402 the loop, we find a direction to move that will take us closer to an
403 objective.
404 */
405
406 void
407 cpiece_move (obj)
408 piece_info_t *obj;
409 {
410         void move1();
411
412         int changed_loc;
413         int max_hits;
414         long saved_loc;
415         city_info_t *cityp;
416
417         if (obj->type == SATELLITE) {
418                 move_sat (obj);
419                 return;
420         }
421         
422         obj->moved = 0; /* not moved yet */
423         changed_loc = FALSE; /* not changed yet */
424         max_hits = piece_attr[obj->type].max_hits;
425
426         if (obj->type == FIGHTER) { /* init fighter range */
427                 cityp = find_city (obj->loc);
428                 if (cityp != NULL) obj->range = piece_attr[FIGHTER].range;
429         }
430         
431         while (obj->moved < obj_moves (obj)) {
432                 saved_loc = obj->loc; /* remember starting location */
433                 move1 (obj);
434                 if (saved_loc != obj->loc) changed_loc = TRUE;
435                 
436                 if (obj->type == FIGHTER && obj->hits > 0) {
437                         if (comp_map[obj->loc].contents == 'X')
438                                 obj->moved = piece_attr[FIGHTER].speed;
439                         else if (obj->range == 0) {
440                                 pdebug ("Fighter at %d crashed and burned\n", loc_disp(obj->loc),0,0,0,0,0,0,0);
441                                 ksend ("Fighter at %d crashed and burned\n", loc_disp(obj->loc),0,0,0,0,0,0,0);
442                                 kill_obj (obj, obj->loc); /* crash & burn */
443                         }
444                 }
445         }
446         /* if a boat is in port, damaged, and never moved, fix some damage */
447         if (obj->hits > 0 /* live piece? */
448                 && !changed_loc /* object never changed location? */
449                 && obj->type != ARMY && obj->type != FIGHTER /* it is a boat? */
450                 && obj->hits != max_hits /* it is damaged? */
451                 && comp_map[obj->loc].contents == 'X') /* it is in port? */
452         obj->hits++; /* fix some damage */
453 }
454
455 /*
456 Move a piece one square.
457 */
458
459 void
460 move1 (obj)
461 piece_info_t *obj;
462 {
463         void army_move(), transport_move(), fighter_move(), ship_move();
464
465         switch (obj->type) {
466         case ARMY: army_move (obj); break;
467         case TRANSPORT: transport_move (obj); break;
468         case FIGHTER: fighter_move (obj); break;
469         default: ship_move (obj); break;
470         }
471 }
472
473 /*
474 Move an army.
475
476 This is a multi-step algorithm:
477
478 1)  See if there is an object we can attack immediately.
479 If so, attack it.
480
481 2)  Look for the nearest land objective.
482
483 3)  If we find an objective reachable by land, figure out
484 how far away that objective is.  Based on the found objective,
485 also figure out how close a loadable tt must be to be of
486 interest.  If the objective is closer than the tt must be,
487 head towards the objective.
488
489 4)  Otherwise, look for the nearest loading tt (or tt producing
490 city).  If the nearest loading tt is farther than our land objective,
491 head towards the land objective.
492
493 5)  Otherwise, head for the tt.
494
495 6)  If we still have no destination and we are in a city,
496 attempt to leave the city.
497
498 7)  Once we have a destination, find the best move toward that
499 destination.  (If there is no destination, sit around and wait.)
500 */
501
502 void
503 army_move (obj)
504 piece_info_t *obj;
505 {
506         long move_away();
507         long find_attack();
508         void make_army_load_map(), make_unload_map(), make_tt_load_map();
509         void board_ship();
510         
511         long new_loc;
512         path_map_t path_map2[MAP_SIZE];
513         long new_loc2;
514         int cross_cost; /* cost to enter water */
515         
516         obj->func = 0; /* army doesn't want a tt */
517         if (vmap_at_sea (comp_map, obj->loc)) { /* army can't move? */
518                 (void) load_army (obj);
519                 obj->moved = piece_attr[ARMY].speed;
520                 if (!obj->ship) obj->func = 1; /* load army on ship */
521                 return;
522         }
523         if (obj->ship) /* is army on a transport? */
524                 new_loc = find_attack (obj->loc, army_attack, "+*");
525         else new_loc = find_attack (obj->loc, army_attack, ".+*");
526                 
527         if (new_loc != obj->loc) { /* something to attack? */
528                 attack (obj, new_loc); /* attack it */
529                 if (map[new_loc].contents == '.' /* moved to ocean? */
530                   && obj->hits > 0) { /* object still alive? */
531                         kill_obj (obj, new_loc);
532                         scan (user_map, new_loc); /* rescan for user */
533                 }
534                 return;
535         }
536         if (obj->ship) {
537                 if (obj->ship->func == 0) {
538                         if (!load_army (obj)) ABORT; /* load army on best ship */
539                         return; /* armies stay on a loading ship */
540                 }
541                 make_unload_map (amap, comp_map);
542                 new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_unload);
543                 move_objective (obj, path_map, new_loc, " ");
544                 return;
545         }
546
547         new_loc = vmap_find_lobj (path_map, comp_map, obj->loc, &army_fight);
548         
549         if (new_loc != obj->loc) { /* something interesting on land? */
550                 switch (comp_map[new_loc].contents) {
551                 case 'A':
552                 case 'O':
553                         cross_cost = 60; /* high cost if enemy present */
554                         break;
555                 case '*':
556                         cross_cost = 30; /* medium cost for attackable city */
557                         break;
558                 case ' ':
559                         cross_cost = 14; /* low cost for exploring */
560                         break;
561                 default:
562                         ABORT;
563                 }
564                 cross_cost = path_map[new_loc].cost * 2 - cross_cost;
565         }
566         else cross_cost = INFINITY;
567         
568         if (new_loc == obj->loc || cross_cost > 0) {
569                 /* see if there is something interesting to load */
570                 make_army_load_map (obj, amap, comp_map);
571                 new_loc2 = vmap_find_lwobj (path_map2, amap, obj->loc, &army_load, cross_cost);
572                 
573                 if (new_loc2 != obj->loc) { /* found something? */
574                         board_ship (obj, path_map2, new_loc2);
575                         return;
576                 }
577         }
578
579         move_objective (obj, path_map, new_loc, " ");
580 }
581
582 /*
583 Remove pruned explore locs from a view map.
584 */
585
586 void
587 unmark_explore_locs (xmap)
588 view_map_t *xmap;
589 {
590         long i;
591
592         for (i = 0; i < MAP_SIZE; i++)
593         if (map[i].on_board && xmap[i].contents == ' ')
594                 xmap[i].contents = emap[i].contents;
595 }
596
597 /*
598 Make a load map.  We copy the view map and mark each loading
599 transport and tt producing city with a '$'.
600 */
601
602 void
603 make_army_load_map (obj, xmap, vmap)
604 piece_info_t *obj;
605 view_map_t *xmap;
606 view_map_t *vmap;
607 {
608         piece_info_t *p;
609         int i;
610         
611         (void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
612
613         /* mark loading transports or cities building transports */
614         for (p = comp_obj[TRANSPORT]; p; p = p->piece_link.next)
615         if (p->func == 0) /* loading tt? */
616         xmap[p->loc].contents = '$';
617         
618         for (i = 0; i < NUM_CITY; i++)
619         if (city[i].owner == COMP && city[i].prod == TRANSPORT) {
620                 if (nearby_load (obj, city[i].loc))
621                         xmap[city[i].loc].contents = 'x'; /* army is nearby so it can load */
622                 else if (nearby_count (city[i].loc) < piece_attr[TRANSPORT].capacity)
623                         xmap[city[i].loc].contents = 'x'; /* city needs armies */
624         }
625         
626         if (print_vmap == 'A') print_xzoom (xmap);
627 }
628
629 /* Return true if an army is considered near a location for loading. */
630
631 int
632 nearby_load (obj, loc)
633 piece_info_t *obj;
634 long loc;
635 {
636         return obj->func == 1 && dist (obj->loc, loc) <= 2;
637 }
638         
639 /* Return number of nearby armies. */
640
641 int
642 nearby_count (loc)
643 long loc;
644 {
645         piece_info_t *obj;
646         int count;
647
648         count = 0;
649         for (obj = comp_obj[ARMY]; obj; obj = obj->piece_link.next) {
650                 if (nearby_load (obj, loc)) count += 1;
651         }
652         return count;
653 }
654
655 /* Make load map for a ship. */
656
657 void
658 make_tt_load_map (xmap, vmap)
659 view_map_t *xmap;
660 view_map_t *vmap;
661 {
662         piece_info_t *p;
663         
664         (void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
665
666         /* mark loading armies */
667         for (p = comp_obj[ARMY]; p; p = p->piece_link.next)
668         if (p->func == 1) /* loading army? */
669         xmap[p->loc].contents = '$';
670         
671         if (print_vmap == 'L') print_xzoom (xmap);
672 }
673         
674 /*
675 Make an unload map.  We copy the view map.  We then create
676 a continent map.  For each of our cities, we mark out the continent
677 that city is on.  Then, for each city that we don't own and which
678 doesn't appear on our continent map, we set that square to a digit.
679
680 We want to assign weights to each attackable city.
681 Cities are more valuable if they are on a continent which
682 has lots of cities.  Cities are also valuable if either it
683 will be easy for us to take over the continent, or if we
684 need to defend that continent from an enemy.
685
686 To implement the above, we assign numbers to each city as follows:
687
688 a)  if unowned_cities > user_cities && comp_cities == 0
689        set number to min (total_cities, 9)
690
691 b)  if comp_cities != 0 && user_cities != 0
692        set number to min (total_cities, 9)
693        
694 b)  if enemy_cities == 1 && total_cities == 1, set number to 2.
695     (( taking the sole enemy city on a continent is as good as
696        getting a two city continent ))
697        
698 c)  Any other attackable city is marked with a '0'.
699 */
700
701 static int owncont_map[MAP_SIZE];
702 static int tcont_map[MAP_SIZE];
703
704 void
705 make_unload_map (xmap, vmap)
706 view_map_t *xmap;
707 view_map_t *vmap;
708 {
709         long i;
710         scan_counts_t counts;
711
712         (void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
713         unmark_explore_locs (xmap);
714         
715         for (i = 0; i < MAP_SIZE; i++)
716                 owncont_map[i] = 0; /* nothing marked */
717
718         for (i = 0; i < NUM_CITY; i++)
719         if (city[i].owner == COMP)
720         vmap_mark_up_cont (owncont_map, xmap, city[i].loc, '.');
721
722         for (i = 0; i < MAP_SIZE; i++)
723         if (strchr ("O*", vmap[i].contents)) {
724                 int total_cities;
725                 
726                 vmap_cont (tcont_map, xmap, i, '.'); /* map continent */
727                 counts = vmap_cont_scan (tcont_map, xmap);
728                 
729                 total_cities = counts.unowned_cities
730                              + counts.user_cities
731                              + counts.comp_cities;
732                              
733                 if (total_cities > 9) total_cities = 0;
734                 
735                 if (counts.user_cities && counts.comp_cities)
736                         xmap[i].contents = '0' + total_cities;
737
738                 else if (counts.unowned_cities > counts.user_cities
739                          && counts.comp_cities == 0)
740                         xmap[i].contents = '0' + total_cities;
741
742                 else if (counts.user_cities == 1 && counts.comp_cities == 0)
743                         xmap[i].contents = '2';
744                         
745                 else xmap[i].contents = '0';
746         }
747         if (print_vmap == 'U') print_xzoom (xmap);
748 }
749
750 /*
751 Load an army onto a ship.  First look for an adjacent ship.
752 If that doesn't work, move to the objective, trying to be
753 close to the ocean.
754 */
755
756 void
757 board_ship (obj, pmap, dest)
758 piece_info_t *obj;
759 path_map_t *pmap;
760 long dest;
761 {
762         if (!load_army (obj)) {
763                 obj->func = 1; /* loading */
764                 move_objective (obj, pmap, dest, "t.");
765         }
766 }
767
768 /*
769 Look for the most full, non-full transport at a location.
770 Prefer switching to staying.  If we switch, we force
771 one of the ships to become more full.
772 */
773
774 piece_info_t *
775 find_best_tt (best, loc)
776 piece_info_t *best;
777 long loc;
778 {
779         piece_info_t *p;
780
781         for (p = map[loc].objp; p != NULL; p = p->loc_link.next)
782         if (p->type == TRANSPORT && obj_capacity (p) > p->count) {
783                 if (!best) best = p;
784                 else if (p->count >= best->count) best = p;
785         }
786         return best;
787 }
788
789 /*
790 Load an army onto the most full non-full ship.
791 */
792
793 int
794 load_army (obj)
795 piece_info_t *obj;
796 {
797         piece_info_t *p;
798         int i;
799         long x_loc;
800
801         p = find_best_tt (obj->ship, obj->loc); /* look here first */
802
803         for (i = 0; i < 8; i++) { /* try surrounding squares */
804                 x_loc = obj->loc + dir_offset[i];
805                 if (map[x_loc].on_board)
806                         p = find_best_tt (p, x_loc);
807
808         }
809         if (!p) return FALSE; /* no tt to be found */
810
811         if (p->loc == obj->loc) { /* stay in same place */
812                 obj->moved = piece_attr[ARMY].speed;
813         }
814         else move_obj (obj, p->loc); /* move to square with ship */
815
816         if (p->ship != obj->ship) { /* reload army to new ship */
817                 disembark (obj);
818                 embark (p, obj);
819         }
820         return TRUE;
821 }
822
823 /*
824 Return the first location we find adjacent to the current location of
825 the correct terrain.
826 */
827
828 long
829 move_away (vmap, loc, terrain)
830 view_map_t *vmap;
831 long loc;
832 char *terrain;
833 {
834         long new_loc;
835         int i;
836
837         for (i = 0; i < 8; i++) {
838                 new_loc = loc + dir_offset[i];
839                 if (map[new_loc].on_board
840                  && strchr (terrain, vmap[new_loc].contents))
841                         return (new_loc);
842         }
843         return (loc);
844 }
845
846 /*
847 Look to see if there is an adjacent object to attack.  We are passed
848 a location and a list of items we attack sorted in order of most
849 valuable first.  We look at each surrounding on board location.
850 If there is an object we can attack, we return the location of the
851 best of these.
852 */
853
854 long
855 find_attack (loc, obj_list, terrain)
856 long loc;
857 char *obj_list;
858 char *terrain;
859 {
860         long new_loc, best_loc;
861         int i, best_val;
862         char *p;
863
864         best_loc = loc; /* nothing found yet */
865         best_val = INFINITY;
866         for (i = 0; i < 8; i++) {
867                 new_loc = loc + dir_offset[i];
868
869                 if (map[new_loc].on_board /* can we move here? */
870                     && strchr (terrain, map[new_loc].contents)) {
871                         p = strchr (obj_list, comp_map[new_loc].contents);
872                         if (p != NULL && p - obj_list < best_val) {
873                                 best_val = p - obj_list;
874                                 best_loc = new_loc;
875                         }
876                 }
877         }
878         return (best_loc);
879 }
880
881 /*
882 Move a transport.
883
884 There are two kinds of transports:  loading and unloading.
885
886 Loading transports move toward loading armies.  Unloading
887 transports move toward attackable cities on unowned continents.
888
889 An empty transport is willing to attack adjacent enemy transports.
890 Transports become 'loading' when empty, and 'unloading' when full.
891 */
892
893 void
894 transport_move (obj)
895 piece_info_t *obj;
896 {
897         void tt_do_move();
898
899         long new_loc;
900
901         /* empty transports can attack */
902         if (obj->count == 0) { /* empty? */
903                 obj->func = 0; /* transport is loading */
904                 new_loc = find_attack (obj->loc, tt_attack, ".");
905                 if (new_loc != obj->loc) { /* something to attack? */
906                         attack (obj, new_loc); /* attack it */
907                         return;
908                 }
909         }
910
911         if (obj->count == obj_capacity (obj)) /* full? */
912                 obj->func = 1; /* unloading */
913
914         if (obj->func == 0) { /* loading? */
915                 make_tt_load_map (amap, comp_map);
916                 new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_load);
917                 
918                 if (new_loc == obj->loc) { /* nothing to load? */
919                         (void) memcpy (amap, comp_map, MAP_SIZE * sizeof (view_map_t));
920                         unmark_explore_locs (amap);
921                         if (print_vmap == 'S') print_xzoom (amap);
922                         new_loc = vmap_find_wobj (path_map, amap, obj->loc,&tt_explore);
923                 }
924                 
925                 move_objective (obj, path_map, new_loc, "a ");
926         }
927         else {
928                 make_unload_map (amap, comp_map);
929                 new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_unload);
930                 move_objective (obj, path_map, new_loc, " ");
931         }
932 }
933
934 /*
935 Move a fighter.
936
937 1)  See if there is an object we can attack immediately.
938 If so, attack it.
939
940 2)  Otherwise, if fighter is low on fuel, move toward nearest city
941 if there is one in range.
942
943 3)  Otherwise, look for an objective.
944 */
945
946 void
947 fighter_move (obj)
948 piece_info_t *obj;
949 {
950         long new_loc;
951
952         new_loc = find_attack (obj->loc, fighter_attack, ".+");
953         if (new_loc != obj->loc) { /* something to attack? */
954                 attack (obj, new_loc); /* attack it */
955                 return;
956         }
957         /* return to base if low on fuel */
958         if (obj->range <= find_nearest_city (obj->loc, COMP, &new_loc) + 2) {
959                 if (new_loc != obj->loc)
960                         new_loc = vmap_find_dest (path_map, comp_map, obj->loc,
961                                                   new_loc, COMP, T_AIR);
962         }
963         else new_loc = obj->loc;
964         
965         if (new_loc == obj->loc) { /* no nearby city? */
966                 new_loc = vmap_find_aobj (path_map, comp_map, obj->loc,
967                                                &fighter_fight);
968         }
969         move_objective (obj, path_map, new_loc, " ");
970 }
971
972 /*
973 Move a ship.
974
975 Attack anything adjacent.  If nothing adjacent, explore or look for
976 something to attack.
977 */
978
979 void
980 ship_move (obj)
981 piece_info_t *obj;
982 {
983         long new_loc;
984         char *adj_list;
985
986         if (obj->hits < piece_attr[obj->type].max_hits) { /* head to port */
987                 if (comp_map[obj->loc].contents == 'X') { /* stay in port */
988                         obj->moved = piece_attr[obj->type].speed;
989                         return;
990                 }
991                 new_loc = vmap_find_wobj (path_map, comp_map, obj->loc, &ship_repair);
992                 adj_list = ".";
993
994         }
995         else {
996                 new_loc = find_attack (obj->loc, ship_attack, ".");
997                 if (new_loc != obj->loc) { /* something to attack? */
998                         attack (obj, new_loc); /* attack it */
999                         return;
1000                 }
1001                 /* look for an objective */
1002                 (void) memcpy (amap, comp_map, MAP_SIZE * sizeof (view_map_t));
1003                 unmark_explore_locs (amap);
1004                 if (print_vmap == 'S') print_xzoom (amap);
1005                 
1006                 new_loc = vmap_find_wobj (path_map, amap, obj->loc,&ship_fight);
1007                 adj_list = ship_fight.objectives;
1008         }
1009
1010         move_objective (obj, path_map, new_loc, adj_list);
1011 }
1012
1013 /*
1014 Move to an objective.
1015 */
1016
1017 void
1018 move_objective (obj, pathmap, new_loc, adj_list)
1019 piece_info_t *obj;
1020 path_map_t pathmap[];
1021 long new_loc;
1022 char *adj_list;
1023 {
1024         char *terrain;
1025         char *attack_list;
1026         int d;
1027         int reuse; /* true iff we should reuse old map */
1028         long old_loc;
1029         long old_dest;
1030         
1031         if (new_loc == obj->loc) {
1032                 obj->moved = piece_attr[obj->type].speed;
1033                 obj->range -= 1;
1034                 pdebug ("No destination found for %d at %d; func=%d\n", obj->type, loc_disp(obj->loc), obj->func,0,0,0,0,0);
1035                 return;
1036         }
1037         old_loc = obj->loc; /* remember where we are */
1038         old_dest = new_loc; /* and where we're going */
1039         
1040         d = dist (new_loc, obj->loc);
1041         reuse = 1; /* try to reuse unless we learn otherwise */
1042         
1043         if (comp_map[new_loc].contents == ' ' && d == 2) { /* are we exploring? */
1044                 vmap_mark_adjacent (pathmap, obj->loc);
1045                 reuse = 0;
1046         }
1047         else vmap_mark_path (pathmap, comp_map, new_loc); /* find routes to destination */
1048         
1049         /* path terrain and move terrain may differ */
1050         switch (obj->type) {
1051         case ARMY: terrain = "+"; break;
1052         case FIGHTER: terrain = "+.X"; break;
1053         default: terrain = ".X"; break;
1054         }
1055         
1056         new_loc = vmap_find_dir (pathmap, comp_map, obj->loc,
1057                                  terrain, adj_list);
1058         
1059         if (new_loc == obj->loc /* path is blocked? */
1060             && (obj->type != ARMY || !obj->ship)) { /* don't unblock armies on a ship */
1061                 vmap_mark_near_path (pathmap, obj->loc);
1062                 reuse = 0;
1063                 new_loc = vmap_find_dir (pathmap, comp_map, obj->loc,
1064                                          terrain, adj_list);
1065         }
1066         
1067         /* encourage army to leave city */
1068         if (new_loc == obj->loc && map[obj->loc].cityp != NULL
1069                                 && obj->type == ARMY) {
1070                 new_loc = move_away (comp_map, obj->loc, "+");
1071                 reuse = 0;
1072         }
1073         if (new_loc == obj->loc) {
1074                 obj->moved = piece_attr[obj->type].speed;
1075                 
1076                 if (obj->type == ARMY && obj->ship) ;
1077                 else pdebug ("Cannot move %d at %d toward objective; func=%d\n", obj->type, loc_disp(obj->loc), obj->func,0,0,0,0,0);
1078         }
1079         else move_obj (obj, new_loc);
1080         
1081         /* Try to make more moves using same path map. */
1082         if (reuse && obj->moved < obj_moves (obj) && obj->loc != old_dest) {
1083                 /* check for immediate attack */
1084                 switch (obj->type) {
1085                 case FIGHTER:
1086                         if (comp_map[old_dest].contents != 'X' /* watch fuel */
1087                                 && obj->range <= piece_attr[FIGHTER].range / 2)
1088                                         return;
1089                         attack_list = fighter_attack;
1090                         terrain = "+.";
1091                         break;
1092                 case ARMY:
1093                         attack_list = army_attack;
1094                         if (obj->ship) terrain = "+*";
1095                         else terrain = "+.*";
1096                         break;
1097                 case TRANSPORT:
1098                         terrain = ".*";
1099                         if (obj->cargo) attack_list = tt_attack;
1100                         else attack_list = "*O"; /* causes tt to wake up */
1101                         break;
1102                 default:
1103                         attack_list = ship_attack;
1104                         terrain = ".";
1105                         break;
1106                 }
1107                 if (find_attack (obj->loc, attack_list, terrain) != obj->loc)
1108                         return;
1109                 
1110                 /* clear old path */
1111                 pathmap[old_loc].terrain = T_UNKNOWN;
1112                 for (d = 0; d < 8; d++) {
1113                         new_loc = old_loc + dir_offset[d];
1114                         pathmap[new_loc].terrain = T_UNKNOWN;
1115                 }
1116                 /* pathmap is already marked, but this should work */
1117                 move_objective (obj, pathmap, old_dest, adj_list);
1118         }
1119 }
1120
1121 /*
1122 Check to see if the game is over.  We count the number of cities
1123 owned by each side.  If either side has no cities and no armies, then
1124 the game is over.  If the computer has less than half as many cities
1125 and armies as the user, then the computer will give up.
1126 */
1127
1128 void
1129 check_endgame () { /* see if game is over */
1130
1131         int nuser_city, ncomp_city;
1132         int nuser_army, ncomp_army;
1133         piece_info_t *p;
1134         int i;
1135         
1136         date += 1; /* one more turn has passed */
1137         if (win != 0) return; /* we already know game is over */
1138
1139         nuser_city = 0; /* nothing counted yet */
1140         ncomp_city = 0;
1141         nuser_army = 0;
1142         ncomp_army = 0;
1143         
1144         for (i = 0; i < NUM_CITY; i++) {
1145                 if (city[i].owner == USER) nuser_city++;
1146                 else if (city[i].owner == COMP) ncomp_city++;
1147         }
1148         
1149         for (p = user_obj[ARMY]; p != NULL; p = p->piece_link.next)
1150                 nuser_army++;
1151         
1152         for (p = comp_obj[ARMY]; p != NULL; p = p->piece_link.next)
1153                 ncomp_army++;
1154                 
1155         if (ncomp_city < nuser_city/3 && ncomp_army < nuser_army/3) {
1156                 clear_screen ();
1157                 prompt ("The computer acknowledges defeat. Do",0,0,0,0,0,0,0,0);
1158                 ksend ("The computer acknowledges defeat.",0,0,0,0,0,0,0,0);
1159                 error ("you wish to smash the rest of the enemy? ",0,0,0,0,0,0,0,0);
1160
1161                 if (get_chx() !=  'Y') empend ();
1162                 (void) addstr ("\nThe enemy inadvertantly revealed its code used for");
1163                 (void) addstr ("\nreceiving battle information. You can display what");
1164                 (void) addstr ("\nthey've learned with the ''E'' command.");
1165                 resigned = TRUE;
1166                 win = 2;
1167                 automove = FALSE;
1168         }
1169         else if (ncomp_city == 0 && ncomp_army == 0) {
1170                 clear_screen ();
1171                 (void) addstr ("The enemy is incapable of defeating you.\n");
1172                 (void) addstr ("You are free to rape the empire as you wish.\n");
1173                 (void) addstr ("There may be, however, remnants of the enemy fleet\n");
1174                 (void) addstr ("to be routed out and destroyed.\n");
1175                 win = 1;
1176                 automove = FALSE;
1177         }
1178         else if (nuser_city == 0 && nuser_army == 0) {
1179                 clear_screen ();
1180                 (void) addstr ("You have been rendered incapable of\n");
1181                 (void) addstr ("defeating the rampaging enemy fascists! The\n");
1182                 (void) addstr ("empire is lost. If you have any ships left, you\n");
1183                 (void) addstr ("may attempt to harass enemy shipping.");
1184                 win = 1;
1185                 automove = FALSE;
1186         }
1187 }