The Lost Woods - Enter Ganondorf

The king of the desert, and Link's arch nemesis, Ganondorf, has learned of the hero's rampant collecting of Heart Pieces, and that hardly aligns with his evil designs. Determined to stop the hero from progressing, the evil king has entered the Lost Woods himself, to collect Heart Pieces before Link can get them.

This link (1) contains a zip file, with a single file in the Input Format specified for this collection of problems, of 100 mazes.

  • Link and Ganondorf begin at different locations within the wood, Link as an 'L', and Ganondorf as a 'G'.
  • Link gets to move first, taking a single step, or remaining still if he chooses. If he touches an uncollected Heart Piece, it becomes his, permanently.
  • Ganondorf moves next, taking a single step, or remaining still if he chooses. If he touches an uncollected Heart Piece, it becomes his, permanently, and Link can never collect it.
  • This process repeats until Link cannot collect any more Heart Pieces.
  • Naturally, Ganondorf is a perfect logician, and will choose his steps optimally so as to minimize the number of Heart Pieces Link is able to collect.

Let S S be the sum of the score for each maze. We define the score to be the index of the maze (1-based) multiplied by the maximum number of Heart Pieces Link can collect despite Ganondorf's interference.

Find S \boxed{S} .


Example:

Input:

4
5 5
##.##
..G..
H###H
.###.
..L..
5 5
##L##
.....
H###H
.###.
..G..
5 5
##.##
..G..
H#.#H
.#.#.
..L..
7 4
##L.G##
H.#.#.H
#.....#
H.###.H

Output:

13

Output Explained:

In the first maze, Ganondorf can indefinitely mimic Link's movements left and right until he becomes adjacent to a Heart Piece, which he can then collect before Link can, and then go fetch the second one before Link as well. Though Link can prolong the opening indefinitely, he will never collect a single Heart Piece either way, so the score for this maze is 0 0 .

In the second maze, Link and Ganondorf are in opposite positions as the first maze, with Link set back one space to compensate for first-turn advantage. Despite this, Link cannot employ the same strategy Ganondorf did - if he did, Ganondorf could simply sit still indefinitely, and Link would never collect a Heart Piece, which is Ganondorf's optimal end result. Link must therefore sacrifice one Heart Piece to Ganondorf by going after the other one, making the score for this maze 1 1 times the index.

In the third maze, the situation is similar to the first maze, but now Link has a new avenue of approach. Though the path down the middle is longer towards each Heart Piece than the paths down the sides, by going up the middle Link forces Ganondorf to choose a single Heart Piece to defend, sacrificing the other to Link, making the score for this maze 1 1 times the index.

In the fourth maze, Link is equidistant from all four Heart Pieces, same as Ganondorf, so with first turn advantage, he can get at least one. However, no matter which path Ganondorf takes, Link will also be able to get a second Heart Piece, but not a third, if he reacts properly to Ganondorf's movements. The score for this maze is thus 2 2 times the index.

1 × 0 + 2 × 1 + 3 × 1 + 4 × 2 = 13 1 \times 0 + 2 \times 1 + 3 \times 1 + 4 \times 2 = 13


Details:

  • Link will always appear once per maze, as an 'L'.
  • Ganondorf will always appear once per maze, as a 'G'.
  • There will be at most five Heart Pieces per maze, as 'H'.
  • Open ground is given as '.'.
  • Impenetrable trees are given as '#'.
  • No other characters will be present in each maze.
  • Ganondorf plays optimally, with the explicit goal of minimizing the number of Heart Pieces Link collects. Both characters have perfect information at all times.
  • Link and Ganondorf can freely pass through each other.
  • 1 W × H 30 1 \leq W \times H \leq 30 in all mazes.

    (1) - This link currently points to my Dropbox account, since there is currently no way to upload non-image content to Brilliant. Admin, feel free to delete this footnote if this is changed.


This is a part of the collection entitled The Lost Woods , a series of problems in CS / Graph Theory.

Image Credit: http://zelda.wikia.com/wiki/Ganondorf


The answer is 10573.

This section requires Javascript.
You are seeing this because something didn't load right. We suggest you, (a) try refreshing the page, (b) enabling javascript if it is disabled on your browser and, finally, (c) loading the non-javascript version of this page . We're sorry about the hassle.

1 solution

Daniel Ploch
Jan 27, 2016

As the examples show, great complexity emerges from simple rules. Any solution that correctly computes the answer to this problem must somehow take into account infinite strings of moves that represent 'stalemate' behavior, so any sort of brute-force approach is highly unlikely to work. A number of heuristic approaches present themselves at first glance, but again, as the examples show, these seem unlikely to produce optimal results in all cases.

