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