[net.sources] New, Improved Ogre

mwm@ea.UUCP (12/20/84)

# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# README attack.c init.c initround.c main.c map.c move.c ogrecom.c ogrestat.c resolve.c termcap.c ext.h ogre.h ogre.6 Makefile

echo x - README
sed 's/^	//' > "README" << '//E*O*F README//'
	As promised, here is the hacked version of OGRE. I have put in the
	following enhancements:
	
	1) Stacking for armor is now correct (I closed the "SIT" hole).
	
	You cannot move an armor unit onto any other unit if that would leave it
	with zero movement points. Nor can you issue the 's' or ' ' (SIT) commands
	while the unit is stacked with another unit.
	
	2) Infantry stacking works as per the MetaGaming rules.
	
	Almost. All infantry units are now 1s. You may have up to three ones on any
	hex at one time. This stacking limit can be exceeded during movement, but
	not at the end of a units movement. Unlike the MetaGaming version of the
	rules, stacked units are treated as multiple units, so a destroyed result
	doesn't destroy everything in the hex.
	
	3) A pass option now exists for movement, as well as for firing.
	
	Any time the game is waiting for you to issue a move command for a unit,
	you may type 'p' to pass on that unit. The unit is put back where it
	started from, and given the number of movement points it had before.  With
	the corrected stacking rules, this is a necessity.
	
	4) You can pick up pieces after you've put them down during setup.
	
	While initially placing units, you can move on top of a unit and issue a
	'u' command. The unit is removed, and you get the armor or infantry
	points back.
	
	5) You can get a mobile CP.
	
	After you place the CP, the game asks how many movement points the CP
	should have. Each movement point costs you one armor point. You can have at
	most 2 movement points - which makes the game far to easy.
	
	6) The ogre strategy has been improved.
	
	The ogre is now much more relentless about mashing the CP. With the change
	in the victory conditions from the MetaGaming version, this is reasonable.
	
	7) Case sensitivity has been stripped.
	
	The system now ignores the case of input characters. With the undo during
	setup, this is reasonable.
	
	8) Clear screen added.
	
	Unlike the posted version, this one works any time the system is waiting
	for input.
	
	8) All bug fixes that I've seen posted.
	
	That doesn't mean I got them all, of course.
	
	The 'ogre 6' variant didn't get done. The screen display defeated me. Maybe
	later...
	
		<mike
//E*O*F README//

echo x - attack.c
sed 's/^	//' > "attack.c" << '//E*O*F attack.c//'
	/*
	    This file contains routines to collect attack orders from the player,
	    and display his odds of success for each target.  It calls the
	    routines in "resolve.c" to determine the outcomes of the attacks.
	
	    Michael Caplinger, Rice University, March 1982.
	*/
	
	#include "ext.h"
	
	static OGRE allocated;
	
	#define NOPASS      '\0'
	#define RESOLVE     'R'
	#define MISSILE     'M'
	#define MAIN        'B'
	#define SECONDARY   'S'
	#define AP          'A'
	#define TREAD       'T'
	
	attack_def()
	{
	    char moreunits;
	    int  i;
	
	    moreunits = TRUE;
	    zero(&allocated, sizeof(allocated));
	    init_def_attack();
	
	    /*
	        The "fired" element of each unit description is here used as a
	        Boolean to keep track of who has fired.
	    */
	
	    while(moreunits) {
	
	        moreunits = FALSE;
	
	        for(i = 0; i < n_units; i++) {
	
	            if(unit[i].status == OK &&
	                !unit[i].fired &&
	                unit[i].attack > 0  &&
	                unit[i].range_to_ogre <= unit[i].range) {
	
	                    describe_action("Fire", i);
	
	                    if(get_target(i) == PASS) moreunits = TRUE;
	                    else unit[i].fired = TRUE;
	
	            }
	        }
	    }
	    ogre_resolve(&allocated);
	}
	
	get_target(i)
	int i;
	{
	
	    char    action, invalid;
	
	    movecur_unit(i);
	
	    do {
	
	        invalid = FALSE;
	        action = readchar();
	    
	        switch(action) {
	    
	            case PASS:
	                return(PASS);
	    
	            case MISSILE:
	                if(ogre.missiles > 0) {
	                    allocated.missiles += unit[i].attack;
	                    update_odds(action);
	                }
	                else {
	                    invalid = TRUE;
	                }
	                break;
	    
	            case MAIN:
	                if(ogre.main_bats > 0) {
	                    allocated.main_bats += unit[i].attack;
	                    update_odds(action);
	                }
	                else {
	                    invalid = TRUE;
	                }
	                break;
	    
	            case SECONDARY:
	                if(ogre.sec_bats > 0) {
	                    allocated.sec_bats += unit[i].attack;
	                    update_odds(action);
	                }
	                else {
	                    invalid = TRUE;
	                }
	                break;
	    
	            case AP:
	                if(ogre.ap > 0) {
	                    allocated.ap += unit[i].attack;
	                    update_odds(action);
	                }
	                else {
	                    invalid = TRUE;
	                }
	                break;
	    
	            case TREAD:
	                if(ogre.treads > 0) {
	                    allocated.treads += unit[i].attack;
	                    update_odds(action);
	                }
	                else {
	                    invalid = TRUE;
	                }
	                if(invalid) break;
	
	                /* TREAD has to be resolved immediately. */
	                ogre_resolve(&allocated);
	                zero(&allocated, sizeof(allocated));
	                break;
	
	            case RESOLVE:
	                ogre_resolve(&allocated);
	                zero(&allocated, sizeof(allocated));
	                return(PASS);
	                break;
	
	            default:
	                invalid = TRUE;
	                break;
	    
	        }
	
	    } while(invalid);
	
	    return(NOPASS);
	
	}
	
	
	zero(area, size)
	char *area;
	int  size;
	{
	
	    int i;
	
	    for(i = 0; i < size; i++) area[i] = '\0';
	
	}
	
	update_odds(weapon)
	char weapon;
	{
	
	    char *odd_str();
	
	    switch(weapon) {
	
	        case MAIN:
	
	            display_xy(18, 40, "%d/%d (%s)", allocated.main_bats, DEF_MAIN,
	                odd_str(allocated.main_bats, DEF_MAIN));
	            break;
	
	        case SECONDARY:
	
	            display_xy(19, 40, "%d/%d (%s)", allocated.sec_bats, DEF_SECONDARY,
	                odd_str(allocated.sec_bats, DEF_SECONDARY));
	            break;
	
	        case MISSILE:
	
	            display_xy(20, 40, "%s", odd_str(allocated.missiles, DEF_MISSILES));
	            break;
	
	        case AP:
	
	            display_xy(21, 40, "%s", odd_str(allocated.ap, DEF_AP));
	            break;
	
	        case TREAD:
	            display_xy(22, 40, "1/1 (%d)", allocated.treads);
	            break;
	
	    }
	
	}
//E*O*F attack.c//

