/* File: borg9.c */

/*
 * Copyright (c) 1997 Ben Harrison
 *
 * This software may be copied and distributed for educational, research,
 * and not for profit purposes provided that this copyright and statement
 * are included in all such copies.
 */

#include "angband.h"

#include "borg.h"


#ifdef ALLOW_BORG


/*
 * The "Angband Borg" is an automatic Angband player.
 *
 * Use of the Borg requires re-compilation with ALLOW_BORG defined,
 * and linking the various "borg source files" into the executable.
 *
 * The "borg source files" have been updated for use with Angband 2.8.3,
 * and will not work with any other version without (minor) modifications.
 *
 * Note that you can only use the Borg if your character has been marked
 * as a "Borg User".  You can do this, if necessary, by responding "y"
 * when asked if you really want to use the Borg.  This will (normally)
 * result in your character being inelligible for the high score list.
 *
 * The "do_cmd_borg()" function, called when the user hits "^Z", allows
 * the user to interact with the Borg, by entering a "borg-command" when
 * prompted.  Any borg-command (except escape) will cause the Borg to be
 * initialized (if needed), and then the borg-command will be executed.
 *
 * The first time you enter any borg-command, the Borg is initialized.
 * This consists of three major steps, and requires at least 400K of
 * free memory.  If enough memory is not available, the game may abort.
 * First, the various "borg" modules are initialized.  Second, various
 * important "state" information is extracted, including the level and
 * race/class of the player, and this information is used to perform
 * more initialization.  Third, some "historical" information (killed
 * uniques, maximum dungeon depth, etc) is "stolen" from the game.
 *
 * The simplest borg-command is "z", which "activates" the Borg, causing it
 * to take control of the character, and play the game, until you hit a key.
 * Other borg-commands, including "?", are described in "do_cmd_borg()".
 *
 * When the Borg is "activated", it uses the "inkey_hack" hook to steal
 * control from the user.  Later, if it detects any input from the user,
 * it gracefully relinquishes control by clearing the "inkey_hack" hook
 * after any pending borg-key-sequences are complete.
 *
 * The Borg will abort if it detects any errors (such as bizarre screens),
 * strange situations (such as death), or panic situations (such as being
 * about to die), if the appropriate flags are set.
 *
 * The Borg is only supposed to "know" what a human playing the game could
 * know, that is, observe the information actually displayed on the screen,
 * and is only supposed to "do" the things that a human playing the game
 * could do, that is, send keypresses to the game.
 *
 * The Borg uses the "z-term.c" package to "look" at the screen, using the
 * screen access function "Term_what()", and the cursor location function
 * "Term_locate()".  Actually, it uses inline versions of these functions
 * (for efficiency).  This is very similar to what a human does, except
 * that a human must actually mentally convert visual impulses into colored
 * symbols.
 *
 * The Borg uses the special "inkey_hack" hook in "util.c" to allow it to
 * "steal" control from the "inkey()" function, which is used by the game,
 * and to "send" keypress events as if it was a normal user.  This is not
 * quite what a human does, in particular, by stealing control in such a
 * manner, the Borg is able to know exactly when the game is waiting for
 * a keypress, which allows it to know when the screen has finished being
 * refreshed.  Also, the Borg uses the "inkey_flag" variable to determine
 * when the game is waiting for a top level command.
 *
 *
 * If the code wqs properly designed, that is, if the Borg actually acted
 * just like a real human player, then it could, in theory, be run as an
 * external process, which would actually examine a physical display and
 * send keypresses directly to a physical keyboard, or at least (to avoid
 * the need for a camera and a robotic arm), which would run the game in
 * a "pseudo-terminal", allowing it to examine the current screen image
 * and send keypress events to the underlying process (the Angband Game).
 *
 * Currently, the code is *not* properly designed, that is, there are a
 * few places in which it does things which a human would not be able to
 * do, such as examine (and even change) the values of certain variables
 * private to the game.  However, there are very few of these places, so
 * with a little work, it should be possible for the Borg to actually
 * simulate a real human player.  All known "problems" are listed below,
 * and note that almost all of them would disappear if the Borg was made
 * into a separate executable and taught to generate its own character,
 * and to determine when the game was ready for a keypress.
 *
 * A major problem is that the Borg "knows" when the game is waiting for
 * a keypress.  There is no single solution for this problem, since even
 * humans sometimes get confused when playing over a very slow connection.
 * There are some heuristics which would probably be sufficient, including
 * waiting until the screen does not change for a full second, and assuming
 * that it is therefore waiting for input, or using clever choices of some
 * "free" commands, such as the "note" command, to allow the Borg to cause
 * special things to appear on the screen when the game is ready for the
 * next command.  The hardest thing would actually be making sure that all
 * possible situations were accounted for.
 *
 * A minor problem is that the Borg must assume that a human has already
 * created a character, and set up various options and such so that the
 * Borg can function correctly.  Currently, to avoid crashing if these
 * assumptions are incorrect, the code actually sets some options and
 * such to known values, and redraws the screen using the new options,
 * any time a human user asks the Borg to initialize or resume playing.
 * Since a human may, at any time, take over and perform arbitrary acts,
 * and since the Borg does not actually generate the character, currently,
 * when the Borg is initialized (or restarted) it "steals" some important
 * values directly from the game, including the deepest level which has
 * been explored, and which uniques are known to have been killed.  Other
 * values which could be stolen are actually extracted from the screen,
 * such as the race and class of the character.  All of these problems
 * would go away if the Borg generated the character and knew that nobody
 * else was going to use the character file.  Note that certain options
 * cannot be changed by the Borg, including "preserve" mode and "maximize"
 * mode, which are determined when the character is created.  If "preserve"
 * mode is not true, then the Borg may lose some artifacts.  If "maximize"
 * mode is not false, then the Borg may break in unexpected ways.
 *
 * There are several things that the current Borg code is "able" to learn
 * without "cheating", such as the descriptions of all of the equipment
 * and inventory items, information about each available spell or prayer,
 * and the location of the current panel relative to the overall dungeon,
 * by actually sending various keypresses to the game and observing the
 * results, but this is *extremely* annoying to watch, so by default, this
 * code is not used, and the Borg uses special "cheat" code to obtain this
 * information directly from the game.
 *
 * There are several pieces of information about the game that are used
 * directly by the current Borg code, including some of the tables in the
 * "tables.c" file, and some of the static arrays of information which are
 * initialized in the "init1.c" and "init2.c" files, but note that this is
 * not cheating, since if the Borg was compiled into a separate executable,
 * it would simply use these files directly.
 *
 * Note that the Borg can play any race/class combination, that is, he can
 * make "effective" use of at least a few spells/prayers, and is not "broken"
 * by low strength, blind-ness, hallucination, etc.  This does not, however,
 * mean that he plays all classes equally well, especially since he is so
 * dependant on a high strength for so many things.
 *
 * Note that the Borg understands the concept of "Cheating Death".  If this
 * is available (see "wizard mode" and the "cheat_live" flag), then he will
 * automatically "cheat death" whenever he is "about to die".  Otherwise, he
 * will stop playing as soon as he notices that he has died.
 *
 * Note that "borg_time" bears a close resemblance to the number of "player
 * turns" that have gone by.  Except that occasionally, the Borg will do
 * something that he *thinks* will take time but which actually does not
 * (for example, attempting to open a hallucinatory door), and sometimes,
 * the Borg performs a "repeated" command (rest, open, tunnel, or search),
 * which may actually take longer than a single turn.  This has the effect
 * that "borg_time" is slightly lacking in "precision".  Note that we can
 * store every time-stamp in a 's16b', since we reset "borg_time" to 1000
 * on entering a new level, and we refuse to stay on any level longer than
 * 30000 turns, unless we are totally stuck, in which case we abort.  This
 * almost never happens unless the Borg is permanently paralyzed, or is in
 * a corner surrounded by lice with no means of escape, or runs out of light
 * in a dark hallway and cannot find the stairs.
 */


