Skip to content

User Guide

Tom edited this page Jul 2, 2019 · 2 revisions

BrogueBot User’s Guide

BrogueBot is a modification which adds a control API to the roguelike game Brogue. It allows writing Lua programs which can inspect the game state, take actions, and hence play the game completely automatically.

Preface

In April 2019, I became quite interested in AI and machine learning after reading about OpenAI’s Dota-playing team of AI bots; specifically about the ability to develop strategy by reinforcement learning. One of my first thoughts was the tractability of teaching a model to play a roguelike, which require complex decision-making based on partial information, just like Dota 2.

I went looking for projects that were using AI to tackle roguelikes, and surprisingly I found RogueInABox, a complete Python toolkit for doing exactly that. However, the authors note in their paper that the agents trained are "still far from being satisfactory." So then I looked for typical, manually-programmed bots and found Rog-O-Matic and the Tactical Amulet Extraction Bot, which play Rogue and NetHack respectively, with quite admirable success.

BrogueBot began as my attempt to create a similar bot for Brogue, a game which is notable for its complex, environmental interactions despite few monster and item types. It provides a Lua interface which bots can use to play the game. It also suits modification for use with other interfaces, such as a machine learning model — all the game state harvesting is clear and in one place.

Running scripts

A BrogueBot script has the following basic structure:

require 'brogue'

function act()
    -- code here
end

The script is loaded once, at the beginning of each new game. Brogue then calls the Lua function act whenever the bot needs to make a decision. The script file is specified on the command line with the -b flag:

$ brogue -b mybot.lua

The -b flag causes the bot to completely take over user input. act is called whenever the game needs player input, and the function must generate events for the game to process. Alternatively, using the -R flag instead puts the bot in reporting mode, where it only runs once per turn and has no control over the game.

Tip
See brogue --help for other useful options to bypass the menus and start games on launch.

Concepts

The three main types of data available via the bot interface are level information, items, and creatures.

Each level of the dungeon is made up of a fixed-size grid of cells. Each cell is made up of four layers of tiles, and may contain a creature and an item. Cells are represented by a single integer, but there are functions to convert to and from coordinate pairs. Items and creatures are represented as tables containing information about the object.

Since BrogueBot is designed for playing Brogue, what it knows about the game state is restricted to that of a player. The API only provides information on visible cells, meaning cells that are either in line-of-sight (and visibility range) or revealed by clairvoyance or telepathy aura. It also receives limited information after using a scroll of magic mapping. Additionally, items and creatures on the level may be partially or fully known after using potions of detect magic or telepathy. The API implements a memory for the tiles on previously-visible cells.

Some information is given in the form of flags. A "union" of flags is an integer where each bit represents the binary state of some option or property. The bit positions are called flags. They can be tested and manipulated with bitwise operators; if flags & SOME_PROPERTY > 0 then …​ end, etc.

There are many constants for use with the API defined in the definitions.lua file, which is loaded when you require 'brogue'. The file is commented and should be considered part of the documentation.

The rest of this section will describe the tables used to store level, creature and item data.

Level tables

A level table represents the terrain of a dungeon depth. It has the following entries. A known cell refers to a cell which has been discovered or was revealed by a scroll of magic mapping.

dungeon
liquid
surface
gas

Tables, indexed by cell, containing the last-seen tile type on the dungeon, liquid, surface, or gas layer of that cell. Only known cells have entries. If a cell is known by use of a scroll of magic mapping instead of being seen, only the dungeon and liquid layers will be valid — the surface and gas layers will show as empty (0).

lastseen

A table, indexed by cell, containing the turn number on which that cell was last seen by the player. Only known cells have entries. If a cell is known by use of a scroll of magic mapping instead of being seen, it will have entry -1.

items

A table, indexed by cell, containing item tables of all items (seen and remembered) on this floor.

flags

A table, indexed by cell, containing the union of all cell flags of that cell. Only known cells have entries. If a cell is known by use of a scroll of magic mapping instead of being seen, only the MAGIC_MAPPED flag will be given.

upstairs
downstairs

The cell locations of the up and down staircases, if known.

Creature tables

A creature table represents the player or a monster. It has the following entries:

cell

Location of the creature.

emanation

(If the creature is known as a psychic emanation) Either the string "large" or "small", or if hallucinating, true.

Psychic emanations have no further entries.

health