echo x - init.c
sed 's/^	//' > "init.c" << '//E*O*F init.c//'
	#include "ext.h"
	#define UNDO	'U'
	
	static char a, b;
	static int cp_set;
	static int infantry_points, armor_points, n_free;
	
	init_units(mark)
	{
	    int unitcmp();
	
		init_screen();
	
	    a = 10;
	    b = 10;
	
	    switch(mark) {
	
	        case 3:
	            armor_points = 10;
	            infantry_points = 18;
	            break;
	
	        case 5: 
	            armor_points = 18;
	            infantry_points = 27;
	            break;
	    }
	
	    n_units = n_free = 0;
	    cp_set = FALSE;
	
	    while(armor_points > 0 || infantry_points > 0 || !cp_set) {
	        display(16, "left to place: %d armor, %d infantry%s",
	            armor_points, infantry_points,
	            (cp_set) ? "." : ", CP");
	        getunit();
	    }
	
	    /* sort the units so lower the i, the more valuable the unit. */
	    qsort( (char *) unit, n_units, sizeof(UNIT), unitcmp);
	
	
	}
	
	getunit()
	{
	    char    no_new, bad_char;
	    char    olda, oldb;
	    char    dir, i;
	
	    no_new = TRUE;
	    bad_char = FALSE;
	
	    movecur_hex(a, b);
	
	    while(no_new) {
	
	        olda = a;
	        oldb = b;
	
	        dir = readchar();
	    
	        switch(dir) {
	    
	            case RIGHT:
	                a--;
	                b--;
	                break;
	    
	            case UPRIGHT:
	                a--;
	                break;
	    
	            case DOWNRIGHT:
	                b--;
	                break;
	    
	            case LEFT:
	                a++;
	                b++;
	                break;
	    
	            case UPLEFT:
	                b++;
	                break;
	    
	            case DOWNLEFT:
	                a++;
	                break;
	    
		    case UNDO:
			if ((i = occupied(a, b)) == 0 || unit[--i].status != OK) {
			    bad_char = TRUE ;
			    break ;
			    }
			if (unit[i].type == CP) {
				cp_set = FALSE ;
				armor_points += unit[i].movement ;
				}
			else if (unit[i].type == INFANTRY) infantry_points += 1 ;
			else if (unit[i].type == HOWITZER) armor_points += 2 ;
			else if (unit[i].type == HVYTANK) armor_points += 1 ;
			else if (unit[i].type == MSLTANK) armor_points += 1 ;
			else if (unit[i].type == GEV) armor_points += 1 ;
			else broken("Internal error in init!") ;
	
			if (i < n_free) n_free = i ;
			unit[i].status = DESTROYED ;
			update_hex(a, b) ;
			no_new = FALSE ;
			break ;
				
	            case CP:
	                if(cp_set) {
	                    bad_char = TRUE;
	                }
	                else {
	                    add_unit(a, b, dir);
	                    no_new = FALSE;
	                    cp_set = TRUE;
	                }
	                break;
	
	            case HVYTANK:
	            case MSLTANK:
	            case GEV:
	                if(occupied(a, b) || blocked(a, b) || armor_points == 0) {
	                    bad_char = TRUE;
	                    break;
	                }
	                add_unit(a, b, dir);
	                no_new = FALSE;
	                armor_points--;
	                break;
	    
	            case INFANTRY:
			dir = '3' ;
		    case '3':
		    case '2':
		    case '1':
			dir = dir - '0' ;
	                if(blocked(a, b) || infantry_points < dir) {
	                    bad_char = TRUE;
	                    break;
	                }
			if ((i = occupied(a, b)) != 0)
			    if (unit[--i].type != INFANTRY
			    || infantry_on(a, b) + dir > 3) {
				bad_char = TRUE ;
				break ;
				}
			while (dir--) {
	                    add_unit(a, b, 'I');
	                    infantry_points -= 1 ;
			    }
	                no_new = FALSE;
	                break;
	    
	            case HOWITZER:
	                if(occupied(a, b) || blocked(a, b) || armor_points <= 1) {
	                    bad_char = TRUE;
	                    break;
	                }
	                add_unit(a, b, dir);
	                no_new = FALSE;
	                armor_points -= 2;
	                break;
	    
	            default:
	                bad_char = TRUE;
	                break;
	    
	        }
	    
	        if(off_obstructed(a, b)  || 
	            bad_char)
	        {
	
		    putchar(BEEP) ;
	            a = olda;
	            b = oldb;
	            bad_char = FALSE;
	    
	        }
	
	        else {
	
	            movecur_hex(a, b);
	
	        }
	
	    }
	
	}
	
	add_unit(a, b, c)
	char a, b, c;
	{
	    int i, j;
	
	    i = n_free;
	    if (n_free == n_units) {
	    	n_free = ++n_units ;
		unit[i].status = DESTROYED ;
		}
	    else
	    	while (++n_free < n_units)
		    if (unit[n_free].status == DESTROYED) break ;
	
	    if (unit[i].status != DESTROYED)
		broken("Using non-free unit in add_unit!") ;
	    if (n_units > N_UNITS)
		broken("Out of Units. Recompile with larger N_UNITS!") ;
	
	    switch(c) {
	
	        case CP:
	            unit[i].type = CP;
	            unit[i].attack = 0;
	            unit[i].range = 0;
	            unit[i].defend = 0;
		    j = 200 ;
		    while (j > armor_points || j > 2) {
			display(17, "Movement points for CP? ", 0) ;
			j = readchar() ;
			j -= '0' ;
			}
		    movecur(17, 0); eeol() ;
	            unit[i].movement = j;
		    armor_points -= j;
	            break; 
	
	        case HVYTANK:
	            unit[i].type = HVYTANK;
	            unit[i].attack = 4;
	            unit[i].range = 2;
	            unit[i].defend = 3;
	            unit[i].movement = 3;
	            break; 
	
	        case MSLTANK:
	            unit[i].type = MSLTANK;
	            unit[i].attack = 3;
	            unit[i].range = 4;
	            unit[i].defend = 2;
	            unit[i].movement = 2;
	            break; 
	
	        case GEV:
	            unit[i].type = GEV;
	            unit[i].attack = 2;
	            unit[i].range = 2;
	            unit[i].defend = 2;
	            unit[i].movement = 4;
	            break; 
	
	        case HOWITZER:
	            unit[i].type = HOWITZER;
	            unit[i].attack = 6;
	            unit[i].range = 8;
	            unit[i].defend = 1;
	            unit[i].movement = 0;
	            break; 
	
		case INFANTRY:
	            unit[i].type = INFANTRY;
	            unit[i].attack = 1;
	            unit[i].range = 1;
	            unit[i].defend = 1;
	            unit[i].movement = 2;
	            break; 
	
	    }
	
	    unit[i].range_to_ogre = 0;
	    unit[i].fired = 0;
	    unit[i].status = OK;
	    unit[i].moves_left = 0;
	    unit[i].l_hex = a;
	    unit[i].r_hex = b;
	
	    disp_unit(i);
	
	}
	
	occupied(a, b)
	char a,b;
	{
	    int i;
	
	    for(i = 0; i < n_units; i++)
	        if(unit[i].status != DESTROYED &&
	           unit[i].l_hex == a &&
	           unit[i].r_hex == b) return(++i);
	
	    return(FALSE);
	
	}
	
	infantry_on(a, b)
	char a,b;
	{
	    int i, c;
	
	    for (c = i = 0; i < n_units; i++)
		if (unit[i].type == INFANTRY && unit[i].status != DESTROYED
		&& unit[i].l_hex == a && unit[i].r_hex == b)
		    c++ ;
	
	    return c ;
	
	}
		   
	init_ogre(mark)
	{
	
	    ogre.l_hex = rand() % 7 + 22; /* 22 - 28 */
	    ogre.r_hex = 50 - ogre.l_hex;
	
	    switch(mark) {
	
	        case 3:
	            ogre.treads = 45;
	            ogre.init_treads = 45;
	            ogre.movement = 3;
	            ogre.missiles = 2;
	            ogre.main_bats = 1;
	            ogre.sec_bats  = 4;
	            ogre.ap = 8;
	            break;
	
	        case 5:
	            ogre.treads = 60;
	            ogre.init_treads = 60;
	            ogre.movement = 3;
	            ogre.missiles = 5;
	            ogre.main_bats = 2;
	            ogre.sec_bats  = 6;
	            ogre.ap = 10;
	            break;
	
	    }
	
	
	    disp_ogre();
	
	}
	
	unitcmp(u1, u2)
	UNIT *u1, *u2;
	{
	    int cmp;
	
	    switch(u1 -> type) {
	
	        case CP:
	
	            switch(u2 -> type) {
	
	                case CP: 
	                    cmp = 0;
	                    break;
	
	                default:
	                    cmp = -1;
	                    break;
	
	            }
	
	            break;
	
	        case HOWITZER:
	            switch(u2 -> type) {
	
	                case CP: 
	                    cmp = 1;
	                    break;
	
	                case HOWITZER:
	                    cmp = 0;
	                    break;
	
	                default:
	                    cmp = -1;
	                    break;
	
	            }
	
	            break;
	
	        case HVYTANK:
	            switch(u2 -> type) {
	
	                case CP:
	                case HOWITZER:
	                    cmp = 1;
	                    break;
	
	                case HVYTANK:
	                    cmp = 0;
	                    break;
	
	                default:
	                    cmp = -1;
	                    break;
	
	            }
	
	            break;
	
	        case MSLTANK:
	            switch(u2 -> type) {
	
	                case CP:
	                case HOWITZER:
	                case HVYTANK:
	                    cmp = 1;
	                    break;
	
	                case MSLTANK:
	                    cmp = 0;
	                    break;
	
	                default:
	                    cmp = -1;
	                    break;
	
	            }
	
	            break;
	
	        case GEV:
	            switch(u2 -> type) {
	
	                case INFANTRY:
	                    cmp = -1;
	                    break;
	
	                case GEV:
	                    cmp = 0;
	                    break;
	
	                default:
	                    cmp = 1;
	                    break;
	
	            }
	
	            break;
	
	        case INFANTRY:
	            switch(u2 -> type) {
	
	                case INFANTRY: 
	                    cmp = 0;
	                    break;
	
	                default:
	                    cmp = 1;
	                    break;
	
	            }
	
	            break;
	
	        }
	
	    return(cmp);
	
	}
	
	broken(thing) char *thing; {
	
		clear_screen() ;
		reset_term() ;
	    	printf("Internal error: %s\n", thing) ;
		exit(1) ;
		}