/*
 * Stat advantages:
 *   High STR (attacks, to-dam, digging, weight limit)
 *   High DEX (attacks, to-hit, armor class)
 *   High CON (hitpoints, recovery)
 *   High WIS (better prayers, saving throws)
 *   High INT (better spells, disarming, device usage)
 *   High CHR (better item costs)
 *
 * Class advantages:
 *   Warrior (good fighting, sensing)
 *   Mage (good spells)
 *   Priest (good prayers, fighting)
 *   Ranger (some spells, fighting)
 *   Rogue (some spells, fighting, sensing)
 *   Paladin (prayers, fighting, sensing)
 *
 * Race advantages:
 *   Gnome (free action)
 *   Dwarf (resist blindness)
 *   High elf (see invisible)
 *   Non-human (infravision)
 */


/*
 * We currently handle:
 *   Level changing (intentionally or accidentally)
 *   Embedded objects (gold) that must be extracted
 *   Ignore embedded objects if too "weak" to extract
 *   Traps (disarm), Doors (open/etc), Rubble (tunnel)
 *   Stores (enter via movement, exit via escape)
 *   Stores (limited commands, and different commands)
 *   Always deal with objects one at a time, not in piles
 *   Discard junk before trying to pick up more stuff
 *   Use "identify" to obtain knowledge and/or money
 *   Rely on "sensing" objects as much as possible
 *   Do not sell junk or worthless items to any shop
 *   Do not attempt to buy something without the cash
 *   Use the non-optimal stairs if stuck on a level
 *   Use "flow" code for all tasks for consistency
 *   Cancel all goals when major world changes occur
 *   Use the "danger" code to avoid potential death
 *   Use the "danger" code to avoid inconvenience
 *   Try to avoid danger (both actively and passively)
 *   Handle "Mace of Disruption", "Scythe of Slicing", etc
 *   Learn spells, and use them when appropriate
 *   Remember that studying prayers is slightly random
 *   Do not try to read scrolls when blind or confused
 *   Do not study/use spells/prayers when blind/confused
 *   Use spells/prayers at least once for the experience
 *   Attempt to heal when "confused", "blind", etc
 *   Attempt to fix "fear", "poison", "cuts", etc
 *   Analyze potential equipment in proper context
 *   Priests should avoid edged weapons (spell failure)
 *   Mages should avoid most gloves (lose mana)
 *   Non-warriors should avoid heavy armor (lose mana)
 *   Keep "best" ring on "tight" right finger in stores
 *   Remove items which do not contribute to total fitness
 *   Wear/Remove/Sell/Buy items in most optimal order
 *   Pursue optimal combination of available equipment
 *   Notice "failure" when using rods/staffs/artifacts
 *   Notice "failure" when attempting spells/prayers
 *   Attempt to correctly track terrain, objects, monsters
 *   Take account of "clear" and "multi-hued" monsters
 *   Take account of "flavored" (randomly colored) objects
 *   Handle similar objects/monsters (mushrooms, coins)
 *   Multi-hued/Clear monsters, and flavored objects
 *   Keep in mind that some monsters can move (quickly)
 *   Do not fire at monsters that may not actually exist
 *   Assume everything is an object until proven otherwise
 *   Parse messages to correct incorrect assumptions
 *   Search for secret doors after exploring the level
 *   React intelligently to changes in the wall structure
 *   Do not recalculate "flow" information unless required
 *   Collect monsters/objects/terrain not currently in view
 *   Keep in mind that word of recall is a delayed action
 *   Keep track of charging items (rods and artifacts)
 *   Be very careful not to access illegal locations!
 *   Do not rest next to dangerous (or immobile) monsters
 *   Recall into dungeon if prepared for resulting depth
 *   Do not attempt to destroy cursed ("terrible") artifacts
 *   Attempted destruction will yield "terrible" inscription
 *   Use "maximum" level and depth to prevent "thrashing"
 *   Use "maximum" hp's and sp's when checking "potentials"
 *   Attempt to recognize large groups of "disguised" monsters
 *   Beware of firing at a monster which is no longer present
 *   Stockpile items in the Home, and use those stockpiles
 *   Discounted spell-books (low level ones are ignored)
 *   Take items out of the home to sell them when no longer needed
 *   Trappers and Mimics (now treated as invisible monsters)
 *   Invisible monsters (induce "fear" of nearby regions)
 *   Fleeing monsters are "followed" down corridors and such
 *
 * We ignore:
 *   Running out of light can (fatally) confuse the Borg
 *   Running out of food can kill you, try not to starve
 *   Long object descriptions may have clipped inscriptions
 *
 * We need to handle:
 *   Better "fleeing" code from nasty monsters
 *   Appearance of "similar" monsters (jackals + grip)
 *   Hallucination (induces fake objects and monsters)
 *   Special screens (including tombstone) with no "walls"
 *   Appearance of the '@' symbol on "special" screens
 *   Technically a room can have no exits, requiring digging
 *   Try to use a shovel/pick to help with tunnelling
 *   If wounded, must run away from monsters, then rest
 *   When blind, monster and object info may be invalid
 *   When totally surrounded by monsters, try to escape rooms
 *   Conserve memory space (each grid byte costs about 15K)
 *   Conserve computation time (especially with the flow code)
 *   Note -- nutrition can come from food, scrolls, or spells
 *   Note -- recall can come from scrolls, rods, or spells
 *   Note -- identify can come from scrolls, rods, staffs, or spells
 *   Becoming "afraid" (attacking takes a turn for no effect)
 *   Beware of firing missiles at a food ration under a mushroom
 *   Attempt to save possibly useful equipment in the home
 *
 * We need to handle "loading" saved games:
 *   The "max_depth" value is lost if loaded in the town
 *   If we track "dead uniques" then this information is lost
 *   The "map" info, "flow" info, "tracking" info, etc is lost
 *   The contents of the shops (and the home) are lost
 *   We may be asked to "resume" a non-Borg character (icky)
 */



/*
 * Mega-Hack -- special "inkey_hack" hook.  XXX XXX XXX
 *
 * A special function hook (see "util.c") which allows the Borg to take
 * control of the "inkey()" function, and substitute in fake keypresses.
 */
extern char (*inkey_hack)(int flush_first);


