π° Clockwork Guardian π€
π Tales from Eldoria
π‘οΈ Synopsis
In the heart of Eldoria’s Skywatch Spire, a treacherous challenge awaits! πͺοΈ The once-loyal Clockwork Sentinels π€ have turned against us, transforming the spire into a perilous maze. π± Our mission is to navigate through the hostile grid, avoiding the sentinels (1’s) and finding the shortest path to the exit (‘E’). πββοΈπ¨ Armed with the power of Breadth-First Search (BFS) π, we’ll explore every nook and cranny to determine the optimal escape route! πΊοΈβ¨
π Description
Picture yourself ascending the Skywatch Spire, a once-majestic tower now overrun by rogue Clockwork Sentinels. π°π€ Each level is a 2D grid, where open paths are marked as 0, hostile sentinels as 1, and the exit as ‘E’. π’ Starting from the top-left corner (0,0), our goal is to find the shortest sentinel-free path to the exit. π―
This is a classic shortest path problem on a grid, and BFS is our trusty ally! π€ By exploring cells in increasing order of distance from the start, BFS guarantees finding the shortest route. π§ Our task is to implement this pathfinding algorithm, navigating the grid step by step, until we reach the exit or determine that escape is impossible. β οΈ
π‘οΈ Skills Required
To conquer this clockwork labyrinth, you’ll need:
- π§ Solid understanding of graph traversal algorithms, especially BFS
- πΊοΈ Familiarity with grid-based pathfinding problems
- π² Comfort with 2D array manipulation
- π§ Ability to translate problem constraints into code
- π Attention to edge cases and boundary conditions
- π Clear and commented code to explain your thought process
π Skills Learned
Upon emerging victorious from the Skywatch Spire, you’ll gain:
- πͺ Confidence in applying BFS to solve shortest path problems
- π Enhanced ability to work with 2D grids and coordinate systems
- π Experience in efficiently tracking visited cells to avoid cycles
- π Improved code organization and documentation habits
- ποΈ Appreciation for the power of queue-based algorithms in pathfinding
- π¬ Ability to visualize algorithms step-by-step for better understanding and debugging
βοΈ Solving The Challenge
Let’s break down our BFS pathfinding approach:
π Understanding Breadth-First Search (BFS)
Think of BFS as exploring a maze by following this simple rule: “Check all paths that are 1 step long, then all paths that are 2 steps long, then 3 steps long,” and so on.

Imagine you’re standing at the entrance of a maze with multiple branching paths. Instead of going deep down one path before trying others:
- First, look at every path that’s exactly 1 step away
- Then examine every path that’s exactly 2 steps away
- Continue this pattern, exploring everything at distance 3, then 4, and so on
This is why we call it “breadth-first” – you explore the entire “breadth” of possibilities at each distance before going deeper.
The real power of BFS is that if you’re searching for something, you’ll always find it using the shortest possible path. This is because you systematically check all shorter paths before considering any longer ones.
π₯ Understanding the Input