//E*O*F init.c//

echo x - initround.c
sed 's/^	//' > "initround.c" << '//E*O*F initround.c//'
	#include "ext.h"
	
	init_round()
	{
	
	    int i;
	
	    for(i = 0; i < n_units; i++) {
	
	        unit[i].moves_left = unit[i].movement;
	        if(unit[i].status == DISABLED) {
	            unit[i].status = OK;
	            update_hex(unit[i].l_hex, unit[i].r_hex);
	        }
	        unit[i].range_to_ogre =
	            range(ogre.l_hex, ogre.r_hex, unit[i].l_hex, unit[i].r_hex);
	
	    }
	
	}
	
	init_move_ogre()
	{
	
	    int i;
	
	    for(i = 0; i < n_units; i++) {
	
	        unit[i].range_to_ogre =
	            range(ogre.l_hex, ogre.r_hex, unit[i].l_hex, unit[i].r_hex);
	
	    }
	
	}
	
	init_def_attack()
	{
	
	    int i;
	
	    for(i = 0; i < n_units; i++) {
	
	        if(unit[i].status == OK) {
	            unit[i].fired = FALSE;
	            unit[i].range_to_ogre =
	                range(ogre.l_hex, ogre.r_hex, unit[i].l_hex, unit[i].r_hex);
	        }
	
	    }
	
	}
	
	init_ogre_attack()
	{
	
	    int i;
	
	    for(i = 0; i < n_units; i++) {
	
	            unit[i].fired = 0;
	            unit[i].range_to_ogre =
	                range(ogre.l_hex, ogre.r_hex, unit[i].l_hex, unit[i].r_hex);
	
	    }
	
	}
	
	init_gev2()
	{
	    int i;
	
	    for(i = 0; i < n_units; i++)
	        if(unit[i].status == OK && unit[i].type == GEV)
	            unit[i].moves_left = 3;
	
	}
//E*O*F initround.c//

echo x - main.c
sed 's/^	//' > "main.c" << '//E*O*F main.c//'
	/*
	    OGRE: a tactical ground combat game set in 2085.
	
	    Adapted from the Metagaming Microgame by Steve Jackson.
	
	    This version was written for a Vax 11/780 under Unix
	    by Michael Caplinger, Rice University, February-March 1982.
	
	    Paper game (c) 1977 by Steve Jackson
	    This implementation (c) 1982, 1984 by Michael Caplinger
	*/
	
	#include <signal.h>
	#include <ctype.h>
	
	#define MAIN
	#include "ext.h"
	
	main(argc, argv)
	char **argv;
	{
	
	    int handler();
	    int mark;
	
	    signal(SIGINT, handler);
	
	    if(argc > 1)
	        switch(argv[1][0]) {
	
	            case '3':
	                mark = 3;
	                break;
	
	            case '5':
	                mark = 5;
	                break;
	
	            default:
	                mark = 3;
	                break;
	        }
	
	    else mark = 3;
	
	    set_term();
	    srand(time(0));
	    init_units(mark);
	    init_ogre(mark);
	    disp_ogre_status(TRUE);
	
	    while(1) {
	
	        init_round();
	
	        /* The Ogre fires. */
	        assign_fire_ogre();
	        check_over(); 
	
	        /* Player moves, and fires. */
	        move_def();
	        attack_def();
	
	        /* Let the GEVs move their extra 3 turns. */
	        init_gev2();
	        move_def();
	
	        /* The Ogre moves. */
	        move_ogre();
	        check_over(); 
	
	    }
	
	}
	
	handler() {
	
	    clear_screen();
	    reset_term();
	    exit(0);
	
	}
	
	/*
	    Get a character. If it's a ^L, redraw the screen.
	*/
	
	readchar() {
		int	c ;
	
		while ((c = getchar()) == REDRAW) redraw_screen() ;
		return (islower(c) ? toupper(c) : c) ;
		}
	
	/*
	    See if the game is over, and die if it is.
	*/
	check_over()
	{
	    char *message;
	    int over;
	
	    over = FALSE;
	
	    if(unit[0].status == DESTROYED) {
	        message = "The Ogre wins!!";
	        over = TRUE;
	    }
	    if(ogre.movement == 0) {
	        message = "You win!!";
	        over = TRUE;
	    }
	
	    if(over) {
	        clear_screen();
	        reset_term();
	        printf("%s\n", message);
	        exit(0);
	    }
	
	}
//E*O*F main.c//