/*
 * This function lets the Borg "steal" control from the user.
 *
 * The "util.c" file provides a special "inkey_hack" hook which we use
 * to steal control of the keyboard, using the special function below.
 *
 * Since this function bypasses the code in "inkey()" which "refreshes"
 * the screen whenever the game has to wait for a keypress, the screen
 * will only get refreshed when (1) an option such as "fresh_before"
 * induces regular screen refreshing or (2) various explicit calls to
 * "Term_fresh" are made, such as in the "project()" function.  This
 * has the interesting side effect that the screen is never refreshed
 * while the Borg is browsing stores, checking his inventory/equipment,
 * browsing spell books, checking the current panel, or examining an
 * object, which reduces the "screen flicker" considerably.  :-)
 *
 * The only way that the Borg can be stopped once it is started, unless
 * it dies or encounters an error, is to press any key.  This function
 * checks for real user input on a regular basic, and if any is found,
 * it is flushed, and after completing any actions in progress, this
 * function hook is removed, and control is returned to the user.
 *
 * We handle "broken" messages, in which long messages are "broken" into
 * pieces, and all but the first message are "indented" by one space, by
 * collecting all the pieces into a complete message and then parsing the
 * message once it is known to be complete.
 *
 * This function hook automatically removes itself when it realizes that
 * it should no longer be active.  Note that this may take place after
 * the game has asked for the next keypress, but the various "keypress"
 * routines should be able to handle this.
 */
static char borg_inkey_hack(int flush_first)
{
	char ch;

	int y = 0;
	int x = 80;

	byte t_a;

	char buf[128];



	/* Deactivate */
	if (borg_active == 0)
	{
		/* Message */
		borg_note("# Removing keypress hook");

		/* Remove hook */
		inkey_hack = NULL;

		/* Flush keys */
		borg_flush();

		/* Flush */
		flush();

		/* Done */
		return (0);
	}


	/* Mega-Hack -- flush keys */
	if (flush_first)
	{
		/* Only flush if needed */
		if (borg_inkey(FALSE) != 0)
		{
			/* Message */
			borg_note("# Flushing keypress buffer");

			/* Flush keys */
			borg_flush();
		}
	}


	/* Locate the cursor */
	(void)Term_locate(&x, &y);


	/* Assume no prompt/message is available */
	borg_prompt = FALSE;

	/* Mega-Hack -- check for possible prompts/messages */
	/* If the first four characters on the message line all */
	/* have the same attribute (or are all spaces), and they */
	/* are not all spaces (ascii value 0x20)... */
	if ((0 == borg_what_text(0, 0, 4, &t_a, buf)) &&
	    (t_a != TERM_DARK) &&
	    (*((u32b*)(buf)) != 0x20202020))
	{
		/* Assume a prompt/message is available */
		borg_prompt = TRUE;
	}


	/* Mega-Hack -- Catch "Die? [y/n]" messages */
	/* If there is text on the first line... */
	/* And the game does not want a command... */
	/* And the cursor is on the top line... */
	/* And the text acquired above is "Die?" */
	if (borg_prompt && !inkey_flag &&
	    (y == 0) && (x >= 4) &&
	    streq(buf, "Die?"))
	{
		/* Flush messages */
		borg_parse(NULL);

		/* Take note */
		borg_note("# Cheating death...");

		/* Reset death flag */
		b_ptr->is_dead = FALSE;

		/* Cheat death */
		return ('n');
	}


	/* Mega-Hack -- Handle death */
	if (b_ptr->is_dead)
	{
		/* Oops */
		borg_oops("player died");

		/* Useless keypress */
		return (KTRL('C'));
	}


	/* Mega-Hack -- Catch "-more-" messages */
	/* If there is text on the first line... */
	/* And the game does not want a command... */
	/* And the cursor is on the top line... */
	/* And there is text before the cursor... */
	/* And that text is "-more-" */
	if (borg_prompt && !inkey_flag &&
	    (y == 0) && (x >= 7) &&
	    (0 == borg_what_text(x-7, y, 7, &t_a, buf)) &&
	    (streq(buf, " -more-")))
	{
		/* Get the message */
		if (0 == borg_what_text(0, 0, x-7, &t_a, buf))
		{
			/* Parse it */
			borg_parse(buf);
		}

		/* Clear the message */
		return (KTRL('M'));
	}


	/* Mega-Hack -- catch normal messages */
	/* If there is text on the first line... */
	/* And the game wants a command */
	if (borg_prompt && inkey_flag)
	{
		/* Get the message(s) */
		if (0 == borg_what_text(0, 0, -80, &t_a, buf))
		{
			int k = strlen(buf);

			/* Strip trailing spaces */
			while ((k > 0) && (buf[k-1] == ' ')) k--;

			/* Terminate */
			buf[k] = '\0';

			/* Parse it */
			borg_parse(buf);
		}

		/* Clear the message */
		return (KTRL('M'));
	}

	/* Flush messages */
	borg_parse(NULL);


	/* Check for key */
	ch = borg_inkey(TRUE);

	/* Use the key */
	if (ch) return (ch);


	/* Check for user abort */
	(void)Term_inkey(&ch, FALSE, FALSE);

	/* User Abort */
	if (ch != 0)
	{
		/* Oops */
		borg_oops("user abort");

		/* Hack -- Escape */
		return (ESCAPE);
	}


	/* Save the system random info */
	borg_rand_quick = Rand_quick;
	borg_rand_value = Rand_value;

	/* Use the local random info */
	Rand_quick = TRUE;
	Rand_value = borg_rand_local;

	/* Think */
	borg_think();

	/* Save the local random info */
	borg_rand_local = Rand_value;

	/* Restore the system random info */
	Rand_quick = borg_rand_quick;
	Rand_value = borg_rand_value;


	/* Check for key */
	ch = borg_inkey(TRUE);

	/* Use the key */
	if (ch) return (ch);


	/* Oops */
	borg_oops("normal abort");

	/* Hack -- Escape */
	return (ESCAPE);
}



/*
 * Prepare to play
 *
 * These things cannot be done during "initialization" because an
 * obnoxious human might change things while he has control.
 *
 * Should also verify that the "misc_to_attr"/"misc_to_char" pairs
 * are reasonable for flavored objects.
 */
