Back to Blog
·Jan Tyl·39 min read

Shadows of Prague: six AI minds, two rival teams, and the journey from blocks to game characters

Ten games, six independent AIs, and real Prague streets. In four matches between different models, the stronger model won every time. Yet the best police tactic was to stop running. What did this teach me about memory, cooperation, rules, and turning blocks into game characters?

Shadows of Prague: six AI minds, two rival teams, and the journey from blocks to game characters

An artistic portrayal of the Shadows of Prague characters.

“I'm in the Northern Passage delivering the second seal. We're going to win!” Hugo announces.

Meanwhile, detective Viktor is somewhere else entirely. He knows the opponents have appeared in the north. He has even just told his teammates. Yet he waits at Sulická.

The Shadows win.

This is not a scripted ending. Both statements emerged in one of my experiments. Each character had its own observations, messages from teammates, diary, and language-model calls. The game confirmed who actually reached each location and what they delivered. I am the only human running this experiment. AI assistants help me with programming, graphics, and analysing the records.

What interests me is the gap between “I know what's happening” and “I take the right action because of it.”

I eventually ran ten AI games for roughly CZK 67 in model calls. In four matches between different models, the stronger model won every time. But the most interesting police strategy was not a spectacular chase. It was deciding to stay put.

The Second Seal: fifteen seconds from a tense game

Oskar flees east. Nora tries to cut off Ela in the south. But the second seal is almost home. I turned decisions from game nine into a short thriller with Czech dialogue, music, and sound effects generated directly by Kling 4.

Shadows of Prague: The Second Seal. A cinematic reconstruction of turns 11 to 15, with shortened dialogue from the records. The audio remains in Czech. Open the video separately.

I first created three keyframes. They established the characters, clothing, and lighting for the video. This is a cinematic interpretation of actual game decisions, not footage of the prototype's graphics. The sound is part of the original generated video, with no added dubbing.

Oskar: change direction while there is still time

Oskar holding a seal in a Prague street at dusk, with Nora approaching behind him

“Nora is behind me. I'm heading east.”

In turns 11 and 12, Oskar warns his teammates and actually changes course through the park towards the eastern exit. This time, the message and the movement tell the same story.

Nora: the right warning does not guarantee an interception

Nora using a radio to request an interception on the southern escape route

“Ela is running south. Cut her off!”

The line comes from the team's warning in turn 13. The police identified the threat but could no longer stop Ela in time.

Ela: the final move is hers

Ela placing the winning second seal on a game receiver beside a low wall

“I'm delivering the second seal. We win.”

In turn 15, the engine confirmed both the delivery and the Shadows' victory. This was no longer wishful thinking in a diary. It really happened in the game.

What happens when AI stops merely discussing things and starts competing?

In City of Ghosts, I sent six agents into real Prague streets. They searched for fragments of memory, exchanged information, and summoned the dawn together.

This time I wanted to try something different: two teams with opposing goals. The same neighbourhood, but a different reason to head outside.

The result was Shadows of Prague, another standalone Elements experiment. Three detectives try to stop three Shadows. The Shadows must collect two of three game seals and take them out through one of four exits.

There are no real banks or burglary plans involved. I designed the seals, rules, and story with AI assistance. The underlying roads and building footprints from OpenStreetMap around Krč and Lhotka are real. The façades and other scenery are an artistic interpretation of the city.

This neighbourhood was not randomly generated

Here is the same area on a real map: Alšovy sady, Zálesí, Sulická, Na Větrově, and the surrounding streets in Prague's Lhotka and Krč districts. The game takes its street network and building footprints from OpenStreetMap. The bends, junctions, and arrangement of houses let you recognise where the characters are moving.

To help follow individual games, I prepared a map of all twelve game locations. It uses the engine's coordinates and the same street network as the game. North is at the top, and the scale helps you estimate distances.

Map of Shadows of Prague with all 12 precisely positioned locations: Alšovy sady, the Veverka memorial, Sulická, and four exits

Gold markers are seal locations, turquoise markers are exits, and grey markers are other landmarks. Open the full-size map. Map and footprints: © OpenStreetMap contributors, ODbL. The same neighbourhood on a conventional map.

When diaries mention Alšovy sady (01), they mean the location by the Mikoláš Aleš monument. Veverka (02) is the memorial to the fallen Václav Veverka. Sulická (03) denotes the Second World War memorial. Game markers sit on the nearest nodes of the agents' walking network, not precisely at the centre of each statue or plaque.

The Western, Eastern, and Northern Passages and the Southern Junction are the four places where seals can be delivered. These names, like Reading Room or Little Square, are game labels. The coordinates and surrounding streets come from the map; the mission names are part of my scenario.

