Initial revision
[vms-empire.git] / compmove.c
1 /* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */
2
3 /*
4  *    Copyright (C) 1987, 1988 Chuck Simmons
5  * 
6  * See the file COPYING, distributed with empire, for restriction
7  * and warranty information.
8  */
9
10 /*
11 compmove.c -- Make a move for the computer. 
12
13 For each move the user wants us to make, we do the following:
14
15     1)  Handle city production;
16     2)  Move computer's pieces;
17     3)  Check to see if the game is over.
18 */
19
20 #ifdef SYSV
21 #include <string.h>
22 #else
23 #include <strings.h>
24 #endif
25
26 #include <curses.h>
27 #include "empire.h"
28 #include "extern.h"
29
30 static view_map_t emap[MAP_SIZE]; /* pruned explore map */
31
32 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...");
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[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[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",
272                 cityp->loc, cityp->prod, type);
273         
274         cityp->prod = type;
275         cityp->work = -(piece_attr[type].build_time / 5);
276 }
277
278 /*
279 See if a city is producing an object which is being overproduced.
280 */
281
282 int
283 overproduced (cityp, city_count)
284 city_info_t *cityp;
285 int *city_count;
286 {
287         int i;
288
289         for (i = 0; i < NUM_OBJECTS; i++) {
290                 /* return true if changing production would improve balance */
291                 if (i != cityp->prod
292                  && ((city_count[cityp->prod] - 1) * ratio[i]
293                    > (city_count[i] + 1) * ratio[cityp->prod]))
294                 return (TRUE);
295         }
296         return (FALSE);
297 }
298
299 /*
300 See if one type of production is needed more than another type.
301 Return the most-needed type of production.
302 */
303
304 int
305 need_more (city_count, prod1, prod2)
306 int *city_count;
307 int prod1, prod2;
308 {
309         if (city_count[prod1] * ratio[prod2]
310                  <= city_count[prod2] * ratio[prod1])
311         return (prod1);
312
313         else return (prod2);
314 }
315
316 /*
317 Figure out the most needed type of production.  We are passed
318 a flag telling us if armies are ok to produce.
319 */
320
321 void
322 comp_set_needed (cityp, city_count, army_ok, is_lake)
323 city_info_t *cityp;
324 int *city_count;
325 int army_ok;
326 int is_lake;
327 {
328         int best_prod;
329         int prod;
330
331         if (!army_ok) city_count[ARMY] = INFINITY;
332
333         if (is_lake) { /* choose fighter or army */
334                 comp_set_prod (cityp, need_more (city_count, ARMY, FIGHTER));
335                 return;
336         }
337         /* don't choose fighter */
338         city_count[FIGHTER] = INFINITY;
339         
340         best_prod = ARMY; /* default */
341         for (prod = 0; prod < NUM_OBJECTS; prod++) {
342                 best_prod = need_more (city_count, best_prod, prod);
343         }
344         comp_set_prod (cityp, best_prod);
345 }
346
347 /*
348 See if a city is on a lake.  We define a lake to be a body of
349 water (where cities are considered to be water) that does not
350 touch either an attackable city or unexplored territory.
351
352 Be careful, because we use the 'emap'.  This predicts whether
353 unexplored territory will be land or water.  The prediction should
354 be helpful, because small bodies of water that enclose unexplored
355 territory will appear as solid water.  Big bodies of water should
356 have unexplored territory on the edges.
357 */
358
359 int
360 lake (loc)
361 long loc;
362 {
363         int cont_map[MAP_SIZE];
364         scan_counts_t counts;
365
366         vmap_cont (cont_map, emap, loc, '+'); /* map lake */
367         counts = vmap_cont_scan (cont_map, emap);
368
369         return !(counts.unowned_cities || counts.user_cities || counts.unexplored);
370 }
371
372 /*
373 Move all computer pieces.
374 */
375
376 static view_map_t amap[MAP_SIZE]; /* temp view map */
377 static path_map_t path_map[MAP_SIZE];
378
379 void
380 do_pieces () { /* move pieces */
381         void cpiece_move();
382
383         int i;
384         piece_info_t *obj, *next_obj;
385
386         for (i = 0; i < NUM_OBJECTS; i++) { /* loop through obj lists */
387                 for (obj = comp_obj[move_order[i]]; obj != NULL;
388                     obj = next_obj) { /* loop through objs in list */
389                         next_obj = obj->piece_link.next;
390                         cpiece_move (obj); /* yup; move the object */
391                 }
392         }
393 }
394
395 /*
396 Move a piece.  We loop until all the moves of a piece are made.  Within
397 the loop, we find a direction to move that will take us closer to an
398 objective.
399 */
400
401 void
402 cpiece_move (obj)
403 piece_info_t *obj;
404 {
405         void move1();
406
407         int changed_loc;
408         int max_hits;
409         long saved_loc;
410         city_info_t *cityp;
411
412         if (obj->type == SATELLITE) {
413                 move_sat (obj);
414                 return;
415         }
416         
417         obj->moved = 0; /* not moved yet */
418         changed_loc = FALSE; /* not changed yet */
419         max_hits = piece_attr[obj->type].max_hits;
420
421         if (obj->type == FIGHTER) { /* init fighter range */
422                 cityp = find_city (obj->loc);
423                 if (cityp != NULL) obj->range = piece_attr[FIGHTER].range;
424         }
425         
426         while (obj->moved < obj_moves (obj)) {
427                 saved_loc = obj->loc; /* remember starting location */
428                 move1 (obj);
429                 if (saved_loc != obj->loc) changed_loc = TRUE;
430                 
431                 if (obj->type == FIGHTER && obj->hits > 0) {
432                         if (comp_map[obj->loc].contents == 'X')
433                                 obj->moved = piece_attr[FIGHTER].speed;
434                         else if (obj->range == 0) {
435                                 pdebug ("Fighter at %d crashed and burned\n", obj->loc);
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         void move_objective();
502         long find_attack();
503         void make_army_load_map(), make_unload_map(), make_tt_load_map();
504         void board_ship();
505         
506         long new_loc;
507         path_map_t path_map2[MAP_SIZE];
508         long new_loc2;
509         int cross_cost; /* cost to enter water */
510         
511         obj->func = 0; /* army doesn't want a tt */
512         if (vmap_at_sea (comp_map, obj->loc)) { /* army can't move? */
513                 (void) load_army (obj);
514                 obj->moved = piece_attr[ARMY].speed;
515                 if (!obj->ship) obj->func = 1; /* load army on ship */
516                 return;
517         }
518         if (obj->ship) /* is army on a transport? */
519                 new_loc = find_attack (obj->loc, army_attack, "+*");
520         else new_loc = find_attack (obj->loc, army_attack, ".+*");
521                 
522         if (new_loc != obj->loc) { /* something to attack? */
523                 attack (obj, new_loc); /* attack it */
524                 if (map[new_loc].contents == '.' /* moved to ocean? */
525                   && obj->hits > 0) { /* object still alive? */
526                         kill_obj (obj, new_loc);
527                         scan (user_map, new_loc); /* rescan for user */
528                 }
529                 return;
530         }
531         if (obj->ship) {
532                 if (obj->ship->func == 0) {
533                         if (!load_army (obj)) ABORT; /* load army on best ship */
534                         return; /* armies stay on a loading ship */
535                 }
536                 make_unload_map (amap, comp_map);
537                 new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_unload);
538                 move_objective (obj, path_map, new_loc, " ");
539                 return;
540         }
541
542         new_loc = vmap_find_lobj (path_map, comp_map, obj->loc, &army_fight);
543         
544         if (new_loc != obj->loc) { /* something interesting on land? */
545                 switch (comp_map[new_loc].contents) {
546                 case 'A':
547                 case 'O':
548                         cross_cost = 60; /* high cost if enemy present */
549                         break;
550                 case '*':
551                         cross_cost = 30; /* medium cost for attackable city */
552                         break;
553                 case ' ':
554                         cross_cost = 14; /* low cost for exploring */
555                         break;
556                 default:
557                         ABORT;
558                 }
559                 cross_cost = path_map[new_loc].cost * 2 - cross_cost;
560         }
561         else cross_cost = INFINITY;
562         
563         if (new_loc == obj->loc || cross_cost > 0) {
564                 /* see if there is something interesting to load */
565                 make_army_load_map (obj, amap, comp_map);
566                 new_loc2 = vmap_find_lwobj (path_map2, amap, obj->loc, &army_load, cross_cost);
567                 
568                 if (new_loc2 != obj->loc) { /* found something? */
569                         board_ship (obj, path_map2, new_loc2);
570                         return;
571                 }
572         }
573
574         move_objective (obj, path_map, new_loc, " ");
575 }
576
577 /*
578 Remove pruned explore locs from a view map.
579 */
580
581 void
582 unmark_explore_locs (xmap)
583 view_map_t *xmap;
584 {
585         long i;
586
587         for (i = 0; i < MAP_SIZE; i++)
588         if (map[i].on_board && xmap[i].contents == ' ')
589                 xmap[i].contents = emap[i].contents;
590 }
591
592 /*
593 Make a load map.  We copy the view map and mark each loading
594 transport and tt producing city with a '$'.
595 */
596
597 void
598 make_army_load_map (obj, xmap, vmap)
599 piece_info_t *obj;
600 view_map_t *xmap;
601 view_map_t *vmap;
602 {
603         piece_info_t *p;
604         int i;
605         
606         (void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
607
608         /* mark loading transports or cities building transports */
609         for (p = comp_obj[TRANSPORT]; p; p = p->piece_link.next)
610         if (p->func == 0) /* loading tt? */
611         xmap[p->loc].contents = '$';
612         
613         for (i = 0; i < NUM_CITY; i++)
614         if (city[i].owner == COMP && city[i].prod == TRANSPORT) {
615                 if (nearby_load (obj, city[i].loc))
616                         xmap[city[i].loc].contents = 'x'; /* army is nearby so it can load */
617                 else if (nearby_count (city[i].loc) < piece_attr[TRANSPORT].capacity)
618                         xmap[city[i].loc].contents = 'x'; /* city needs armies */
619         }
620         
621         if (print_vmap == 'A') print_xzoom (xmap);
622 }
623
624 /* Return true if an army is considered near a location for loading. */
625
626 int
627 nearby_load (obj, loc)
628 piece_info_t *obj;
629 long loc;
630 {
631         return obj->func == 1 && dist (obj->loc, loc) <= 2;
632 }
633         
634 /* Return number of nearby armies. */
635
636 int
637 nearby_count (loc)
638 long loc;
639 {
640         piece_info_t *obj;
641         int count;
642
643         count = 0;
644         for (obj = comp_obj[ARMY]; obj; obj = obj->piece_link.next) {
645                 if (nearby_load (obj, loc)) count += 1;
646         }
647         return count;
648 }
649
650 /* Make load map for a ship. */
651
652 void
653 make_tt_load_map (xmap, vmap)
654 view_map_t *xmap;
655 view_map_t *vmap;
656 {
657         piece_info_t *p;
658         
659         (void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
660
661         /* mark loading armies */
662         for (p = comp_obj[ARMY]; p; p = p->piece_link.next)
663         if (p->func == 1) /* loading army? */
664         xmap[p->loc].contents = '$';
665         
666         if (print_vmap == 'L') print_xzoom (xmap);
667 }
668         
669 /*
670 Make an unload map.  We copy the view map.  We then create
671 a continent map.  For each of our cities, we mark out the continent
672 that city is on.  Then, for each city that we don't own and which
673 doesn't appear on our continent map, we set that square to a digit.
674
675 We want to assign weights to each attackable city.
676 Cities are more valuable if they are on a continent which
677 has lots of cities.  Cities are also valuable if either it
678 will be easy for us to take over the continent, or if we
679 need to defend that continent from an enemy.
680
681 To implement the above, we assign numbers to each city as follows:
682
683 a)  if unowned_cities > user_cities && comp_cities == 0
684        set number to min (total_cities, 9)
685
686 b)  if comp_cities != 0 && user_cities != 0
687        set number to min (total_cities, 9)
688        
689 b)  if enemy_cities == 1 && total_cities == 1, set number to 2.
690     (( taking the sole enemy city on a continent is as good as
691        getting a two city continent ))
692        
693 c)  Any other attackable city is marked with a '0'.
694 */
695
696 static int owncont_map[MAP_SIZE];
697 static int tcont_map[MAP_SIZE];
698
699 void
700 make_unload_map (xmap, vmap)
701 view_map_t *xmap;
702 view_map_t *vmap;
703 {
704         long i;
705         scan_counts_t counts;
706
707         (void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
708         unmark_explore_locs (xmap);
709         
710         for (i = 0; i < MAP_SIZE; i++)
711                 owncont_map[i] = 0; /* nothing marked */
712
713         for (i = 0; i < NUM_CITY; i++)
714         if (city[i].owner == COMP)
715         vmap_mark_up_cont (owncont_map, xmap, city[i].loc, '.');
716
717         for (i = 0; i < MAP_SIZE; i++)
718         if (strchr ("O*", vmap[i].contents)) {
719                 int total_cities;
720                 
721                 vmap_cont (tcont_map, xmap, i, '.'); /* map continent */
722                 counts = vmap_cont_scan (tcont_map, xmap);
723                 
724                 total_cities = counts.unowned_cities
725                              + counts.user_cities
726                              + counts.comp_cities;
727                              
728                 if (total_cities > 9) total_cities = 0;
729                 
730                 if (counts.user_cities && counts.comp_cities)
731                         xmap[i].contents = '0' + total_cities;
732
733                 else if (counts.unowned_cities > counts.user_cities
734                          && counts.comp_cities == 0)
735                         xmap[i].contents = '0' + total_cities;
736
737                 else if (counts.user_cities == 1 && counts.comp_cities == 0)
738                         xmap[i].contents = '2';
739                         
740                 else xmap[i].contents = '0';
741         }
742         if (print_vmap == 'U') print_xzoom (xmap);
743 }
744
745 /*
746 Load an army onto a ship.  First look for an adjacent ship.
747 If that doesn't work, move to the objective, trying to be
748 close to the ocean.
749 */
750
751 void
752 board_ship (obj, pmap, dest)
753 piece_info_t *obj;
754 path_map_t *pmap;
755 long dest;
756 {
757         if (!load_army (obj)) {
758                 obj->func = 1; /* loading */
759                 move_objective (obj, pmap, dest, "t.");
760         }
761 }
762
763 /*
764 Look for the most full, non-full transport at a location.
765 Prefer switching to staying.  If we switch, we force
766 one of the ships to become more full.
767 */
768
769 piece_info_t *
770 find_best_tt (best, loc)
771 piece_info_t *best;
772 long loc;
773 {
774         piece_info_t *p;
775
776         for (p = map[loc].objp; p != NULL; p = p->loc_link.next)
777         if (p->type == TRANSPORT && obj_capacity (p) > p->count) {
778                 if (!best) best = p;
779                 else if (p->count >= best->count) best = p;
780         }
781         return best;
782 }
783
784 /*
785 Load an army onto the most full non-full ship.
786 */
787
788 int
789 load_army (obj)
790 piece_info_t *obj;
791 {
792         piece_info_t *p;
793         int i;
794         long x_loc;
795
796         p = find_best_tt (obj->ship, obj->loc); /* look here first */
797
798         for (i = 0; i < 8; i++) { /* try surrounding squares */
799                 x_loc = obj->loc + dir_offset[i];
800                 if (map[x_loc].on_board)
801                         p = find_best_tt (p, x_loc);
802
803         }
804         if (!p) return FALSE; /* no tt to be found */
805
806         if (p->loc == obj->loc) { /* stay in same place */
807                 obj->moved = piece_attr[ARMY].speed;
808         }
809         else move_obj (obj, p->loc); /* move to square with ship */
810
811         if (p->ship != obj->ship) { /* reload army to new ship */
812                 disembark (obj);
813                 embark (p, obj);
814         }
815         return TRUE;
816 }
817
818 /*
819 Return the first location we find adjacent to the current location of
820 the correct terrain.
821 */
822
823 long
824 move_away (vmap, loc, terrain)
825 view_map_t *vmap;
826 long loc;
827 char *terrain;
828 {
829         long new_loc;
830         int i;
831
832         for (i = 0; i < 8; i++) {
833                 new_loc = loc + dir_offset[i];
834                 if (map[new_loc].on_board
835                  && strchr (terrain, vmap[new_loc].contents))
836                         return (new_loc);
837         }
838         return (loc);
839 }
840
841 /*
842 Look to see if there is an adjacent object to attack.  We are passed
843 a location and a list of items we attack sorted in order of most
844 valuable first.  We look at each surrounding on board location.
845 If there is an object we can attack, we return the location of the
846 best of these.
847 */
848
849 long
850 find_attack (loc, obj_list, terrain)
851 long loc;
852 char *obj_list;
853 char *terrain;
854 {
855         long new_loc, best_loc;
856         int i, best_val;
857         char *p;
858
859         best_loc = loc; /* nothing found yet */
860         best_val = INFINITY;
861         for (i = 0; i < 8; i++) {
862                 new_loc = loc + dir_offset[i];
863
864                 if (map[new_loc].on_board /* can we move here? */
865                     && strchr (terrain, map[new_loc].contents)) {
866                         p = strchr (obj_list, comp_map[new_loc].contents);
867                         if (p != NULL && p - obj_list < best_val) {
868                                 best_val = p - obj_list;
869                                 best_loc = new_loc;
870                         }
871                 }
872         }
873         return (best_loc);
874 }
875
876 /*
877 Move a transport.
878
879 There are two kinds of transports:  loading and unloading.
880
881 Loading transports move toward loading armies.  Unloading
882 transports move toward attackable cities on unowned continents.
883
884 An empty transport is willing to attack adjacent enemy transports.
885 Transports become 'loading' when empty, and 'unloading' when full.
886 */
887
888 void
889 transport_move (obj)
890 piece_info_t *obj;
891 {
892         void tt_do_move();
893
894         long new_loc;
895
896         /* empty transports can attack */
897         if (obj->count == 0) { /* empty? */
898                 obj->func = 0; /* transport is loading */
899                 new_loc = find_attack (obj->loc, tt_attack, ".");
900                 if (new_loc != obj->loc) { /* something to attack? */
901                         attack (obj, new_loc); /* attack it */
902                         return;
903                 }
904         }
905
906         if (obj->count == obj_capacity (obj)) /* full? */
907                 obj->func = 1; /* unloading */
908
909         if (obj->func == 0) { /* loading? */
910                 make_tt_load_map (amap, comp_map);
911                 new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_load);
912                 
913                 if (new_loc == obj->loc) { /* nothing to load? */
914                         (void) memcpy (amap, comp_map, MAP_SIZE * sizeof (view_map_t));
915                         unmark_explore_locs (amap);
916                         if (print_vmap == 'S') print_xzoom (amap);
917                         new_loc = vmap_find_wobj (path_map, amap, obj->loc, &tt_explore);
918                 }
919                 
920                 move_objective (obj, path_map, new_loc, "a ");
921         }
922         else {
923                 make_unload_map (amap, comp_map);
924                 new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_unload);
925                 move_objective (obj, path_map, new_loc, " ");
926         }
927 }
928
929 /*
930 Move a fighter.
931
932 1)  See if there is an object we can attack immediately.
933 If so, attack it.
934
935 2)  Otherwise, if fighter is low on fuel, move toward nearest city
936 if there is one in range.
937
938 3)  Otherwise, look for an objective.
939 */
940
941 void
942 fighter_move (obj)
943 piece_info_t *obj;
944 {
945         long new_loc;
946
947         new_loc = find_attack (obj->loc, fighter_attack, ".+");
948         if (new_loc != obj->loc) { /* something to attack? */
949                 attack (obj, new_loc); /* attack it */
950                 return;
951         }
952         /* return to base if low on fuel */
953         if (obj->range <= find_nearest_city (obj->loc, COMP, &new_loc) + 2) {
954                 if (new_loc != obj->loc)
955                         new_loc = vmap_find_dest (path_map, comp_map, obj->loc,
956                                                   new_loc, COMP, T_AIR);
957         }
958         else new_loc = obj->loc;
959         
960         if (new_loc == obj->loc) { /* no nearby city? */
961                 new_loc = vmap_find_aobj (path_map, comp_map, obj->loc,
962                                                &fighter_fight);
963         }
964         move_objective (obj, path_map, new_loc, " ");
965 }
966
967 /*
968 Move a ship.
969
970 Attack anything adjacent.  If nothing adjacent, explore or look for
971 something to attack.
972 */
973
974 void
975 ship_move (obj)
976 piece_info_t *obj;
977 {
978         long new_loc;
979         char *adj_list;
980
981         if (obj->hits < piece_attr[obj->type].max_hits) { /* head to port */
982                 if (comp_map[obj->loc].contents == 'X') { /* stay in port */
983                         obj->moved = piece_attr[obj->type].speed;
984                         return;
985                 }
986                 new_loc = vmap_find_wobj (path_map, comp_map, obj->loc,
987                                                &ship_repair);
988                 adj_list = ".";
989
990         }
991         else {
992                 new_loc = find_attack (obj->loc, ship_attack, ".");
993                 if (new_loc != obj->loc) { /* something to attack? */
994                         attack (obj, new_loc); /* attack it */
995                         return;
996                 }
997                 /* look for an objective */
998                 (void) memcpy (amap, comp_map, MAP_SIZE * sizeof (view_map_t));
999                 unmark_explore_locs (amap);
1000                 if (print_vmap == 'S') print_xzoom (amap);
1001                 
1002                 new_loc = vmap_find_wobj (path_map, amap, obj->loc,
1003                                                &ship_fight);
1004                 adj_list = ship_fight.objectives;
1005         }
1006
1007         move_objective (obj, path_map, new_loc, adj_list);
1008 }
1009
1010 /*
1011 Move to an objective.
1012 */
1013
1014 void
1015 move_objective (obj, pathmap, new_loc, adj_list)
1016 piece_info_t *obj;
1017 path_map_t pathmap[];
1018 long new_loc;
1019 char *adj_list;
1020 {
1021         char *terrain;
1022         char *attack_list;
1023         int d;
1024         int reuse; /* true iff we should reuse old map */
1025         long old_loc;
1026         long old_dest;
1027         
1028         if (new_loc == obj->loc) {
1029                 obj->moved = piece_attr[obj->type].speed;
1030                 obj->range -= 1;
1031                 pdebug ("No destination found for %d at %d; func=%d\n",
1032                         obj->type, obj->loc, obj->func);
1033                 return;
1034         }
1035         old_loc = obj->loc; /* remember where we are */
1036         old_dest = new_loc; /* and where we're going */
1037         
1038         d = dist (new_loc, obj->loc);
1039         reuse = 1; /* try to reuse unless we learn otherwise */
1040         
1041         if (comp_map[new_loc].contents == ' ' && d == 2) { /* are we exploring? */
1042                 vmap_mark_adjacent (pathmap, obj->loc);
1043                 reuse = 0;
1044         }
1045         else vmap_mark_path (pathmap, comp_map, new_loc); /* find routes to destination */
1046         
1047         /* path terrain and move terrain may differ */
1048         switch (obj->type) {
1049         case ARMY: terrain = "+"; break;
1050         case FIGHTER: terrain = "+.X"; break;
1051         default: terrain = ".X"; break;
1052         }
1053         
1054         new_loc = vmap_find_dir (pathmap, comp_map, obj->loc,
1055                                  terrain, adj_list);
1056         
1057         if (new_loc == obj->loc /* path is blocked? */
1058             && (obj->type != ARMY || !obj->ship)) { /* don't unblock armies on a ship */
1059                 vmap_mark_near_path (pathmap, obj->loc);
1060                 reuse = 0;
1061                 new_loc = vmap_find_dir (pathmap, comp_map, obj->loc,
1062                                          terrain, adj_list);
1063         }
1064         
1065         /* encourage army to leave city */
1066         if (new_loc == obj->loc && map[obj->loc].cityp != NULL
1067                                 && obj->type == ARMY) {
1068                 new_loc = move_away (comp_map, obj->loc, "+");
1069                 reuse = 0;
1070         }
1071         if (new_loc == obj->loc) {
1072                 obj->moved = piece_attr[obj->type].speed;
1073                 
1074                 if (obj->type == ARMY && obj->ship) ;
1075                 else pdebug ("Cannot move %d at %d toward objective; func=%d\n",
1076                              obj->type, obj->loc, obj->func);
1077         }
1078         else move_obj (obj, new_loc);
1079         
1080         /* Try to make more moves using same path map. */
1081         if (reuse && obj->moved < obj_moves (obj) && obj->loc != old_dest) {
1082                 /* check for immediate attack */
1083                 switch (obj->type) {
1084                 case FIGHTER:
1085                         if (comp_map[old_dest].contents != 'X' /* watch fuel */
1086                                 && obj->range <= piece_attr[FIGHTER].range / 2)
1087                                         return;
1088                         attack_list = fighter_attack;
1089                         terrain = "+.";
1090                         break;
1091                 case ARMY:
1092                         attack_list = army_attack;
1093                         if (obj->ship) terrain = "+*";
1094                         else terrain = "+.*";
1095                         break;
1096                 case TRANSPORT:
1097                         terrain = ".*";
1098                         if (obj->cargo) attack_list = tt_attack;
1099                         else attack_list = "*O"; /* causes tt to wake up */
1100                         break;
1101                 default:
1102                         attack_list = ship_attack;
1103                         terrain = ".";
1104                         break;
1105                 }
1106                 if (find_attack (obj->loc, attack_list, terrain) != obj->loc)
1107                         return;
1108                 
1109                 /* clear old path */
1110                 pathmap[old_loc].terrain = T_UNKNOWN;
1111                 for (d = 0; d < 8; d++) {
1112                         new_loc = old_loc + dir_offset[d];
1113                         pathmap[new_loc].terrain = T_UNKNOWN;
1114                 }
1115                 /* pathmap is already marked, but this should work */
1116                 move_objective (obj, pathmap, old_dest, adj_list);
1117         }
1118 }
1119
1120 /*
1121 Check to see if the game is over.  We count the number of cities
1122 owned by each side.  If either side has no cities and no armies, then
1123 the game is over.  If the computer has less than half as many cities
1124 and armies as the user, then the computer will give up.
1125 */
1126
1127 void
1128 check_endgame () { /* see if game is over */
1129
1130         int nuser_city, ncomp_city;
1131         int nuser_army, ncomp_army;
1132         piece_info_t *p;
1133         int i;
1134         
1135         date += 1; /* one more turn has passed */
1136         if (win != 0) return; /* we already know game is over */
1137
1138         nuser_city = 0; /* nothing counted yet */
1139         ncomp_city = 0;
1140         nuser_army = 0;
1141         ncomp_army = 0;
1142         
1143         for (i = 0; i < NUM_CITY; i++) {
1144                 if (city[i].owner == USER) nuser_city++;
1145                 else if (city[i].owner == COMP) ncomp_city++;
1146         }
1147         
1148         for (p = user_obj[ARMY]; p != NULL; p = p->piece_link.next)
1149                 nuser_army++;
1150         
1151         for (p = comp_obj[ARMY]; p != NULL; p = p->piece_link.next)
1152                 ncomp_army++;
1153                 
1154         if (ncomp_city < nuser_city/3 && ncomp_army < nuser_army/3) {
1155                 clear_screen ();
1156                 prompt ("The computer acknowledges defeat. Do");
1157                 error ("you wish to smash the rest of the enemy? ");
1158
1159                 if (get_chx() !=  'Y') empend ();
1160                 (void) addstr ("\nThe enemy inadvertantly revealed its code used for");
1161                 (void) addstr ("\nreceiving battle information. You can display what");
1162                 (void) addstr ("\nthey've learned with the ''E'' command.");
1163                 resigned = TRUE;
1164                 win = 2;
1165                 automove = FALSE;
1166         }
1167         else if (ncomp_city == 0 && ncomp_army == 0) {
1168                 clear_screen ();
1169                 (void) addstr ("The enemy is incapable of defeating you.\n");
1170                 (void) addstr ("You are free to rape the empire as you wish.\n");
1171                 (void) addstr ("There may be, however, remnants of the enemy fleet\n");
1172                 (void) addstr ("to be routed out and destroyed.\n");
1173                 win = 1;
1174                 automove = FALSE;
1175         }
1176         else if (nuser_city == 0 && nuser_army == 0) {
1177                 clear_screen ();
1178                 (void) addstr ("You have been rendered incapable of\n");
1179                 (void) addstr ("defeating the rampaging enemy fascists! The\n");
1180                 (void) addstr ("empire is lost. If you have any ships left, you\n");
1181                 (void) addstr ("may attempt to harass enemy shipping.");
1182                 win = 1;
1183                 automove = FALSE;
1184         }
1185 }