Connecting My Maze Visualizer to Redis
I had two C++ projects that worked well on their own: one generated and solved mazes, while the other stored key-value data. Connecting them through TCP made both projects feel much more like parts of a real system.
The problem was not the algorithm
My maze visualizer generates random mazes using recursive backtracking, then runs A* and Dijkstra side by side. It shows how each algorithm explores the grid, which path it finds, and how their behaviour differs.
There was one limitation that kept bothering me: every maze existed only in memory. The moment I closed the visualizer, that maze was gone. I could generate another one, but I could not reopen the exact layout I had just tested.
I had also built a minimal Redis-compatible server in C++ some time earlier. It could receive commands over TCP, store key-value pairs, and persist its data to disk. The two projects seemed unrelated at first, but the missing persistence in the visualizer gave the server a practical job.
Keeping both projects independent
I did not want to merge the Redis server into the maze project. That would make the integration easier, but it would also create tight coupling: the visualizer would depend on internal server classes, data structures, and implementation details.
Instead, I added a small client to the visualizer using POSIX TCP sockets. The visualizer only knows an address, a port, and the Redis Serialization Protocol. The server does not know what a maze is. It simply receives a key and a string value, exactly as it would from any other client.
This boundary became my favourite part of the feature. I could stop either program, rebuild it, or change its internals without changing the contract between them.
Turning a maze into a string
A generated maze lives in memory as a grid of cells. Each cell stores bit flags describing information such as which walls are open. A pointer to that grid is meaningful only inside the running process, so saving it required a portable representation.
I chose a deliberately simple text format: the maze dimensions come first, followed by the numeric bitmask for every cell.
19,8|18,26,10,12,...
The comma separates numbers and the vertical bar separates metadata from cell data. It is not a sophisticated file format, but it is compact, easy to inspect, and straightforward to parse. More importantly, it contains everything needed to rebuild the original grid.
Saving with SET
When I press P, the visualizer serializes the current maze, opens a TCP connection to my server, and wraps the result in a RESP SET command under the key maze:last.
*3
$3
SET
$9
maze:last
$...
19,8|18,26,10,12,...
RESP includes the number of command elements and the exact byte length of each string. That length matters: the server does not need to guess where the serialized maze ends, and the value can safely contain punctuation used by my own format.
Try the two projects working together
The terminal and maze visualizer below share the same maze:last value. Generate a maze, inspect it with GET maze:last, replace it through the terminal, or use the visualizer’s save and load controls.
Inspect the stored maze
Try GET maze:last, KEYS, or DEL maze:last. The terminal and visualization update the same browser-persisted key-value store.
ZaidDB browser sandbox · local session
Type HELP to see available commands.
Pathfinding + Redis / Shared browser sandbox
Watch Dijkstra and A* search side by side.
Generating maze…This matches the C++ project’s 19×8 cell bitmasks. Every new maze is serialized and auto-saved to maze:last, so the terminal above can retrieve the exact same data.
Loading and rebuilding
Pressing L follows the path in reverse. The client sends GET maze:last, reads the RESP bulk-string response, and passes the stored value to the deserializer.
I treat data from the server as untrusted input, even though both programs are mine. Before modifying the active maze, the visualizer checks the dimensions, confirms that every token is numeric, verifies the expected number of cells, and rejects values that do not fit the allowed cell representation.
Only after the full value passes validation does it replace the current grid. This avoids leaving the program with a half-loaded maze when a response is incomplete or malformed.
Persistence across both programs
The integration became more useful because the Redis-compatible server already saves its key-value data to disk. The maze therefore survives more than a visualizer restart. I can close the visualizer, restart the server, launch the visualizer again, press L, and recover the same layout.
That small interaction made persistence feel less abstract. It was no longer just a server feature verified by a test command; it preserved something visible that another program depended on.
What connecting them taught me
The amount of new code was not huge, but it forced several ideas to work together:
- Serialization turns an in-memory structure into a portable representation.
- TCP provides the connection, while RESP defines how both programs understand the bytes.
- Input validation protects the visualizer from corrupt or incomplete state.
- Persistence becomes more meaningful when another application actually relies on it.
- A stable protocol lets two systems remain independent while still being useful together.
Individually, one project generated and solved mazes while the other stored key-value data. Connecting them gave the Redis server a real client and gave the visualizer memory beyond a single process. More than anything, it showed me why combining small systems can teach more than adding another isolated feature to either one.