In order to determine optimal behavior on Ganondorf's part, and therefore the maximum score Link can achieve, we need to be able to consider all possible behaviors in a comprehensive manner. We'll do this by composing a directed graph modeling all potential game states. What makes a game state unique?:

  • Is it Link's turn, or Ganondorf's turn?
  • Where is Link positoned?
  • Where is Ganondorf positioned?
  • Where are all the remaining Heart Pieces?
  • How many has Link collected thus far?

Multiplying the cardinality of all of these pieces of information together, and declaring N N to be the number of Heart Pieces within a maze, we find that there are O ( 2 ( W H ) ( W H ) ( 2 N ) N ) = O ( W 2 H 2 N 2 N ) O(2 \cdot (WH) \cdot (WH) \cdot (2^N) \cdot N) = O(W^2 H^2 N 2^N) possible game states approximately speaking. Given the problem constraints, this amounts to about 300k states in the worst case, so any algorithm we attempt should be close to linear if it is to succeed in reasonable time!

Computing the game state graph is doable in linear time via BFS , by starting with the initial state, and using computation of all possible subsequent states due to any of Link's / Ganondorf's available moves, properly reducing the set of Heart Pieces, and/or incrementing Link's score when one is collected. Any state which has no Heart Pieces remaining is a game-over state, so we can stop searching from those. Once we have this game state graph, which is directed , and bipartite , but definitely not acyclic , we're ready to determine optimality.


To solve the problem, we will ask two generic questions about the game state graph, show that they can be used to recursively answer each other in finitely many iterations, and then show that these questions allow us to compute Link's maximum score. The algorithm presented is very similar to the minimax algorithm used in game theory. We can't use it directly because our game has more than 2 possible outcomes, and because the game state graph has cycles, and therefore cannot be modeled as a tree.

The first question is, "Given a set of candidate states one may traverse, including a start node, is it possible for Link to force the game to reach any one of a set of arbitrary target states?", and the second question is, "Given a set of candidate states one may traverse, including a start node, can Ganondorf prevent the game from ever reaching any one of a set of arbitrary target states?"

Now consider the first question. Let's consider the set of candidate nodes which have at least one out-edge pointing to one of the targeted states, and of course only candidate nodes for which it is Link's turn. If Link is to reach any of the targeted states, Link must force traversal to one of these nodes. If the start node is contained within the set, the answer is 'Yes' and we're done - likewise, if the set is empty, the answer is 'No', and we're done. Otherwise, we consider the complement problem: Of all of the nodes in this set, can Ganondorf indefinitely avoid traversal to any of them? If he can, then Link cannot force traversal to any of the target nodes, but if he can't, then Link certainly can.

Let's consider the second question! We consider the set of candidate nodes for which all out-edges point to one of the targeted states, and only candidate nodes for which it is Ganondorf's turn. If Ganondorf is to avoid all of the targeted states, then Link must not be able to force traversal to any of the nodes in this set, for if he does, Ganondorf will be forced to make a move that lets Link reach the target nodes. Similar logic follows. If the set is empty, the answer is 'Yes', and we're done. Otherwise, consider the compliment: Of all the nodes in this set, can Link force traversal to any of them? If he can, then Ganondorf cannot avoid the target nodes, but if he can't, then Ganondorf can.

This leads to the following pseudo-code:

ganon_must_avoid = emptyset()
def canLinkForce(candidate_states, start, target_states):
  mustReach = candidate_states.filter(at least one out-edge points to target_states)
  candidate_states -= mustReach
  ganon_must_avoid += mustReach
  if start in mustReach:
    return True
  else if mustReach is empty:
    return False
  else:
    return !canGanonAvoid(candidate_states, start, ganon_must_avoid)

def canGanonAvoid(candidate_states, start, target_states):
  mustAvoid = candidate_states.filter(all out-edges point to target_states)
  candidate_states -= mustAvoid
  if mustAvoid is empty:
    return True
  else:
    return !canLinkForce(candidate_states, start, mustAvoid)

In this way, from a set of target_states, nodes are examined in reverse order up the graph, and discarded from the candidate set as they are determined to be either a 'win' or 'lose' node for Link/Ganondorf. Since every method call must discard at least one candidate state to recurse, and, by using the inverted game state graph for efficiency, the in-edges of every state need only be considered once, the algorithm can be executed in linear time with the right data structures, since each game state is capped at 5 out-going, and in-coming edges.

Once we have these functions, it is trivial to determine Link's maximum score. For each score Link might obtain, simply ask:

canLinkForce(all states with score < target_score, start, all states with score = target_score)

And return the maximum score for which the above function call returns True.


Click this link to view the scores for each maze from the input in order, sans multiplication by the index. If you were not able to get the right answer, consulting these individual results may help you find the problem (or, if I got something wrong, the bug in my own solution!)

0 pending reports

×

Problem Loading...

Note Loading...

Set Loading...