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