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