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