echo x - map.c
sed 's/^	//' > "map.c" << '//E*O*F map.c//'
	/*
	    These routines implement all functions associated with the map and display
	    thereof.
	
	    (Thanks to Bob Hood of Rice for coming up with 
	     the x-y and range algorithms.)
	
	    Michael Caplinger, Rice University, March 1982.
	*/
	
	#include "ext.h"
	#include <ctype.h>
	
	int lastunit = 0 ;
	char *lastaction = (char *) 0 ;
	
	
	/* Initialize the map display, at the beginning of the game. */
	init_screen() {
	
	    int a, b;
	
	    tc_setup();
	    clear_screen();
	
	    for(a = 1; a <= 28; a++) {
	        for(b = 1; b <= 28; b++) {
	            if(!off_map(a, b)) {
	                disp_hex(a, b, '.');
	            }
	        }
	    }
	    disp_craters();
	}
	
	redraw_screen() {
	
	    int a, b;
	
	    clear_screen();
	
	    for(a = 1; a <= 28; a++) {
	        for(b = 1; b <= 28; b++) {
	            if(!off_map(a, b)) {
	                update_hex(a, b);
	            }
	        }
	    }
	
	    disp_ogre_status(1);
	    describe_action(lastaction, lastunit) ;
	
	}
	
	/* 
	    Convert a left and right hex pair (eg, the hex 2015 has an l_hex of 20 and
	    an r_hex of 15) to x-y screen coordinates.
	*/
	to_xy(lhex, rhex, row, col)
	char lhex, rhex, *row, *col;
	{
	
	    *row = (lhex - rhex) + 7;
	    *col = 50 - (lhex + rhex);
	
	}
	
	/* Check to see if an lr pair is off the map. */
	off_map(a, b)
	char a, b;
	{
	    char row, col;
	
	    to_xy(a, b, &row, &col);
	    if(col < 0 || col > 38 || row < 0 || row > 14) return(TRUE);
	    else return(FALSE);
	
	}
	
	/* Check to see if an lr pair is off the obstructed area of the map. */
	off_obstructed(a, b)
	char a, b;
	{
	    char row, col;
	
	    to_xy(a, b, &row, &col);
	    if(col < 10 || col > 38 || row < 0 || row > 14) return(TRUE);
	    else return(FALSE);
	
	}
	
	/* Display a character at a given hex. */
	disp_hex(a, b, c)
	char a, b, c;
	{
	    char row, col;
	
	    to_xy(a, b, &row, &col);
	
	    movecur(row, col * 2 + 1);
	    putchar(c);
	
	}
	
	/* 
	    Display the contents of a hex.  If more than one item is in a hex,
	    the following precedence applies:
	        1) Ogre
	        2) Defending units (by value)
	        3) Craters (not that anything can be in a crater hex.)
	*/
	update_hex(a, b)
	char a, b;
	{
	
	    int i;
	
	    if(ogre.l_hex == a && ogre.r_hex == b) {
	        disp_ogre();
	        return;
	    }
	
	    for(i = 0; i < n_units; i++)
	        if(unit[i].l_hex == a && unit[i].r_hex == b &&
	            unit[i].status != DESTROYED) {
	            disp_unit(i);
	            return;
	        }
	
	    if(blocked(a, b)) {
	        disp_hex(a, b, '*');
	        return;
	    }
	
	    disp_hex(a, b, '.');
	
	}
	
	/* Display the ith unit. */
	disp_unit(i)
	int i;
	{
	    char a, b;
	
	    a = unit[i].l_hex;
	    b = unit[i].r_hex;
	
	    switch(unit[i].status) {
	
	        case OK:
	
	            switch(unit[i].type) {
	
	                case INFANTRY:
			    disp_hex(a, b, '0' + infantry_on(a, b));
	                    break;
	
	                default:
			    disp_hex(a, b, unit[i].type);
	                    break;
	
	            }
	            break;
	
	        case DISABLED:
	            disp_hex(a, b, tolower(unit[i].type));
	            break;
	
	        case DESTROYED:
	            disp_hex(a, b, '.');
	            break;
	
	    }
	
	}
	
	/* Display the Ogre. */
	disp_ogre()
	{
	    char a, b;
	
	    a = ogre.l_hex;
	    b = ogre.r_hex;
	
	    disp_hex(a, b, 'O');
	
	}
	
	
	/* Move the cursor to the specified hex on the screen. */
	movecur_hex(a, b)
	char a, b;
	{
	    char row, col;
	
	    to_xy(a, b, &row, &col);
	
	    movecur(row, col * 2 + 1);
	
	}
	
	/* Point at the ith unit with the cursor. */
	movecur_unit(i)
	int i;
	{
	
	    movecur_hex(unit[i].l_hex, unit[i].r_hex);
	
	}
	
	#define ABS(i) (((i) < 0) ? -(i) : (i))
	#define BIGINT 32767
	
	/* Calculate the range between 2 hexes. */
	range(a1, b1, a2, b2)
	char a1, b1, a2, b2;
	{
	
	    char    diff1, diff2, temp;
	    int     subrange[3];
	    int     min, i;
	    int     rangesum;
	
	    diff1 = a1 - b1;
	    diff2 = a2 - b2;
	
	    subrange[0] = ABS(a1 - a2);
	    subrange[1] = ABS(b1 - b2);
	    subrange[2] = ABS(diff1 - diff2);
	
	    min = 0;
	    for(i = 1; i < 3; i++)
	        if(subrange[i] < subrange[min]) min = i;
	
	    rangesum = subrange[min];
	
	    temp = subrange[min]; subrange[min] = subrange[2]; subrange[2] = temp;
	
	    min = 0;
	    for(i = 1; i < 2; i++)
	        if(subrange[i] < subrange[min]) min = i;
	
	    rangesum += subrange[min];
	
	    return(rangesum);
	
	}
	
	/*
	    This is a hardwired set of craters, taken from the paper game's map.
	*/
	static struct {
	    char l_hex;
	    char r_hex;
	} craters[] = {
	    17, 16,
	    19, 13,
	    13, 18,
	    14, 15,
	    13, 15,
	    15, 10,
	    9,  15,
	    10, 12,
	    7,  14,
	    11, 10,
	    14, 7,
	    12, 6,
	    7,  10,
	    8,  6,
	    4,  9,
	    9,  4,
	    9,  3,
	};
	
	#define NCRATERS    (sizeof(craters) / 2 * sizeof(char))
	
	/* Determine if a hex has a crater. */
	blocked(a, b)
	char a, b;
	{
	    int i;
	
	    for(i = 0; i < NCRATERS; i++) 
	        if(craters[i].l_hex == a && craters[i].r_hex == b) return(TRUE);
	
	    return(FALSE);
	
	}
	
	/* Display the craters. */
	disp_craters()
	{
	    int i;
	
	    for(i = 0; i < NCRATERS; i++) 
	        disp_hex(craters[i].l_hex, craters[i].r_hex, '*');
	
	}
	
	#include <stdio.h>
	
	describe_action(action, i)
	char *action;
	int i;
	{
	
	    lastunit = i;
	    lastaction = action ;
	
	    switch(unit[i].type) {
	
	        case HOWITZER:
	            display(16, "%s howitzer (%d/%d D%d M%d)", action,
	                unit[i].attack, unit[i].range, 
	                unit[i].defend, unit[i].moves_left);
	            break;
	
	        case MSLTANK:
	            display(16, "%s missile tank (%d/%d D%d M%d)", action,
	                unit[i].attack, unit[i].range, 
	                unit[i].defend, unit[i].moves_left);
	            break;
	
	        case GEV:
	            display(16, "%s GEV (%d/%d D%d M%d)", action,
	                unit[i].attack, unit[i].range, 
	                unit[i].defend, unit[i].moves_left);
	            break;
	
	        case HVYTANK:
	            display(16, "%s heavy tank (%d/%d D%d M%d)", action,
	                unit[i].attack, unit[i].range, 
	                unit[i].defend, unit[i].moves_left);
	            break;
	
	        case INFANTRY:
	            display(16, "%s infantry (%d/%d D%d M%d)", action,
	                unit[i].attack, unit[i].range, 
	                unit[i].defend, unit[i].moves_left);
	            break;
	
		case CP:
		    display(16, "%s CP (%d/%d D%d M%d)", action,
	                unit[i].attack, unit[i].range, 
	                unit[i].defend, unit[i].moves_left);
		    break;
	    }
	
	}
	
	/* VARARGS */
	display(line, format, args)
	int line;
	char *format;
	int args;
	{
	
	    movecur(line, 0);
	    eeol();
	    _doprnt(format, &args, stdout);
	
	}
	
	/* VARARGS */
	display_xy(line, col, format, args)
	int line, col;
	char *format;
	int args;
	{
	
	    movecur(line, col);
	    eeol();
	    _doprnt(format, &args, stdout);
	
	}
	
	
//E*O*F map.c//