Proportion of health remaining, between 0 and 1.

If the player is hallucinating, there are no further entries.

flags
bookflags

Unions of monster behaviour and bookkeeping flags.

hp
maxhp

Current and maximum hitpoints, respectively.

attackticks
moveticks

The number of ticks taken to attack and move. One turn is 100 ticks.

accuracy
defense
mindamage
maxdamage

Combat stats of the creature. These values take into account any status effects and equipped items.

weakness

Strength penalty caused by the weakness status. This value is effectively subtracted from the creature’s strength.

poison

Amount of damage taken per turn by poison.

statuses

A table, indexed by the STATUS_* constants, containing the number of turns remaining of that status effect. In particular, statuses[STATUS_NUTRITION] is the number of turns until starvation begins.

Monsters only
type

Monster type; kobold, jackal, eel, etc. One of the creature type constants.

state

The current monster state. Takes the value of one of the MONSTER_* constants.

abilities

A union of monster ability flags (MA_*) representing the abilities the monster knows.

abilityslots

Number of times the monster can learn from a corpse.

turnstoabsorb

(If learning from a corpse) Number of turns until an ability is learned.

Item tables

An item table has the following entries. If the item is on the ground and the player is hallucinating, only the cell, blessed and cursed entries are given.

category
kind

Category (weapon, staff, charm, …​) and kind (sword, tunneling, protection, …​) of the item. If the kind is unknown, kind is nil.

flavor

A value which compares equal between items of the same kind, regardless of whether they’re identified. This allows you to see if two potions are the same colour, or two scrolls have the same title, etc.

flags

Union of item flags (ITEM_*).

cell

(If on the ground) Location.

letter

(If in the pack) The inventory letter of the item.

blessed
cursed

true if the item is magic-identified as "good" (blue icon) or "bad" (red icon), respectively; nil otherwise.

Note
For equippable items, cursed here does not mean the item has "a malevolent magic lurking within it," i.e. that you will be unable to remove it. (That property is given by the ITEM_CURSED flag.) It just means the item is identified as having negative enchant level. Similarly, blessed does not indicate the existence of a runic.
enchant

(Enchantable items only) The enchant level, if identified; nil otherwise.

netenchant

(Enchantable items only) Like enchant, but also takes into account any strength bonuses/penalties.

killstoreveal

(Weapons only) Number of kills needed to identify the weapon.

turnstoreveal

(Armor and rings only) Number of turns to identify the item.

charges

(Staves and wands only)

maxcharges

(Staves only)

timesused

(Wands only)

turnstocharge

(Charms only)

Weapons and armor only
strength

Required strength level.

strengthmod

Bonus/penalty to enchant level due to excess/insufficient strength.

runic

false if we know there is no runic; true if we know there is an unknown runic; a runic constant (W_* or A_*) if we know the runic; nil if we don’t know whether there is a runic.

defense

(Armor only) The defense the player would obtain if they equipped the armor. If the enchant level is not known, it is assumed to be +0.

accuracy
mindamage
maxdamage

(Weapons only) The combat stats the player would obtain if they equipped the weapon. If the enchant level is not known, it is assumed to be +0.

basedefense
minbasedamage
maxbasedamage

Like above, but assuming the item is +0 and the player’s strength is exactly the item’s requirement.

Variables

In addition to the variables in this section, many constants are defined and documented in the file definitions.lua, which is loaded with require 'brogue'.

celldiffs

A table, indexed by direction, of relative cell offsets. In other words, cell + celldiffs[dir] is the cell adjacent to cell in direction dir. (Note that the top and bottom of the cell grid wrap when doing this, and the result may not be a valid cell.)

levels

A table, indexed by depth, of all visited dungeon level tables.

world

The level table of the current depth; levels[rogue.depth].

creatures

A table, indexed by cell location, of creature tables of all visible monsters on the current level. (Excludes the player.)

rogue

rogue represents the player and other game information. It is a creature table for the player character, with the following additional entries:

depth

Current depth; 1-40.

turn

Number of turns that have passed. Includes any turns spent paralyzed, unlike the number given in-game.

strength

Strength level, ignoring the effects of weakness.

stealthrange

Stealth range.

pack

Table, indexed by inventory letter, of items in the pack.

weapon

Item table of the currently-equipped weapon in the pack, if any.

armor

Item table of the currently-equipped armor in the pack, if any.

rings

