Video of Torchbearer in action:

(Apologies for the slight static and muffled voice!)

Project Summary:

The Torchbearer A.I. has changed somewhat from our previous idea, in that its algorithms are not necessarily the same. Instead of randomly running down the series of coordinates given to it, hoping to find a good coordinate to jump off of, the A.I. now knows that an optimal solution is n-coordinates large; thus, it tries some (if not all) of the n-coordinate combinations to see if it can come up with an optimal solution.

The goal of the A.I. is largely the same (in a grid nXn size, place just enough torches to light up the area), but two potential new goals include:

Of course, these goals are rather lofty, as there are many more variables to consider outside of our simple grid system (how far does the wall go to block light? Can we make multiple grids for the A.I. to accurately travel on?), and we still have some issues to iron out before then. Still, they are things to consider as we go along with the project.

Approach:

The Torchbearer A.I. determines the best locations to place torches in a grid by first determining the minimum amount of torches needed to light up the whole grid, then iterating over an n-sized combination of the valid coordinates of the grid, scoring each combination based on how many squares it left dark.

All of this is done with a brute-force method, iterating over the whole list of n-sized combinations. We are currently working on optimizing the algorithm by breaking the grids into smaller chunks (submodular optimization), but the algorithm works fine for grids sized 7 and below in terms of time taken. Given enough time, the A.I. can actually find all solutions to an nXn grid; it just takes a very long while.

Our baseline test – a 6x6 grid – works like this:

Evaluation

Our evaluation goals were straightforward:

  1. Can the A.I. navigate a given grid?
  2. Can the A.I. light up that grid properly?
  3. Can the A.I. navigate and light up the grid in a timely manner?

By utilizing code from CS 175’s Assignment #2, we were able to set up our agent to teleport to given coordinates (x, z) easily. With the “taxicab” function, we also set up an simple way to update the light levels of a current grid if a torch was placed at coordinate (x, z). Malmo’s agent functions allowed us to simulate our grids in the Minecraft space, having the agent look down and place a torch (in Creative mode) to visually represent our light levels.

We learned quickly that while the first and second terms were easily accomplished, the third was much, much harder to achieve. The algorithm we utilize takes up vastly larger amounts of time and memory for every two increments on the nXn square:

Eventually, our algorithm can solve that 8x8 square (in fact it is possible to solve by hand), but it will take an incredibly long time to do so (approximately an hour). Our issue is that it checks every solution and not the best ones (e.g. placing two torches directly next to each other).

images showing possible solution to 8x8 grid

While optimization is a major issue, there is no doubt that the program works. The function determining current lighting is simple, only having to iterate over nXn numbers and update them if they would grow brighter. Each test only takes about two seconds of real time, and most of that is on the agent taking a half-second to look down in the Minecraft client and Malmo’s “world_state waiting” function that is standard within Malmo projects (the line stating “while not world_state.has_mission_begun” that is in all Malmo projects).

Remaining Goals and Challenges

Our current goal is optimizing the algorithm. Mathematically the algorithm is sound, but each solution is checked one by one, which quickly becomes inefficient and time-consuming. Checking 1-sized combinations is efficient as we only have to worry about nXm positions (the m is an important distinction here, discussed below). 2-sized combinations (8x8 and 9x9) become daunting as all possible combinations become 2016 and 3240, respectively; and a 3-sized combination of a 10x10 grid has 161700 combinations to check!

A way to break down our problem, as mentioned above, is to use submodular optimization:

This process has to be followed 3 more times (each “corner” of the grid that fits a 7x7 square), for a total of (4 * (30 + 21 + 1)) = 204 solutions to check. Much easier than 1275, and much, much easier than 161700.

Grid 10x10: image of 10x10

Grid 10x10 with 7x7 removed: image of 10x10-7x7

Grids 3x10 and 3x7: images of 3x10 and 3x7