echo x - move.c
sed 's/^	//' > "move.c" << '//E*O*F move.c//'
	/*
	    Move the defender's units.
	
	    Michael Caplinger, Rice University, March 1982.
	*/
	
	#include "ext.h"
	
	move_def()
	{
	    int i;
	    char moreunits, l_old, r_old, m_old ;
	
	    for (moreunits = TRUE; moreunits;) {
		moreunits = FALSE ;
		for(i = 0; i < n_units; i++)
		    if (unit[i].status == OK && unit[i].moves_left > 0) {
			describe_action("Move", i);
			m_old = unit[i].moves_left ;
			l_old = unit[i].l_hex ;
			r_old = unit[i].r_hex ;
			while(unit[i].moves_left > 0 && unit[i].status == OK)
			    if (getmove(i, l_old, r_old, m_old)) {
			    	moreunits = TRUE ;
				break ;
				}
			}
		}
	}
	/*
	 * getmove - retrieves one move, verifies that the board is correct, and
	 *	returns whether or not this unit can move again.
	 */
	getmove(i, l_old, r_old, m_old)
	int i;
	char l_old, r_old, m_old ;
	{
	
	    char    nomove, bad_char;
	    char    a, b, dir;
	    char    olda, oldb ;
	
	    nomove = TRUE;
	
	    while(nomove) {
	    
	        a = unit[i].l_hex;
	        b = unit[i].r_hex;
	
	        movecur_hex(a, b);
	
	        bad_char = FALSE;
	    
	        dir = readchar();
	    
	        switch(dir) {
	    
	            case RIGHT:
	                a--;
	                b--;
	                break;
	    
	            case UPRIGHT:
	                a--;
	                break;
	    
	            case DOWNRIGHT:
	                b--;
	                break;
	    
	            case LEFT:
	                a++;
	                b++;
	                break;
	    
	            case UPLEFT:
	                b++;
	                break;
	    
	            case DOWNLEFT:
	                a++;
	                break;
	    
	            case SIT:
	            case ' ':
			for (dir = 0; dir < n_units; dir++)
			    if (dir != i && unit[dir].status != DESTROYED &&
				unit[dir].l_hex == a && unit[dir].r_hex == b)
				    bad_char = unit[dir].type != INFANTRY
			               || unit[i].type != INFANTRY
				       || infantry_on(a, b) > 3 ;
			if (bad_char) break ;
	
	                unit[i].moves_left = 0;
			return FALSE ;
	
		    case PASS:
			unit[i].l_hex = l_old ;
			unit[i].r_hex = r_old ;
			unit[i].moves_left = m_old ;
			update_hex(a, b) ;	
			disp_unit(i) ;
		    	return TRUE ;
	
	            default:
	                bad_char = TRUE;
	                break;
	    
	        }
	
	        /* Rule 5.02 */
	
	        if(bad_char ||
		    off_map(a, b) || 
	            blocked(a, b) ||
		    ( ((dir = occupied(a, b)) && unit[i].moves_left == 1) &&
		      (unit[i].type != INFANTRY || unit[--dir].type !=INFANTRY ||
		       unit[i].attack + infantry_on(a, b) > 3)))
		
	        {
		    putchar(BEEP) ;
	            bad_char = FALSE;
	    
	        }
	
	        else {
	            /* move the thing */
	
		    olda = unit[i].l_hex;
		    oldb = unit[i].r_hex;
		    unit[i].l_hex = a;
	            unit[i].r_hex = b;
		    update_hex(olda, oldb);
		    disp_unit(i) ;
	
	            nomove = FALSE;
	            unit[i].moves_left -= 1;
	
	            def_ram(i);
	
	        }
	
	    }
	    return FALSE  ;
	}
//E*O*F move.c//

echo x - ogrecom.c
sed 's/^	//' > "ogrecom.c" << '//E*O*F ogrecom.c//'
	/*
	    These routines define the Ogre's stategy (such as it is).
	    There's lots of room for improvement here.
	*/
	
	#include "ext.h"
	
	move_ogre()
	{
	
	    init_move_ogre();
	
	    ogre.moves_left = ogre.movement;
	
	    while(ogre.moves_left > 0) {
	        move_ogre1();
	        ogre_ram();
	        cycle();
	    }
	
	}
	
	#define INFINITY 32767
	
	/* Move the Ogre one hex. */
	move_ogre1()
	{
	
	    int weight[7];
	    int i, max;
	    char a, b;
	    char olda, oldb;
	/* dyt - prevent oscillation */
	    static int osccnt;
	    static int oscdir;
	    static int mapdir[7] = { 0, 4, 5, 6, 1, 2, 3 };
	
	    a = ogre.l_hex;
	    b = ogre.r_hex;
	
	    /* Collect weights for each possible move. These will be maximized. */
	
	    weight[0] = - INFINITY; /* temp patch: getweight(a, b); */
	    weight[1] = getweight(a - 1, b - 1);
	    weight[2] = getweight(a - 1, b);
	    weight[3] = getweight(a, b + 1);
	    weight[4] = getweight(a + 1, b + 1);
	    weight[5] = getweight(a + 1, b);
	    weight[6] = getweight(a, b - 1);
	
	/* dyt - prevent oscillation - decrease weight if returning to old position */
	    weight[mapdir[oscdir]] -= 10 * osccnt;
	
	    max = 0;
	    for(i = 1; i < 7; i++)
	        if(weight[i] > weight[max]) max = i;
	
	/* dyt - record new direction */
	    if (max == mapdir[oscdir])
	     	  osccnt++;
	    else
	        osccnt = 0;
	    oscdir = max;
	
	/* display(17, "max %d weight %d cnt %d odir %d ndir %d",
		max, weight[max], osccnt, oscdir, mapdir[oscdir]);
	   cycle(); */
	
	    switch(max) {
	
	        case 0:
	            break;
	
	        case 1:
	            a--;
	            b--;
	            break;
	
	        case 2:
	            a--;
	            break;
	
	        case 3:
	            b++;
	            break;
	
	        case 4:
	            a++;
	            b++;
	            break;
	
	        case 5:
	            a++;
	            break;
	
	        case 6:
	            b--;
	            break;
	
	    }
	
	    olda = ogre.l_hex;
	    oldb =  ogre.r_hex;
	
	    ogre.l_hex = a;
	    ogre.r_hex = b;
	
	    update_hex(olda, oldb);
	
	    disp_ogre();
	    ogre.moves_left -= 1;
	
	}
	
	/*
	    The weight for each hex is a measure of how desirable it is to be in that
	    hex; the weights of the six possible directions are maximized.
	
	    The primary consideration is distance to the CP; in the absence of other
	    factors, the Ogre will take the fastest course to the CP.
	    However, the Ogre will crush any unit it can (units with higher attacks
	    chosen first) and moves towards units that are within range of its missiles
	    or batteries.  It attempts to weight so that a concentration of dangerous,
	    immobile units (like howitzers) is attacked first.
	
	    Testing indicates that this isn't a bad strategy.
	*/
	getweight(a, b)
	char a, b;
	{
	    int weight = 0;
	    int total_attacks;
	    int to_target;
	    int i;
	
	    total_attacks = ogre.missiles + ogre.main_bats + ogre.sec_bats;
	
	    for(i = 1; i < n_units; i++) {
	
	        if(unit[i].status == DESTROYED) continue;
	
	        to_target = range(a, b, unit[i].l_hex, unit[i].r_hex);
	
	        /*
	             If you can crush somebody, do it.
	             More dangerous units get crushed first.
	        */
	        if(to_target == 0) {
		    if(unit[i].type == CP) weight = 50;
	            else weight = 10 * unit[i].attack;
	            break;
	        }
	
	        if(total_attacks <= 0) continue;
	
	        if(to_target <= RANGE_MISSILES && ogre.missiles > 0) {
	            weight += unit[i].attack;
	            weight += 4 - unit[i].movement;
	            total_attacks -= 1;
	            continue;
	        }
	
	        if(to_target <= RANGE_MAIN && ogre.main_bats > 0) {
	            weight += unit[i].attack;
	            weight += 4 - unit[i].movement;
	            total_attacks -= 1;
	            continue;
	        }
	
	        if(to_target <= RANGE_SECONDARY && ogre.sec_bats > 0) {
	            weight += unit[i].attack;
	            weight += 4 - unit[i].movement;
	            total_attacks -= 1;
	            continue;
	        }
	
	        if(to_target <= RANGE_AP && ogre.ap > 0 && 
		    (unit[i].type == INFANTRY || unit[i].type == CP)) {
	            weight += unit[i].attack;
	            weight += 4 - unit[i].movement;
	            total_attacks -= 1;
	            continue;
	        }
	
	    }
	
	/* make moving towards the CP a goal even in the face of crushing things */
	    weight += 40 *
	        (range(ogre.l_hex, ogre.r_hex, unit[0].l_hex, unit[0].r_hex)
		    - range(a, b, unit[0].l_hex, unit[0].r_hex)) ;
	
	    if(off_map(a, b) || blocked(a, b)) weight = - INFINITY;
	/*
	display(17, "%d %d weight %d", a, b, weight); cycle();
	*/
	
	    return(weight);
	
	}
	
	#define INCR(i) i = (i == n_units - 1) ? 0 : i + 1
	
	#define MIN(a, b) (((a) > (b)) ? (a) : (b))
	
	/* 
	    Figure out who the Ogre will fire at. In this code, the "fired" element
	    of the unit description is the number of hit points assigned against that
	    unit.
	*/
	assign_fire_ogre()
	{
	
	    int i, unitno, nmissiles;
	
	    init_ogre_attack();
	
	    /*
	        The basic strategy here is to fire at the next unit in range. Since
	        the units are sorted by value, this will hopefully (although not 
	        always) result in reasonable choices for targets.
	        Experience indicates that the Ogre often overkills (which is OK)
	        but fails to attack some valuable possibility (not OK).  Some
	        work needs to be done here.
	    */
	
	    unitno = nextunit(RANGE_AP, 0);
	
	/*
	 * The difference between this and the board game - the board game only
	 * lets the ogre AP a hex once per turn, as opposed to once per unit.
	 */
	    for(i = 0; i < ogre.ap; i++) {
	
	        if(unit[unitno].range_to_ogre <= RANGE_AP &&
	          (unit[unitno].type == CP || unit[unitno].type == INFANTRY)) {
	
	            unit[unitno].fired += ATK_AP;
	            display_attack("AP", unitno);
	
	        }
	        unitno = nextunit(RANGE_AP, unitno);
	
	    }
	
	    unitno = nextunit(RANGE_SECONDARY, unitno);
	
	    for(i = 0; i < ogre.sec_bats; i++) {
	
	        if(unit[unitno].range_to_ogre <= RANGE_SECONDARY) {
	
	            unit[unitno].fired += ATK_SECONDARY;
	            display_attack("secondary battery", unitno);
	
	        }
	        unitno = nextunit(RANGE_SECONDARY, unitno);
	
	    }
	
	    unitno = nextunit(RANGE_MAIN, unitno);
	
	    for(i = 0; i < ogre.main_bats; i++) {
	
	        if(unit[unitno].range_to_ogre <= RANGE_MAIN) {
	
	            unit[unitno].fired += ATK_MAIN;
	            display_attack("main battery", unitno);
	
	        }
	        unitno = nextunit(RANGE_MAIN, unitno);
	
	    }
	
	    unitno = nextunit(RANGE_MISSILES, unitno);
	
	    nmissiles = ogre.missiles;
	
	    for(i = 0; i < nmissiles; i++) {
	
	        if(unit[unitno].status != DESTROYED &&
		    /* don't fire at infantry... 27 Oct 83 */
		    unit[unitno].type != INFANTRY && 
	            unit[unitno].range_to_ogre <= RANGE_MISSILES) {
	
	            unit[unitno].fired += ATK_MISSILES;
	            ogre.missiles -= 1;
	            display_attack("missile", unitno);
	            disp_ogre_status(FALSE);
	
	        }
	        unitno = nextunit(RANGE_MISSILES, unitno);
	
	    }
	
	}
	
	#include <stdio.h>
	 
	cycle()
	{
	
	    fflush(stdout);
	    sleep(1);
	
	}
	
	/*
	    Display and resolve an attack on a single defending unit.
	*/
	display_attack(weapon, target)
	char *weapon;
	int  target;
	{
	
	    /* No point if the unit is already destroyed. */
	    if(unit[target].status == DESTROYED) return;
	
	    display(16, "Ogre fires %s at unit at hex %d%d", weapon,
	        unit[target].l_hex, unit[target].r_hex);
	
	    movecur_hex(unit[target].l_hex, unit[target].r_hex);
	
	    cycle();
	
	    def_resolve(target);
	
	}
	
	nextunit(range, unitno)
	int range;
	int unitno;
	{
	    int start;
	
	    start = unitno;
	    INCR(unitno);
	    while(unitno != start) {
	        if(range == 1) {
	            if(unit[unitno].status != DESTROYED &&
	                (unit[unitno].type == CP || unit[unitno].type == INFANTRY) &&
	                unit[unitno].range_to_ogre <= range)
	                return(unitno);
	        }
	        else {
	            if(unit[unitno].status != DESTROYED &&
	                unit[unitno].range_to_ogre <= range)
	                return(unitno);
	        }
	        INCR(unitno);
	    }
	
	    return(unitno);
	
	}
