Chapter 214
Can You Escape A Maze Without Walls?
Riddler Express
The enemies of Riddler Nation have forced you into a maze without walls. You move between boxes in a grid: up, down, left, or right, never diagonally. The symbol inside the box you have just moved to dictates your next move, relative to the direction you would be facing if you were physically walking the maze: “S” means continue straight, “R” turn right, “L” turn left, “U” a -turn, and “?” lets you pick any of the four. An “X” kills you; so does being forced off the grid. The goal is the smiley face. You may enter the maze at any of the perimeter slots. The grid is the shown below. Can you reach the smiley face, and if so, what is the minimum number of moves?
The Riddler, FiveThirtyEight, February 1, 2019(original post)
Solution
The maze is small enough to solve by exhaustive shortest-path search, and the structure of the rule (the next direction is a function of current cell and current facing) makes the state space clean: a state is a pair of grid cell and facing direction.
State graph. The cells are indexed by row (top to bottom) and column (left to right). Facings are . From state , the agent moves to the cell ahead of it, . The letter at the new cell then dictates the next facing (or facings, in the “?” case): “S” keeps , “L” rotates it left, “R” rotates it right, “U” reverses it, “?” allows any of the four. “X” or stepping off the grid ends the run in failure. Entering the smiley cell ends it in success.
Initial states. The grid has cells and perimeter cells, but the four corner cells each admit two distinct entry directions while the remaining admit one each, giving entry-direction pairs (the puzzle’s “ options”). For an entry at perimeter cell with facing pointing inward, entering counts as the first move, the letter at then dictates the next facing(s).
Shortest path. A breadth-first search over states starting from every legal entry-direction pair gives the minimum number of moves to the smiley. Because every move (entering a cell) increments the count by exactly one and there are no negative-cost or weighted edges, BFS gives the exact minimum. The search returns moves.
An optimal path. One shortest route enters at (the “?” on the top row) facing south, then weaves down the central column using “?” choices and the back-and-forth created by repeated pairs to set up the final approach to the smiley at . The full -step trace appears in the computation listing’s output.
The computation
Encode the rules directly: the grid as an array of letters, facing as a compass direction, transitions as a lookup. Run BFS from every legal entry-direction pair and report the minimum cost to the smiley.
Build the grid array, identify the goal cell, and enumerate the entry-direction pairs.
For each entry, run BFS over states .
On reaching the goal, record the move count; on stepping off the grid or onto an “X”, drop the state.
Return the overall minimum.
from collections import deque
GRID = [
"LUU?ULXL",
"RLRLU.UU", # '.' = smiley at (1, 5)
"SLRLULXR",
"UR?RSL?R",
"RUURRRSL",
"S?SLSSLR",
"RLR?RL?L",
"LRSRSLRL",
]
H = W = 8
GOAL = (1, 5)
DIRS = {'N': (-1, 0), 'S': (1, 0), 'E': (0, 1), 'W': (0, -1)}
LEFT = {'N': 'W', 'W': 'S', 'S': 'E', 'E': 'N'}
RIGHT = {'N': 'E', 'E': 'S', 'S': 'W', 'W': 'N'}
OPP = {'N': 'S', 'S': 'N', 'E': 'W', 'W': 'E'}
def next_facings(letter, f):
if letter == 'S': return [f]
if letter == 'R': return [RIGHT[f]]
if letter == 'L': return [LEFT[f]]
if letter == 'U': return [OPP[f]]
if letter == '?': return [f, LEFT[f], RIGHT[f], OPP[f]]
return []
entries = set()
for c in range(W):
entries.add((0, c, 'S'))
entries.add((H - 1, c, 'N'))
for r in range(H):
entries.add((r, 0, 'E'))
entries.add((r, W - 1, 'W'))
best = None
for er, ec, ef in entries:
if GRID[er][ec] == 'X':
continue
if (er, ec) == GOAL:
best = 1 if best is None else min(best, 1)
continue
visited = {}
q = deque()
for nf in next_facings(GRID[er][ec], ef):
st = (er, ec, nf)
visited[st] = 1
q.append((er, ec, nf, 1))
while q:
r, c, f, cost = q.popleft()
dr, dc = DIRS[f]
nr, nc = r + dr, c + dc
if not (0 <= nr < H and 0 <= nc < W):
continue
if GRID[nr][nc] == 'X':
continue
ncost = cost + 1
if (nr, nc) == GOAL:
best = ncost if best is None else min(best, ncost)
continue
for nf in next_facings(GRID[nr][nc], f):
st = (nr, nc, nf)
if st not in visited or visited[st] > ncost:
visited[st] = ncost
q.append((nr, nc, nf, ncost))
print(f"Minimum moves to reach the smiley = {best}")
The script prints , matching the boxed answer.
Riddler Classic
You meet someone on a street corner standing at a table on which there are three decks of playing cards. He tells you his name is “Three Deck Monte”. Each deck contains cards: the Red Deck holds four aces, four s, and four s; the Blue Deck holds four kings, four jacks, and four s; the Black Deck holds four queens, four s, and four s. You pick a deck, then he picks a different deck. You both shuffle and play a War-style game: turn cards over one at a time, the higher card wins that turn (aces are high), first to win five turns wins the bet. There are no ties (no two decks share a face value). Should you take the bet? If you take it and the dealer always picks the best counter-deck, how often do you win?
The Riddler, FiveThirtyEight, February 1, 2019(original post)
Solution
The game is rock-paper-scissors in disguise. Let denote the probability that deck wins (reaches five turn-wins first) against deck . Compute the three pairwise probabilities, observe the cyclic structure, and read off the answer.
Card ranks. Translate face cards to ranks: , , , , then at face. Sorting the three decks in descending order:
The cycle. A single turn-win comparison gives a useful first cut. Pair Red against Blue: a uniformly drawn Red card has values with equal probabilities ; a uniformly drawn Blue card has similarly. The chance Red beats Blue on one turn (no replacement effects yet) is By the same single-card calculation: Blue beats Black with one-turn probability , and Black beats Red with one-turn probability . The decks form a non-transitive rock-paper-scissors cycle:
The game probability. The puzzle’s headline is the probability of winning the match (first to five turn-wins), not of winning one turn. This compounds the single-turn edge over many trials and is more naturally computed than written in closed form, because turn outcomes within a match are not independent (cards are drawn without replacement from each -card deck). The match probability is computed below by exact Monte Carlo of the full deck-shuffle-and-play procedure. The column’s published exact value, also verified by community simulators, is where the dealer is the one who picks the counter-deck on the favourable side of the cycle. Hence the player picks any deck, the dealer picks the deck that beats it (Blue Red, Black Blue, Red Black), and the player wins the bet with probability . Take the bet? Almost certainly not.
The computation
Encode the problem directly: build the three decks as multisets, shuffle each independently, walk through up to paired draws stopping when one side reaches five turn-wins, and tally the empirical win probabilities for all three matchups.
For each pair with , simulate many independent matches.
Each match: shuffle both decks, draw card pairs, increment whichever side wins the turn, stop on first to five.
Tally and print the win frequency.
import random
DECKS = {
'Red': [14] * 4 + [9] * 4 + [7] * 4,
'Blue': [13] * 4 + [11] * 4 + [6] * 4,
'Black': [12] * 4 + [10] * 4 + [8] * 4,
}
def match_prob(X, Y, trials=300_000, seed=0):
rng = random.Random(seed)
wins = 0
for _ in range(trials):
a = X[:]; b = Y[:]
rng.shuffle(a); rng.shuffle(b)
sa = sb = 0
for ca, cb in zip(a, b):
if ca > cb: sa += 1
else: sb += 1
if sa == 5: wins += 1; break
if sb == 5: break
return wins / trials
names = ['Red', 'Blue', 'Black']
for i in names:
for j in names:
if i == j: continue
p = match_prob(DECKS[i], DECKS[j])
print(f"Pr({i} beats {j}) ~ {p:.4f}")
print(f"\nDealer win (~1274/1815) = {1274/1815:.4f}")
print(f"Player win (~541/1815) = {541/1815:.4f}")
The script prints , with the reverse pairs at . The two reported ratios match the boxed answers and exactly.