/* File: borg9.c */

/* Purpose: Highest level functions for the Borg -BEN- */

#include "angband.h"



#ifdef ALLOW_BORG

#include "borg1.h"
#include "borg2.h"
#include "borg3.h"
#include "borg4.h"
#include "borg5.h"
#include "borg6.h"
#include "borg7.h"
#include "borg8.h"
#include "borg9.h"


/*
 * 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 "cheat_live" flag, and if it is set,
 * he will automatically "cheat death" whenever he dies.  If this flag is
 * not set, then the Borg will automatically stop playing as soon as the
 * special "You die." message appears, allowing the human user to observe
 * the tombstone screen if desired.  Currently, the "cheat_live" flag is
 * pretty much required for low level mage characters, since they almost
 * always die once or twice before they become strong enough to survive.
 *
 * 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)
 */




/*
 * Some variables
 */

static bool initialized;	/* Hack -- Initialized */



/*
 * Mega-Hack -- extract some "hidden" variables
 *
 * XXX XXX XXX This step would not be necessary if more info
 * was available on the screen.  Perhaps we should track the
 * maximal value of "my_stat_cur" so we have "my_stat_max".
 */
static void borg_hidden(void)
{
	int i;

	int stat_add[6];


	/* Clear "stat_add[]" */
	for (i = 0; i < 6; i++) stat_add[i] = 0;

	/* Scan the equipment */
	for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
	{
		auto_item *item = &auto_items[i];

		/* Skip empty items */
		if (!item->iqty) continue;

		/* Affect stats */
		if (item->flags1 & TR1_STR) stat_add[A_STR] += item->pval;
		if (item->flags1 & TR1_INT) stat_add[A_INT] += item->pval;
		if (item->flags1 & TR1_WIS) stat_add[A_WIS] += item->pval;
		if (item->flags1 & TR1_DEX) stat_add[A_DEX] += item->pval;
		if (item->flags1 & TR1_CON) stat_add[A_CON] += item->pval;
		if (item->flags1 & TR1_CHR) stat_add[A_CHR] += item->pval;
	}

	/* Mega-Hack -- Guess at "my_stat_cur[]" */
	for (i = 0; i < 6; i++)
	{
		int value;

		/* Hack -- reverse the known bonus */
		value = modify_stat_value(auto_stat[i], -stat_add[i]);

		/* Hack -- save the maximum/current stats */
		my_stat_max[i] = my_stat_cur[i] = value;
	}
}



/*
 * Think about the world and perform an action
 *
 * Check inventory/equipment/spells/panel once per "turn"
 *
 * Process "store" and other modes when necessary
 *
 * Note that the non-cheating "inventory" and "equipment" parsers
 * will get confused by a "weird" situation involving an ant ("a")
 * on line one of the screen, near the left, next to a shield, of
 * the same color, and using --(-- the ")" symbol, directly to the
 * right of the ant.  This is very rare, but perhaps not completely
 * impossible.  I ignore this situation.
 *
 * The handling of stores is a complete and total hack, but seems
 * to work remarkably well, considering...  Note that while in a
 * store, time does not pass, and most actions are not available,
 * and a few new commands are available ("sell" and "purchase").
 *
 * Note the use of "cheat" functions to extract the current inventory,
 * the current equipment, the current panel, and the current spellbook
 * information.  These can be replaced by (very expensive) "parse"
 * functions, which cause an insane amount of "screen flashing".
 *
 * Technically, we should attempt to parse all the messages that
 * indicate that it is necessary to re-parse the equipment, the
 * inventory, or the books, and only set the appropriate flags at
 * that point.  This would not only reduce the potential screen
 * flashing, but would also optimize the code a lot, since the
 * "cheat_inven()" and "cheat_equip()" functions are expensive.
 * For paranoia, we could always select items and spells using
 * capital letters, and keep a global verification buffer, and
 * induce failure and recheck the inventory/equipment any time we
 * get a mis-match.  We could even do some of the state processing
 * by hand, for example, charge reduction and such.  This might also
 * allow us to keep track of how long we have held objects, especially
 * if we attempt to do "item tracking" in the inventory extraction code.
 */
