/* File: borg9.c */

/* Purpose: Top level interface to the Borg -BEN- */

#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.2,
 * 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 "Term_inkey_hook" to steal
 * control from the user.  Later, if it detects any input from the user,
 * it gracefully relinquishes control by clearing the "Term_inkey_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()", the cursor location function
 * "Term_locate()", and the cursor visibility function "Term_get_cursor()".
 * 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 a special hook in the "z-term.c" package to allow it to
 * "steal" control from the "Term_inkey()" and "Term_flush()" functions,
 * 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.
 *
 * 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, a
 * special hack will cause him to stop playing when his player dies.
 *
 * Note that the "c_t" parameter 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 that 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 the "c_t" variable is slightly lacking in "precision".  Note that
 * we can store every time-stamp in a 's16b', since we reset the clock to
 * 1000 on each 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)
 */



/*
 * XXX XXX XXX Mega-Hack -- keypress stealer from "z-term.c"
 */
extern errr (*Term_inkey_hook)(char *ch, bool wait, bool take);


/*
 * This function lets the Borg "steal" control from the user.
 *
 * The "z-term.c" file provides a special hook which we use to
 * bypass the standard "Term_flush()" and "Term_inkey()" functions
 * and replace them with the function below.
 *
 * 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 user input on a regular basic, and
 * when any is encountered, it relinquishes control gracefully.
 *
 * 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.
 *
 * Note the complete hack that allows the Borg to run in "demo"
 * mode, which allows the Borg to cheat death 100 times, and stamps
 * the number of cheats in the player "age" field.  XXX XXX XXX
 *
 * Note that 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.
 *
 * XXX XXX XXX We do not correctly handle the "take" flag
 */
static errr Term_inkey_borg(char *ch, bool wait, bool take)
{
	int i, x, y;

	int visible;

	byte t_a;

	char buf[128];


	/* XXX XXX XXX */
	if (!borg_active)
	{
		/* Message */
		borg_note("# Removing keypress hook");

		/* Remove hook */
		Term_inkey_hook = NULL;

		/* Flush keys */
		borg_flush();

		/* Clear if needed */
		if (ch) (*ch) = '\0';

		/* Nothing ready */
		return (1);
	}


	/* Hack -- flush keys */
	if (!ch)
	{
		/* Flush keys */
		borg_flush();

		/* Success */
		return (0);
	}


	/* Clear the key */
	(*ch) = '\0';


	/* Always wait for keys */
	if (!wait) return (1);


	/* Check the "cursor state" XXX XXX XXX */
	/* Note that the cursor visibility determines whether the */
	/* game is asking us for a "command" or for some other key. */
	/* This requires that "hilite_player" be FALSE. */

	/* Hack -- Extract the cursor visibility */
	(void)Term_get_cursor(&visible);


	/* XXX XXX XXX Mega-Hack -- Catch "Die? [y/n]" messages */

	/* Hack -- cheat death */
	if (visible &&
	    (0 == Term_locate(&x, &y)) && (y == 0) && (x >= 4) &&
	    (0 == borg_what_text(0, 0, 4, &t_a, buf)) &&
	    (prefix(buf, "Die?")))
	{
		/* Take note */
		borg_note("# Cheating death...");

		/* Cheat death */
		(*ch) = 'n';

		/* Success */
		return (0);
	}


	/* XXX XXX XXX Mega-Hack -- Catch "-more-" messages */

	/* If the cursor is visible... */
	/* And the cursor is on the top line... */
	/* And there is text before the cursor... */
	/* And that text is "-more-" */
	if (visible &&
	    (0 == Term_locate(&x, &y)) && (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);
		}

		/* Hack -- not active */
		if (!borg_active) return (1);

		/* Clear the message */
		(*ch) = ' ';

		/* Done */
		return (0);
	}


	/* XXX XXX XXX Mega-Hack -- catch normal messages */

	/* If the cursor is NOT visible... */
	/* And there is text on the first line... */
	if (!visible &&
	    (0 == borg_what_text(0, 0, 3, &t_a, buf)) &&
	    (t_a == TERM_WHITE) && buf[0] &&
	    ((buf[0] != ' ') || (buf[1] != ' ') || (buf[2] != ' ')))
	{
		/* Get the message */
		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);
		}

		/* Hack -- not active */
		if (!borg_active) return (1);

		/* Clear the message */
		(*ch) = ' ';

		/* Done */
		return (0);
	}


	/* Flush messages */
	borg_parse(NULL);


	/* Check for key */
	i = borg_inkey(FALSE);

	/* Need more keys */
	if (!i)
	{
		/* Hack -- Process events (do not wait) */
		(void)Term_xtra(TERM_XTRA_EVENT, FALSE);

		/* Hack -- User Abort XXX XXX XXX */
		if (Term->key_head != Term->key_tail)
		{
			/* Take note */
			borg_note("# Preparing to abort");

			/* Cancel later */
			borg_cancel = TRUE;

			/* Hack -- Forget all keypresses */
			Term->key_head = Term->key_tail = 0;

			/* Hack -- Flush all events */
			(void)Term_xtra(TERM_XTRA_FLUSH, 0);
		}

		/* XXX XXX XXX */
		if (p_ptr->is_dead)
		{
			/* Oops */
			borg_oops("unexpected death!");

			/* Return */
			return (1);
		}

		/* 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;

		/* Update and Think */
		while (!borg_think()) /* loop */;

		/* 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;

		/* Hack -- allow stepping to induce a clean cancel */
		if (borg_step && (!--borg_step)) borg_cancel = TRUE;
	}

	/* Check for key */
	i = borg_inkey(take);

	/* Use it */
	if (!i)
	{
		/* Oops */
		borg_oops("user abort");

		/* Hack -- Escape! */
		i = ESCAPE;
	}

	/* Enqueue the keypress */
	(*ch) = i;

	/* Success */
	return (0);
}