static void borg_prepare(void)
{
	int i;

	int book;

	bool hack = FALSE;


	/*** Options ***/

	/* We prefer not to check for user aborts when resting */
	avoid_abort = TRUE;

	/* We must use the original keypress codes */
	rogue_like_commands = FALSE;

	/* We must pick up items when we step on them */
	always_pickup = TRUE;

	/* We must specify targets by hand (?) */
	use_old_target = FALSE;

	/* We must use the top object in stacks */
	floor_query_flag = FALSE;

	/* We must pick items up without verification */
	carry_query_flag = FALSE;

	/* We must destroy items without verification */
	verify_destroy = FALSE;

	/* We must not allow quantity specification (?) */
	allow_quantity = FALSE;

	/* We must not auto-repeat commands */
	always_repeat = FALSE;

	/* We must not haggle */
	auto_haggle = TRUE;

	/* We must have maximal object description space (needs redraw) */
	show_flavors = FALSE;

	/* We must have maximal object description space (needs redraw) */
	show_labels = FALSE;

	/* We must have maximal object description space (needs redraw) */
	show_weights = FALSE;

	/* We prefer to allow items to stack */
	testing_stack = TRUE;

	/* We prefer to allow monsters to carry */
	testing_carry = TRUE;

	/* We prefer to not ignore discounts */
	stack_force_costs = FALSE;

	/* We prefer to ignore inscriptions */
	stack_force_notes = TRUE;

	/* We must not see hitpoint warnings */
	op_ptr->hitpoint_warn = 0;


	/*** Redraw ***/

	/* Redraw map */
	p_ptr->redraw |= (PR_MAP);

	/* Window stuff */
	p_ptr->window |= (PW_OVERHEAD);

	/* Redraw everything */
	do_cmd_redraw();


	/*** Race/Class/Spells ***/

	/* Extract the race */
	b_ptr->prace = p_ptr->prace;

	/* Extract the class */
	b_ptr->pclass = p_ptr->pclass;

	/* Extract the race pointer */
	rb_ptr = &race_info[b_ptr->prace];

	/* Extract the class pointer */
	cb_ptr = &class_info[b_ptr->pclass];

	/* Extract the magic pointer */
	mb_ptr = &magic_info[b_ptr->pclass];

	/* Initialize the books */
	for (book = 0; book < 9; book++)
	{
		borg_prepare_book(book);
	}


	/*** Reset State ***/

	/* Update some stuff */
	borg_do_update_view = TRUE;

	/* Examine self */
	borg_do_inven = TRUE;
	borg_do_equip = TRUE;
	borg_do_spell = TRUE;
	borg_do_spell_aux = 0;

	/* Examine the world */
	borg_do_panel = TRUE;
	borg_do_frame = TRUE;

	/* Enable some functions */
	borg_do_crush_junk = TRUE;
	borg_do_crush_hole = TRUE;
	borg_do_crush_slow = TRUE;

	/* Allowable Cheat -- Obtain "recall" flag */
	borg_recalling = (p_ptr->word_recall ? TRUE : FALSE);


	/*** Cheat -- dead uniques ***/

	/* Hack -- Extract dead uniques */
	for (i = 1; i < MAX_R_IDX-1; i++)
	{
		monster_race *r_ptr = &r_info[i];

		/* Skip non-monsters */
		if (!r_ptr->name) continue;

		/* Skip non-uniques */
		if (!(r_ptr->flags1 & RF1_UNIQUE)) continue;

		/* Mega-Hack -- Access "dead unique" list */
		if (r_ptr->max_num == 0) borg_race_death[i] = 1;
	}


	/*** Nothing analysis ***/

	/* Check the nothing */
	if (TRUE)
	{
		feature_type *f_ptr = &f_info[0];

		/* Notice use of "graphics" */
		if (f_ptr->x_attr & 0x80) hack = TRUE;

		/* Verify nothing char */
		if (f_ptr->x_char != ' ') hack = TRUE;
	}


	/*** Feature analysis ***/

	/* Scan the features */
	for (i = 1; i < MAX_F_IDX; i++)
	{
		feature_type *f_ptr = &f_info[i];

		/* Skip non-features */
		if (!f_ptr->name) continue;

		/* Notice use of "graphics" */
		if (f_ptr->x_attr & 0x80) hack = TRUE;

		/* Notice first picture use */
		if (borg_char_feat[(byte)(f_ptr->x_char)] == 0)
		{
			borg_char_feat[(byte)(f_ptr->x_char)] = i;
		}
	}


	/*** Verify the features ***/

	/* XXX XXX XXX */


	/*** Object analysis ***/

	/* Scan the objects */
	for (i = 1; i < MAX_K_IDX; i++)
	{
		object_kind *k_ptr = &k_info[i];

		/* Skip non-items */
		if (!k_ptr->name) continue;

		/* Notice use of "graphics" */
		if (k_ptr->x_attr & 0x80) hack = TRUE;

		/* Notice this object */
		borg_char_is_take[(byte)(k_ptr->x_char)] = TRUE;
	}


	/*** Player analysis ***/

	/* Check the player */
	if (TRUE)
	{
		monster_race *r_ptr = &r_info[0];

		/* Notice use of "graphics" */
		if (r_ptr->x_attr & 0x80) hack = TRUE;

		/* Verify player char */
		if (r_ptr->x_char != '@') hack = TRUE;
	}


	/*** Monster analysis ***/

	/* Scan the monsters */
	for (i = 1; i < MAX_R_IDX-1; i++)
	{
		monster_race *r_ptr = &r_info[i];

		/* Skip non-monsters */
		if (!r_ptr->name) continue;

		/* Notice use of "graphics" */
		if (r_ptr->x_attr & 0x80) hack = TRUE;

		/* Hack -- Skip "clear" monsters XXX XXX XXX */
		if (r_ptr->flags1 & RF1_CHAR_CLEAR) continue;

		/* Hack -- Skip "multi" monsters XXX XXX XXX */
		if (r_ptr->flags1 & RF1_CHAR_MULTI) continue;

		/* Notice this monster */
		borg_char_is_kill[(byte)(r_ptr->x_char)] = TRUE;
	}


	/*** Disallow Graphics ***/

	/* Oops */
	if (hack) borg_oops("cannot use graphics");
}



/*
 * Initialize some stuff
 */
