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