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