static bool borg_think(void)
{
	int i;

	byte t_a;

	char buf[128];


	/*** Process inventory/equipment ***/

	/* Cheat */
	if (auto_cheat_equip && auto_do_equip)
	{
		/* Only do it once */
		auto_do_equip = FALSE;

		/* Cheat the "equip" screen */
		borg_cheat_equip();

		/* Done */
		return (FALSE);
	}

	/* Cheat */
	if (auto_cheat_inven && auto_do_inven)
	{
		/* Only do it once */
		auto_do_inven = FALSE;

		/* Cheat the "inven" screen */
		borg_cheat_inven();

		/* Done */
		return (FALSE);
	}


	/* Parse "equip" mode */
	if ((0 == borg_what_text(0, 0, 10, &t_a, buf)) &&
	    (streq(buf, "Equipment ")))
	{
		/* Parse the "equip" screen */
		borg_parse_equip();

		/* Leave this mode */
		borg_keypress(ESCAPE);

		/* Done */
		return (TRUE);
	}


	/* Parse "inven" mode */
	if ((0 == borg_what_text(0, 0, 10, &t_a, buf)) &&
	    (streq(buf, "Inventory ")))
	{
		/* Parse the "inven" screen */
		borg_parse_inven();

		/* Leave this mode */
		borg_keypress(ESCAPE);

		/* Done */
		return (TRUE);
	}


	/* Check "equip" */
	if (auto_do_equip)
	{
		/* Only do it once */
		auto_do_equip = FALSE;

		/* Enter "equip" mode */
		borg_keypress('e');

		/* Done */
		return (TRUE);
	}

	/* Check "inven" */
	if (auto_do_inven)
	{
		/* Only do it once */
		auto_do_inven = FALSE;

		/* Enter "inven" mode */
		borg_keypress('i');

		/* Done */
		return (TRUE);
	}


	/*** Find books ***/

	/* Only if needed */
	if (auto_do_spell && (auto_do_spell_aux == 0))
	{
		/* Assume no books */
		for (i = 0; i < 9; i++) auto_book[i] = -1;

		/* Scan the pack */
		for (i = 0; i < INVEN_PACK; i++)
		{
			auto_item *item = &auto_items[i];

			/* Skip non-books */
			if (item->tval != mb_ptr->spell_book) continue;

			/* Note book locations */
			auto_book[item->sval] = i;
		}
	}


	/*** Process books ***/

	/* Hack -- Warriors never browse */
	if (auto_class == 0) auto_do_spell = FALSE;

	/* Hack -- Blind or Confused prevents browsing */
	if (do_blind || do_confused) auto_do_spell = FALSE;

	/* XXX XXX XXX Dark */

	/* Hack -- Stop doing spells when done */
	if (auto_do_spell_aux > 8) auto_do_spell = FALSE;

	/* Cheat */
	if (auto_cheat_spell && auto_do_spell)
	{
		/* Look for the book */
		i = auto_book[auto_do_spell_aux];

		/* Cheat the "spell" screens (all of them) */
		if (i >= 0)
		{
			/* Cheat that page */
			borg_cheat_spell(auto_do_spell_aux);
		}

		/* Advance to the next book */
		auto_do_spell_aux++;

		/* Done */
		return (FALSE);
	}

	/* Check for "browse" mode */
	if ((0 == borg_what_text(COL_SPELL, ROW_SPELL, -12, &t_a, buf)) &&
	    (streq(buf, "Lv Mana Fail")))
	{
		/* Parse the "spell" screen */
		borg_parse_spell(auto_do_spell_aux);

		/* Advance to the next book */
		auto_do_spell_aux++;

		/* Leave that mode */
		borg_keypress(ESCAPE);

		/* Done */
		return (TRUE);
	}

	/* Check "spells" */
	if (auto_do_spell)
	{
		/* Look for the book */
		i = auto_book[auto_do_spell_aux];

		/* Enter the "spell" screen */
		if (i >= 0)
		{
			/* Enter "browse" mode */
			borg_keypress('b');

			/* Pick the next book */
			borg_keypress(I2A(i));

			/* Done */
			return (TRUE);
		}

		/* Advance to the next book */
		auto_do_spell_aux++;

		/* Done */
		return (FALSE);
	}


	/*** Handle stores ***/

	/* Hack -- Check for being in a store */
	if ((0 == borg_what_text(3, 5, 16, &t_a, buf)) &&
	    (streq(buf, "Item Description")))
	{
		/* Assume the Home */
		shop_num = 7;

		/* Extract the "store" name */
		if (0 == borg_what_text(50, 3, -20, &t_a, buf))
		{
			int i;

			/* Check the store names */
			for (i = 0; i < 7; i++)
			{
				cptr name = (f_name + f_info[0x08+i].name);
				if (prefix(buf, name)) shop_num = i;
			}
		}

		/* Hack -- reset page/more */
		auto_shops[shop_num].page = 0;
		auto_shops[shop_num].more = 0;

		/* React to new stores */
		if (auto_do_browse_what != shop_num)
		{
			/* Clear all the items */
			for (i = 0; i < 24; i++)
			{
				/* XXX Wipe the ware */
				WIPE(&auto_shops[shop_num].ware[i], auto_item);
			}

			/* Save the store */
			auto_do_browse_what = shop_num;
		}


		/* Extract the "page", if any */
		if ((0 == borg_what_text(20, 5, 8, &t_a, buf)) &&
		    (prefix(buf, "(Page "))) /* --)-- */
		{
			/* Take note of the page */
			auto_shops[shop_num].more = 1;
			auto_shops[shop_num].page = (buf[6] - '0') - 1;
		}

		/* React to disappearing pages */
		if (auto_do_browse_more != auto_shops[shop_num].more)
		{
			/* Clear the second page */
			for (i = 12; i < 24; i++)
			{
				/* XXX Wipe the ware */
				WIPE(&auto_shops[shop_num].ware[i], auto_item);
			}

			/* Save the new one */
			auto_do_browse_more = auto_shops[shop_num].more;
		}

		/* Extract the current gold (unless in home) */
		if (0 == borg_what_text(68, 19, -9, &t_a, buf))
		{
			/* Save the gold, if valid */
			if (buf[0]) auto_gold = atol(buf);
		}

		/* Parse the store (or home) inventory */
		for (i = 0; i < 12; i++)
		{
			int n;

			char desc[80];
			char cost[10];

			/* Default to "empty" */
			desc[0] = '\0';
			cost[0] = '\0';

			/* Verify "intro" to the item */
			if ((0 == borg_what_text(0, i + 6, 3, &t_a, buf)) &&
			    (buf[0] == I2A(i)) && (buf[1] == p2) && (buf[2] == ' '))
			{
				int k;

				/* Extract the item description */
				if (0 != borg_what_text(3, i + 6, -65, &t_a, desc))
				{
					desc[0] = '\0';
				}

				/* Strip trailing spaces */
				for (k = strlen(desc); (k > 0) && (desc[k-1] == ' '); k--) /* loop */;
				desc[k] = '\0';

				/* Extract the item cost in stores */
				if (shop_num != 7)
				{
					if (0 != borg_what_text(68, i + 6, -9, &t_a, cost))
					{
						cost[0] = '\0';
					}
				}
			}

			/* Extract actual index */
			n = auto_shops[shop_num].page * 12 + i;

			/* Ignore "unchanged" descriptions */
			if (streq(desc, auto_shops[shop_num].ware[n].desc)) continue;

			/* Analyze the item */
			borg_item_analyze(&auto_shops[shop_num].ware[n], desc);

			/* Hack -- Save the declared cost */
			auto_shops[shop_num].ware[n].cost = atol(cost);
		}

		/* Hack -- browse as needed */
		if (auto_shops[shop_num].more && auto_do_browse)
		{
			/* Check next page */
			borg_keypress(' ');

			/* Done browsing */
			auto_do_browse = FALSE;

			/* Done */
			return (TRUE);
		}

		/* Recheck inventory */
		auto_do_inven = TRUE;

		/* Recheck equipment */
		auto_do_equip = TRUE;

		/* Recheck spells */
		auto_do_spell = TRUE;

		/* Restart spells */
		auto_do_spell_aux = 0;

		/* Hack -- browse again later */
		auto_do_browse = TRUE;

		/* Examine the inventory */
		borg_notice();

		/* Evaluate the current world */
		my_power = borg_power();

		/* Hack -- allow user abort */
		if (auto_cancel) return (TRUE);

		/* Think until done */
		return (borg_think_store());
	}



	/*** Determine panel ***/

	/* Hack -- cheat */
	if (auto_cheat_panel)
	{
		/* Cheat */
		w_y = panel_row * (SCREEN_HGT / 2);
		w_x = panel_col * (SCREEN_WID / 2);

		/* Done */
		auto_do_panel = FALSE;
	}

	/* Hack -- Check for "sector" mode */
	if ((0 == borg_what_text(0, 0, 16, &t_a, buf)) &&
	    (prefix(buf, "Map sector ")))
	{
		/* Hack -- get the panel info */
		w_y = (buf[12] - '0') * (SCREEN_HGT / 2);
		w_x = (buf[14] - '0') * (SCREEN_WID / 2);

		/* Leave panel mode */
		borg_keypress(ESCAPE);

		/* Done */
		return (TRUE);
	}

	/* Check equipment */
	if (auto_do_panel)
	{
		/* Only do it once */
		auto_do_panel = FALSE;

		/* Enter "panel" mode */
		borg_keypress('L');

		/* Done */
		return (TRUE);
	}


	/*** Analyze the Frame ***/

	/* Analyze the frame */
	if (auto_do_frame)
	{
		/* Only once */
		auto_do_frame = FALSE;

		/* Analyze the "frame" */
		borg_update_frame();
	}


	/*** Re-activate Tests ***/

	/* Check equip again later */
	auto_do_equip = TRUE;

	/* Check inven again later */
	auto_do_inven = TRUE;

	/* Check panel again later */
	auto_do_panel = TRUE;

	/* Check frame again later */
	auto_do_frame = TRUE;

	/* Check spells again later */
	auto_do_spell = TRUE;

	/* Hack -- Start the books over */
	auto_do_spell_aux = 0;


	/*** Analyze status ***/

	/* Track best level */
	if (auto_level > auto_max_level) auto_max_level = auto_level;
	if (auto_depth > auto_max_depth) auto_max_depth = auto_depth;


	/*** Think about it ***/

	/* Increment the clock */
	c_t++;

	/* Examine the screen */
	borg_update();

	/* Extract some "hidden" variables */
	borg_hidden();

	/* Examine the equipment/inventory */
	borg_notice();

	/* Evaluate the current world */
	my_power = borg_power();

	/* Hack -- allow user abort */
	if (auto_cancel) return (TRUE);

	/* Do something */
	return (borg_think_dungeon());
}