The spire layout is a 2D grid, where:
- 0 represents a safe empty cell π©
- 1 represents a sentinel that must be avoided π₯
- ‘E’ marks the exit cell πͺ
- Our starting position is always (0,0) π
The grid is provided as a Python-style 2D list, like:
[
[0, 0, 1, 0, 0, 1],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 'E']
]
π Implementing BFS
Here’s our Python code to find the shortest safe path:
from collections import deque
def shortest_safe_path(grid):
# Find exit position
exit_position = None
rows = len(grid)
cols = len(grid[0])
for i in range(rows):
for j in range(cols):
if grid[i][j] == 'E':
exit_position = (i, j)
break
if exit_position:
break
# If no exit, no path possible
if not exit_position:
return -1
# Define possible movement directions
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
# Initialize BFS
queue = deque([(0, 0, 0)]) # (row, col, distance)
visited = set([(0, 0)]) # Track visited cells
# BFS until queue empty or exit found
while queue:
row, col, distance = queue.popleft()
if (row, col) == exit_position:
return distance # Exit found, return path length
# Explore four adjacent cells
for dr, dc in directions:
new_row, new_col = row + dr, col + dc
# Check if new position is valid
if (0 <= new_row < rows and
0 <= new_col < cols and
(new_row, new_col) not in visited and
grid[new_row][new_col] != 1):
queue.append((new_row, new_col, distance + 1))
visited.add((new_row, new_col))
# Queue exhausted, no path found
return -1
# Read grid from input and run pathfinding
input_text = input()
grid = eval(input_text)
result = shortest_safe_path(grid)
print(result)
Now, let’s understand the code step by step:
from collections import deque
def shortest_safe_path(grid):
- We import the
deque
(double-ended queue) class from thecollections
module, which provides an efficient way to implement the BFS queue. - We define the main function
shortest_safe_path
that takes the grid as input.
# Find exit position
exit_position = None
rows = len(grid)
cols = len(grid[0])
for i in range(rows):
for j in range(cols):
if grid[i][j] == 'E':
exit_position = (i, j)
break
if exit_position:
break
- We initialize
exit_position
toNone
and get the number of rows and columns in the grid. - We iterate through each cell in the grid using nested loops.
- If we find the exit cell marked as ‘E’, we store its position as a tuple
(i, j)
inexit_position
and break out of both loops.
# If no exit, no path possible
if not exit_position:
return -1
- If
exit_position
is stillNone
after the loop, it means there is no exit cell in the grid. - In this case, we return -1 to indicate that no path is possible.
# Define possible movement directions
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
- We define a list
directions
that contains four tuples representing the possible movement directions: up, right, down, and left. - Each tuple
(dr, dc)
represents the change in row and column when moving in that direction.
# Initialize BFS
queue = deque([(0, 0, 0)]) # (row, col, distance)
visited = set([(0, 0)]) # Track visited cells
- We initialize the BFS queue
queue
as adeque
with a single tuple(0, 0, 0)
, representing the starting position (0, 0) and a distance of 0. - We also initialize a set
visited
to keep track of the cells we have already visited, starting with the initial position (0, 0).
# BFS until queue empty or exit found
while queue:
row, col, distance = queue.popleft()
if (row, col) == exit_position:
return distance # Exit found, return path length
# Explore four adjacent cells
for dr, dc in directions:
new_row, new_col = row + dr, col + dc
# Check if new position is valid
if (0 <= new_row < rows and
0 <= new_col < cols and
(new_row, new_col) not in visited and
grid[new_row][new_col] != 1):
queue.append((new_row, new_col, distance + 1))
visited.add((new_row, new_col))
- We start the BFS loop, which continues until the queue is empty or we find the exit.
- In each iteration, we remove the leftmost element from the queue using
queue.popleft()
, which represents the current cell we are processing. - If the current cell matches the exit position, we have found the shortest path, and we return the distance.
- Otherwise, we explore the four adjacent cells by iterating over the
directions
list. - For each direction, we calculate the new row and column by adding the corresponding
dr
anddc
values to the current row and column. - We check if the new position is valid by ensuring it is within the grid boundaries, has not been visited before, and is not a sentinel (value 1).
- If the new position is valid, we append it to the queue along with the updated distance (incremented by 1) and mark it as visited by adding it to the
visited
set.
# Queue exhausted, no path found
return -1
- If the BFS loop completes without finding the exit, it means the queue was exhausted, and no path exists.
- In this case, we return -1 to indicate that no safe path was found.
# Read grid from input and run pathfinding
input_text = input()
grid = eval(input_text)
result = shortest_safe_path(grid)
print(result)
- We read the grid as a string from the user input using
input()
. - We convert the input string to a 2D list using
eval()
and store it in thegrid
variable. - We call the
shortest_safe_path
function with thegrid
as an argument and store the result in theresult
variable. - Finally, we print the
result
, which represents the length of the shortest safe path or -1 if no path exists.

π Triumph in the Skywatch Spire and Beyond! π
Congratulations, brave adventurer! π Your unwavering determination and sharp programming skills have led you to conquer the treacherous Skywatch Spire and outsmart the rogue Clockwork Sentinels. πͺ The power of Breadth-First Search has illuminated the path to victory, guiding you through the twists and turns of this challenging quest. πΊοΈβ¨
As you stand tall atop the spire, the panoramic view of Eldoria stretches out before youβa testament to the incredible journey you’ve undertaken. π This CTF has not only tested your technical prowess but also immersed you in a captivating narrative that brought the world of Eldoria to life. ππ‘οΈ
Through the Clockwork Guardian challenge, you’ve honed your skills in graph traversal, grid-based pathfinding, and efficient algorithm design. 𧩠But more than that, you’ve experienced the thrill of being a hero in a fantastical realm, facing formidable foes and emerging triumphant. π‘οΈπ
As we conclude this epic blog post, I can’t help but feel a sense of accomplishment.
πΊοΈ Ready for More Adventures?
Want to explore more Cyber Apocalypse 2025 writeups? Check out my other solutions here!