Below is the game version of the same neighbourhood, viewed at an angle. The streets and house placements have a real map foundation; façades, roof details, lighting, and game locations are stylised. This is not a photographic reconstruction. “Reading Room,” for example, is a game location, not a verified institution at that address.

Early map view of Shadows of Prague showing the positions of six agents

The early game map. Characters are deliberately enlarged in the overview so you can find them in the neighbourhood.

From the map down to the pavement

This is what the same neighbourhood now looks like in the browser. For the new shot, I steadied the camera and adjusted the daylight to make the streets, roofs and buildings easier to read. The footprints still come from the real map of Prague.

The real street layout of Krč and Lhotka in the Shadows of Prague 3D engine

A new capture directly from the engine. Map data: © OpenStreetMap contributors, ODbL. Facades and roofs are stylised.

Here is Nora walking between the houses. This is an eleven-second Full HD recording of the running prototype. The camera follows her from the front and side, showing her steps, arm movement and shadow on the pavement.

Nora out for a walk. A technical demonstration using the same renderer and character model as the game. I set the route and camera in advance for filming; this was not another AI match. Open the video separately.

Under the hood, every frame is rendered from a 3D scene. Three.js uses WebGPU to draw the map, materials and lighting, while an animated skeleton deforms the model exported from Blender. The pavement moves beneath her feet because the character actually travels through the scene. That makes it possible to watch her from another angle or pause mid-stride. Recording this footage added no paid model calls.

Six people, six independent decisions

In the initial cast, Ada, Viktor, and Nora are the detectives. Ela, Hugo, and Oskar oppose them. Later tests also swap the teams. In the first six paid experiments, everyone used Gemini 3.5 Flash Lite. In the next two games, I gave one side Gemini 3.8 Flash, then swapped the models between sides. Finally, I added two matches between Gemini 4 Argon and Gemini 3.8 Flash. The results are below.

The model receives its location, state, available actions, teammates' messages, and part of its own history. It cannot see opponents' private diaries. Local visibility has a limited range. Occasionally a public clue reveals the area where a Shadow was located.

Each model then returns three things: an action, a message to its own team, and a diary entry. The engine prepares all decision inputs from the same world state and evaluates the results together afterwards. A faster API response does not confer a head start on the street.

A normal turn allows up to 130 metres of movement, or 221 metres with a dash token. Routes follow the map network rather than cutting straight through houses. If a detective gets close enough to a Shadow during the same part of a turn, the engine confirms a capture. Saying “I caught him” is not enough.

The police who wanted to deliver the seals themselves

The first paid round produced a rather funny mistake. Some detectives tried to collect or deliver seals themselves. They seemed to understand part of the game world while forgetting which side they were on.

Of 39 police decisions, 13 attempted these prohibited actions. The game rejected them. To a viewer, this would look like awkward standing around and confused conversation.

It reminded me of Obinn from the first Czech season of The Traitors and his misunderstanding of the rules when using the first shield. He later discussed it in an interview with Vojta Kotek, from 34:06. The parallel is the game situation: a person or agent intends something but misreads what they are allowed to do. With a weaker language model, I particularly need to anticipate this when designing the game. A better model can help, but so can a better engine: a clear role, available-action options, and a comprehensible explanation when a move cannot be performed.

In the next version, each agent received a more explicit goal and a list of currently available actions. Detectives were told they neither collect nor deliver seals. In the second experiment, these errors fell to zero out of 18 police decisions.

That is useful, but it does not prove the model permanently learned anything. I did not change its weights. I improved the instructions and the interface between the model and the game. And the police still lost.

First two paid pilotsFirst experimentSecond experiment
WinnerShadowsShadows
Turns2113
API responses6136
Captures of Shadows40
Police attempts to collect or deliver seals130
Model-call costCZK 1.74CZK 0.94

Eliminating an invalid action is not the same as creating a good strategy.

“I'll help Ada.” Suddenly everyone runs the same way

In the second experiment, Viktor wrote:

“I'm at the Villa. The Shadows have escaped or moved on. I'm heading towards the park to help Ada close off the area.”

That sounds reasonable. But when several people independently interpret “helping” the same way, they may all chase one old clue. Another route remains open.

The Shadows had a similar problem. At one point, both Hugo and Oskar claimed to have a seal. In reality, Hugo had it. The engine did not allow Oskar to create a duplicate merely by writing about one in his diary.

Convincing conversation is not yet cooperation. Cooperation shows up in how a team divides tasks, reacts to actual outcomes, and avoids getting in its own way.