static void borg_init_1(void)
{
	/*** Allow direct access to visual information ***/

	/* Point to the main window */
	borg_term_pointer = angband_term[0];


	/*** Various things ***/

	/* Allocate the "keypress queue" */
	C_MAKE(borg_key_queue, BORG_KEY_SIZE, char);

	/* Prepare a local random number seed */
	borg_rand_local = rand_int(0x10000000);


	/*** Special counters ***/

	/* Count racial appearances */
	C_MAKE(borg_race_count, MAX_R_IDX, s16b);

	/* Count racial deaths */
	C_MAKE(borg_race_death, MAX_R_IDX, s16b);


	/*** Map analysis arrays ***/

	/* Char code may be a feature */
	C_MAKE(borg_char_feat, 256, byte);


	/*** Classification arrays ***/

	/* Char code may be an object */
	C_MAKE(borg_char_is_take, 256, bool);

	/* Char code may be a monster */
	C_MAKE(borg_char_is_kill, 256, bool);


	/*** Grid arrays ***/

	C_MAKE(borg_view_g, BORG_VIEW_MAX, u16b);

	C_MAKE(borg_temp_g, BORG_TEMP_MAX, u16b);

	borg_temp_y = ((byte*)(borg_temp_g)) + 0;
	borg_temp_x = ((byte*)(borg_temp_g)) + BORG_TEMP_MAX;

	C_MAKE(borg_hack_g, BORG_HACK_MAX, u16b);

	borg_hack_y = ((byte*)(borg_hack_g)) + 0;
	borg_hack_x = ((byte*)(borg_hack_g)) + BORG_HACK_MAX;

	C_MAKE(borg_flow_g, BORG_FLOW_MAX, u16b);

	borg_flow_y = ((byte*)(borg_flow_g)) + 0;
	borg_flow_x = ((byte*)(borg_flow_g)) + BORG_FLOW_MAX;


	/*** Dungeon arrays ***/

	C_MAKE(borg_cave_info, DUNGEON_HGT, byte_256);
	C_MAKE(borg_cave_feat, DUNGEON_HGT, byte_256);

	C_MAKE(borg_cave_o_idx, DUNGEON_HGT, byte_wid);
	C_MAKE(borg_cave_m_idx, DUNGEON_HGT, byte_wid);

	C_MAKE(borg_cave_search, DUNGEON_HGT, byte_wid);

	C_MAKE(borg_cave_danger, DUNGEON_HGT, s16b_wid);

	C_MAKE(borg_flow_cost, DUNGEON_HGT, byte_wid);
	C_MAKE(borg_flow_work, DUNGEON_HGT, byte_wid);


	/*** Tracking arrays ***/

	/* Track the shop locations */
	C_MAKE(borg_track_shop_x, BORG_MAX_SHOP, byte);
	C_MAKE(borg_track_shop_y, BORG_MAX_SHOP, byte);

	/* Track "up" stairs */
	borg_track_less_num = 0;
	C_MAKE(borg_track_less_x, BORG_MAX_LESS, byte);
	C_MAKE(borg_track_less_y, BORG_MAX_LESS, byte);

	/* Track "down" stairs */
	borg_track_more_num = 0;
	C_MAKE(borg_track_more_x, BORG_MAX_MORE, byte);
	C_MAKE(borg_track_more_y, BORG_MAX_MORE, byte);


	/*** Object tracking ***/

	/* No objects yet */
	borg_takes_cnt = 0;
	borg_takes_nxt = 1;


	/*** Monster tracking ***/

	/* No monsters yet */
	borg_kills_cnt = 0;
	borg_kills_nxt = 1;


	/*** Depth tracking ***/

	/* Allowable Cheat -- Access max depth */
	b_ptr->max_depth = p_ptr->max_depth;

	/* Hack -- Assume happy depth */
	borg_happy_depth = b_ptr->max_depth;
}


/*
 * Sorting hook -- comp function -- see below
 *
 * We use "u" to point to an array of strings, and "v" to point to
 * an array of indexes, and we sort them together by the strings.
 */
static bool ang_sort_comp_hook(vptr u, vptr v, int a, int b)
{
	cptr *text = (cptr*)(u);
	s16b *what = (s16b*)(v);

	int cmp;

	/* Compare the two strings */
	cmp = (strcmp(text[a], text[b]));

	/* Strictly less */
	if (cmp < 0) return (TRUE);

	/* Strictly more */
	if (cmp > 0) return (FALSE);

	/* Enforce "stable" sort */
	return (what[a] <= what[b]);
}


/*
 * Sorting hook -- swap function -- see below
 *
 * We use "u" to point to an array of strings, and "v" to point to
 * an array of indexes, and we sort them together by the strings.
 */
static void ang_sort_swap_hook(vptr u, vptr v, int a, int b)
{
	cptr *text = (cptr*)(u);
	s16b *what = (s16b*)(v);

	cptr texttmp;
	s16b whattmp;

	/* Swap "text" */
	texttmp = text[a];
	text[a] = text[b];
	text[b] = texttmp;

	/* Swap "what" */
	whattmp = what[a];
	what[a] = what[b];
	what[b] = whattmp;
}



/*
 * Initialize some stuff
 *
 * Note that the Borg will never find Grond/Morgoth, but we
 * prepare the item parsers for them anyway.  Actually, the
 * Borg might get lucky and find some of the special artifacts,
 * so it is always best to prepare for a situation if it does
 * not cost any effort.
 *
 * Note that all six artifact "Rings" will parse as "kind 506"
 * (the first artifact ring) and both artifact "Amulets" will
 * parse as "kind 503" (the first of the two artifact amulets),
 * but as long as we use the "name1" field (and not the "kind"
 * or "sval" fields) we should be okay.
 *
 * We sort the two arrays of items names in reverse order, so that
 * we will catch "mace of disruption" before "mace", "Scythe of
 * Slicing" before "Scythe", and for "Ring of XXX" before "Ring".
 *
 * Note that we do not have to parse "plural artifacts" (!)
 *
 * Hack -- This entire routine is a giant hack, but it works
 */