/*
 * Hack -- methods of hurting a monster (order not important).
 *
 * See "message_pain()" for details.
 */
static cptr suffix_pain[] =
{
	" barely notices.",
	" flinches.",
	" squelches.",
	" quivers in pain.",
	" writhes about.",
	" writhes in agony.",
	" jerks limply.",

	" shrugs off the attack.",
	" snarls with pain.",
	" yelps in pain.",
	" howls in pain.",
	" howls in agony.",
	/* xxx */
	" yelps feebly.",

	" ignores the attack.",
	" grunts with pain.",
	" squeals in pain.",
	" shrieks in pain.",
	" shrieks in agony.",
	/* xxx */
	" cries out feebly.",

	/* xxx */
	/* xxx */
	" cries out in pain.",
	" screams in pain.",
	" screams in agony.",
	/* xxx */
	/* xxx */

	NULL
};


/*
 * Hack -- methods of killing a monster (order not important).
 *
 * See "mon_take_hit()" for details.
 */
static cptr prefix_kill[] =
{
	"You have killed ",
	"You have slain ",
	"You have destroyed ",
	NULL
};


/*
 * Hack -- methods of monster death (order not important).
 *
 * See "project_m()", "do_cmd_fire()", "mon_take_hit()" for details.
 */
static cptr suffix_died[] =
{
	" dies.",
	" is destroyed.",
	" dissolves!",
	" shrivels away in the light!",
	NULL
};


/*
 * Hack -- methods of hitting the player (order not important).
 *
 * The "insult", "moan", and "begs you for money" messages are ignored.
 *
 * See "make_attack_normal()" for details.
 */