This is also why I enjoy these small worlds. In ordinary chat, we may believe a persuasive explanation. In a game, we can check whether a character really is at the exit carrying the right object.

Memory: what the game knows and what a character merely believes

I store the world's foundation in structured JSON files: positions, carried seals, captures, teams, time, results, and API costs. Events are recorded in JSONL, a file containing one JSON record per line.

Diaries are different. They are text created by each character, also stored in JSON in this experiment. They are not automatically true. Markdown would be a pleasant reading format, but a filename extension does not improve memory.

The new version therefore separates:

  • Engine-confirmed outcomes: what a character actually did, what it carries, and whether it is detained.
  • Its own diary: interpretations, assumptions, and reflections.
  • Team plans: what a teammate intended on a particular turn.
  • Observations and clues: what was visible and how old that information is.

If a diary says “I have the seal” while the current state shows empty hands, the game state takes precedence. And a teammate's promise several turns ago to guard the north does not confirm they are still there.

First an image, then actual characters

This original concept set the mood: six distinct people, two sides, and an evening in Prague. I want to keep it alongside the newer cover image because it shows where I started. But going from a beautiful drawing to a model that can be rotated and animated takes more work.

Original concept art of the six Shadows of Prague heroes

The original concept. What follows are actual development stages: a geometric figure, mesh and materials in Blender, and the finished model in a browser.

How I turned blocks into people with AI

The most direct feedback during development was simple: the characters were ugly.

They were initially assembled in code from basic shapes. With AI help, I added collars, pockets, fingers, shoes, and hairstyles. That was enough to test functionality quickly, but not for a close-up face. Another little ball in place of a nose would not solve it.

Original procedural Nora character built from simple geometric shapes

The original Nora. A functional figure for initial tests, but a poor foundation for a human face.

Instead of attaching more primitives, I used Microsoft Rocketbox, a library of rigged human characters under the MIT licence. I selected six different models and processed them in Blender with AI assistance. AI did not create the base geometry and textures from scratch. My work with AI involved selection, conversion, material adjustments, animation integration, and bringing them into my game. Model source and licence.

Six new characters in the actual Shadows of Prague 3D studio

Ada, Viktor, Nora, Ela, Hugo, and Oskar after conversion. A real live 3D view, not a repainted screenshot.

What does that conversion involve?

First, the face. Instead of a few smooth shapes, the game gets a continuous mesh defining cheeks, eyelids, nose, and mouth. Skin textures cover it. Hair has its own geometry and transparent parts that the renderer must handle correctly.

The same Nora model in Blender, with polygon mesh on the left and textures and materials on the right

The same Nora in Blender: polygon mesh on the left, textures and lighting on the right. A study render of the source character.

Then, materials. Colour is not enough. Skin, fabric, hair, and metal reflect light differently. Normal maps add small surface variations without turning every wrinkle into a separate polygon. In this version, colour textures are 1,024 × 1,024 and normal maps 512 × 512 to keep browser loading reasonable.

Next, the skeleton and movement. A rig is a skeleton defining how a mesh deforms when an elbow bends or a character steps forward. Copying bone rotations from one file to another is not enough. Conversion required matching bone orientations and transferring animation onto the resulting skeleton. Otherwise a character stays in a T-pose or twists unnaturally.

Finally, GLB export and an in-game check. GLB is a practical package for web 3D: the model, skeleton, materials, textures, and animations can all live in one file. Editable source files remain in Blender for further work.

The new Nora with a textured face and clothing directly in the running game

Nora after the change. She now has actual facial geometry, hair texture, and fabric. The city remains simpler than the characters.

I also added photographic surface materials for asphalt and some masonry, normal and roughness maps, benches, and a more nuanced sky. The materials come from Poly Haven, under CC0. That does not turn a simple map into photorealistic Prague. There is still plenty of visible work to do on the streets and houses.

What about Claude Code, Codex, WebGPU, or ray tracing?

An AI assistant is useful here as a programmer and model-processing helper. It can prepare a script to import six characters, fix texture links, configure materials, and export a consistent format. It can also look for skeleton and scale problems.

Blender has a Python API, making this work repeatable and checkable. There is also an official Claude connector, described by Claude Academy. For this conversion, I used Blender scripts directly, without an additional connector.

Adding the word “WebGPU” does not make an image beautiful. I need good models, materials, lighting, cameras, and a reasonably detailed environment. WebGPU is a way for browsers to work with the graphics card. By itself, it does not provide ray tracing, DLSS, or the quality of a big game with years of funding.

What matters for this experiment is that a new character is more than a prettier portrait card. The same 3D model walks around the map, has its own portrait, and can be viewed in a separate studio, including close-ups and a walking animation.

