Kex
My association with the game»
It’s been a while since the last game, Block Puzlle, and my explanations may have been too extensive and chaotic back then. Now I’ve built another game with Svelte and I’m going to try again here to show you how I went about it. It’s called KEX, I think it’s an acronym for the German word Kern-EXplosion (Nuclear explosion). There are now several versions of it in various app stores, then under the name Atoms.
But there are also online KC85 emulators and (still) even a site where you can play the original. Unfortunately I can’t put a direct deeplink to the game here, so you’ll have to search for KEX yourself.
There used to be sites to download the original KC files like https://www.iee.et.tu-dresden.de/~kc-club/09/RUBRIK15.HTM, which is now only available thanks to the Wayback machine.
I got to know the game when I must have been about 12 years old. I went a few times to the local computer club in Greifswald, where I grew up. There were several small East German computers there, the so-called KC85, and often at the end of these meetings some of us would sit in front of a TV and this little computer to play tis game, also because it worked for up to 8 players. Not far from Greifswald, in Lubmin, was the only nuclear power plant in the GDR and somehow the topic of nuclear explosions fitted in quite well, didn’t it?
We entered short forms for our names in turn and off we went. In my opinion, the first four players had a big advantage because they could place their first atom on a corner to reserve it. Why is that important?
Game concept»
KEX is a strategic grid-based game where players take turns placing atoms in cells. Each cell has a critical mass.
- 2 in corners (green)
- 3 on edges (yellow)
- and 4 in the center (blue)
When a cell reaches its critical mass, it explodes, spreading atoms to adjacent cells and potentially triggering further chain reactions. Explosions convert any neighboring opponent’s cells to your control, effectively capturing their atoms. All conquered atoms take on the colour of the player.
The aim is to strategically cause explosions to dominate the board, capturing all of your opponent’s atoms and ultimately taking full control of the grid. The size of the field and the situation of the different atoms on the fields makes it seemingly unpredictable how the field will look after the player’s turn.
The last picture is showing an endless chain reaction of red atoms and how I managed to animate the transition of atoms from a field to its neighboring cells.
Implementation details»
What I like about re-creating such games with Svelte is that it is easy to split the whole story into (two) parts. On one side we have the game or business logic in the form of simple Javascript functions, on the other side there is the representative part with visualisation, animation and interaction. Both are connected by a state with some specific data structure. And Svelte often takes care of updating the latter when the state changes.
This time I won’t bore you with all the details, but will pick out just a few special ones. Let’s start with the data structure. I came up with a fields and later on an atoms array like this:
let fields = [
{
"i": 0,
"x": 0,
"y": 0,
"player": 1,
"atoms": [1],
"maxAtoms": 1
},
{
"i": 1,
"x": 1,
"y": 0,
"player": -1,
"atoms": [],
"maxAtoms": 2
},
{
"i": 2,
"x": 2,
"y": 0,
"player": 0,
"atoms": [0,2],
"maxAtoms": 2
} // , ...
];
let atoms = [
{
"transform": "translate(45.5 45.5)",
"player": 1,
"key": 1
},
{
"transform": "translate(209.3 45.5)",
"player": 0,
"key": 0
},
{
"transform": "translate(245.7 45.5)",
"player": 0,
"key": 2
}
];
The fields array consists of all possible fields. It tells us which player owns a field, which atoms are there and what is the maximal amount of atoms before the field is going to explode. This my main state and I can change this on every user interaction or chain reaction by adding and (re)moving atoms. I can also have methods to count all atoms of a specific user, check for explosives etc.
And I convert fields easily into the atoms array, which on the other hand is the visual representation how to display these atoms. It already consists of the position the atom needs to move to, the player and thus the color of the atom and the atom identifier or key. Best part is, I can put the fields to atoms conversion in a reative Svelte statement.
import { getAtomsFromFields } from "./render";
$: atoms = getAtomsFromFields(fields, dist, radius);
This came naturally because I realised during creation that all atoms must have the same parent in order to use CSS animations when atoms are moved from one field to another.
const atomTransforms = [
[],
[{ dx: 0, dy: 0 }],
[{ dx: -1, dy: 0 }, { dx: 1, dy: 0 }],
[{ dx: -1, dy: 0.87 }, { dx: 1, dy: 0.87 }, { dx: 0, dy: -0.87 }],
[{ dx: -1, dy: 1 }, { dx: 1, dy: 1 }, { dx: -1, dy: -1 }, { dx: 1, dy: -1 }],
[{ dx: -1, dy: 1 }, { dx: 1, dy: 1 }, { dx: -1, dy: -1 }, { dx: 1, dy: -1 }, { dx: 0, dy: 0 }]
];
export const getAtomsFromFields = (fields, dist, radius) => fields.flatMap(({x, y, player, atoms}) =>
atomTransforms[atoms.length].map(({ dx, dy }, index) => ({
transform: `translate(${(x + 0.5) * dist + dx * radius} ${(y + 0.5) * dist + dy * radius})`,
player,
key: atoms[index]
}))
);
The rendering turns out to be really easy using SVG. The circles inside the surrounding SVG are encapsulated in such a small svelte compontent.
<script>
import { flip } from "svelte/animate";
export let duration = 0;
export let radius = 0;
export let atoms = [];
</script>
{#each atoms as {transform, player, key}, i (key)}
<circle
animate:flip={{duration: duration}}
r={radius}
transform="{transform}"
style="fill:url(#p{player}"
key={key}
/>
{/each}
If you wonder that there are no event handlers, I’ve put these on the board fields not the atoms. As mentioned before the rest is all about the field array, I have methods with speaking names.
let fields = createFields(size);
fields = addNewAtom(fields, x, y, player, atomKey);
const allowed = isMoveAllowedForCurrentPlayer(fields, x, y, player);
const numberOfAtoms = getNumberOfAtoms(fields);
const numberOfAtomsPerPlayer = getNumberOfAtomsPerPlayer(fields, maxPlayers);
const explosive = hasExplosiveFields(fields);
fields = explode(fields);
So, I’ll leave it at that, you get an impression of how I structure the code. I put most of the functions in their own .js files and try to use the .svelte files only for reactive statements and rendering.
Play the game»
So here’s the current state of the game. It is already fully playable and you can enjoy with your friends sitting in front of this website, just as I did with my friends back in den computer club in front of the KC-85. So please enjoy!
When I do projects like this alongside work, I tend to start eagerly but drop the whole project in favour of something new. I would say the Pareto principle has kicked in. The first 80% of the project is done, in 20% of the time. The remaining 20% would require a lot more effort, which keeps me away from other ideas.
So instead I have preserved the status quo here and can return at any time to…
- make an AppStore App or fully functional PWA out of it
- implement a multiplayer mode with websockets
- build a computer or AI opponent to play against