static void borg_init_3(void)
{
	int i, k, n;

	int num;

	s16b what[512];
	cptr text[512];

	char buf[256];


	/*** Item/Ware arrays ***/

	/* Make the inventory array */
	C_MAKE(borg_items, INVEN_TOTAL, auto_item);

	/* Make the stores in the town */
	C_MAKE(borg_shops, BORG_MAX_SHOP, auto_shop);


	/*** Item/Ware arrays (simulation) ***/

	/* Make the "safe" inventory array */
	C_MAKE(borg_safe_items, INVEN_TOTAL, auto_item);

	/* Make the "safe" stores in the town */
	C_MAKE(borg_safe_shops, BORG_MAX_SHOP, auto_shop);


	/*** Analysis info ***/

	/* Allocate "ab_ptr" */
	MAKE(ab_ptr, player_anal);


	/*** Plural Object Templates ***/

	/* Start with no objects */
	num = 0;

	/* Analyze some "item kinds" */
	for (k = 1; k < MAX_K_IDX; k++)
	{
		object_type hack;

		/* Get the kind */
		object_kind *k_ptr = &k_info[k];

		/* Skip "empty" items */
		if (!k_ptr->name) continue;

		/* Skip "gold" objects */
		if (k_ptr->tval == TV_GOLD) continue;

		/* Skip "artifacts" */
		if (k_ptr->flags3 & TR3_INSTA_ART) continue;

		/* Hack -- make an item */
		object_prep(&hack, k);

		/* Describe a "plural" object */
		hack.number = 2;
		object_desc_store(buf, &hack, FALSE, 0);

		/* Save an entry */
		text[num] = string_make(buf);
		what[num] = k;
		num++;
	}

	/* Set the sort hooks */
	ang_sort_comp = ang_sort_comp_hook;
	ang_sort_swap = ang_sort_swap_hook;

	/* Sort */
	ang_sort(text, what, num);

	/* Save the num */
	ab_ptr->plural_num = num;

	/* Allocate the "item parsing arrays" (plurals) */
	C_MAKE(ab_ptr->plural_text, ab_ptr->plural_num, cptr);
	C_MAKE(ab_ptr->plural_what, ab_ptr->plural_num, s16b);

	/* Save the entries */
	for (i = 0; i < num; i++) ab_ptr->plural_text[i] = text[i];
	for (i = 0; i < num; i++) ab_ptr->plural_what[i] = what[i];


	/*** Singular Object Templates ***/

	/* Start with no objects */
	num = 0;

	/* Analyze some "item kinds" */
	for (k = 1; k < MAX_K_IDX; k++)
	{
		object_type hack;

		/* Get the kind */
		object_kind *k_ptr = &k_info[k];

		/* Skip "empty" items */
		if (!k_ptr->name) continue;

		/* Skip "dungeon terrain" objects */
		if (k_ptr->tval == TV_GOLD) continue;

		/* Skip "artifacts" */
		if (k_ptr->flags3 & TR3_INSTA_ART) continue;

		/* Hack -- make an item */
		object_prep(&hack, k);

		/* Describe a "singular" object */
		hack.number = 1;
		object_desc_store(buf, &hack, FALSE, 0);

		/* Save an entry */
		text[num] = string_make(buf);
		what[num] = k;
		num++;
	}

	/* Analyze the "INSTA_ART" items */
	for (i = 1; i < MAX_A_IDX; i++)
	{
		object_type hack;

		artifact_type *a_ptr = &a_info[i];

		cptr name = (a_name + a_ptr->name);

		/* Skip "empty" items */
		if (!a_ptr->name) continue;

		/* Skip non INSTA_ART things */
		if (!(a_ptr->flags3 & TR3_INSTA_ART)) continue;

		/* Extract the "kind" */
		k = lookup_kind(a_ptr->tval, a_ptr->sval);

		/* Hack -- make an item */
		object_prep(&hack, k);

		/* Save the index */
		hack.name1 = i;

		/* Describe a "singular" object */
		hack.number = 1;
		object_desc_store(buf, &hack, FALSE, 0);

		/* Extract the "suffix" length */
		n = strlen(name) + 1;

		/* Remove the "suffix" */
		buf[strlen(buf) - n] = '\0';

		/* Save an entry */
		text[num] = string_make(buf);
		what[num] = k;
		num++;
	}

	/* Set the sort hooks */
	ang_sort_comp = ang_sort_comp_hook;
	ang_sort_swap = ang_sort_swap_hook;

	/* Sort */
	ang_sort(text, what, num);

	/* Save the num */
	ab_ptr->single_num = num;

	/* Allocate the "item parsing arrays" (plurals) */
	C_MAKE(ab_ptr->single_text, ab_ptr->single_num, cptr);
	C_MAKE(ab_ptr->single_what, ab_ptr->single_num, s16b);

	/* Save the entries */
	for (i = 0; i < num; i++) ab_ptr->single_text[i] = text[i];
	for (i = 0; i < num; i++) ab_ptr->single_what[i] = what[i];


	/*** Artifact and Ego-Item Parsers ***/

	/* No entries yet */
	num = 0;

	/* Collect the "artifact names" */
	for (k = 1; k < MAX_A_IDX; k++)
	{
		artifact_type *a_ptr = &a_info[k];

		/* Skip non-items */
		if (!a_ptr->name) continue;

		/* Extract a string */
		sprintf(buf, " %s", (a_name + a_ptr->name));

		/* Save an entry */
		text[num] = string_make(buf);
		what[num] = k;
		num++;
	}

	/* Collect the "ego-item names" */
	for (k = 1; k < MAX_E_IDX; k++)
	{
		ego_item_type *e_ptr = &e_info[k];

		/* Skip non-items */
		if (!e_ptr->name) continue;

		/* Extract a string */
		sprintf(buf, " %s", (e_name + e_ptr->name));

		/* Save an entry */
		text[num] = string_make(buf);
		what[num] = k + 256;
		num++;
	}

	/* Set the sort hooks */
	ang_sort_comp = ang_sort_comp_hook;
	ang_sort_swap = ang_sort_swap_hook;

	/* Sort */
	ang_sort(text, what, num);

	/* Save the num */
	ab_ptr->artego_num = num;

	/* Allocate the "item parsing arrays" (plurals) */
	C_MAKE(ab_ptr->artego_text, ab_ptr->artego_num, cptr);
	C_MAKE(ab_ptr->artego_what, ab_ptr->artego_num, s16b);

	/* Save the entries */
	for (i = 0; i < num; i++) ab_ptr->artego_text[i] = text[i];
	for (i = 0; i < num; i++) ab_ptr->artego_what[i] = what[i];
}


/*
 * Initialize some stuff
 */
static void borg_init_5(void)
{
	int i;

	int num;

	s16b what[512];
	cptr text[512];


	/*** Old Info ***/

	/* Old depth */
	xb_ptr->old_depth = -1;

	/* Old stuff */
	xb_ptr->old_chp = -1;
	xb_ptr->old_csp = -1;

	/* Old panel */
	xb_ptr->old_wx = -1;
	xb_ptr->old_wy = -1;

	/* Old location */
	xb_ptr->old_px = -1;
	xb_ptr->old_py = -1;


	/*** Message tracking ***/

	/* No chars saved yet */
	borg_msg_len = 0;

	/* Maximum buffer num */
	borg_msg_siz = 4096;

	/* Allocate a buffer */
	C_MAKE(borg_msg_buf, borg_msg_siz, char);

	/* No msg's saved yet */
	borg_msg_num = 0;

	/* Maximum number of messages */
	borg_msg_max = 128;

	/* Allocate array of positions */
	C_MAKE(borg_msg_pos, borg_msg_max, s16b);

	/* Allocate array of use-types */
	C_MAKE(borg_msg_use, borg_msg_max, s16b);


	/*** Object/Monster tracking ***/

	/* Hack -- Array of "wanks" */
	C_MAKE(borg_wanks, BORG_VIEW_MAX, auto_wank);


	/*** Reset the map ***/

	/* Forget the map */
	borg_forget_map();


	/*** Parse "unique" monster names ***/

	/* Start over */
	num = 0;

	/* Collect "unique" monsters */
	for (i = 1; i < MAX_R_IDX-1; i++)
	{
		monster_race *r_ptr = &r_info[i];

		/* Skip non-monsters */
		if (!r_ptr->name) continue;

		/* Skip non-unique monsters */
		if (!(r_ptr->flags1 & RF1_UNIQUE)) continue;

		/* Use it */
		text[num] = r_name + r_ptr->name;
		what[num] = i;
		num++;
	}

	/* Set the sort hooks */
	ang_sort_comp = ang_sort_comp_hook;
	ang_sort_swap = ang_sort_swap_hook;

	/* Sort */
	ang_sort(text, what, num);

	/* Save the num */
	ab_ptr->unique_num = num;

	/* Allocate the arrays */
	C_MAKE(ab_ptr->unique_text, ab_ptr->unique_num, cptr);
	C_MAKE(ab_ptr->unique_what, ab_ptr->unique_num, s16b);

	/* Save the entries */
	for (i = 0; i < num; i++) ab_ptr->unique_text[i] = text[i];
	for (i = 0; i < num; i++) ab_ptr->unique_what[i] = what[i];

	/* Precalculate the lengths */
	C_MAKE(ab_ptr->unique_size, ab_ptr->unique_num, s16b);

	/* Save the entries */
	for (i = 0; i < num; i++) ab_ptr->unique_size[i] = strlen(text[i]);


	/*** Parse "normal" monster names ***/

	/* Start over */
	num = 0;

	/* Collect "normal" monsters */
	for (i = 1; i < MAX_R_IDX-1; i++)
	{
		monster_race *r_ptr = &r_info[i];

		/* Skip non-monsters */
		if (!r_ptr->name) continue;

		/* Skip unique monsters */
		if (r_ptr->flags1 & RF1_UNIQUE) continue;

		/* Use it */
		text[num] = r_name + r_ptr->name;
		what[num] = i;
		num++;
	}

	/* Set the sort hooks */
	ang_sort_comp = ang_sort_comp_hook;
	ang_sort_swap = ang_sort_swap_hook;

	/* Sort */
	ang_sort(text, what, num);

	/* Save the num */
	ab_ptr->normal_num = num;

	/* Allocate the arrays */
	C_MAKE(ab_ptr->normal_text, ab_ptr->normal_num, cptr);
	C_MAKE(ab_ptr->normal_what, ab_ptr->normal_num, s16b);

	/* Save the entries */
	for (i = 0; i < num; i++) ab_ptr->normal_text[i] = text[i];
	for (i = 0; i < num; i++) ab_ptr->normal_what[i] = what[i];

	/* Precalculate the lengths */
	C_MAKE(ab_ptr->normal_size, ab_ptr->normal_num, s16b);

	/* Save the entries */
	for (i = 0; i < num; i++) ab_ptr->normal_size[i] = strlen(text[i]);
}