static cptr suffix_hit_by[] =
{
	" hits you.",				/* RBM_HIT */
	" touches you.",			/* RBM_TOUCH */
	" punches you.",			/* RBM_PUNCH */
	" kicks you.",				/* RBM_KICK */
	" claws you.",				/* RBM_CLAW */
	" bites you.",				/* RBM_BITE */
	" stings you.",				/* RBM_STING */
	/* xxx */					/* RBM_XXX1 */
	" butts you.",				/* RBM_BUTT */
	" crushes you.",			/* RBM_CRUSH */
	" engulfs you.",			/* RBM_ENGULF */
	/* xxx */					/* RBM_XXX2 */
	" crawls on you.",			/* RBM_CRAWL */
	" drools on you.",			/* RBM_DROOL */
	" spits on you.",			/* RBM_SPIT */
	/* xxx */					/* RBM_XXX3 */
	" gazes at you.",			/* RBM_GAZE */
	" wails at you.",			/* RBM_WAIL */
	" releases spores at you.",	/* RBM_SPORE */
	/* xxx */					/* RBM_XXX4 */
	/* xxx */					/* RBM_BEG */
	/* xxx */					/* RBM_INSULT */
	/* xxx */					/* RBM_MOAN */
	/* xxx */					/* RBM_XXX5 */
	NULL
};


/*
 * Hack -- methods of casting spells at the player (order important).
 *
 * See "make_attack_spell()" for details.
 */
static cptr suffix_spell[] =
{
	" makes a high pitched shriek.",				/* RF4_SHRIEK */
	" does something.",								/* RF4_XXX2X4 */
	" does something.",								/* RF4_XXX3X4 */
	" does something.",								/* RF4_XXX4X4 */
	" fires an arrow.",								/* RF4_ARROW_1 */
	" fires an arrow!",								/* RF4_ARROW_2 */
	" fires a missile.",							/* RF4_ARROW_3 */
	" fires a missile!",							/* RF4_ARROW_4 */
	" breathes acid.",								/* RF4_BR_ACID */
	" breathes lightning.",							/* RF4_BR_ELEC */
	" breathes fire.",								/* RF4_BR_FIRE */
	" breathes frost.",								/* RF4_BR_COLD */
	" breathes gas.",								/* RF4_BR_POIS */
	" breathes nether.",							/* RF4_BR_NETH */
	" breathes light.",								/* RF4_BR_LITE */
	" breathes darkness.",							/* RF4_BR_DARK */
	" breathes confusion.",							/* RF4_BR_CONF */
	" breathes sound.",								/* RF4_BR_SOUN */
	" breathes chaos.",								/* RF4_BR_CHAO */
	" breathes disenchantment.",					/* RF4_BR_DISE */
	" breathes nexus.",								/* RF4_BR_NEXU */
	" breathes time.",								/* RF4_BR_TIME */
	" breathes inertia.",							/* RF4_BR_INER */
	" breathes gravity.",							/* RF4_BR_GRAV */
	" breathes shards.",							/* RF4_BR_SHAR */
	" breathes plasma.",							/* RF4_BR_PLAS */
	" breathes force.",								/* RF4_BR_WALL */
	" does something.",								/* RF4_BR_MANA */
	" does something.",								/* RF4_XXX5X4 */
	" does something.",								/* RF4_XXX6X4 */
	" does something.",								/* RF4_XXX7X4 */
	" does something.",								/* RF4_XXX8X4 */

	" casts an acid ball.",							/* RF5_BA_ACID */
	" casts a lightning ball.",						/* RF5_BA_ELEC */
	" casts a fire ball.",							/* RF5_BA_FIRE */
	" casts a frost ball.",							/* RF5_BA_COLD */
	" casts a stinking cloud.",						/* RF5_BA_POIS */
	" casts a nether ball.",						/* RF5_BA_NETH */
	" gestures fluidly.",							/* RF5_BA_WATE */
	" invokes a mana storm.",						/* RF5_BA_MANA */
	" invokes a darkness storm.",					/* RF5_BA_DARK */
	" draws psychic energy from you!",				/* RF5_DRAIN_MANA */
	" gazes deep into your eyes.",					/* RF5_MIND_BLAST */
	" looks deep into your eyes.",					/* RF5_BRAIN_SMASH */
	" points at you and curses.",					/* RF5_CAUSE_1 */
	" points at you and curses horribly.",			/* RF5_CAUSE_2 */
	" points at you, incanting terribly!",			/* RF5_CAUSE_3 */
	" points at you, screaming the word DIE!",		/* RF5_CAUSE_4 */
	" casts a acid bolt.",							/* RF5_BO_ACID */
	" casts a lightning bolt.",						/* RF5_BO_ELEC */
	" casts a fire bolt.",							/* RF5_BO_FIRE */
	" casts a frost bolt.",							/* RF5_BO_COLD */
	" does something.",								/* RF5_BO_POIS */
	" casts a nether bolt.",						/* RF5_BO_NETH */
	" casts a water bolt.",							/* RF5_BO_WATE */
	" casts a mana bolt.",							/* RF5_BO_MANA */
	" casts a plasma bolt.",						/* RF5_BO_PLAS */
	" casts an ice bolt.",							/* RF5_BO_ICEE */
	" casts a magic missile.",						/* RF5_MISSILE */
	" casts a fearful illusion.",					/* RF5_SCARE */
	" casts a spell, burning your eyes!",			/* RF5_BLIND */
	" creates a mesmerising illusion.",				/* RF5_CONF */
	" drains power from your muscles!",				/* RF5_SLOW */
	" stares deep into your eyes!",					/* RF5_HOLD */

	" concentrates on XXX body.",					/* RF6_HASTE */
	" does something.",								/* RF6_XXX1X6 */
	" concentrates on XXX wounds.",					/* RF6_HEAL */
	" does something.",								/* RF6_XXX2X6 */
	" blinks away.",								/* RF6_BLINK */
	" teleports away.",								/* RF6_TPORT */
	" does something.",								/* RF6_XXX3X6 */
	" does something.",								/* RF6_XXX4X6 */
	" commands you to return.",						/* RF6_TELE_TO */
	" teleports you away.",							/* RF6_TELE_AWAY */
	" gestures at your feet.",						/* RF6_TELE_LEVEL */
	" does something.",								/* RF6_XXX5 */
	" gestures in shadow.",							/* RF6_DARKNESS */
	" casts a spell and cackles evilly.",			/* RF6_TRAPS */
	" tries to blank your mind.",					/* RF6_FORGET */
	" does something.",								/* RF6_XXX6X6 */
	" does something.",								/* RF6_XXX7X6 */
	" does something.",								/* RF6_XXX8X6 */
	" magically summons help!",						/* RF6_S_MONSTER */
	" magically summons monsters!",					/* RF6_S_MONSTERS */
	" magically summons ants.",						/* RF6_S_ANT */
	" magically summons spiders.",					/* RF6_S_SPIDER */
	" magically summons hounds.",					/* RF6_S_HOUND */
	" magically summons hydras.",					/* RF6_S_HYDRA */
	" magically summons an angel!",					/* RF6_S_ANGEL */
	" magically summons a hellish adversary!",		/* RF6_S_DEMON */
	" magically summons an undead adversary!",		/* RF6_S_UNDEAD */
	" magically summons a dragon!",					/* RF6_S_DRAGON */
	" magically summons greater undead!",			/* RF6_S_HI_UNDEAD */
	" magically summons ancient dragons!",			/* RF6_S_HI_DRAGON */
	" magically summons mighty undead opponents!",	/* RF6_S_WRAITH */
	" magically summons special opponents!",		/* RF6_S_UNIQUE */

	NULL
};



