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