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