The agents themselves do not see this prettier scene. These games use textual JSON observations. Graphics help viewers follow the story; changes in decision quality must be traced to rules, input information, and model responses.

A better game needs better options, not just better minds

I also changed the rules and observations. The new version lets a player change destination during a long journey. Rerouting starts from the next nearby node on the actual route, so the player neither teleports back to the previous location nor cuts through houses.

The detectives received one shared exit closure for the entire game. Declaring it costs an action at that location. A warning comes first; deliveries through that exit are blocked for the next two turns. The closure itself catches nobody.

A Shadow can instead spend one action to falsify the area of its next public clue. The clue arrives at the normal time with no “this is a trick” label. It cannot fool direct local observation.

This creates real choices. Spend a turn preparing a decoy, or escape faster? Close a useful route now, or save the only token for the finish? Trust an old report, or guard somewhere the opponent must eventually visit?

Another agent independently critiqued the design. It pointed out that the four exits are not equally advantageous: some are much closer to particular seals. It also found that the original test seeds merely swapped identities at the same locations. I now vary the actual team placements too.

Automated tests check message privacy, movement distance, capture, seal conservation, closure timing, and the distinction between diaries and confirmed history. Separate scripted checks verify that either side can win. That does not prove equally capable AI players have exactly even odds.

Two police wins, two Shadows wins. But where matters

I ran the new version four times: two different starting layouts, each with the original teams and then the teams swapped. The model and victory conditions stayed the same. Every round began with empty diaries; no player received memories from a previous experiment.

New pilotsLayoutTeam assignmentWinnerTurnsAPI callsCost, CZK
3FirstDefaultDetectives11662.20
4FirstSwappedDetectives5300.89
5SecondDefaultShadows7401.23
6SecondSwappedShadows7421.31

Every game has a small story

For readers who do not want to go through entire diaries, I picked the key moments from each round. I verify deliveries and captures against the engine, not the characters' beliefs.

Game 1: The police chased victory using the other side's rules. Nora caught Hugo on the first turn, and the detectives eventually recorded four captures. The Shadows still won: Ela delivered the first seal on turn 9 and Oskar the second on turn 21. The police misunderstood their role. They tried thirteen times to collect or deliver seals, even though only their opponents could do that. On the final turn, Viktor was still promising a delivery for the police while the actual winner was going through the western exit. Captures worked; a shared plan did not.

Game 2: He knew where they were, then waited elsewhere. After the instruction changes, detectives stopped trying to take seals. Strategically, they still lagged behind. Ela delivered on turn 12, Hugo on turn 13, and the police caught nobody. Viktor was the clearest example: he correctly reported that the Shadows were in the Northern Passage while remaining at Sulická, waiting for them to arrive. He had the information but did not change his plan. Sequential deliveries worked for the Shadows; better rule knowledge did not save the detectives. Invalid JSON responses also appeared near the end, causing the engine to substitute waiting.

Game 3: One detective, three captures in one turn. Ela brought the first seal to the Northern Passage on turn 10. Hugo and Oskar accompanied her and waited there instead of securing the second seal. Viktor arrived one turn later, and the engine confirmed all three captures at once. The detectives won on turn 11. The Shadows completed one real task, but gathering the entire team around a single success cost them their next opportunity. This time, Viktor's movement achieved more than all their assurances of cooperation.

Game 4: Nora identified the problem. The fix came too late. After swapping sides, Ada, Viktor, and Nora played as Shadows. On turn 2, Nora warned: “We're all heading to Alšovy sady, which is unnecessary crowding.” They essentially all knew it, but postponed changing course. Ada collected the first seal on turn 4; only then did Nora redirect towards Sulická. On turn 5, Hugo caught Ada, Oskar caught Viktor, and Ela caught Nora. The detectives won without a single opposing delivery. The striking point was not ignorance but the gap between a correct diagnosis and timely action.

Game 5: South and north. Two seals at once. Ada detained Hugo on turn 2, but the Shadows' story did not end there. Ela took the Alšovy sady seal south, while Oskar carried the Sulická seal north. Both delivered on turn 7 and won. After his release, Hugo was returning for the remaining seal, but his teammates no longer needed help. Task division produced a concrete result: two different seals, two different exits. The detectives guarded other areas and could not stop both deliveries.

Game 6: New faces as Shadows, the same escape. The characters swapped sides again on the same layout. Ada and Nora took seals to the southern and northern exits, delivering both on turn 7. Viktor was carrying the third seal west, so the team was also pursuing another option. As a detective, Ela followed Ada but could not stop her in time; Oskar kept guarding Alšovy sady after its seal had gone. The Shadows won without a capture. Seeing the same side win after changing characters alerted me that starting positions might matter more than a hero's name.

