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