/*
 * Initialize the game
 */
static void borg_prepare(void)
{
	int book;


	/*** Options/Redraw ***/

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

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

	/* We specify targets by hand */
	use_old_target = FALSE;

	/* We do NOT query any commands */
	carry_query_flag = FALSE;
	other_query_flag = FALSE;

	/* We repeat by hand */
	always_repeat = FALSE;

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

	/* We need space */
	show_labels = FALSE;
	show_weights = FALSE;

	/* We need the dungeon level */
	depth_in_feet = FALSE;

	/* Allow items to stack */
	testing_stack = TRUE;

	/* Allow monsters to carry */
	testing_carry = TRUE;

	/* Do not ignore discounts */
	stack_force_costs = FALSE;

	/* Ignore inscriptions */
	stack_force_notes = TRUE;

	/* Efficiency */
	avoid_abort = TRUE;

	/* Efficiency */
	op_ptr->hitpoint_warn = 0;

	/* Hack -- notice "command" mode */
	hilite_player = FALSE;

	/* Reset the visuals */
	reset_visuals(FALSE);
	
	/* 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);
	}
}



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


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


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


	/*** Dungeon ***/

	/* Allocate */
	MAKE(db_ptr, auto_dungeon);


	/*** Very special "tracking" array ***/

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


	/*** Special "tracking" arrays ***/

	/* 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;

	/* 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 this object */
		borg_char_is_take[(byte)(k_ptr->x_char)] = TRUE;
	}


	/*** Monster tracking ***/

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

	/* 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;

		/* 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;
	}


	/*** 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);


	/*** Hack -- Cheat XXX XXX XXX ***/

	/* 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;
	}

	/* Hack -- 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 size;

	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 */
	size = 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[size] = string_make(buf);
		what[size] = k;
		size++;
	}

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

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

	/* Save the size */
	ab_ptr->plural_size = size;

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

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


	/*** Singular Object Templates ***/

	/* Start with no objects */
	size = 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[size] = string_make(buf);
		what[size] = k;
		size++;
	}

	/* 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[size] = string_make(buf);
		what[size] = k;
		size++;
	}

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

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

	/* Save the size */
	ab_ptr->single_size = size;

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

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


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

	/* No entries yet */
	size = 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[size] = string_make(buf);
		what[size] = k;
		size++;
	}

	/* 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[size] = string_make(buf);
		what[size] = k + 256;
		size++;
	}

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

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

	/* Save the size */
	ab_ptr->artego_size = size;

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

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


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

	int size;

	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 size */
	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 ***/

	/* Array of "wanks" */
	C_MAKE(borg_wanks, AUTO_VIEW_MAX, auto_wank);


	/*** Reset the map ***/

	/* Forget the map */
	borg_forget_map();


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

	/* Start over */
	size = 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[size] = r_name + r_ptr->name;
		what[size] = i;
		size++;
	}

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

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

	/* Save the size */
	ab_ptr->unique_size = size;

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

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


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

	/* Start over */
	size = 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[size] = r_name + r_ptr->name;
		what[size] = i;
		size++;
	}

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

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

	/* Save the size */
	ab_ptr->normal_size = size;

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

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