The 2:2 result looks good, but the important detail lies elsewhere. The police won twice on the first layout; the Shadows won twice on the second. Swapping characters did not change the winning side. That is a clue for adjusting starts and routes, not proof of perfect balance. Four experiments cannot reliably identify the cause.

During these rounds, agents changed destination mid-journey sixteen times. They never once used the new exit closure or false clue. Adding a clever ability to the rules is not enough. I still need to test whether a model recognises when giving up immediate movement is worth a later advantage.

What if one team gets a stronger mind?

After six games, I tried strengthening the detectives once and the Shadows once. I chose Gemini 3.8 Flash. All three players on the selected side received it; their opponents stayed on Gemini 3.5 Flash Lite.

Both matches started on the same second layout with the same characters, rules, and empty diaries. Instructions stayed unchanged, and response speed did not affect movement. Both models had a 4096-output-token cap; Flash used low thinking and Lite minimal. These are the two specific configurations being compared.

GameSide using Gemini 3.8 FlashWinnerTurnsCapturesCost, CZK
7DetectivesDetectives28910.50
8ShadowsShadows712.11

Game 7: The stronger patrol found a way to wait out its opponents. Gemini 3.8 Flash controlled all three detectives, Lite all three Shadows. Ela delivered the first seal on turn 7, but the team never got the second. The detectives gradually divided the remaining locations: Nora guarded Veverka, Viktor Sulická, and Ada covered the surrounding area. The engine recorded nine captures, and the police won after 28 turns. Guarding specific objectives worked; the Shadows struggled to react to repeated captures. A rule advantage also emerged: a stationary guard at a seal can disrupt every collection attempt. Four invalid Lite JSON responses additionally became waits.

Game 8: The stronger Shadows divided tasks immediately. This time Ela, Hugo, and Oskar used Gemini 3.8 Flash. On the first turn, they chose three distinct locations: Ela Alšovy sady, Hugo Veverka, and Oskar Sulická. Ela then headed south, Oskar north. Both delivered and won on turn 7. Nora caught Hugo on that same turn, but it was no longer enough. Task division was visible in both messages and actual routes. It was not faster than the earlier Lite Shadows, however: they had also won in seven turns on this layout. The weaker detectives also produced invalid JSON three times.

On the last turn of game seven, Viktor reported:

“I'm standing right on Sulická and not letting the seal go. Ela is within range, Nora covers Veverka. One last turn until victory!”

This time his diary matched the actual game: he guarded a specific seal and relied on automatic capture. In the second comparison game, Oskar wrote on the very first turn:

“I'm going for the seal at Sulická, then taking it to the Northern Passage. Hugo, take Veverka or Alšovy sady. Ela should secure Alšovy sady.”

The stronger-model side won twice. That is an interesting result, not a measured win probability. Lite Shadows had already won in seven turns on the same layout. The biggest change was on the detective side. Even there, one experiment cannot establish the cause: the earlier control game had a lower 900-token output cap, and model responses are not deterministic.

Interface reliability matters too. Across these games, Lite produced 7 syntactically invalid JSON responses out of 89, Flash 0 out of 105. There might be an extra bracket or a missing quotation mark. The engine replaced such responses with waiting. That belongs in an agent's usability assessment, but it is not the same as tactical quality. The stronger model was not flawless either: detectives sometimes confused who was carrying a seal.

The stronger model exposed a weakness in my game

The detectives did not need an elaborate trick. They gradually started standing directly on the remaining seals. An arriving Shadow was automatically captured, and any just-collected seal was returned. Once one seal was gone, covering the remaining two was enough.

Afterwards, I independently checked this behaviour at all three locations without paid model calls. Guarding a seal is a very strong defence under the current rules. That does not mean the police can get there in time from every start. It does mean winning this match and being a generally better agent are two different things.

In the next version, I want to give attackers a genuine way to disrupt that defence, then repeat more matches with sides and starts swapped. I did not modify the rules during these measurements. Neither model used exit closures or false clues in the new experiments.

Argon versus Flash: does the stronger model win after swapping sides?

I then went one step further: Gemini 4 Argon against Gemini 3.8 Flash, once as Shadows and once as detectives. Each model controlled all three characters on its team. Both games began on the same second layout with empty diaries, unchanged rules, and identical instructions. Both used low thinking and a 4096-output-token cap. I also checked the actual model identifiers returned in the logs.