//E*O*F ogrecom.c//

echo x - ogrestat.c
sed 's/^	//' > "ogrestat.c" << '//E*O*F ogrestat.c//'
	/*
	    Handle the Ogre status display.
	*/
	
	#include "ext.h"
	
	disp_ogre_status(redraw)
	
	/* If redraw is false, the display is not touched if nothing has changed. */
	int redraw;
	{
	    static OGRE last;
	
	    /*
	        The Ogre status display occupies the bottom 6 lines of the display.
	    */
	
	    /*               0        1         2         3         4
	                     1234567890123456789012345678901234567890       */
	
	    if(redraw || last.main_bats != ogre.main_bats)
	        if(ogre.main_bats > 0)
	        display(18, "Main Batteries:      %d (4/3 D4)", ogre.main_bats);
	        else display(18, " ");
	
	    if(redraw || last.sec_bats != ogre.sec_bats)
	        if(ogre.sec_bats > 0)
	        display(19, "Secondary Batteries: %d (3/2 D3)", ogre.sec_bats);
	        else display(19, " ");
	
	    if(redraw || last.missiles != ogre.missiles)
	        if(ogre.missiles > 0)
	        display(20, "Missiles:            %d (6/5 D3)", ogre.missiles);
	        else display(20, " ");
	
	    if(redraw || last.ap != ogre.ap)
	        if(ogre.ap > 0)
	        display(21, "Anti-personnel:     %2d (1/1 D1)", ogre.ap);
	        else display(21, " ");
	
	    if(redraw || last.treads != ogre.treads)
	        if(ogre.treads > 0)
	        display(22, "Treads:             %2d (1/* D1)", ogre.treads);
	        else display(22, " ");
	
	    if(redraw || last.movement != ogre.movement)
	        display(23, "Movement:            %d", ogre.movement);
	
	    copy(&last, &ogre, sizeof(last));
	
	}
	
	copy(to, from, size)
	char *to, *from;
	int size;
	{
	    int i;
	
	    for(i = 0; i < size; i++) to[i] = from[i];
	
	}
//E*O*F ogrestat.c//

