|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T i
Length: 57397 (0xe035) Types: TextFile Names: »improved_ogre«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Ogre/news/improved_ogre«
Article 1341 of net.sources: Relay-Version: version B 2.10.1 6/24/83 (MC840302); site turing.UUCP Posting-Version: Notesfiles $Revision: 1.6.2.17 $; site ea.UUCP Path: turing!mcvax!seismo!harvard!godot!mit-eddie!genrad!decvax!bellcore!allegra!ulysses!mhuxr!ihnp4!inuxc!pur-ee!uiucdcs!ea!mwm From: mwm@ea.UUCP Newsgroups: net.sources Subject: New, Improved Ogre Message-ID: <12000005@ea.UUCP> Date: Thu, 20-Dec-84 21:52:00 GMT Article-I.D.: ea.12000005 Posted: Thu Dec 20 21:52:00 1984 Date-Received: Sat, 29-Dec-84 03:21:08 GMT Lines: 2551 Nf-ID: #N:ea:12000005:000:56782 Nf-From: ea!mwm Dec 20 13:52:00 1984 # 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