GameShadowsDetectivesWinnerTurnsShadows capturedCost, CZK
9Gemini 4 ArgonGemini 3.8 FlashShadows15313.68
10Gemini 3.8 FlashGemini 4 ArgonDetectives28832.12

Game 9: Two early captures, followed by a comeback. Ada caught Ela and Nora caught Oskar on turn 3. Argon's start was far from perfect. Oskar even saw Nora nearby but wrongly judged he would avoid capture that turn. Meanwhile, Hugo completed his own task, delivering the Veverka seal west on turn 8. After release, Ela and Oskar went back for the remaining two. When Nora approached Oskar again, he headed through the park to the east. Ela continued south and delivered the winning seal on turn 15. The police could no longer reach her in time.

Oskar wrote on turn eleven:

“WARNING! I can see Nora right beside me southwest of sulicka (pos [-87, 251])! I'm escaping east through the park with the seal (2 turns), then I'll head north or east depending on where Nora goes.”

This was more than a sentence. The record confirms movement towards the park and a switch to the eastern exit on the following turn. His diary then described Nora's distance and why he changed direction. That link between message, plan, and actual movement is more interesting than winning alone.

Hugo also noticed that the game returns detained characters to an exit far from the detectives, and used that as guidance for the team. However, his messages described it overconfidently as “completely safe.” The return location is chosen at capture time, while the police keep moving. It was a useful clue, not a guarantee of a safe route.

Game 10: The smartest idea was to stop running. With Argon controlling the police, a different game emerged. The detectives recorded eight captures and, from the end of turn 10, guarded three different seals: Ada at Alšovy sady, Viktor at Veverka, and Nora at Sulická. They stayed there until the final turn. The Flash Shadows looked for alternatives and changed destination several times, but delivered no seals. The police won when time ran out.

On turn eighteen, Nora reminded her teammates:

“Ada, Viktor, STAY PUT on ales and veverka (wait)! As long as we stand directly on our seals, they cannot take a single one!”

On the last turn, Ada wrote:

“All three seals are safely in place under our watch (me on ales, Nora on sulicka, Viktor on veverka). I'm staying where I am (wait); victory is certain.”

Here the diary matched reality. Smarter decision-making does not necessarily create more chases. Sometimes it reveals that the best strategy is to stand still. For a game designer, that matters: to create a more exciting game, I also need to fix rules that favour passive defence so strongly.

Actual ending of game ten: Argon detectives, three guarded seals, and Nora's diary

The actual state after turn 28. The Shadows delivered no seals. On the right, Nora explains why she will not leave her post. The turn number inside her text is the model's own entry; the label above it comes from the engine.

Argon won both directions of this small comparison. As Shadows, it returned to objectives after setbacks and reacted to nearby opponents; as police, it consistently divided guarding duties. It was not flawless. In the police game, one response contained no usable action, and the engine waited for Nora. Of Argon's 123 responses, one was unusable in this way; of Flash's 113, none were. Nobody used a closure or false clue.

Two wins are a promising observation, not a measurement of general superiority. This is still one layout and one match in each direction. The police victory especially also exposes a rule weakness. A meaningful next test needs repetitions, different starts, and a way to disrupt permanent seal guarding. Changing models alone does not guarantee a better story.

What those ten games actually taught me

In everyday terms, with better models I more often saw characters use what they already knew. They did more than write a nice message. They divided locations, returned to a task after capture, or changed route when threatened. I also learned how many problems I had created through poor interfaces and rules.

ObservationWhat actually happenedWhat it does not yet establish
Better instructions helped even the cheap modelPolice attempts to collect or deliver seals fell from 13 in the first pilot to 0 in the secondPermanent retraining or good tactics
Flash beat Lite on both sidesThe Flash team won games 7 and 8Its win probability on another map
Argon beat Flash on both sidesThe Argon team won games 9 and 10That two matches isolate general intelligence
A stronger model need not be more activeArgon police guarded all three seals from turn 10That more movement, longer diaries, or more words make a better game
Offering an ability is not enoughNobody used a closure or false clue in eight v3 gamesThat models cannot use them; they may not have been worthwhile in these situations
Tool use is not knowledge acquisitionNora earned a research token for finding an article without a relevance checkThat she learned something correct about the memorial

The stronger variant therefore won all four mixed-model matches. But they all used one layout, some shared tactical advantages, and just one run in each direction. This motivates further work; it does not justify saying “the better model has a 100% success rate.” Even the overall v3 score, four police wins and four Shadows wins, does not prove balance.

I learned the most during development. I did not retrain model weights or transfer diaries between paid rounds. Within a game, characters could use new messages and experiences. Between versions, I changed instructions, the memory interface, and rules. These are different kinds of improvement and deserve to be distinguished.

