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