/*
 * Initialize the Borg
 */
static errr borg_init(void)
{
	/* Prepare */
	borg_prepare();

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

	/* Success */
	return (0);
}



/*
 * Hack -- interact with the Borg
 */
void do_cmd_borg(void)
{
	char cmd;

	static bool initialized = FALSE;



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


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

		/* Save the screen */
		Term_save();

		/* Clear the screen */
		Term_clear();

		/* Dump commands */
		Term_putstr(5, i++, -1, TERM_WHITE, "Command '?' displays this screen.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command '$' initializes the Borg.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'z' activates the Borg.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'u' updates the Borg.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'x' steps the Borg.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'f' modifies the normal flags.");
		Term_putstr(5, i++, -1, TERM_WHITE, "Command 'c' modifies the cheat 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.");

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

		/* Restore the screen */
		Term_load();

		/* Done */
		return;
	}


	/* Initialize (once) */
	if (!initialized)
	{
		byte *test;

		/* Message */
		prt("Initializing the Borg (system)...", 0, 0);

		/* Refresh */
		Term_fresh();

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

		/* Message */
		prt("Initializing the Borg (internal)...", 0, 0);

		/* Refresh */
		Term_fresh();

		/* Mega-Hack -- Cheat a lot */
		borg_flag_cheat_inven = TRUE;
		borg_flag_cheat_equip = TRUE;

		/* Mega-Hack -- Cheat a lot */
		borg_flag_cheat_spell = TRUE;

		/* Mega-Hack -- Cheat a lot */
		borg_flag_cheat_panel = TRUE;

		/* Initialize */
		(void)borg_init();
		
		/* Clear line */
		prt("", 0, 0);

		/* Prepare */
		borg_prepare();

		/* Refresh */
		Term_fresh();

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

		/* Now it is ready */
		initialized = TRUE;
	}


	/* Command: Nothing */
	if (cmd == '$')
	{
		/* Nothing */
	}


	/* Command: Activate */
	else if (cmd == 'z')
	{
		/* Prepare */
		borg_prepare();

		/* Activate */
		borg_active = TRUE;

		/* Reset cancel */
		borg_cancel = FALSE;

		/* Step forever */
		borg_step = 0;

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

		/* Message */
		borg_note("# Installing keypress hook");

		/* Activate the key stealer */
		Term_inkey_hook = Term_inkey_borg;
	}


	/* Command: Update */
	else if (cmd == 'u')
	{
		/* Prepare */
		borg_prepare();

		/* Activate */
		borg_active = TRUE;

		/* Immediate cancel */
		borg_cancel = TRUE;

		/* Step forever */
		borg_step = 0;

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

		/* Message */
		borg_note("# Installing keypress hook");

		/* Activate the key stealer */
		Term_inkey_hook = Term_inkey_borg;
	}


	/* Command: Step */
	else if (cmd == 'x')
	{
		/* Prepare */
		borg_prepare();

		/* Activate */
		borg_active = TRUE;

		/* Reset cancel */
		borg_cancel = FALSE;

		/* Step for one (or more) turns */
		borg_step = (p_ptr->command_arg > 0) ? p_ptr->command_arg : 1;

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

		/* Message */
		borg_note("# Installing keypress hook");

		/* Activate the key stealer */
		Term_inkey_hook = Term_inkey_borg;
	}


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

		/* Dump savefile at each death */
		if (cmd == 'd')
		{
			borg_flag_dump = !borg_flag_dump;
			msg_format("Borg -- borg_flag_dump is now %d.", borg_flag_dump);
		}

		/* Dump savefile at each level */
		else if (cmd == 's')
		{
			borg_flag_save = !borg_flag_save;
			msg_format("Borg -- borg_flag_save is now %d.", borg_flag_save);
		}
	}


	/* Command: toggle "cheat" flags */
	else if (cmd == 'c')
	{
		/* Get a "Borg command", or abort */
		if (!get_com("Borg command: Toggle Cheat Flag (i/e/s/p): ", &cmd)) return;

		/* Toggle */
		if (cmd == 'i')
		{
			borg_flag_cheat_inven = !borg_flag_cheat_inven;
			msg_format("Borg -- borg_flag_cheat_inven is now %d.", borg_flag_cheat_inven);
		}
		else if (cmd == 'e')
		{
			borg_flag_cheat_equip = !borg_flag_cheat_equip;
			msg_format("Borg -- borg_flag_cheat_equip is now %d.", borg_flag_cheat_equip);
		}
		else if (cmd == 's')
		{
			borg_flag_cheat_spell = !borg_flag_cheat_spell;
			msg_format("Borg -- borg_flag_cheat_spell is now %d.", borg_flag_cheat_spell);
		}
		else if (cmd == 'p')
		{
			borg_flag_cheat_panel = !borg_flag_cheat_panel;
			msg_format("Borg -- borg_flag_cheat_panel is now %d.", borg_flag_cheat_panel);
		}
	}


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

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

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

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

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

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

		/* 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, 70))
		{
			/* 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 |= (BORG_MARK); break;
			case 'g': mask |= (BORG_GLOW); break;
			case 'd': mask |= (BORG_DARK); break;
			case 'o': mask |= (BORG_OKAY); break;
			case 'l': mask |= (BORG_LITE); break;
			case 'v': mask |= (BORG_VIEW); break;
			case 't': mask |= (BORG_TEMP); break;
			case 'x': mask |= (BORG_XTRA); break;
		}

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

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

				/* Given no mask, show unknown grids */
				if (!mask && (db_ptr->cave_info[y][x] & (BORG_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 = xb_ptr->wy; y < xb_ptr->wy + SCREEN_HGT; y++)
		{
			for (x = xb_ptr->wx; x < xb_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 = xb_ptr->wy; y < xb_ptr->wy + SCREEN_HGT; y++)
		{
			for (x = xb_ptr->wx; x < xb_ptr->wx + SCREEN_WID; x++)
			{
				/* Obtain danger */
				p = db_ptr->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: 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 = xb_ptr->wy; y < xb_ptr->wy + SCREEN_HGT; y++)
			{
				for (x = xb_ptr->wx; x < xb_ptr->wx + SCREEN_WID; x++)
				{
					byte a = TERM_RED;

					/* Verify flow cost */
					if (db_ptr->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;

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

		/* Danger of grid */
		msg_format("Danger(%d,%d,%d) is %d",
		           p_ptr->target_row, p_ptr->target_col, n,
		           borg_danger(p_ptr->target_row, p_ptr->target_col, 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);
	}


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


#else

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

#endif /* ALLOW_BORG */