echo x - resolve.c
sed 's/^	//' > "resolve.c" << '//E*O*F resolve.c//'
	/*
	    Resolve all attacks and rams from both directions.
	
	    Michael Caplinger, Rice University, March 1982.
	*/
	
	#include "ext.h"
	
	static char *odd_names[] = {
	    "0/1",
	    "1/2",
	    "1/1",
	    "2/1",
	    "3/1",
	    "4/1",
	    "+",
	};
	
	static char crt[6][7] = {
	
	    OK, OK,        OK,        OK,        DISABLED,  DISABLED,  DESTROYED,
	    OK, OK,        OK,        DISABLED,  DISABLED,  DESTROYED, DESTROYED,
	    OK, OK,        DISABLED,  DISABLED,  DESTROYED, DESTROYED, DESTROYED,
	    OK, OK,        DISABLED,  DESTROYED, DESTROYED, DESTROYED, DESTROYED,
	    OK, DISABLED,  DESTROYED, DESTROYED, DESTROYED, DESTROYED, DESTROYED,
	    OK, DESTROYED, DESTROYED, DESTROYED, DESTROYED, DESTROYED, DESTROYED,
	
	};
	
	odds(attack, defend)
	int attack, defend;
	{
	    int result;
	
	    result = (defend > 0) ? attack / defend + 1 : 6;
	
	    if(result > 6) result = 6;
	
	    if(result == 1)
	        result = (2 * attack < defend) ? 0 : 1;
	
	    return(result);
	
	}
	
	char *odd_str(attack, defend)
	int attack, defend;
	{
	
	    return(odd_names[odds(attack, defend)]);
	
	}
	
	
	/* Resolve all attacks on the Ogre. */
	ogre_resolve(allocations)
	OGRE *allocations;
	{
	
	    display(16, "Resolving..."); cycle();
	
	    if(allocations -> missiles > 0) {
	        if(crt[roll()][odds(allocations -> missiles, DEF_MISSILES)] ==
	            DESTROYED) ogre.missiles -= 1;
	    }
	
	    if(allocations -> main_bats > 0) {
	        if(crt[roll()][odds(allocations -> main_bats, DEF_MAIN)] ==
	            DESTROYED) ogre.main_bats -= 1;
	    }
	
	    if(allocations -> sec_bats > 0) {
	        if(crt[roll()][odds(allocations -> sec_bats, DEF_SECONDARY)] ==
	            DESTROYED) ogre.sec_bats -= 1;
	    }
	
	    if(allocations -> ap > 0) {
	        if(crt[roll()][odds(allocations -> ap, DEF_AP)] ==
	            DESTROYED) ogre.ap -= 1;
	    }
	
	    if(allocations -> treads > 0) {
	        if(crt[roll()][odds(1, 1)] == DESTROYED)
	            decrease_treads(allocations -> treads);
	
	    }
	
	    /* erase the odds. */
	    movecur(18, 40); eeol();
	    movecur(19, 40); eeol();
	    movecur(20, 40); eeol();
	    movecur(21, 40); eeol();
	    movecur(22, 40); eeol();
	
	    /* update the Ogre status display. */
	    disp_ogre_status(FALSE);
	    check_over();
	}
	
	/* Resolve an Ogre attack on a defending unit. */
	def_resolve(i)
	{
	    char result;
	
	    if(unit[i].status != DESTROYED && unit[i].fired > 0) {
	
	        result = crt[roll()][odds(unit[i].fired, unit[i].defend)];
	
	        /* Infantry is a special case. */
	        if(unit[i].type == INFANTRY) {
	            if(result != OK)		/* Infantry is fragile */
			unit[i].status = DESTROYED;
	            update_hex(unit[i].l_hex, unit[i].r_hex);
		    return;
	            }
	
	        switch(unit[i].status) {
	
	            case OK:
	                unit[i].status = result;
	                break;
	
	            case DISABLED:
	                if(result != OK) unit[i].status = DESTROYED;
	                break;
	
	        }
	
	        if(unit[i].status != OK) 
	            update_hex(unit[i].l_hex, unit[i].r_hex);
	
	    }
	
	}
	
	roll()
	{
	
	    return(rand() % 6);
	
	}
	
	/* Routine called for each hex the Ogre moves through, to handle rams. */
	ogre_ram()
	{
	    int i, hit_infantry = FALSE;
	
	    /* Rule 5.03 */
	    for(i = 0; i < n_units; i++)
	        if(unit[i].l_hex == ogre.l_hex &&
	           unit[i].r_hex == ogre.r_hex &&
	           unit[i].status != DESTROYED)
	
	            switch(unit[i].type) {
	
	                case INFANTRY:
	
	                    /* Rule 5.04 */
	                    if(ogre.ap > 0 && !hit_infantry) {
	                        unit[i].status = DESTROYED ;
				hit_infantry = TRUE ;
	                    }
	                    break;
	
	                default:
	
	                    /* Rule 5.031 */
	                    if(unit[i].movement == 0 || 
	                       unit[i].status == DISABLED) {
	                        unit[i].status = DESTROYED;
	                        decrease_treads( (unit[i].type == HVYTANK) ? 2 : 1);
	                        disp_ogre_status(FALSE);
	                    }
	                    else {
	                        unit[i].status = (roll() > 3) ? DESTROYED : DISABLED;
	                        decrease_treads( (unit[i].type == HVYTANK) ? 2 : 1);
	                        disp_ogre_status(FALSE);
	                    }
	                    break;
	
	            }
	
	
	}
	
	/* See if a defender has rammed the Ogre. */
	def_ram(i)
	int i;
	{
	    if(unit[i].l_hex == ogre.l_hex &&
	       unit[i].r_hex == ogre.r_hex &&
	       unit[i].type != INFANTRY) {
	
	        /* Rule 5.036 */
	
	        decrease_treads(1);
	        unit[i].status = DESTROYED;
	        disp_ogre_status(FALSE);
		disp_ogre() ;
	
	    }
	
	}
	
	decrease_treads(attack)
	int attack;
	{
	
	    /* Rule 6.05 */
	
	    /* Now, where is the movement factor going? */
	
	    ogre.treads  -= attack;
	    ogre.movement = 0;
	    if(ogre.treads > 0)  ogre.movement = 1;
	    if(ogre.treads > ogre.init_treads / 3) ogre.movement = 2;
	    if(ogre.treads > 2 * ogre.init_treads / 3) ogre.movement = 3;
	
	}
	
//E*O*F resolve.c//

echo x - termcap.c
sed 's/^	//' > "termcap.c" << '//E*O*F termcap.c//'
	#include <sgtty.h>
	
	/*
	    Interface to termcap library.
	*/
	
	char    *BC, *UP;
	char    *eeolseq, *cmseq;
	char    *clearseq;
	short   ospeed;
	int     putchar();
	
	tc_setup() {
	
	    static  char    bp[1024];
	    static  char    buffer[1024];
	    char    *area = buffer;
	    char    *getenv();
	    char    *tgetstr();
	    char    *name;
	    int     retcode;
	
	    name = getenv("TERM");
	
	    retcode = tgetent(bp, name);
	
	    switch(retcode) {
	
	        case -1:
	            printf("can't open termcap file.\n");
	            exit(1);
	            break;
	
	        case 0:
	            printf("No termcap entry for %s.\n", name);
	            exit(1);
	            break;
	
	    }
	
	    eeolseq = tgetstr("ce", &area);
	    cmseq   = tgetstr("cm", &area);
	    clearseq = tgetstr("cl", &area);
	    BC   = tgetstr("bc", &area);
	    UP   = tgetstr("up", &area);
	
	}
	
	eeol() {
	
	    tputs(eeolseq, 0, putchar);
	
	}
	
	clear_screen() {
	
	    tputs(clearseq, 0, putchar);
	
	}
	
	movecur(row, col)
	int row, col;
	{
	    char *tgoto() ;
	
	    tputs(tgoto(cmseq, col, row), 0, putchar);
	
	}
	
	struct sgttyb old_term;
	struct sgttyb new_term;
	
	/*
	    Set terminal to CBREAK and NOECHO.
	*/
	set_term() {
	    static int  first = 1;
	
	    if(first) {
	        gtty(0, &old_term);
	        gtty(0, &new_term);
	
	        new_term.sg_flags &= ~(ECHO | XTABS); /* | CRMOD); */
	        new_term.sg_flags |= CBREAK;
		
		ospeed = new_term.sg_ospeed;
	
	        first = 0;
	    }
	
	    stty(0, &new_term);
	
	}
	
	/*
	    Reset the terminal to normal mode.
	*/
	reset_term() {
	
	    stty(0, &old_term);
	
	}
//E*O*F termcap.c//

echo x - ext.h
sed 's/^	//' > "ext.h" << '//E*O*F ext.h//'
	#include "ogre.h"
	
	#ifdef MAIN
	
	UNIT unit[N_UNITS];
	OGRE ogre;
	int n_units;
	
	#else
	
	extern UNIT unit[N_UNITS];
	extern OGRE ogre;
	extern int n_units;
	
	#endif
	
//E*O*F ext.h//