Under the hood, for anyone who wants to build this

The decision loop has four steps: observation → model response → engine validation and calculation → outcome recording. Graphics simply show what happened.

What the agent receives. The structured observation includes its team and goal, current position, carried item, available actions, public seal state, known clues, and estimated route lengths. The engine directly shares teammates' positions, destinations, and carried items. Team messages work like a shared radio with no distance limit and are not sent to opponents. Exact opponent details are visible only locally; older area reports are also available.

This matters when interpreting cooperation. Players had to agree on intentions and divide tasks, but did not need to infer teammates' positions from conversation. Nor did they solve every metre of navigation themselves. They chose a destination and the engine calculated a route through the street network. In these games, models do not see screenshots or recognise faces. Richer scenery changes the viewer's experience, not the models' input.

What a response looks like. A simplified format example, not a quotation from a specific turn:

{
  "action": { "type": "move", "target": "east" },
  "message": "I'm heading east instead; there is a patrol in the north.",
  "diary": "The north is risky. I'll try another exit."
}

action is a request, not an accomplished fact. The engine checks permission, calculates movement and any capture. Teammates receive message, while diary is saved for that character. If a response cannot be parsed, the character waits that turn. Movement along an already selected route continues during wait, so counting these actions does not automatically count wasted turns.

How much memory returns to the prompt. Each call includes the last six personal diary entries, twelve received team messages, eight engine-confirmed outcomes, and at most three recent research results. Complete diaries and the continuous JSONL record remain on disk. There is no vector database, knowledge graph, or automatic recall of important old memories yet. Memory development therefore has a clear purpose.

How the engine avoids an API speed race. At the start of each turn, it creates immutable observations for all active characters. Responses run concurrently, but the world advances only after they finish. A slower model does not receive less game time. The seed determines placement, not the randomness of language-model responses. Repeating an identical start can therefore produce a different result.

Where the research reward let me down. An agent at a memorial can submit its own Wikipedia query. The tool returns up to three article titles, links, and short snippets. In game ten, Nora first failed with “Sulická,” then tried “Vladimír Mandl” and earned a token for a dash or a more precise clue. The engine checked only whether the search found something, not whether the article related to the local memorial.

Nora wrote: “I gained 1 intel for researching Vladimír Mandl at sulicka.” She did receive the token, but that does not prove correct knowledge about the location. I do not attribute an intention to cheat. I simply rewarded a condition that was too easy to satisfy. Next time, the agent should provide a specific claim about the place, a source, and supporting evidence. Search count is not a good measure of knowledge.

What can be checked afterwards. Each run has a protocol, a copy of the code used, requests, responses, costs, photos, and diaries. For the Argon pair, I compared hashes of game code, initial states, and system instructions. Before launch, 31 automated tests passed, covering privacy, concurrency, movement, capture, and correct multi-model accounting. That does not turn a small experiment into a major scientific study, but it lets me distinguish an interesting anecdote from what actually happened.

How much does a world like this cost me?

All ten games cost roughly CZK 67 in model calls, producing 705 responses. This is the cost of character decisions in these experiments, not the full development cost.

Part of the experimentGamesModel callsApproximate cost
Initial Gemini 3.5 Flash Lite-only experiments6275CZK 8.31
Gemini 3.8 Flash versus Lite, swapping sides2194CZK 12.61
Gemini 4 Argon versus Gemini 3.8 Flash, swapping sides2236CZK 45.80
Total10705CZK 66.72

In the final pair, approximately CZK 35.93 went to Argon and CZK 9.87 to Flash. In the earlier Flash/Lite pair, it was CZK 9.37 for Flash and CZK 3.24 for Lite. The longer Argon police game naturally needed more decisions than the fifteen-turn escape, so a round's total cost does not by itself measure model efficiency.

I calculate costs from API-reported tokens and official Gemini pricing. Argon used the introductory pricing described in its model documentation. I convert to Czech crowns at the Czech National Bank rate for 27 October 2026: CZK 21.834 per dollar. This is an approximate conversion, not an invoice or the exact card exchange rate.

All ten rounds are finished, no further queries are running, and unsettled reserves are zero. The four initial v3 games produced 35 screenshots, Flash versus Lite added 17, and the Argon matches another 23. Each character retains its own diary.

I keep a shared campaign cap of approximately CZK 437. Part of the budget is reserved before each request. If a response is lost or accounting is missing, that uncertain amount is not automatically treated as zero.

Model-call costs exclude development work, computer operation, and concept art. The selected character models and surface materials were freely available.

What if it eventually looked like a big-budget game?