#if 0

/*
 * These messages are not currently matched
 */
static void some_extra_messages()
{
	/* XXX XXX XXX */
	msg_format("%^s looks healthier.", m_name);
	msg_format("%^s looks REALLY healthy!", m_name);
}

#endif



/*
 * Hack -- Spontaneous level feelings (order important).
 *
 * See "do_cmd_feeling()" for details.
 */
static cptr prefix_feeling[] =
{
	"Looks like any other level",
	"You feel there is something special",
	"You have a superb feeling",
	"You have an excellent feeling",
	"You have a very good feeling",
	"You have a good feeling",
	"You feel strangely lucky",
	"You feel your luck is turning",
	"You like the look of this place",
	"This level can't be all bad",
	"What a boring place",
	NULL
};



/*
 * Hack -- Parse a message from the world
 *
 * Note that detecting "death" is EXTREMELY important, to prevent
 * all sorts of errors arising from attempting to parse the "tomb"
 * screen, and to allow the user to "observe" the "cause" of death.
 *
 * Note that detecting "failure" is EXTREMELY important, to prevent
 * bizarre situations after failing to use a staff of perceptions,
 * which would otherwise go ahead and send the "item index" which
 * might be a legal command (such as "a" for "aim").  This method
 * is necessary because the Borg cannot parse "prompts", and must
 * assume the success of the prompt-inducing command, unless told
 * otherwise by a failure message.  Also, we need to detect failure
 * because some commands, such as detection spells, need to induce
 * furthur processing if they succeed, but messages are only given
 * if the command fails.
 *
 * Note that certain other messages may contain useful information,
 * and so they are "analyzed" and sent to "borg_react()", which just
 * queues the messages for later analysis in the proper context.
 *
 * Along with the actual message, we send a special formatted buffer,
 * containing a leading "opcode", which may contain extra information,
 * such as the index of a spell, and an "argument" (for example, the
 * capitalized name of a monster), with a "colon" to separate them.
 *
 * XXX XXX XXX Several message strings take a "possessive" of the form
 * "his" or "her" or "its".  These strings are all represented by the
 * encoded form "XXX" in the various match strings.  Unfortunately,
 * the encode form is never decoded, so the Borg currently ignores
 * messages about several spells (heal self and haste self).
 *
 * XXX XXX XXX We notice a few "terrain feature" messages here so
 * we can acquire knowledge about wall types and door types.
 */