/*
 * Initialize the Borg
 */
static errr borg_init(void)
{
	byte *test;

	s16b tester;

	/* Hack -- verify memory */
	C_MAKE(test, 300 * 1024L, byte);
	C_KILL(test, 300 * 1024L, byte);

	/* Hack -- verify integers */
	*(((byte*)(&tester))+0) = 0xFF;
	*(((byte*)(&tester))+1) = 0xFF;
	if (tester != -1) quit("Weird machine!");

	/* Initialize */
	borg_init_1();
	borg_init_3();
	borg_init_5();

	/* Initialize */
	borg_vinfo_init();

	/* Success */
	return (0);
}



/*
 * Hack -- interact with the Borg
 */
void do_cmd_borg(void)
{
	static bool initialize = TRUE;

	char cmd;


	/* Initialize */
	if (initialize)
	{
		/* Message */
		prt("Initializing the Borg...", 0, 0);

		/* Refresh */
		Term_fresh();

		/* Initialize */
		(void)borg_init();

		/* Clear line */
		prt("", 0, 0);

		/* Refresh */
		Term_fresh();

		/* Prepare */
		borg_prepare();

		/* Official message */
		borg_note("# Ready...");

		/* Now it is ready */
		initialize = FALSE;
	}


	/* Get a "Borg command", or abort */
	if (!get_com("Borg command: ", &cmd)) return;


	/* Simple help */
	if (cmd == '?')
	{
		int i = 2;

		/* Save screen */
		screen_save();

		/* Clear the screen */
		Term_clear();

		/* Dump commands */
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'z' runs the Borg.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'x' steps the Borg.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'u' updates the Borg.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'f' modifies some flags.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'l' activates a log file.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 's' activates search mode.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'i' displays grid info.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'a' displays borg_avoids.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'k' displays monster info.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 't' displays object info.");

		/* Dump header */
		Term_putstr(5, 23, -1, TERM_WHITE,
		            "[See the source code for a complete list of commands]");

		/* Prompt for key */
		msg_print("Available commands: z,x,u,f,l,s,i,a,k,t.");
		msg_print(NULL);

		/* Load screen */
		screen_load();

		/* Done */
		return;
	}


	/* Command: Run */
	if (cmd == 'z')
	{
		/* Run forever */
		borg_active = -1;

		/* Prepare */
		borg_prepare();

		/* Message */
		borg_note("# Installing 'inkey_hack' keypress stealer");

		/* Activate the key stealer */
		inkey_hack = borg_inkey_hack;
	}


	/* Command: Step */
	else if (cmd == 'x')
	{
		/* Step once */
		borg_active = 2;

		/* Step N times */
		if (p_ptr->command_arg > 0)
		{
			borg_active = p_ptr->command_arg + 1;
		}

		/* Prepare */
		borg_prepare();

		/* Message */
		borg_note("# Installing 'inkey_hack' keypress stealer");

		/* Activate the key stealer */
		inkey_hack = borg_inkey_hack;
	}


	/* Command: Update */
	else if (cmd == 'u')
	{
		/* Update */
		borg_active = 1;

		/* Prepare */
		borg_prepare();

		/* Message */
		borg_note("# Installing 'inkey_hack' keypress stealer");

		/* Activate the key stealer */
		inkey_hack = borg_inkey_hack;
	}


	/* Command: toggle "strategy" flags */
	else if (cmd == 'f')
	{
		/* Get a "Borg command", or abort */
		if (!get_com("Borg command: Toggle Savefile Flag (s/d): ", &cmd)) return;

		/* Dump savefile when level changes */
		if (cmd == 's')
		{
			borg_flag_save_level = !borg_flag_save_level;
			msg_format("Borg -- borg_flag_save_level is now %d.",
			           borg_flag_save_level);
		}

		/* Dump savefile when depth changes */
		else if (cmd == 'd')
		{
			borg_flag_save_depth = !borg_flag_save_depth;
			msg_format("Borg -- borg_flag_save_depth is now %d.",
			           borg_flag_save_depth);
		}
	}


	/* Start a new log file */
	else if (cmd == 'l')
	{
		char buf[81];

		/* Close the log file */
		if (borg_fff) my_fclose(borg_fff);

		/* Hack -- drop permissions */
		safe_setuid_drop();

		/* Default  */
		strcpy(buf, "borg.log");

		/* Get the name and open the log file */
		if (get_string("Borg Log File: ", buf, 80))
		{
			/* Open a new file */
			borg_fff = my_fopen(buf, "w");

			/* Failure */
			if (!borg_fff) msg_print("Cannot open that file.");
		}

		/* Oops */
		else if (borg_fff)
		{
			/* Mention closing */
			msg_print("Closed borg file.");

			/* Forget old file */
			borg_fff = NULL;
		}

		/* Hack -- grab permissions */
		safe_setuid_grab();
	}


	/* Activate a search string */
	else if (cmd == 's')
	{
		/* Get the new search string (or cancel the matching) */
		if (!get_string("Borg Match String: ", borg_match_string, 80))
		{
			/* Cancel it */
			strcpy(borg_match_string, "");

			/* Message */
			msg_print("Borg Match String de-activated.");
		}
	}


	/* Command: check "info" flags */
	else if (cmd == 'i')
	{
		int x, y;

		u16b mask = 0x00;

		/* Get a "Borg command", or abort */
		if (!get_com("Borg command: Show grids: ", &cmd)) return;

		/* Extract a flag */
		switch (cmd)
		{
			case '0': mask = (1 << 0); break;
			case '1': mask = (1 << 1); break;
			case '2': mask = (1 << 2); break;
			case '3': mask = (1 << 3); break;
			case '4': mask = (1 << 4); break;
			case '5': mask = (1 << 5); break;
			case '6': mask = (1 << 6); break;
			case '7': mask = (1 << 7); break;

			case 'm': mask |= (CAVE_MARK); break;
			case 'g': mask |= (CAVE_GLOW); break;
			case 'd': mask |= (CAVE_DARK); break;
			case 'h': mask |= (CAVE_HACK); break;
			case 's': mask |= (CAVE_SEEN); break;
			case 'v': mask |= (CAVE_VIEW); break;
			case 't': mask |= (CAVE_TEMP); break;
			case 'w': mask |= (CAVE_WALL); break;
		}

		/* Scan map */
		for (y = b_ptr->wy; y < b_ptr->wy + SCREEN_HGT; y++)
		{
			for (x = b_ptr->wx; x < b_ptr->wx + SCREEN_WID; x++)
			{
				byte a = TERM_RED;

				/* Given mask, show only those grids */
				if (mask && !(borg_cave_info[y][x] & mask)) continue;

				/* Given no mask, show unknown grids */
				if (!mask && (borg_cave_info[y][x] & (CAVE_MARK))) continue;

				/* Color */
				if (borg_cave_floor_bold(y, x)) a = TERM_YELLOW;

				/* Display */
				print_rel('*', a, y, x);
			}
		}

		/* Get keypress */
		msg_print("Press any key.");
		msg_print(NULL);

		/* Redraw map */
		prt_map();
	}


	/* Command: check actual danger */
	else if (cmd == 'a')
	{
		int x, y, p;

		int limit = (p_ptr->command_arg ? p_ptr->command_arg : borg_avoid/2);

		/* Scan map */
		for (y = b_ptr->wy; y < b_ptr->wy + SCREEN_HGT; y++)
		{
			for (x = b_ptr->wx; x < b_ptr->wx + SCREEN_WID; x++)
			{
				/* Obtain danger */
				p = borg_danger(y, x, 1);

				/* High danger */
				if (p > limit)
				{
					print_rel('*', TERM_RED, y, x);
				}
			}
		}

		/* Get keypress */
		msg_format("Danger %d and above.", limit);
		msg_print(NULL);

		/* Redraw map */
		prt_map();
	}


	/* Command: check memorized danger */
	else if (cmd == 'd')
	{
		int x, y, p;

		int limit = (p_ptr->command_arg ? p_ptr->command_arg : borg_avoid/2);

		/* Scan map */
		for (y = b_ptr->wy; y < b_ptr->wy + SCREEN_HGT; y++)
		{
			for (x = b_ptr->wx; x < b_ptr->wx + SCREEN_WID; x++)
			{
				/* Obtain danger */
				p = borg_cave_danger[y][x];

				/* Unknown danger */
				if (p < 0)
				{
					print_rel('*', TERM_YELLOW, y, x);
				}

				/* Irrelevant danger */
				else if (borg_do_wipe_danger)
				{
					print_rel('*', TERM_VIOLET, y, x);
				}

				/* High danger */
				else if (p > limit)
				{
					print_rel('*', TERM_RED, y, x);
				}
			}
		}

		/* Get keypress */
		msg_format("Danger %d and above.", limit);
		msg_print(NULL);

		/* Redraw map */
		prt_map();
	}


	/* Command: show "monsters" */
	else if (cmd == 'k')
	{
		int i, n = 0;

		/* Scan the monsters */
		for (i = 1; i < borg_kills_nxt; i++)
		{
			auto_kill *kill = &borg_kills[i];

			/* Still alive */
			if (kill->r_idx)
			{
				int x = kill->x;
				int y = kill->y;

				/* Display */
				print_rel('*', TERM_RED, y, x);

				/* Count */
				n++;
			}
		}

		/* Get keypress */
		msg_format("There are %d known monsters.", n);
		msg_print(NULL);

		/* Redraw map */
		prt_map();
	}


	/* Command: show "objects" */
	else if (cmd == 't')
	{
		int i, n = 0;

		/* Scan the objects */
		for (i = 1; i < borg_takes_nxt; i++)
		{
			auto_take *take = &borg_takes[i];

			/* Still alive */
			if (take->k_idx)
			{
				int x = take->x;
				int y = take->y;

				/* Display */
				print_rel('*', TERM_RED, y, x);

				/* Count */
				n++;
			}
		}

		/* Get keypress */
		msg_format("There are %d known objects.", n);
		msg_print(NULL);

		/* Redraw map */
		prt_map();
	}


	/* Command: show "projectable" */
	else if (cmd == 'p')
	{
		int py = b_ptr->py;
		int px = b_ptr->px;

		int x, y;

		/* Scan map */
		for (y = b_ptr->wy; y < b_ptr->wy + SCREEN_HGT; y++)
		{
			for (x = b_ptr->wx; x < b_ptr->wx + SCREEN_WID; x++)
			{
				byte a = TERM_RED;

				/* Verify flow cost */
				if (!borg_projectable(py, px, y, x)) continue;

				/* Display */
				print_rel('*', a, y, x);
			}
		}

		/* Get keypress */
		msg_format("Projectable grids.");
		msg_print(NULL);

		/* Redraw map */
		prt_map();
	}


	/* Command: debug -- current flow */
	else if (cmd == '%')
	{
		int i, x, y;

		char ch;

		/* Flow */
		for (i = 0; i < 250; i++)
		{
			int n = 0;

			/* Scan map */
			for (y = b_ptr->wy; y < b_ptr->wy + SCREEN_HGT; y++)
			{
				for (x = b_ptr->wx; x < b_ptr->wx + SCREEN_WID; x++)
				{
					byte a = TERM_RED;

					/* Verify flow cost */
					if (borg_flow_cost[y][x] != i) continue;

					/* Display */
					print_rel('*', a, y, x);

					/* Count */
					n++;
				}
			}

			/* Get keypress */
			(void)get_com(format("Flow depth %d (SPC/ESC): ", i), &ch);

			/* Redraw map */
			prt_map();

			/* Stop */
			if (ch == ESCAPE) break;
		}
	}


	/* Command: debug -- danger of grid */
	else if (cmd == '#')
	{
		int n, y, x;

		/* Turns */
		n = (p_ptr->command_arg ? p_ptr->command_arg : 1);

		/* Use target */
		if (target_okay())
		{
			y = p_ptr->target_row;
			x = p_ptr->target_col;
		}

		/* Use player */
		else
		{
			y = p_ptr->py;
			x = p_ptr->px;
		}

		/* Danger of grid */
		msg_format("Danger(%d,%d,%d) is %d",
		           y, x, n, borg_danger(y, x, n));
	}


	/* Command: debug -- fear of depth */
	else if (cmd == '_')
	{
		/* Max depth */
		msg_format("Max depth %d:", b_ptr->max_depth);

		/* Happy depth */
		msg_format("Happy depth %d:", borg_happy_depth);

		/* Happy count */
		msg_format("Happy count %d:", borg_happy_count);
	}


	/* XXX XXX XXX */
	else if (cmd == '&')
	{
		char buf[81];

		sprintf(buf, "%d", borg_when_began);

		if (get_string("Modify borg_when_began: ", buf, 80))
		{
			borg_when_began = atoi(buf);
		}
	}


	/* Oops */
	else
	{
		/* Message */
		msg_print("That is not a legal Borg command.");
	}
}


#else

#ifdef MACINTOSH
static int HACK = 0;
#endif /* MACINTOSH */

#endif /* ALLOW_BORG */

