games@tekred.TEK.COM (06/29/88)
Submitted by: "Stanley T. Shebs" <shebs%defun@cs.utah.edu> Comp.sources.games: Volume 4, Issue 91 Archive-name: xconq5/Part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 18)." # Contents: draw.c lib/four.map mplay.c # Wrapped by billr@saab on Wed Jun 29 08:55:32 1988 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f draw.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"draw.c\" else echo shar: Extracting \"draw.c\" \(16834 characters\) sed "s/^X//" >draw.c <<'END_OF_draw.c' X/* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */ X/* This program may be used, copied, modified, and redistributed freely */ X/* for noncommercial purposes, so long as this notice remains intact. */ X X/* RCS $Header: draw.c,v 1.1 88/06/21 12:30:08 shebs Exp $ */ X X/* Geometry is somewhat tricky because our viewports are supposed */ X/* to wrap around a cylinder transparently. The general idea is that */ X/* if modulo opns map x coordinates onto a cylinder, adding and subtracting */ X/* the diameter of the cylinder "un-modulos" things. If this doesn't make */ X/* any sense to you, then be careful about fiddling with the code! */ X X/* If that wasn't bad enough, the hexes constitute an oblique coordinate */ X/* where the axes form a 60 degree angle to each other. Fortunately, no */ X/* trig is necessary - to convert to/from rectangular, add/subtract 1/2 of */ X/* the y coordinate to x, and leave the y coordinate alone. */ X X/* The graphical code uses mostly text drawing functions, which are more */ X/* likely to be efficient than is random bitblting. (The interface may */ X/* implement the operations as random blitting, but that's OK.) */ X X#include "config.h" X#include "misc.h" X#include "period.h" X#include "side.h" X#include "unit.h" X#include "map.h" X Xextern bool populations; /* used to decide about running pop display */ X Xchar rowbuf[BUFSIZE]; /* buffer for terrain row drawing */ X X/* Completely redo a screen, making no assumptions about appearance. */ X/* This one is used frequently, especially when a window is exposed. */ X Xredraw(side) XSide *side; X{ X if (active_display(side)) { X erase_cursor(side); X clear_window(side, side->main); X show_note(side); X show_info(side); X show_prompt(side); X show_all_sides(side); X show_timemode(side); X show_clock(side); X show_state(side); X show_map(side); X show_world(side); X flush_output(side); X flush_input(side); X } X} X X/* Ensure that given location is visible. We also flush the input because */ X/* any input relating to a different screen is probably worthless. */ X Xput_on_screen(side, x, y) XSide *side; Xint x, y; X{ X /* Ugly hack to prevent extra boxes being drawn during init - don't ask!*/ X if (x == 0 && y == 0) return; X if (active_display(side)) { X if (!in_middle(side, x, y)) { X side->vcx = wrap(x); X side->vcy = min(max(side->vh2-(1-(side->vh&1)), y), X (world.height-1)-side->vh2); X if (side->lastvcx >= 0) undraw_box(side); X show_map(side); X flush_output(side); X flush_input(side); X } X } X} X X/* Decide whether given location is not too close to edge of screen. */ X/* We do this because it's a pain to move units when half the adjacent */ X/* places aren't even visible. This routine effectively places a lower */ X/* limit of 5x5 for the map window. (I think) */ X Xin_middle(side, x, y) XSide *side; Xint x, y; X{ X int vcx = side->vcx, vcy = side->vcy; X int vw2 = side->vw2, vh2 = side->vh2; X X if (!between(vcy-vh2+2, y, vcy+vh2-2) && between(2, y, world.height-3)) X return FALSE; X x = unwrap(side, x + (y - vcy) / 2); X return between(vcx-vw2+2, x, vcx+vw2-3+(side->vw&1)); X} X X/* Transform map coordinates into screen coordinates, relative to the given */ X/* side. Allow for cylindricalness and number of pixels in a hex. */ X Xxform(side, x, y, sxp, syp) XSide *side; Xint x, y, *sxp, *syp; X{ X *sxp = ((side->hw * (x - (side->vcx - side->vw2))) + X (side->hw * (y - side->vcy)) / 2); X *syp = side->hch * ((side->vcy + side->vh2) - y); X} X X/* Undo the wrapping effect, relative to viewport location. */ X/* Note that both conditions cannot both be true at the same time, */ X/* since viewport is smaller than map. */ X Xunwrap(side, x) XSide *side; Xint x; X{ X int vcx = side->vcx, vw2 = side->vw2, vw34 = side->vw2 + (side->vw2 >> 1); X X if (vcx - vw2 < 0 && x > vcx + vw34) x -= world.width; X if (vcx + vw2 > world.width-1 && x < vcx - vw34) x += world.width; X return x; X} X X/* Un-transform screen coordinates (as supplied by mouse perhaps) into */ X/* map coordinates. This doesn't actually account for the details of */ X/* hexagonal boundaries, and actually discriminates box-shaped areas. */ X Xdeform(side, sx, sy, xp, yp) XSide *side; Xint sx, sy, *xp, *yp; X{ X int vcx = side->vcx, vcy = side->vcy, adjust; X X *yp = (vcy + side->vh2) - (sy / side->hch); X adjust = (((*yp - vcy) & 1) ? ((side->hw/2) * (*yp >= vcy ? 1 : -1)) : 0); X *xp = wrap(((sx - adjust) / side->hw) - (*yp - vcy) / 2 + X (vcx - side->vw2)); X} X X/* Transform coordinates in the world display. Simpler, since no moving */ X/* viewport nonsense. */ X Xw_xform(side, x, y, sxp, syp) XSide *side; Xint x, y, *sxp, *syp; X{ X *sxp = side->mm * x + (side->mm * y) / 2; X *syp = side->mm * (world.height - 1 - y); X} X X/* Redraw the map of the whole world. We use square blobs instead of icons, */ X/* since individual "hexes" may be as little as 1x1 pixels in size, and */ X/* there are lots of them. Algorithm uses run-length encoding to find and */ X/* draw bars of constant color. Monochrome just draws units - no good */ X/* choices for terrain display. */ X Xshow_world(side) XSide *side; X{ X int x, y, color, barcolor, x1; X X if (active_display(side) && world_display(side)) { X clear_window(side, side->world); X for (y = world.height-1; y >= 0; --y) { X x1 = 0; X barcolor = world_color(side, x1, y); X for (x = 0; x < world.width; ++x) { X color = world_color(side, x, y); X if (color != barcolor) { X draw_bar(side, x1, y, x - x1, barcolor); X x1 = x; X barcolor = color; X } X } X draw_bar(side, x1, y, world.width - x1, barcolor); X } X if (side->vcy >= 0) draw_box(side); X } X} X X/* Compute the color representing a hex from the given side's point of view. */ X Xworld_color(side, x, y) XSide *side; Xint x, y; X{ X int view = side_view(side, x, y); X Side *side2; X X if (side->monochrome) { X return ((view == UNSEEN || view == EMPTY) ? side->bgcolor : X side->fgcolor); X } else { X if (view == UNSEEN) { X return (side->bgcolor); X } else if (view == EMPTY) { X return (side->hexcolor[terrain_at(x, y)]); X } else { X side2 = side_n(vside(view)); X return ((side2 == NULL) ? side->neutcolor : X (allied_side(side2, side) ? side->altcolor : X side->enemycolor)); X } X } X} X X/* Draw an outline box on the world map. Since we adopt the dubious trick */ X/* of inverting through all planes, must be careful to undo before moving; */ X/* also, draw/undraw shiftedly so both boxes appear on both sides of world. */ X Xdraw_box(side) XSide *side; X{ X invert_box(side, side->vcx, side->vcy); X invert_box(side, side->vcx - world.width, side->vcy); X side->lastvcx = side->vcx; side->lastvcy = side->vcy; X} X Xundraw_box(side) XSide *side; X{ X invert_box(side, side->lastvcx, side->lastvcy); X invert_box(side, side->lastvcx - world.width, side->lastvcy); X} X X/* Draw immediate area in more detail. We make a little effort to avoid */ X/* drawing hexes off the visible part of the screen, but are still somewhat */ X/* conservative, so as not to get holes in the display. Implication is that */ X/* some lower level of routines has to be able to clip the map window. */ X Xshow_map(side) XSide *side; X{ X int y1, y2, y, x1, x2, adj; X X if (active_display(side)) { X clear_window(side, side->map); X y1 = side->vcy + side->vh2; X y2 = side->vcy - side->vh2 + 1 - (side->vh & 1); X for (y = y1; y >= y2; --y) { X adj = (y - side->vcy) / 2; X x1 = side->vcx - side->vw2 - adj - 1; X x2 = side->vcx + side->vw2 - adj + 1 + (side->vw & 1); X draw_row(side, x1, y, x2 - x1); X } X draw_cursor(side); X flush_output(side); X draw_box(side); /* must be after flush */ X } X} X X/* Draw an individual detailed hex, as a row of one. */ X/* This routine may be called in cases where the hex is not on the main */ X/* screen; if so, then the world map but not the local map is drawn on. */ X/* (should the display be shifted to make visible?) */ X Xdraw_hex(side, x, y, flushit) XSide *side; Xint x, y; Xbool flushit; X{ X int sx, sy; X X if (active_display(side)) { X if (side->monochrome || side->showmode == TERRICONS) { X xform(side, unwrap(side, x), y, &sx, &sy); X draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX); X } X draw_row(side, unwrap(side, x), y, 1); X draw_bar(side, x, y, 1, world_color(side, x, y)); X if (flushit) flush_output(side); X } X} X X/* The basic map drawing routine does an entire row at a time, which yields */ X/* order-of-magnitude speedups (!). This routine is complicated by several */ X/* tricks: 1) in monochrome, the entire line can be drawn at once; 2) in */ X/* color, run-length encoding maximizes the length of constant-color strings */ X/* and 3) anything which is in the background color need not be drawn. */ X/* In general, this routine dominates the map viewing process, so efficiency */ X/* here is very important. */ X Xdraw_row(side, x0, y0, len) XSide *side; Xint x0, y0, len; X{ X bool empty = TRUE; X char ch; X int i = 0, x, x1, color, segcolor, sx, sy; X X if (side->monochrome) { X xform(side, x0, y0, &sx, &sy); X for (x = x0; x < x0 + len; ++x) { X if (side_view(side, wrap(x), y0) == EMPTY) { X rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar; X empty = FALSE; X } else { X rowbuf[i++] = ' '; X } X } X if (!empty) draw_terrain_row(side, sx, sy, rowbuf, i, side->fgcolor); X } else { X x1 = x0; X segcolor = hex_color(side, x0, y0); X for (x = x0; x < x0 + len; ++x) { X color = hex_color(side, x, y0); X if (color != segcolor) { X if (segcolor != side->bgcolor) { X xform(side, x1, y0, &sx, &sy); X draw_terrain_row(side, sx, sy, rowbuf, i, segcolor); X } X i = 0; X x1 = x; X segcolor = color; X } X switch(side->showmode) { X case FULLHEX: X case BOTHICONS: X ch = HEX; X break; X case BORDERHEX: X ch = OHEX; X break; X case TERRICONS: X ch = ttypes[terrain_at(wrap(x), y0)].tchar; X break; X } X rowbuf[i++] = ch; X } X if (len == 1) i = 1; X xform(side, x1, y0, &sx, &sy); X draw_terrain_row(side, sx, sy, rowbuf, i, segcolor); X if (side->showmode == BOTHICONS) { X i = 0; X x1 = x0; X segcolor = terricon_color(side, x0, y0); X for (x = x0; x < x0 + len; ++x) { X color = terricon_color(side, x, y0); X if (color != segcolor) { X xform(side, x1, y0, &sx, &sy); X draw_terrain_row(side, sx, sy, rowbuf, i, segcolor); X i = 0; X x1 = x; X segcolor = color; X } X rowbuf[i++] = ttypes[terrain_at(wrap(x), y0)].tchar; X } X if (len == 1) i = 1; X xform(side, x1, y0, &sx, &sy); X draw_terrain_row(side, sx, sy, rowbuf, i, segcolor); X } X } X /* Units are much harder to optimize - fortunately they're sparse */ X for (x = x0; x < x0 + len; ++x) { X draw_unit(side, x, y0); X } X} X X/* Return the color of the hex. (for color displays only) */ X Xhex_color(side, x, y) XSide *side; Xint x, y; X{ X return ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor : X side->hexcolor[terrain_at(wrap(x), y)]); X} X X/* Return the color of a terrain icon overlaying a colored hex. */ X Xterricon_color(side, x, y) XSide *side; Xint x, y; X{ X return ((side_view(side, wrap(x), y) == UNSEEN) ? side->bgcolor : X (ttypes[terrain_at(wrap(x), y)].dark ? side->fgcolor : X side->bgcolor)); X} X X/* Draw a single unit icon as appropriate. This *also* has a bunch of */ X/* details to worry about: centering of icon in hex, clearing a rectangular */ X/* area for the icon, picking a color for the unit, using either a bitmap */ X/* or font char, and adding a side number for many-player games. */ X/* Must also be careful not to draw black-on-black for units in space. */ X/* This routine has also been drafted into drawing populace side numbers */ X/* for otherwise empty hexes. */ X Xdraw_unit(side, x, y) XSide *side; Xint x, y; X{ X int view = side_view(side, wrap(x), y), sx, sy, ucolor, hcolor, n; X int terr = terrain_at(wrap(x), y), pop, pcolor; X Side *side2; X X if (view != UNSEEN) { X if (view == EMPTY) { X if (populations) { X pop = people_at(wrap(x), y); X if (pop != NOBODY) { X side2 = side_n(pop-8); X pcolor = (allied_side(side, side2) ? side->owncolor : X (enemy_side(side, side2) ? side->enemycolor : X side->neutcolor)); X if (pcolor == side->owncolor && X (ttypes[terr].dark || X side->monochrome || X (side->showmode == TERRICONS))) X pcolor = side->fgcolor; X xform(side, x, y, &sx, &sy); X draw_side_number(side, side->map, sx, sy, pop-8, pcolor); X } X } X } else { X xform(side, x, y, &sx, &sy); X side2 = side_n(vside(view)); X ucolor = (allied_side(side, side2) ? side->owncolor : X (enemy_side(side, side2) ? side->enemycolor : X side->neutcolor)); X if (ucolor == side->owncolor && X (ttypes[terr].dark || X side->monochrome || X (side->showmode == TERRICONS))) X ucolor = side->fgcolor; X if (side->monochrome && side != side2) X ucolor = side->bgcolor; X hcolor = (side == side2 ? side->bgcolor : side->fgcolor); X if (side->monochrome) { X /* erasing background */ X draw_hex_icon(side, side->map, sx, sy, hcolor, HEX); X } else if (side->showmode != TERRICONS) { X draw_hex_icon(side, side->map, sx, sy, hex_color(side, x, y), X ((side->showmode == BORDERHEX) ? OHEX : HEX)); X } X draw_unit_icon(side, side->map, sx, sy, vtype(view), ucolor); X n = side_number(side2); X if ((numsides > 2 || side->monochrome) && n != side_number(side)) { X draw_side_number(side, side->map, sx, sy, n, ucolor); X } X } X } X} X X/* Cursor drawing also draws the unit in some other color if it's not the */ X/* "top-level" unit in a hex, as well as getting the player's attention */ X/* if the new location is sufficiently far from the last. */ X Xdraw_cursor(side) XSide *side; X{ X int sx, sy; X X if (active_display(side)) { X /* ugly hack to prevent extra cursor draw */ X if (side->cury == 0) return; X xform(side, unwrap(side, side->curx), side->cury, &sx, &sy); X if (side->curunit != NULL && side->curunit->transport != NULL) { X if (side->monochrome) { X draw_hex_icon(side, side->map, sx, sy, side->bgcolor, HEX); X draw_unit_icon(side, side->map, sx, sy, X side->curunit->type, side->fgcolor); X } else { X draw_unit_icon(side, side->map, sx, sy, X side->curunit->type, side->diffcolor); X } X } X /* Flash something to draw the eye a long ways */ X if (humanside(side)) { X if (distance(side->curx, side->cury, side->lastx, side->lasty) > 3) X flash_position(side, sx, sy); X draw_cursor_icon(side, sx, sy); X side->lastx = side->curx; side->lasty = side->cury; X } X } X} X X/* Get rid of cursor by redrawing the hex. */ X Xerase_cursor(side) XSide *side; X{ X if (side->lasty > 0) draw_hex(side, side->lastx, side->lasty, TRUE); X} X X/* Draw a splat visible to both sides at a given location. Several splats */ X/* available, depending on the seriousness of the hit. Make an extra-flashy */ X/* display when The Bomb goes off. Because of the time delays involved, we */ X/* have to update both sides' displays more or less simultaneously. Would be */ X/* better to exhibit to all sides maybe, but I'm not going to bother! */ X Xdraw_blast(unit, es, hit) XUnit *unit; XSide *es; Xint hit; X{ X char ch; X int ux = unit->x, uy = unit->y, sx, sy, i; X Side *us = unit->side; X X if (hit >= period.nukehit) { X if (active_display(us)) invert_whole_map(us); X if (active_display(es)) invert_whole_map(es); X /* may need a time delay if X is too speedy */ X if (active_display(us)) invert_whole_map(us); X if (active_display(es)) invert_whole_map(es); X for (i = 0; i < 4; ++i) { X if (active_display(us)) draw_mushroom(us, ux, uy, i); X if (active_display(es)) draw_mushroom(es, ux, uy, i); X if (i != 2 && (active_display(us) || active_display(es))) sleep(1); X } X if (active_display(us) || active_display(es)) sleep(1); X } else { X ch = ((hit >= unit->hp) ? 'd' : ((hit > 0) ? 'c' : 'b')); X if (active_display(us)) { X xform(us, unwrap(us, ux), uy, &sx, &sy); X draw_blast_icon(us, us->map, sx, sy, ch, us->enemycolor); X flush_output(us); X } X if (active_display(es)) { X xform(es, unwrap(es, ux), uy, &sx, &sy); X draw_blast_icon(es, es->map, sx, sy, ch, es->owncolor); X flush_output(es); X } X } X} X X/* Draw all the units in a column. They should be spaced so they don't */ X/* overlap or get misaligned with text, and can be inverted if desired. */ X Xdraw_unit_list(side, hilite) XSide *side; Xbool hilite[]; X{ X int u, pos = 0, spacing = max(side->hh, side->fh); X X if (active_display(side)) { X for_all_unit_types(u) { X draw_hex_icon(side, side->state, side->margin, pos, X (hilite[u] ? side->fgcolor : side->bgcolor), OHEX); X draw_unit_icon(side, side->state, side->margin, pos, u, X (hilite[u] ? side->bgcolor : side->fgcolor)); X pos += spacing; X } X } X} END_OF_draw.c if test 16834 -ne `wc -c <draw.c`; then echo shar: \"draw.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f lib/four.map -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"lib/four.map\" else echo shar: Extracting \"lib/four.map\" \(1621 characters\) sed "s/^X//" >lib/four.map <<'END_OF_lib/four.map' XXconq 0 --+--+ XMap 184 30 100 1 0 X184_ X184. X184. X184. X39.4,42.4,42.4,42.4,3. X39.4+,41.4+,41.4+,41.4+,2. X,+,,+34.3,3+,,+,,+34.3,3+,,+,,+34.3,3+,,+,,+34.3,3+1, X+,.3,33.,7+,.3,33.,7+,.3,33.,7+,.3,33.,6+ X+5.%,,32.,5+5.%,,32.,5+5.%,,32.,5+5.%,,32.,4+ X..,..5,30.,~~^++..,..5,30.,~~^++..,..5,30.,~~^++..,..5,30.,~~^2+ X++.,.,,3%31.,~~^^++.,.,,3%31.,~~^^++.,.,,3%31.,~~^^++.,.,,3%31.,~~2^ X3+3.,%^+33.~^^3+3.,%^+33.~^^3+3.,%^+33.~^^3+3.,%^+33.~2^ X++,4.,,+%,30.+~^3+,4.,,+%,30.+~^3+,4.,,+%,30.+~^3+,4.,,+%,30.+~^1+ X^^3+4.,,%,28.4~3^3+4.,,%,28.4~3^3+4.,,%,28.4~3^3+4.,,%,28.4~1^ X^_^++8.,+,27.~~^^_^++8.,+,27.~~^^_^++8.,+,27.~~^^_^++8.,+,27.~~1^ X3^+,,39.+3^+,,39.+3^+,,39.+3^+,,39.1+ X+^++,,+39.+^++,,+39.+^++,,+39.+^++,,+39. X++43.,++43.,++43.,++43.1, X++42.+,++42.+,++42.+,++42.+1, X184. X184. X184. X184. X184. X184. X184. X184. X184. X184. X184_ XUnits 32 1 0 X* Far*West*North 43,23 -1 X* West*North 89,23 -1 X* Mideast*North 135,23 -1 X* East*North 181,23 -1 X* East*Harbor 0,21 -1 X* Far*West*Plains 43,21 -1 X* Far*West*Harbor 46,21 -1 X* West*Plains 89,21 -1 X* West*Harbor 92,21 -1 X* Mideast*Plains 135,21 -1 X* Mideast*Harbor 138,21 -1 X* East*Plains 181,21 -1 X* East*Island 9,17 -1 X* Far*West*Desert 42,17 -1 X* Far*West*Valley 45,17 -1 X* Far*West*Island 55,17 -1 X* West*Desert 88,17 -1 X* West*Valley 91,17 -1 X* West*Island 101,17 -1 X* Mideast*Desert 134,17 -1 X* Mideast*Valley 137,17 -1 X* Mideast*Island 147,17 -1 X* East*Desert 180,17 -1 X* East*Valley 183,17 -1 X@ West*Main 94,16 -1 X@ Far*West*Main 48,16 -1 X@ East*Main 2,16 -1 X@ Mideast*Main 140,16 -1 X* East*South 2,13 -1 X* Far*West*South 48,13 -1 X* West*South 94,13 -1 X* Mideast*South 140,13 -1 END_OF_lib/four.map if test 1621 -ne `wc -c <lib/four.map`; then echo shar: \"lib/four.map\" unpacked with wrong size! fi # end of overwriting check fi if test -f mplay.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"mplay.c\" else echo shar: Extracting \"mplay.c\" \(32778 characters\) sed "s/^X//" >mplay.c <<'END_OF_mplay.c' X/* Copyright (c) 1987, 1988 Stanley T. Shebs, University of Utah. */ X/* This program may be used, copied, modified, and redistributed freely */ X/* for noncommercial purposes, so long as this notice remains intact. */ X X/* RCS $Header: mplay.c,v 1.1 88/06/21 12:30:25 shebs Exp $ */ X X/* This file implements all of machine strategy. Not much room for fancy */ X/* tricks, just solid basic play. The code emphasizes avoidance of mistakes */ X/* instead of strategic brilliance, so machine behavior is akin to bulldozer */ X/* plodding. Nevertheless, bulldozers can be very effective when they */ X/* outnumber the human players... */ X X/* It is also very important to prevent infinite loops, so no action of the */ X/* machine player is 100% certain. */ X X#include "config.h" X#include "misc.h" X#include "dir.h" X#include "period.h" X#include "side.h" X#include "unit.h" X#include "map.h" X#include "global.h" X X/* Maximum number of unit groups that can be maintained. Need groups for */ X/* both offense and defense. */ X X#define MAXGROUPS 80 X X/* the non-group */ X X#define NOGROUP 0 X X/* Group goals. */ X X#define HITTARGET 1 X#define CAPTARGET 2 X#define OCCUPYHEX 3 X#define EXPLORE 4 X#define DEFEND 5 X X/* Individual goals. */ X X#define NOGOAL 0 X#define DRIFT 1 X#define ASSAULT 2 X#define DISCOVER 3 X#define LOAD 4 X#define APPROACH 5 X#define RELOAD 6 X X/* Groups organize machine player activity at the multiple-unit level. */ X Xtypedef struct a_group { X short goal; /* the intended purpose of the group */ X short priority; /* how important the group is */ X short x, y; /* a relevant location */ X short etype; /* type of a unit there (or NOTHING) */ X short area; /* radius of relevance of group activity */ X short size; /* number of units in the group */ X} Group; X X/* This structure is where machine sides keep all the plans and planning */ X/* related data. */ X/* Group 0 is never actually used (a sort of a dummy for various purposes). */ X Xtypedef struct a_plan { X short knowns[MAXSIDES]; /* estimated strength of other sides */ X short allieds[MAXSIDES]; /* strength of other alliances */ X short cx, cy; /* "centroid" of all our units */ X short shouldresign; /* true if machine thinks it should resign */ X short lastreplan; /* last turn we rechecked the plans */ X Group group[MAXGROUPS]; /* all the groups that can be formed */ X} Plan; X X#define side_plan(s) ((Plan *) (s)->plan) X X/* Malloced integer array accessors and modifers. */ X X#define aref(m,x,y) ((m)[(x)+world.width*(y)]) X X#define aset(m,x,y,v) ((m)[(x)+world.width*(y)] = (v)) X X/* General collections of numbers used by all machine players. */ X Xint bhw[MAXUTYPES][MAXUTYPES]; /* basic worth for hitting */ Xint bcw[MAXUTYPES][MAXUTYPES]; /* basic worth for capturing */ Xint bthw[MAXUTYPES][MAXUTYPES]; /* basic worth for carrying hitters */ Xint btcw[MAXUTYPES][MAXUTYPES]; /* basic worth for carrying capturers */ Xint maxoccupant[MAXUTYPES]; /* total capacity of a transport */ Xint *localworth; /* for evaluation of nearby hexes */ X XUnit *munit; /* Unit being decided about */ X XSide *mside; /* Side whose unit is being decided about */ X X/* Init used by all machine players. Precompute useful information */ X/* relating to unit types in general, and that usually gets referenced */ X/* in inner loops. */ X Xinit_mplayers() X{ X int u, u2, g; X Side *side; X X localworth = (int *) malloc(world.width*world.height*sizeof(int)); X for_all_unit_types(u) { X maxoccupant[u] = 0; X for_all_unit_types(u2) { X bhw[u][u2] = basic_hit_worth(u, u2); X bcw[u][u2] = basic_capture_worth(u, u2); X bthw[u][u2] = basic_transport_worth(u, u2); X btcw[u][u2] = basic_transport_worth(u, u2); X maxoccupant[u] += utypes[u].capacity[u2]; X } X } X /* tell us about how things rated */ X if (Debug) { X for_all_unit_types(u) { X for_all_unit_types(u2) printf("%5d", bhw[u][u2]); X printf("\n"); X } X printf("\n"); X for_all_unit_types(u) { X for_all_unit_types(u2) printf("%5d", bcw[u][u2]); X printf("\n"); X } X printf("\n"); X for_all_unit_types(u) { X for_all_unit_types(u2) printf("%5d", bthw[u][u2]); X printf("\n"); X } X printf("\n"); X for_all_unit_types(u) { X for_all_unit_types(u2) printf("%5d", btcw[u][u2]); X printf("\n"); X } X printf("\n"); X } X /* For all sides, because human might use "robot" option */ X for_all_sides(side) { X side->plan = (long) malloc(sizeof(Plan)); X side_plan(side)->shouldresign = FALSE; X side_plan(side)->cx = side_plan(side)->cy = 0; X for (g = 0; g < MAXGROUPS; ++g) { X side_plan(side)->group[g].goal = NOGROUP; X side_plan(side)->group[g].priority = 0; X } X side_plan(side)->lastreplan = -100; X } X} X X/* A crude estimate of the payoff of one unit type hitting on another type. */ X/* This is just for general estimation, since actual worth may depend on */ X/* damage already sustained, unit's goals, etc. */ X Xbasic_hit_worth(u, e) Xint u, e; X{ X int worth, anti; X X worth = utypes[u].hit[e] * min(utypes[e].hp, utypes[u].damage[e]); X if (utypes[e].hp > utypes[u].damage[e]) { X worth /= utypes[e].hp; X } else { X worth *= utypes[e].hp; X } X if (period.counterattack) { X anti = utypes[e].hit[u] * min(utypes[u].hp, utypes[e].damage[u]); X if (utypes[u].hp > utypes[e].damage[u]) { X anti /= utypes[u].hp; X } else { X anti *= utypes[u].hp; X } X } X if (utypes[e].territory > 0) worth *= utypes[e].territory; X worth -= anti; X return worth; X} X X/* A crude estimate of the payoff of one unit type trying to capture. */ X Xbasic_capture_worth(u, e) Xint u, e; X{ X int worth = 0, anti = 0; X X if (could_capture(u, e)) { X worth += utypes[e].territory * utypes[u].capture[e]; X } X return worth; X} X X/* This should account for volume also... */ X Xbasic_transport_worth(u, e) Xint u, e; X{ X int worth = 0, u2; X X for_all_unit_types(u2) { X if (could_capture(u2, e)) { X worth += utypes[u].capacity[u2] * utypes[u2].capture[e]; X } X } X worth *= utypes[u].speed; X return worth; X} X X/* At the beginning of each turn, review situation, and maybe make some */ X/* new plans. */ X Xinit_machine_turn(side) XSide *side; X{ X if (global.time > 10 || probability(20)) decide_resignation(side); X if (!side_plan(side)->shouldresign) { X review_groups(side); X form_new_groups(side); X } X} X X/* Sometimes there is no point in going on, but be careful not to be too */ X/* pessimistic. */ X Xdecide_resignation(side) XSide *side; X{ X int opposed, own, odds, chance; X Side *side1, *side2; X Plan *plan = side_plan(side); X X for_all_sides(side1) { X plan->allieds[side_number(side1)] = plan->knowns[side_number(side1)]; X for_all_sides(side2) { X if (side1 != side2 && allied_side(side1, side2)) { X plan->allieds[side_number(side1)] += X plan->knowns[side_number(side2)]; X } X } X } X own = plan->allieds[side_number(side)]; X for_all_sides(side1) { X if (enemy_side(side, side1)) { X opposed = plan->allieds[side_number(side1)]; X if (own == 0) { X if (opposed > 0) plan->shouldresign = TRUE; X } else { X odds = (100 * opposed) / own; X if (odds > 200) { X chance = odds / 10; X if (probability(chance)) plan->shouldresign = TRUE; X } X } X } X } X} X X/* Review existing groups and get rid of useless ones. Start by recomputing */ X/* the size, since we don't update when units die or get transferred. */ X Xreview_groups(side) XSide *side; X{ X int g, view; X Plan *plan = side_plan(side); X Unit *unit; X X for (g = 1; g < MAXGROUPS; ++g) plan->group[g].size = 0; X for_all_units(unit) { X if (unit->side == side) plan->group[unit->group].size++; X } X for (g = 1; g < MAXGROUPS; ++g) { X switch (plan->group[g].goal) { X case NOGROUP: X /* a non-existent group */ X break; X case HITTARGET: X view = side_view(side, plan->group[g].x, plan->group[g].y); X if (view == EMPTY || view == UNSEEN || X side_n(vside(view)) == NULL || X allied_side(side, side_n(vside(view)))) { X disband_group(side, g); X } X break; X case CAPTARGET: X view = side_view(side, plan->group[g].x, plan->group[g].y); X if (view == EMPTY || view == UNSEEN || X allied_side(side, side_n(vside(view)))) { X disband_group(side, g); X } X break; X case EXPLORE: X view = side_view(side, plan->group[g].x, plan->group[g].y); X if (view != UNSEEN) { X disband_group(side, g); X } X break; X case DEFEND: X /* should study area to decide about desirabilty of disbanding */ X if (probability(3)) disband_group(side, g); X break; X case OCCUPYHEX: X /* occupying should only end if no longer a victory condition */ X break; X default: X case_panic("group goal", plan->group[g].goal); X break; X } X } X} X X/* Decide about the formation of new groups, but don't recreate copies of */ X/* groups that already exist. */ X Xform_new_groups(side) XSide *side; X{ X int i, g, x, y, view, etype, x0, x1, x2, y1, y2, choice; X int pri, sumx = 0, sumy = 0, n = 0; X Plan *plan = side_plan(side); X Side *eside; X X if (!world.known) { X if (!find_group(side, EXPLORE, -1, -1)) { X x0 = 0 + random(world.width/3); X x1 = world.width/3 + random(world.width/3); X x2 = (2*world.width)/3 + random(world.width/3); X y1 = 0 + random(world.height/2); X y2 = (3*world.height)/4 + random(world.height/2); X form_group(side, EXPLORE, x0, y1, 0); X form_group(side, EXPLORE, x1, y1, 0); X form_group(side, EXPLORE, x2, y1, 0); X form_group(side, EXPLORE, x0, y2, 0); X form_group(side, EXPLORE, x1, y2, 0); X form_group(side, EXPLORE, x2, y2, 0); X } X } X if (global.time < 10 || probability(20)) { X for (i = 0; i < MAXSIDES; ++i) plan->knowns[i] = 0; X for (y = 0; y < world.height; ++y) { X for (x = 0; x < world.width; ++x) { X view = side_view(side, x, y); X if (view != EMPTY && view != UNSEEN) { X if (side == side_n(vside(view))) { X sumx += x; sumy += y; n++; X } X } X } X } X if (n > 0) { X plan->cx = sumx / n; plan->cy = sumy / n; X } X for (y = 0; y < world.height; ++y) { X for (x = 0; x < world.width; ++x) { X view = side_view(side, x, y); X if (view != EMPTY && view != UNSEEN) { X eside = side_n(vside(view)); X etype = vtype(view); X if (!allied_side(side, eside)) { X choice = (capturable(etype) ? CAPTARGET : HITTARGET); X if (!find_group(side, choice, x, y)) { X pri = X 100 / (distance(x, y, plan->cx, plan->cy) + 1); X if ((g = form_group(side, choice, x, y, pri))) { X plan->group[g].etype = etype; X } X } X } else { X if (!mobile(etype) && !defended(side, x, y)) { X if ((g = form_group(side, DEFEND, x, y, 0))) { X plan->group[g].area = 3; X } X } X } X if (eside != NULL) X plan->knowns[side_number(eside)] += X utypes[etype].territory; X } X } X } X } X /* form a hex occupation group if hex mentioned in win/lose */ X} X X/* Decides if unit has nothing covering it. */ X Xdefended(side, x, y) XSide *side; Xint x, y; X{ X int g; X Plan *plan = side_plan(side); X X for (g = 1; g < MAXGROUPS; ++g) { X if ((plan->group[g].goal == DEFEND) && X (distance(x, y, plan->group[g].x, plan->group[g].y) <= X plan->group[g].area)) X return TRUE; X } X return FALSE; X} X X/* When forming a group, first pick out an unused group, then bump a lower */ X/* priority group if there's too many. If it's of lower or equal priority, */ X/* then don't form the group at all (failure on equal priorities reduces */ X/* fickleness). */ X Xform_group(side, goal, x, y, priority) XSide *side; Xint goal, x, y, priority; X{ X int g; X Plan *plan = side_plan(side); X X for (g = 1; g < MAXGROUPS; ++g) { X if (plan->group[g].goal == NOGROUP) break; X } X if (g == MAXGROUPS) { X for (g = 1; g < MAXGROUPS; ++g) { X if (priority > plan->group[g].priority) { X disband_group(side, g); X break; X } X } X } X if (g < MAXGROUPS) { X plan->group[g].goal = goal; X plan->group[g].priority = priority; X plan->group[g].x = x; X plan->group[g].y = y; X plan->group[g].etype = NOTHING; X plan->group[g].area = 1; X plan->group[g].size = 0; X if (Debug) printf("%s form group %d with goal %d -> %d,%d\n", X side->name, g, goal, x, y); X return g; X } else { X return 0; X } X} X X/* When group's goal accomplished, release the units for other activities. */ X/* Not very efficient to scan all units, but simpler and safer than links. */ X Xdisband_group(side, g) XSide *side; Xint g; X{ X Unit *unit; X Plan *plan = side_plan(side); X X if (Debug) printf("%s disband group %d with goal %d -> %d,%d\n", X side->name, g, plan->group[g].goal, X plan->group[g].x, plan->group[g].y); X plan->group[g].goal = NOGROUP; X plan->group[g].size = 0; X for_all_units(unit) { X if (unit->side == side && unit->group == g) { X unit->group = NOGROUP; X unit->goal = NOGOAL; X } X } X} X X/* Given a goal and argument, see if a group already exists like that. */ X Xfind_group(side, goal, x, y) XSide *side; Xint goal, x, y; X{ X int g; X X for (g = 1; g < MAXGROUPS; ++g) { X if ((side_plan(side)->group[g].goal == goal) && X (x == -1 || side_plan(side)->group[g].x == x) && X (y == -1 || side_plan(side)->group[g].y == y)) X return g; X } X return 0; X} X X/* Decide whether a change of product is desirable. */ X Xchange_machine_product(unit) XUnit *unit; X{ X int u = unit->type; X X if (Freeze) { X return FALSE; X } else if (utypes[u].maker) { X if (producing(unit)) { X if ((unit->built > 5) || X ((utypes[u].make[unit->product] * unit->built) > 50)) { X return TRUE; X } X } else { X return TRUE; X } X } X return FALSE; X} X X/* Machine algorithm for deciding what a unit should build. This routine */ X/* must return the type of unit decided upon. Variety of production is */ X/* important, as is favoring types which can leave the builder other than */ X/* on a transport. Capturers of valuable units are also highly preferable. */ X Xmachine_product(unit) XUnit *unit; X{ X int u = unit->type, type; X int i, j, k, d, x, y, value, bestvalue, besttype; X int adjterr[MAXTTYPES]; X X mside = unit->side; X for_all_terrain_types(i) adjterr[i] = 0; X for_all_directions(d) { X x = wrap(unit->x + dirx[d]); y = unit->y + diry[d]; X adjterr[terrain_at(x, y)]++; X } X besttype = period.firstptype; X bestvalue = 0; X for_all_unit_types(i) { X value = 0; X if (!could_make(u, i)) value = -1000; X if (mobile(i)) { X for_all_terrain_types(j) { X if (could_move(i, j)) value += adjterr[j]; X } X if (value <= 0) value = -1000; X } X for_all_unit_types(j) { X if (could_capture(i, j)) value += 2; X if (could_carry(i, j)) { X for_all_unit_types(k) { X if (could_capture(j, k)) value += 1; X } X } X } X if (mside->building[i] == 0) value += period.numutypes / 2; X if (mside->building[i] == 1) value += 1; X if (mside->units[i] == 0) value += period.numutypes / 4; X if (value > bestvalue) { X besttype = i; X bestvalue = value; X } X } X type = besttype; X /* safety check */ X if (!could_make(unit->type, type)) type = NOTHING; X if (Debug) printf("%s will now build %s units\n", X unit_handle(NULL, unit), X (type == NOTHING ? "no" : utypes[type].name)); X return type; X} X X/* Decide on and make a move or set orders for a machine player. */ X Xmachine_move(unit) XUnit *unit; X{ X munit = unit; X mside = unit->side; X if (Freeze) { X order_sentry(unit, 1); X } else if (humanside(mside)) { X unit->goal = DRIFT; X if (maybe_return_home(unit)) return; X /* need to decide about "short-term" tactics */ X search_for_best_move(unit); X } else if (side_plan(mside)->shouldresign && flip_coin()) { X resign_game(mside, NULL); X } else { X if (unit->group == NOGROUP) decide_group(unit); X if (unit->goal == NOGOAL) decide_goal(unit); X if (maybe_return_home(unit)) return; X if (probability(50) && short_term(unit)) return; X search_for_best_move(unit); X } X} X X/* Picking the correct units for a group is essential to its success. */ X/* We rate the unit for its suitability for each group (which will also */ X/* be adjusted by the group's needs). */ X Xdecide_group(unit) XUnit *unit; X{ X int g, t, suitability, best = 0, bestgroup = 0; X Plan *plan = side_plan(unit->side); X X for (g = 1; g < MAXGROUPS; ++g) { X suitability = 0; X switch (plan->group[g].goal) { X case NOGROUP: X break; X case HITTARGET: X suitability += bhw[unit->type][plan->group[g].etype]; X suitability += bthw[unit->type][plan->group[g].etype]; X suitability = min(suitability, 10000); X suitability /= (distance(unit->x, unit->y, X plan->group[g].x, plan->group[g].y) + 1); X suitability /= (plan->group[g].size / 10 + 1); X /* should be fast units (?) with good odds against etype */ X /* also need some way to decide about adding transports */ X break; X case CAPTARGET: X suitability += bcw[unit->type][plan->group[g].etype]; X suitability += btcw[unit->type][plan->group[g].etype]; X suitability = min(suitability, 10000); X suitability /= (distance(unit->x, unit->y, X plan->group[g].x, plan->group[g].y) + 1); X suitability /= (plan->group[g].size / 10 + 1); X /* also need some way to decide about adding transports */ X /* and maybe defenders for vulnerable capturers/transports */ X /* size must be sufficient to be nearly certain of taking */ X break; X case EXPLORE: X suitability = 100; X suitability /= (plan->group[g].size + 1); X break; X case DEFEND: X suitability = random(100); X break; X case OCCUPYHEX: X /* assign a group capable of reaching the hex */ X break; X default: X case_panic("group goal", plan->group[g].goal); X break; X } X suitability *= (plan->group[g].priority + 1); X if (suitability > best) { X best = suitability; X bestgroup = g; X } X } X unit->group = bestgroup; X unit->goal = NOGOAL; X plan->group[bestgroup].size++; X if (Debug) printf("%s assigned to group %d\n", X unit_handle(NULL, unit), bestgroup); X} X X/* Set up goals for units that need them. */ X/* Goals should differ according to unit's role in group... */ X Xdecide_goal(unit) XUnit *unit; X{ X Plan *plan = side_plan(unit->side); X X switch (plan->group[unit->group].goal) { X case NOGOAL: X /* dubious */ X unit->goal = DRIFT; X unit->gx = unit->gy = 0; X break; X case HITTARGET: X unit->goal = ASSAULT; X unit->gx = plan->group[unit->group].x; X unit->gy = plan->group[unit->group].y; X break; X case CAPTARGET: X if (could_capture(unit->type, plan->group[unit->group].etype)) { X unit->goal = ASSAULT; X } else if (probability(fullness(unit))) { X unit->goal = APPROACH; X } else { X unit->goal = LOAD; X } X unit->gx = plan->group[unit->group].x; X unit->gy = plan->group[unit->group].y; X break; X case EXPLORE: X unit->goal = APPROACH; X unit->gx = plan->group[unit->group].x; X unit->gy = plan->group[unit->group].y; X break; X case DEFEND: X unit->goal = DRIFT; X unit->gx = plan->group[unit->group].x; X unit->gy = plan->group[unit->group].y; X break; X case OCCUPYHEX: X unit->goal = APPROACH; X unit->gx = plan->group[unit->group].x; X unit->gy = plan->group[unit->group].y; X break; X default: X case_panic("group goal", plan->group[unit->group].goal); X break; X } X if (Debug) printf("%s group %d assigned goal %d -> %d,%d\n", X unit_handle(NULL, unit), unit->group, unit->goal, X unit->gx, unit->gy); X} X X/* See if the location has a unit that can take us in for refueling */ X/* (where's the check for refueling ability?) */ X Xhaven_p(x, y) Xint x, y; X{ X Unit *unit = unit_at(x, y); X X return ((unit != NULL && mside == unit->side && alive(unit) && X can_carry(unit, munit) && !might_be_captured(unit))); X} X X/* See if the location has a unit that can repair us */ X Xshop_p(x, y) Xint x, y; X{ X Unit *unit = unit_at(x, y); X X return (unit != NULL && munit->side == unit->side && alive(unit) && X can_carry(unit, munit) && X could_repair(unit->type, munit->type)); X} X X/* See if we're in a bad way, either on supply or hits, and get to safety */ X/* if possible. If not, then move on to other actions. */ X/* Can't be 100% though, there might be some problem preventing move */ X Xmaybe_return_home(unit) XUnit *unit; X{ X int ux = unit->x, uy = unit->y, ox, oy, range; X X if (low_supplies(unit) && probability(98)) { X range = range_left(unit); X if (unit->transport) { X order_sentry(unit, 1); X return TRUE; X } else if (!unit->occupant) { X if (Debug) printf("%s should return\n", unit_handle(NULL, unit)); X if ((range * range < numunits) ? X (search_area(ux, uy, range, haven_p, &ox, &oy)) : X (find_closest_unit(ux, uy, range, haven_p, &ox, &oy))) { X order_moveto(unit, ox, oy); X unit->orders.flags |= SHORTESTPATH; X unit->orders.flags &= X ~(ENEMYWAKE|NEUTRALWAKE|SUPPLYWAKE|ATTACKUNIT); X if (Debug) printf(" will resupply\n"); X return TRUE; X } X } else { X return FALSE; /* should be more detailed */ X } X } X if (cripple(unit) && probability(98)) { X if (unit->transport) { X order_sentry(unit, 1); X return TRUE; X } else { X if (Debug) printf("%s should repair\n", unit_handle(NULL, unit)); X range = range_left(unit); X if ((range * range < numunits) ? X (search_area(ux, uy, range, haven_p, &ox, &oy)) : X (find_closest_unit(ux, uy, range, shop_p, &ox, &oy))) { X order_moveto(unit, ox, oy); X unit->orders.flags &= ~SHORTESTPATH; X unit->orders.flags &= X ~(ENEMYWAKE|NEUTRALWAKE|SUPPLYWAKE|ATTACKUNIT); X if (Debug) printf(" will repair\n"); X return TRUE; X } else { X return FALSE; X } X } X } X if (out_of_ammo(unit) >= 0 && probability(80)) { X if (unit->transport) { X order_sentry(unit, 1); X return TRUE; X } else { X range = range_left(unit); X if (Debug) printf("%s should reload\n", unit_handle(NULL, unit)); X if ((range * range < numunits) ? X (search_area(ux, uy, range, haven_p, &ox, &oy)) : X (find_closest_unit(ux, uy, range, haven_p, &ox, &oy))) { X order_moveto(unit, ox, oy); X unit->orders.flags &= ~SHORTESTPATH; X unit->orders.flags &= X ~(ENEMYWAKE|NEUTRALWAKE|SUPPLYWAKE|ATTACKUNIT); X if (Debug) printf(" will reload\n"); X return TRUE; X } else { X return FALSE; X } X } X } X return FALSE; X} X X/* Return the distance that we can go by shortest path before running out */ X/* of important supplies. Will return at least 1, since we can *always* */ X/* move one hex to safety. This is a worst-case routine, too complicated */ X/* to worry about units getting refreshed by terrain or whatever. */ X Xrange_left(unit) XUnit *unit; X{ X int u = unit->type, r, least = 12345; X X for_all_resource_types(r) { X if (utypes[u].tomove[r] > 0) least = min(least, unit->supply[r]); X if (utypes[u].consume[r] > 0) X least = min(least, unit->supply[r] / utypes[u].consume[r]); X } X return (least == 12345 ? 1 : least); X} X X/* Do short-range planning. */ X Xshort_term(unit) XUnit *unit; X{ X switch (unit->goal) { X case DRIFT: X case LOAD: X case APPROACH: X case ASSAULT: X break; X default: X case_panic("unit goal", munit->goal); X break; X } X return FALSE; X} X X/* Given a position nearby the unit, evaluate it with respect to goals, */ X/* general characteristics, and so forth. -10000 is very bad, 0 is OK, */ X/* 10000 or so is best possible. */ X X/* Should downrate hexes within reach of enemy retaliation. */ X/* Should downrate hexes requiring supply consumption to enter/occupy. */ X Xevaluate_hex(x, y) Xint x, y; X{ X bool adjhex, ownhex; X int view, etype, dist, worth = 0; X int terr = terrain_at(x, y); X Side *es; X Unit *eunit; X X view = side_view(mside, x, y); X dist = distance(munit->x, munit->y, x, y); X adjhex = (dist == 1); X ownhex = (dist == 0); X X if (y <= 0 || y >= world.height-1) { X worth = -10000; X } else { X switch (munit->goal) { X case DRIFT: X if (ownhex) { X worth = -1; X } else if (view == UNSEEN) { X worth = random(100) / dist; X } else if (view == EMPTY) { X worth = -100; X if (impassable(munit, x, y)) worth -= 900; X } else { X es = side_n(vside(view)); X etype = vtype(view); X if (es == NULL) { X if (could_capture(munit->type, etype)) { X worth = 20000 / dist; X } else { X worth = -10000; X } X } else if (!allied_side(mside, es)) { X worth = 200 + attack_worth(munit, etype); X worth += threat(mside, etype, x, y); X worth /= dist; X } else { X } X } X break; X case LOAD: X if (ownhex || view == UNSEEN || view == EMPTY) { X worth = -1; X } else { X es = side_n(vside(view)); X if (mside == es) { X if ((eunit = unit_at(x, y)) != NULL) { X if (eunit->group == munit->group) { X worth = 4000; X worth /= dist; X } X } X } else { X worth = -100; X } X } X break; X case APPROACH: X case ASSAULT: X if (ownhex) { X worth = -100; X } else if (view == UNSEEN) { X } else if (view == EMPTY) { X if (impassable(munit, x, y)) worth -= 900; X } else if (x == munit->gx && y == munit->gy) { X worth = 10000; X } else { X es = side_n(vside(view)); X etype = vtype(view); X if (es == NULL) { X if (could_capture(munit->type, etype)) { X worth = 20000 / dist; X } else { X worth = -10000; X } X } else if (!allied_side(mside, es)) { X worth = 200 + attack_worth(munit, etype); X worth += threat(mside, etype, x, y); X worth /= dist; X } else { X es = side_n(vside(view)); X if (mside == es) { X if ((eunit = unit_at(x, y)) != NULL) { X if (eunit->group == munit->group && X eunit->goal == LOAD && X could_carry(eunit->type, munit->type)) { X worth = 4000; X worth /= dist; X } X } X } else { X worth = -100; X } X } X } X break; X default: X case_panic("unit goal", munit->goal); X break; X } X } X if ((munit->gx > 0 || munit->gy > 0) && X (distance(x, y, munit->gx, munit->gy) < X distance(munit->x, munit->y, munit->gx, munit->gy))) { X worth += 1000; X } X worth -= 100; X worth += utypes[munit->type].productivity[terr]; X aset(localworth, x, y, worth); X} X X/* Scan evaluated area looking for best overall hex. */ X Xint bestworth = -10000, bestx, besty; X Xmaximize_worth(x, y) Xint x, y; X{ X int worth; X X worth = aref(localworth, x, y); X if (worth >= 0) { X if (worth > bestworth) { X bestworth = worth; bestx = x; besty = y; X } else if (worth == bestworth && flip_coin()) { X bestworth = worth; bestx = x; besty = y; X } X } X} X X/* Search for most favorable odds anywhere in the area, but only for */ X/* the remaining moves in this turn. Multi-turn tactics is elsewhere. */ X X/* Need to be able to direct unit to sit at best hex if not at max move. */ X X/* Could compare value of hexes to value of goal here and go for goal maybe? */ X Xsearch_for_best_move(unit) XUnit *unit; X{ X int ux = unit->x, uy = unit->y, range = unit->movesleft; X X if (!mobile(unit->type)) { X order_sentry(unit, 100); X return; X } X if (Debug) printf("%s ", unit_handle(NULL, unit)); X bestworth = -20000; X apply_to_area(ux, uy, range, evaluate_hex); X apply_to_area(ux, uy, range, maximize_worth); X if (bestworth >= 5000) { X if (unit->transport != NULL && mobile(unit->transport->type)) { X if (Debug) printf("sleeping on transport\n"); X order_sentry(unit, 5); X } else if ((ux == bestx && uy == besty) || !can_move(unit)) { X if (Debug) printf("staying put\n"); X order_sentry(unit, 1); X } else if (probability(90)) { X if (Debug) printf("moving to %d,%d (worth %d)\n", X bestworth, bestx, besty); X order_moveto(unit, bestx, besty); X unit->orders.flags &= ~SHORTESTPATH; X } else { X if (Debug) printf("hanging around\n"); X order_sentry(unit, random(5)); X } X } else if (unit->goal != DRIFT && probability(90)) { X switch (unit->goal) { X case DRIFT: X break; X case LOAD: X if (unit->occupant != NULL) { X if (Debug) printf("starting off to goal\n"); X unit->goal = APPROACH; X order_moveto(unit, unit->gx, unit->gy); X } else { X if (bestworth >= 0) { X if (Debug) printf("loading at %d,%d (worth %d)\n", X bestworth, bestx, besty); X order_moveto(unit, bestx, besty); X unit->orders.flags &= ~SHORTESTPATH; X } else { X if (Debug) printf("moving slowly about\n"); X order_movedir(unit, random_dir(), 1); X } X } X break; X case APPROACH: X case ASSAULT: X if (unit->transport != NULL) { X if (unit->transport->group == unit->group) { X if (Debug) printf("riding in transport\n"); X order_sentry(unit, 4); X } else if (!can_move(unit)) { X if (Debug) printf("waiting to get off\n"); X order_sentry(unit, 2); X } else { X if (Debug) printf("leaving for %d,%d\n", X unit->gx, unit->gy); X order_moveto(unit, unit->gx, unit->gy); X } X } else { X if (Debug) printf("approaching %d,%d\n", unit->gx, unit->gy); X order_moveto(unit, unit->gx, unit->gy); X } X break; X default: X case_panic("unit goal", munit->goal); X break; X } X } else { X if (can_produce(unit) && unit->transport == NULL && probability(90)) { X if (Debug) printf("going to build something\n"); X set_product(unit, machine_product(unit)); X set_schedule(unit); X order_sentry(unit, unit->schedule+1); X } else if (probability(90)) { X if (Debug) printf("going in random direction\n"); X order_movedir(unit, random_dir(), random(3)+1); X } else { X if (Debug) printf("hanging around\n"); X order_sentry(unit, random(4)+1); X } X } X} X X/* This is a heuristic estimation of the value of one unit type hitting */ X/* on another. Should take cost of production into account as well as the */ X/* chance and significance of any effect. */ X Xattack_worth(unit, etype) XUnit *unit; Xint etype; X{ X int utype = unit->type, worth; X X worth = bhw[utype][etype]; X if (utypes[utype].damage[etype] >= utypes[etype].hp) X worth *= 2; X if (utypes[etype].damage[utype] >= unit->hp) X worth /= (could_capture(utype, etype) ? 1 : 4); X if (could_capture(utype, etype)) worth *= 4; X return worth; X} X X X/* Support functions. */ X X/* True if unit is in immediate danger of being captured. */ X/* Needs check on capturer transport being seen. */ X Xmight_be_captured(unit) XUnit *unit; X{ X int d, x, y; X Unit *unit2; X X for_all_directions(d) { X x = wrap(unit->x + dirx[d]); y = unit->y + diry[d]; X if (((unit2 = unit_at(x, y)) != NULL) && X (enemy_side(unit->side, unit2->side)) && X (could_capture(unit2->type, unit->type))) return TRUE; X } X return FALSE; X} X X/* Return true if the given unit type at given position is threatened. */ X Xthreat(side, u, x0, y0) XSide *side; Xint u, x0, y0; X{ X int d, x, y, view, thr = 0; X Side *side2; X X for_all_directions(d) { X x = wrap(x0 + dirx[d]); y = y0 + diry[d]; X view = side_view(side, x, y); X if (view != UNSEEN && view != EMPTY) { X side2 = side_n(vside(view)); X if (allied_side(side, side2)) { X if (could_capture(u, vtype(view))) thr += 1000; X if (bhw[u][vtype(view)] > 0) thr += 100; X } X } X } X return thr; X} X X/* Test if unit can move out into adjacent hexes. */ X Xcan_move(unit) XUnit *unit; X{ X int d, x, y; X X for_all_directions(d) { X x = wrap(unit->x + dirx[d]); y = limit(unit->y + diry[d]); X if (could_move(unit->type, terrain_at(x, y))) return TRUE; X } X return FALSE; X} X X/* Returns the type of missing supplies. */ X Xout_of_ammo(unit) XUnit *unit; X{ X int u = unit->type, r; X X for_all_resource_types(r) { X if (utypes[u].hitswith[r] > 0 && unit->supply[r] <= 0) X return r; X } X return (-1); X} X X/* Returns TRUE if type is capturable somehow. */ X Xcapturable(e) Xint e; X{ X int u; X X if (utypes[e].surrender > 0 || utypes[e].siege > 0) return TRUE; X for_all_unit_types(u) if (could_capture(u, e)) return TRUE; X return FALSE; X} X X/* True if the given unit is a sort that can build other units. */ X Xcan_produce(unit) XUnit *unit; X{ X int p; X X for_all_unit_types(p) { X if (could_make(unit->type, p)) return TRUE; X } X return FALSE; X} X X/* Return percentage of capacity. */ X Xfullness(unit) XUnit *unit; X{ X int u = unit->type, o, cap = 0, num = 0, vol = 0; X Unit *occ; X X for_all_unit_types(o) cap += utypes[u].capacity[o]; X for_all_occupants(unit, occ) { X num++; X vol += utypes[occ->type].volume; X } X if (utypes[u].holdvolume > 0) { X return ((100 * vol) / utypes[u].holdvolume); X } else if (cap > 0) { X return ((100 * num) / cap); X } else { X fprintf(stderr, "Fullness ???\n"); X } X} X Xfind_closest_unit(x0, y0, maxdist, pred, rxp, ryp) Xint x0, y0, maxdist, (*pred)(), *rxp, *ryp; X{ X Unit *unit; X X for_all_units(unit) { X if (distance(x0, y0, unit->x, unit->y) <= maxdist) { X if ((*pred)(unit->x, unit->y)) { X *rxp = unit->x; *ryp = unit->y; X return TRUE; X } X } X } X return FALSE; X} END_OF_mplay.c if test 32778 -ne `wc -c <mplay.c`; then echo shar: \"mplay.c\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of archive 3 \(of 18\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 18 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0