static void borg_parse_aux(cptr msg, int len)
{
	int i, tmp;

	char who[256];
	char buf[256];

	auto_grid *ag = &auto_grids[g_y][g_x];


	/* Log (if needed) */
	if (auto_fff) borg_info(format("& Msg <%s>", msg));


	/* Hack -- Notice death */
	if (prefix(msg, "You die."))
	{
		/* Abort (unless cheating) */
		if (!(wizard || cheat_live))
		{
			/* Abort */
			borg_oops("death");

			/* Abort right now! */
			auto_active = FALSE;

			/* Noise XXX XXX XXX */
			Term_xtra(TERM_XTRA_NOISE, 1);
		}

		/* Done */
		return;
	}


	/* Hack -- Notice "failure" */
	if (prefix(msg, "You failed "))
	{
		/* Hack -- store the keypress */
		borg_note("# Normal failure.");

		/* Set the failure flag */
		auto_failure = TRUE;

		/* Flush our key-buffer */
		borg_flush();
		return;
	}


	/* Ignore teleport trap */
	if (prefix(msg, "You hit a teleport")) return;

	/* Ignore arrow traps */
	if (prefix(msg, "An arrow ")) return;

	/* Ignore dart traps */
	if (prefix(msg, "A small dart ")) return;


	/* Hit somebody */
	if (prefix(msg, "You hit "))
	{
		tmp = strlen("You hit ");
		strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp);
		strnfmt(buf, 256, "HIT:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* Miss somebody */
	if (prefix(msg, "You miss "))
	{
		tmp = strlen("You miss ");
		strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp);
		strnfmt(buf, 256, "MISS:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* Miss somebody (because of fear) */
	if (prefix(msg, "You are too afraid to attack "))
	{
		tmp = strlen("You are too afraid to attack ");
		strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp);
		strnfmt(buf, 256, "MISS:%^s", who);
		borg_react(msg, buf);
		return;
	}


	/* "It screams in pain." (etc) */
	for (i = 0; suffix_pain[i]; i++)
	{
		/* "It screams in pain." (etc) */
		if (suffix(msg, suffix_pain[i]))
		{
			tmp = strlen(suffix_pain[i]);
			strnfmt(who, 1 + len - tmp, "%s", msg);
			strnfmt(buf, 256, "PAIN:%^s", who);
			borg_react(msg, buf);
			return;
		}
	}


	/* "You have killed it." (etc) */
	for (i = 0; prefix_kill[i]; i++)
	{
		/* "You have killed it." (etc) */
		if (prefix(msg, prefix_kill[i]))
		{
			tmp = strlen(prefix_kill[i]);
			strnfmt(who, 1 + len - (tmp + 1), "%s", msg + tmp);
			strnfmt(buf, 256, "KILL:%^s", who);
			borg_react(msg, buf);
			return;
		}
	}


	/* "It dies." (etc) */
	for (i = 0; suffix_died[i]; i++)
	{
		/* "It dies." (etc) */
		if (suffix(msg, suffix_died[i]))
		{
			tmp = strlen(suffix_died[i]);
			strnfmt(who, 1 + len - tmp, "%s", msg);
			strnfmt(buf, 256, "DIED:%^s", who);
			borg_react(msg, buf);
			return;
		}
	}


	/* "It misses you." */
	if (suffix(msg, " misses you."))
	{
		tmp = strlen(" misses you.");
		strnfmt(who, 1 + len - tmp, "%s", msg);
		strnfmt(buf, 256, "MISS_BY:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* "It hits you." (etc) */
	for (i = 0; suffix_hit_by[i]; i++)
	{
		/* "It hits you." (etc) */
		if (suffix(msg, suffix_hit_by[i]))
		{
			tmp = strlen(suffix_hit_by[i]);
			strnfmt(who, 1 + len - tmp, "%s", msg);
			strnfmt(buf, 256, "HIT_BY:%^s", who);
			borg_react(msg, buf);
			return;
		}
	}


	/* "It casts a spell." (etc) */
	for (i = 0; suffix_spell[i]; i++)
	{
		/* "It casts a spell." (etc) */
		if (suffix(msg, suffix_spell[i]))
		{
			tmp = strlen(suffix_spell[i]);
			strnfmt(who, 1 + len - tmp, "%s", msg);
			strnfmt(buf, 256, "SPELL_%03d:%^s", 96+i, who);
			borg_react(msg, buf);
			return;
		}
	}


	/* State -- Asleep */
	if (suffix(msg, " falls asleep!"))
	{
		tmp = strlen(" falls asleep!");
		strnfmt(who, 1 + len - tmp, "%s", msg);
		strnfmt(buf, 256, "STATE_SLEEP:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* State -- Not Asleep */
	if (suffix(msg, " wakes up."))
	{
		tmp = strlen(" wakes up.");
		strnfmt(who, 1 + len - tmp, "%s", msg);
		strnfmt(buf, 256, "STATE_AWAKE:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* State -- Afraid */
	if (suffix(msg, " flees in terror!"))
	{
		tmp = strlen(" flees in terror!");
		strnfmt(who, 1 + len - tmp, "%s", msg);
		strnfmt(buf, 256, "STATE__FEAR:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* State -- Not Afraid */
	if (suffix(msg, " recovers his courage."))
	{
		tmp = strlen(" recovers his courage.");
		strnfmt(who, 1 + len - tmp, "%s", msg);
		strnfmt(buf, 256, "STATE__BOLD:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* State -- Not Afraid */
	if (suffix(msg, " recovers her courage."))
	{
		tmp = strlen(" recovers her courage.");
		strnfmt(who, 1 + len - tmp, "%s", msg);
		strnfmt(buf, 256, "STATE__BOLD:%^s", who);
		borg_react(msg, buf);
		return;
	}

	/* State -- Not Afraid */
	if (suffix(msg, " recovers its courage."))
	{
		tmp = strlen(" recovers its courage.");
		strnfmt(who, 1 + len - tmp, "%s", msg);
		strnfmt(buf, 256, "STATE__BOLD:%^s", who);
		borg_react(msg, buf);
		return;
	}


	/* Feature XXX XXX XXX */
	if (streq(msg, "The door appears to be broken."))
	{
		/* Only process open doors */
		if (ag->feat == FEAT_OPEN)
		{
			/* Mark as broken */
			ag->feat = FEAT_BROKEN;
		}
		return;
	}

	/* Feature XXX XXX XXX */
	if (streq(msg, "The door appears to be stuck."))
	{
		/* Only process non-jammed doors */
		if ((ag->feat >= FEAT_DOOR_HEAD) && (ag->feat <= FEAT_DOOR_HEAD + 0x07))
		{
			/* Mark the door as jammed */
			ag->feat = FEAT_DOOR_HEAD + 0x08;

			/* Clear goals */
			goal = 0;
		}

		return;
	}


#if 0

	/* Feature XXX XXX XXX */
	if (streq(msg, "This seems to be permanent rock."))
	{
		/* Skip granite walls */
		if (ag->feat == FEAT_WALL_EXTRA) continue;

		/* Only process walls */
		if ((ag->feat >= FEAT_WALL_EXTRA) && (ag->feat <= FEAT_PERM_SOLID))
		{
			/* Mark the wall as permanent */
			ag->feat = FEAT_PERM_SOLID;

			/* Clear goals */
			goal = 0;
		}

		return;
	}

	/* Feature XXX XXX XXX */
	if (streq(msg, "You tunnel into the granite wall."))
	{
		/* Skip granite walls */
		if (ag->feat == FEAT_WALL_EXTRA) continue;

		/* Only process walls */
		if ((ag->feat >= FEAT_WALL_EXTRA) && (ag->feat <= FEAT_PERM_SOLID))
		{
			/* Mark the wall as granite */
			ag->feat = FEAT_WALL_EXTRA;

			/* Clear goals */
			goal = 0;
		}

		return;
	}

#endif

	/* Feature XXX XXX XXX */
	if (streq(msg, "You tunnel into the quartz vein."))
	{
		/* Process magma veins with treasure */
		if (ag->feat == FEAT_MAGMA_K)
		{
			/* Mark the vein */
			ag->feat = FEAT_QUARTZ_K;

			/* Clear goals */
			goal = 0;
		}

		/* Process magma veins */
		else if (ag->feat == FEAT_MAGMA)
		{
			/* Mark the vein */
			ag->feat = FEAT_QUARTZ;

			/* Clear goals */
			goal = 0;
		}

		return;
	}

	/* Feature XXX XXX XXX */
	if (streq(msg, "You tunnel into the magma vein."))
	{
		/* Process quartz veins with treasure */
		if (ag->feat == FEAT_QUARTZ_K)
		{
			/* Mark the vein */
			ag->feat = FEAT_MAGMA_K;

			/* Clear goals */
			goal = 0;
		}

		/* Process quartz veins */
		else if (ag->feat == FEAT_QUARTZ)
		{
			/* Mark the vein */
			ag->feat = FEAT_MAGMA;

			/* Clear goals */
			goal = 0;
		}

		return;
	}


	/* Word of Recall -- Ignition */
	if (prefix(msg, "The air about you becomes "))
	{
		/* Initiate recall */
		goal_recalling = TRUE;
		return;
	}

	/* Word of Recall -- Lift off */
	if (prefix(msg, "You feel yourself yanked "))
	{
		/* Recall complete */
		goal_recalling = FALSE;
		return;
	}

	/* Word of Recall -- Cancelled */
	if (prefix(msg, "A tension leaves "))
	{
		/* Hack -- Oops */
		goal_recalling = FALSE;
		return;
	}


	/* Feelings about the level */
	for (i = 0; prefix_feeling[i]; i++)
	{
		/* "You feel..." (etc) */
		if (prefix(msg, prefix_feeling[i]))
		{
			strnfmt(buf, 256, "FEELING:%d", i);
			borg_react(msg, buf);
			return;
		}
	}
}



/*
 * Parse a message, piece of a message, or set of messages.
 *
 * We must handle long messages which are "split" into multiple
 * pieces, and also multiple messages which may be "combined"
 * into a single set of messages.
 */
static void borg_parse(cptr msg)
{
	static char len = 0;
	static char buf[1024];


	/* Flush messages */
	if (len && (!msg || (msg[0] != ' ')))
	{
		int i, j;

		/* Split out punctuation */
		for (j = i = 0; i < len-1; i++)
		{
			/* Check for punctuation */
			if ((buf[i] == '.') ||
			    (buf[i] == '!') ||
			    (buf[i] == '?') ||
			    (buf[i] == '"'))
			{
				/* Require space */
				if (buf[i+1] == ' ')
				{
					/* Terminate */
					buf[i+1] = '\0';

					/* Parse fragment */
					borg_parse_aux(buf + j, (i + 1) - j);

					/* Restore */
					buf[i+1] = ' ';

					/* Advance past spaces */
					for (j = i + 2; buf[j] == ' '; j++) /* loop */;
				}
			}
		}

		/* Parse tail */
		borg_parse_aux(buf + j, len - j);

		/* Forget */
		len = 0;
	}


	/* No message */
	if (!msg)
	{
		/* Start over */
		len = 0;
	}

	/* Continued message */
	else if (msg[0] == ' ')
	{
		/* Collect, verify, and grow */
		len += strnfmt(buf+len, 1024-len, "%s", msg+1);
	}

	/* New message */
	else
	{
		/* Collect, verify, and grow */
		len = strnfmt(buf, 1024, "%s", msg);
	}
}




/*
 * 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 (!auto_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 (!auto_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 (!auto_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 */
			auto_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 (death)
		{
			/* Oops */
			borg_oops("unexpected death!");

			/* Return */
			return (1);
		}

		/* Save the system random info */
		auto_rand_quick = Rand_quick;
		auto_rand_value = Rand_value;

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

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

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

		/* Restore the system random info */
		Rand_quick = auto_rand_quick;
		Rand_value = auto_rand_value;

		/* Hack -- allow stepping to induce a clean cancel */
		if (auto_step && (!--auto_step)) auto_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 color */
	use_color = TRUE;

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

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

	/* Allow wands to stack */
	stack_allow_wands = TRUE;

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

	/* Ignore inscriptions */
	stack_force_notes = TRUE;

	/* Efficiency */
	avoid_abort = TRUE;

	/* Efficiency */
	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 */
	auto_race = p_ptr->prace;
	
	/* Extract the class */
	auto_class = p_ptr->pclass;

	/* Extract the race pointer */
	rb_ptr = &race_info[auto_race];

	/* Extract the class pointer */
	cb_ptr = &class_info[auto_class];

	/* Extract the magic pointer */
	mb_ptr = &magic_info[auto_class];

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



/*
 * Initialize the Borg
 *
 * We should verify "legal" values for various things, since,
 * for example, when the "wall" symbols have been redefined,
 * the Borg may crash the machine.  XXX XXX XXX
 */
void borg_init_9(void)
{
	byte *test;


	/*** Initialize (system) ***/

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


	/*** Initialize (internal) ***/

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

	/* Refresh */
	Term_fresh();

	/* Mega-Hack -- Cheat a lot */
	auto_cheat_inven = TRUE;
	auto_cheat_equip = TRUE;

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

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

	/* Initialize */
	borg_init_1();
	borg_init_2();
	borg_init_3();
	borg_init_4();
	borg_init_5();
	borg_init_6();
	borg_init_7();
	borg_init_8();

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

	/* Prepare */
	borg_prepare();

	/* Refresh */
	Term_fresh();

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

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


/*
 * Hack -- forward declare
 */
extern void do_cmd_borg(void);


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


	/* 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 avoidances.");
		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;
	}


	/* Hack -- force initialization */
	if (!initialized) borg_init_9();


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


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

		/* Activate */
		auto_active = TRUE;

		/* Reset cancel */
		auto_cancel = FALSE;

		/* Step forever */
		auto_step = 0;

		/* Allowable Cheat -- Obtain "recall" flag */
		goal_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 */
		auto_active = TRUE;

		/* Immediate cancel */
		auto_cancel = TRUE;

		/* Step forever */
		auto_step = 0;

		/* Allowable Cheat -- Obtain "recall" flag */
		goal_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 */
		auto_active = TRUE;

		/* Reset cancel */
		auto_cancel = FALSE;

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

		/* Allowable Cheat -- Obtain "recall" flag */
		goal_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 "flags" */
	else if (cmd == 'f')
	{
		/* Get a "Borg command", or abort */
		if (!get_com("Borg command: Toggle Flag: ", &cmd)) return;

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

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


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

		/* Toggle */
		if (cmd == 'i')
		{
			auto_cheat_inven = !auto_cheat_inven;
			msg_format("Borg -- auto_cheat_inven is now %d.", auto_cheat_inven);
		}
		else if (cmd == 'e')
		{
			auto_cheat_equip = !auto_cheat_equip;
			msg_format("Borg -- auto_cheat_equip is now %d.", auto_cheat_equip);
		}
		else if (cmd == 's')
		{
			auto_cheat_spell = !auto_cheat_spell;
			msg_format("Borg -- auto_cheat_spell is now %d.", auto_cheat_spell);
		}
		else if (cmd == 'p')
		{
			auto_cheat_panel = !auto_cheat_panel;
			msg_format("Borg -- auto_cheat_panel is now %d.", auto_cheat_panel);
		}
	}


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

		/* Close the log file */
		if (auto_fff) my_fclose(auto_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 */
			auto_fff = my_fopen(buf, "w");

			/* Failure */
			if (!auto_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: ", auto_match, 70))
		{
			/* Cancel it */
			strcpy(auto_match, "");

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


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

		u16b mask;

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

			default: mask = 0x00; break;
		}

		/* Scan map */
		for (y = w_y; y < w_y + SCREEN_HGT; y++)
		{
			for (x = w_x; x < w_x + SCREEN_WID; x++)
			{
				byte a = TERM_RED;

				auto_grid *ag = &auto_grids[y][x];

				/* Given mask, show only those grids */
				if (mask && !(ag->info & mask)) continue;

				/* Given no mask, show unknown grids */
				if (!mask && (ag->info & 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 "avoidances" */
	else if (cmd == 'a')
	{
		int x, y, p;

		/* Scan map */
		for (y = w_y; y < w_y + SCREEN_HGT; y++)
		{
			for (x = w_x; x < w_x + SCREEN_WID; x++)
			{
				byte a = TERM_RED;

				/* Obtain danger */
				p = borg_danger(y, x, 1);

				/* Skip non-avoidances */
				if (p <= avoidance / 2) continue;

				/* Use yellow for less painful */
				if (p <= avoidance) a = TERM_YELLOW;

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

		/* Get keypress */
		msg_format("Avoidance value %d.", avoidance);
		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 < auto_kills_nxt; i++)
		{
			auto_kill *kill = &auto_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 < auto_takes_nxt; i++)
		{
			auto_take *take = &auto_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;

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

			/* Scan map */
			for (y = w_y; y < w_y + SCREEN_HGT; y++)
			{
				for (x = w_x; x < w_x + SCREEN_WID; x++)
				{
					byte a = TERM_RED;

					/* Verify flow cost */
					if (auto_data_flow->data[y][x] != i) continue;

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

					/* Count */
					n++;
				}
			}

			/* Nothing */
			if (!n) break;

			/* Get keypress */
			msg_format("Flow depth %d.", i);
			msg_print(NULL);

			/* Redraw map */
			prt_map();
		}
	}


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

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

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


	/* Command: debug -- fear of depth */
	else if (cmd == '_')
	{
		int i;

		/* Max depth */
		msg_format("Max depth %d:", auto_max_depth);

		/* Dump fear codes */
		for (i = 0; i <= auto_max_depth; i++)
		{
			/* Dump fear code */
			msg_format(" %d", auto_fear_depth[i]);
		}
	}


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


#else

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

#endif /* ALLOW_BORG */