"Set" of equipped rings; a table with indices of item tables and values of true. In other words, if ring is an equipped ring item table, rogue.rings[ring] will give true. Hence equipped rings can be iterated with for ring, _ in pairs(rogue.rings) do body end

Functions

Functions marked as actions provide input to the game. When act returns, the input is processed (which may advance turns), and then act is run again with the new game state. This means your act function should return after making one action. It may make more, but the state will be invalid after the first.

tocoords(cell)

Returns as two values the x and y coordinates corresponding to cell. Coordinates begin at 1, at the top left of the screen.

tocell(x, y)

The inverse of tocoords.

iscell(n)

Returns n if it is a valid cell, or false if not.

cellknown(cell)

Returns a Boolean indicating whether the cell is known, i.e. whether it is discovered or revealed by magic mapping. Only known cells have indices in level tables.

neighborhood(cell)

Returns a table, indexed by the direction constants, of cells adjacent to cell. Diagonals are included, so the table contains at most 8 elements (less at borders).

Example: Counting the number of moveable directions from a cell
local dirs = 0
for d, c in pairs(neighborhood(cell)) do
    if cellknown(c) and canstepto(cell, d) then dirs = dirs+1 end
end

tileflags(tile)

Returns a union of tile flags for the given tile type.

hastileflag(cell, flags)

Returns the name of a layer of cell which has a tile with any of the given flags, or false if none do.

stepto(direction)

(Action) Tries to move to the adjacent cell specified by direction, one of the direction constants.

canstepto(cell, direction)

Returns true if it is known possible to move from cell in the given direction, and false otherwise. Only terrain is considered; that is, the function checks the destination cell tile is passable and no diagonal-blocking tiles like walls would obstruct the move. By "known possible" we mean that if we have insufficient information to determine whether the move is possible, the function returns false.

hitprobability(defender[, attacker])

The probability attacker hits defender when attacking, where both are full creature tables. attacker defaults to rogue.

distancemap(goals, blockflags[, monstersblock])

Returns a table, indexed by known cells, containing the known pathing distance from that cell to the nearest cell in the table goals. If a cell is unreachable, it will have value UNREACHABLE. blockflags is a union of tile flags, and cells containing tiles with any of those flags will be considered impassable. (T_OBSTRUCTS_PASSABILITY is implicitly taken as a blocking flag and does not need to specified.) The optional monstersblock is a Boolean specifying whether non-allied monsters are also considered impassable. It defaults to false. (Damage-immune immobile monsters are always considered impassable.)

This function is restricted to player knowledge: if a cell is not visible, it will use the tiles it last remembers seeing to determine the flags, and undiscovered cells are considered impassable. Hence, whether a cell is returned as reachable or not may not reflect the truth.

nextstep(cell, dmap)

Returns a direction from cell of a path of minimal distance, according to dmap; effectively a step towards the nearest goal cell. Only possible steps, as determined by canstepto, are considered. If cell is unreachable, returns nil.

closer(dmap)

Returns a function which takes two known cells as arguments and returns one of minimal distance, according to the distance map dmap. Suitable for use with fold/reduce.

closerthing(dmap)

Returns a function which takes two creatures / floor items / tables with a "cell" index as arguments and returns one of minimal distance, according to the distance map dmap. Suitable for use with fold/reduce.

iskindknown(category, kind)

Returns a Boolean indicating whether the given item kind is identified. This is essentially a way of reading the discovered items screen. Categories with no kinds (gold, lumenstones, the amulet) will return true.

drop(item)

(Action) Drops item.

throw(item, cell)

(Action) Throws item towards cell.

equip(item)

(Action) Equips item. Raises an error if the item is not armor, a weapon or a ring.

apply(item, choice)

(Action) Applies item. The purpose of choice is to provide additional information if applying requires making a selection.

Scrolls
  • If you are applying an unidentified scroll, you must have defined the global variables onenchant and onidentify each as either an item table or a function returning an item table. If the applied scroll is revealed as one of these two kinds, the corresponding item is selected as its target.

  • If you are applying an identified scroll of one of the above two kinds, choice may be a single item table or function returning an item table. Alternatively, choice may be omitted and apply will work as above.

  • In all other cases, choice is unrequired and has no effect.

Staves and wands

choice must be a cell, which is used as the target if required.

message(string)

Displays string as a message in Brogue’s message log.