echo x - ogre.h
sed 's/^	//' > "ogre.h" << '//E*O*F ogre.h//'
	typedef struct {
	
	    char    type;
	    char    attack;
	    char    range;
	    char    defend;
	    char    movement;
	    char    range_to_ogre;
	    char    fired;
	    char    moves_left;
	    char    status;
	    char    l_hex;
	    char    r_hex;
	
	} UNIT;
	
	typedef struct {
	
	    char    missiles;
	    char    main_bats;
	    char    sec_bats;
	    char    ap;
	    char    treads;
	    char    movement;
	    char    moves_left;
	    char    l_hex;
	    char    r_hex;
	    char    init_treads;
	    char    where;
	
	} OGRE;
	
	/* unit types */
	
	#define CP          'C'
	#define HVYTANK     'T'
	#define MSLTANK     'M'
	#define GEV         'G'
	#define HOWITZER    'H'
	#define INFANTRY    'I'
	
	
	/* unit statuses */
	#define OK          1
	#define DISABLED    2
	#define DESTROYED   3
	
	/* directions */
	#define RIGHT       'D'
	#define UPRIGHT     'E'
	#define DOWNRIGHT   'X'
	#define LEFT        'A'
	#define UPLEFT      'W'
	#define DOWNLEFT    'Z'
	#define SIT         'S'
	#define PASS        'P'
	#define REDRAW      '\014'
	#define BEEP	    '\07'
	
	#define TRUE        1
	#define FALSE       0
	
	#define N_UNITS     47
	
	#define DEF_MISSILES    3
	#define DEF_MAIN        4
	#define DEF_SECONDARY   3
	#define DEF_AP          1
	
	#define ATK_MISSILES    6
	#define ATK_MAIN        4
	#define ATK_SECONDARY   3
	#define ATK_AP          1
	
	#define RANGE_MISSILES      5  
	#define RANGE_MAIN          3
	#define RANGE_SECONDARY     2
	#define RANGE_AP            1
	
//E*O*F ogre.h//

echo x - ogre.6
sed 's/^	//' > "ogre.6" << '//E*O*F ogre.6//'
	.TH OGRE 6
	.UC 4
	.SH NAME
	Ogre - a game of tank warfare in the 21st century.
	.SH SYNOPSIS
	/usr/games/ogre [ogre type (3 or 5)]
	.SH DESCRIPTION
	.PP
	Ogre is a game of tank warfare in the 21st century.  You command a force of
	infantry, armor, and howitzers pitted against a giant cybernetic tank, the
	Ogre.  Your mission is to destroy the Ogre, or at least render it immobile,
	before it reaches and destroys your command post.
	.PP
	A more complete reference on how to play can be found in the Ogre rule book
	for the Metagaming MicroGame, now distributed by Steve Jackson's company.
	Here's some very sketchy and incomplete documentation for Ogre players:
	.PP
	The game has the following phases:
	.PP
	1) Initialization.  The player's armor units, infantry, and command post
	are placed on the map.  Nothing can be placed on the leftmost 7
	columns of hexes, or on craters (*'s), or on any unit already placed.
	Valid commands are:
	.nf
	
	           w   e
	
	        a         d    (hex movement keys)
	
	           z   x
	
	            place a:
	
	        H   howitzer
	        T   heavy tank
	        M   missile tank
	        G   GEV
	        1   1 infantry unit
	        2   2 infantry units
	        3   3 infantry units
	        I   3 infantry units
	        C   command post
	        U   undo - pick up the unit
	.fi
	
	on the space currently pointed at by the cursor.
	.PP
	Units are displayed as these characters, except infantry, which appear
	as '1', '2', or '3' depending on the number of units in the hex.
	.PP
	After placing the command post, you will be asked what its movement
	allowance should be. You may give the CP a movement allowance of
	0, 1, or 2 hexes. Each point of movement allowance you give the CP
	will cost one armor point. Note that you must have enough armor
	points left to provide the CP the desired movement. In any case, a
	movement of 0 is always valid.
	.PP
	2) The Ogre (an O) now appears.
	.PP
	3) You are given the opportunity to move all your vehicles and infantry
	that can move.  The cursor motion keys are used to move the unit indicated
	by the cursor.  Additionally, the following commands are available:
	.TP
	.B 's',' '
	Stop moving the unit where it is.
	.TP
	.B 'p'
	Put this unit back where it started, and move it later.
	.PP
	No vehicle can move through a crater hex, or end it's movement on a friendly
	unit, with the exception of infantry (see below).
	Moving through the hex occupied by the Ogre is an attempt to ram the
	Ogre.  This reduces the Ogre's treads by some amount, and destroys the
	unit.
	.PP
	4) You now fire all your vehicles in range at designated targets on the
	Ogre.  The following commands are used:
	.TP     
	.B m  
	fire at missiles
	.TP
	.B b   
	fire at main batteries
	.TP
	.B s   
	fire at secondary batteries
	.TP        
	.B a   
	fire at anti-personnel guns
	.TP        
	.B t   
	fire at treads
	.PP
	The odds of destroying the target are displayed, but no action
	is taken until 'r' is used, or until you run out of attack points.
	(except for attacks on treads - see below.)
	(in the odds display, '+' means a sure thing.)
	.TP 
	.B p   
	Pass. The unit is passed over, and given the opportunity to fire
	later.
	.TP        
	.B r   
	Resolve all allocations so far, and display the results.  This
	is implied by 't', as tread attacks cannot be grouped.  A resolve
	is done automatically when you run out of attacking units.
	.PP
	5) Second movement phase for GEVs.  Just like step 3, except that only GEVs
	can move.
	.PP
	6) The Ogre moves.  If it runs over any of your units, they are damaged
	or destroyed.
	.PP
	7) The Ogre fires at all units in range.  Destroyed units are removed from
	the map.  Disabled units are displayed in lower case, and may not
	move or fire until the end of the NEXT Ogre attack.
	.PP
	Steps 3 through 7 are repeated until either
	a) the Ogre has no movement points left, in which case you win, or
	b) your command post is destroyed, in which case the Ogre wins.
	.SH MISCELLANEOUS
	.PP
	In general, you cannot have more than one unit in a hex (no stacking),
	except for infantry. You may have as many as three infantry units in a hex.
	Instead of an 'I' to indicate infantry, the number of units in the hex is
	displayed.  Even when stacked, infantry units still attack, defend, and
	move as seperate units.  Currently, the only place where this game differs
	from the board game is that the ogre may use AP on a hex with multiple
	units in it once per unit, as opposed to once per hex.
	.PP
	The display "a/r Dd Mm" means the unit concerned attacks at a, at range r,
	defends at d, and moves m hexes per turn.
	.PP
	The Ogre by default is a Mark III.  An argument of '5' on the command line
	makes it a Mark V, and gives you more armor points.
	.PP
	The game can be interrupted at any point with a control-C.  There's now
	no way to restart.
	.PP
	The screen can be redrawn (after say a control-Z) with a control-L.
	.PP
	The paper game is copyright (c) 1977 by Steve Jackson.  This computer
	implementation is copyright (c) 1984 by Michael Caplinger.
	Modifications copyright (c) 1984 by Mike Meyer.
	.SH AUTHOR
	Michael Caplinger, Rice University (mike@rice.ARPA), from a Microgame of the
	same name published by Metagaming of Austin, Texas, and written by Steve
	Jackson.  This implementation is not authorized in any way by Mr. Jackson,
	and should not be sold for profit.
	.SH SEE ALSO
	termcap(5)
	.SH BUGS
	.PP
	The Ogre sometimes gets confused and doesn't know where to go, so it
	oscillates from one hex to another, and then back.
//E*O*F ogre.6//

echo x - Makefile
sed 's/^	//' > "Makefile" << '//E*O*F Makefile//'
	OBJ = init.o termcap.o map.o main.o move.o initround.o ogrecom.o ogrestat.o \
		  attack.o resolve.o
	
	ogre: $(OBJ)
		cc -o ogre $(OBJ) -ltermcap
	
	init.o: init.c ogre.h
		cc -c init.c
	
	main.o: main.c ogre.h
		cc -c main.c
	
	move.o: move.c ogre.h
		cc -c move.c
	
	attack.o: attack.c ogre.h
		cc -c attack.c
	
	resolve.o: resolve.c ogre.h
		cc -c resolve.c
	
	initround.o: initround.c ogre.h
		cc -c initround.c
	
	termcap.o: termcap.c
		cc -c termcap.c
	
	ogrecom.o: ogrecom.c ogre.h
		cc -c ogrecom.c
	
	ogrestat.o: ogrestat.c ogre.h
		cc -c ogrestat.c
	
	map.o: map.c ogre.h
		cc -c map.c
	
	backup:
		tar cvf /arch/mike/ogre.tar *
	
	rcs: init.c termcap.c map.c main.c move.c initround.c ogrecom.c ogrestat.c \
		  attack.c resolve.c ogre.6
		ci -l $?
		touch rcs
//E*O*F Makefile//

exit 0