I imagine an autumn evening in this neighbourhood. Window light reflects on wet asphalt. Hugo stops by a fence, turns a seal in his hand, and watches Ela across the street. She subtly signals that the passage is clear. Around the corner, Nora is deciding whether to follow a fresh clue or stay where an opponent will eventually have to come.

Vision of a future Shadows of Prague: Hugo holding a seal, teammates, and an autumn residential street

An artistic vision for another version. This is the atmosphere I would gradually like to bring to a playable Prague neighbourhood.

That is the direction I want to take. The image presents an intended atmosphere, not the current renderer or the exact local houses. Hugo's expression interests me as much as his reason for stopping there.

Start with one convincing street

I would begin with a short stretch between two game locations. Houses would get accurate proportions, entrances, fences, gardens, and varied materials. Characters need better skin, eyes, hair, and especially natural transitions between walking, turning, and gesturing. A fixed stare or feet sliding along the pavement quickly ruins a good face. Blender can prepare geometry and animation; the same scene must then work in the actual game on an ordinary computer.

For a more demanding standalone version, Unreal Engine would make sense. Its Lumen provides dynamic indirect lighting and reflections. Game rules and AI can remain on the server while another client renders their results. This is a possible direction, not an existing feature of my browser prototype.

The 2023 research on 3D Gaussian Splatting offers another interesting route to capturing a real place: it builds a scene representation from photos for viewing from new positions. It could help me reproduce a particular corner faithfully. A beautiful space does not automatically give characters usable doors, collisions, or movement rules, however. I would have to add and verify those separately.

Better memory than the last six entries

Today, an agent gets only a selection of recent messages and diary entries. An old but important discovery can disappear. In the 2023 Generative Agents study, researchers combined experience records, relevant memory retrieval, reflection, and planning. Their small world also exhibited coordinated social behaviour.

For Shadows of Prague, I would take one concrete change: recall situations similar to the present one before deciding. For example, the same passage has already led to capture twice. Each memory would carry a time, source, and link to an engine-confirmed outcome. The impression “this route should work” would remain separate from the verified “I actually got through here.”

Finish a plan, and know when to abandon it

Voyager used a library of reusable skills and environmental feedback in Minecraft. It did not need to rebuild all behaviour from scratch each time, nor did it continually retrain language-model weights.

Here, a similar idea could mean a verified procedure for “collect the seal and carry it through a clear exit.” The language model would choose an objective and react to change; the engine would reliably execute permitted steps. The procedure must include reasons to stop: a detective appeared, a teammate already took the object, or the route is no longer clear. Otherwise, I would merely automate repeating the same mistake.

Agreements that turn into action

The CICERO project combined a language model with planning and modelling other players' behaviour in Diplomacy. Its performance did not come simply from adding more chat. Conversation was linked to a concrete plan.

Alongside free-form messages, teams could therefore make simple agreements: who guards which location, until when, who confirmed it, and what happens after a capture. If Nora accepts the north, Ada need not head there to “help” without another reason. After three turns of silence, the team should know that an earlier promise may no longer hold.

How will I know I have really improved it?

DeepMind's Melting Pot tests agents in varied cooperative and competitive situations with different partners. It is a multi-agent reinforcement-learning research environment, not a ready-made benchmark for my prototype. Its emphasis on diverse social situations is useful inspiration for further measurement.

A prettier face and a single winning game would not be enough. I need more starts, swapped sides, repetitions, and identical conditions. Alongside wins, I would track unnecessary travel, repeated mistakes, fulfilled agreements, information actually used, and cost per completed goal. I would also fix the overly advantageous seal guarding before measuring models again.

My next goal is concrete: one beautiful street, reliable memory, and a team whose conversation changes what it actually does. A much more interesting world could grow from that foundation.

One neighbourhood, more possible stories

Shadows of Prague is now a playable tactical prototype. I am also preparing two other versions of the idea.

Prague Estate would be a historical adventure with a messenger, scribe, blacksmith, guilds, reputation, and a missing charter. It takes inspiration from the kind of experience Kingdom Come offers, but has its own story. Today's map cannot simply be declared medieval; period buildings would require additional sources.

Republic in the Shadows would take place in a fictional Czechoslovak noir setting: a reporter, informants, an investigator, a driver, and rival gangs. Instead of seals, there would be agreements, testimonies, and deliveries where it matters whom you trust.

Both are still concepts. I do not want ten attractive sets in which nobody makes interesting decisions. First, I want one neighbourhood where staying put, changing plans, trusting a teammate, or disagreeing with them matters.

And next time Hugo says “we're going to win,” I want to be genuinely curious whether he will pull it off.

More experiments in the series

Related articles