War - Boardgame to Video Game + AI (Kotlin + LibGDX)
This is the video game version of my custom chess-like strategy board game, War. Written in Kotlin + LibGDX, with a MiniMax AI opponent.
I originally designed and built the physical board game. This project brings it to a screen so I can test piece balance, experiment with AI strategies, and play test it more easily in general.
The source code can be found on GitHub.
I wrote the core game engine and AI entirely before AI coding tools were commonplace, which was fun, and I hope that my manual code skills don't degrade and whither away. XD More recently, I used Claude to help expand unit tests and search for bugs across the codebase. Having an AI review the game logic turned up several subtle bugs in the minimax tree search that would have been difficult to catch.
The Game
War is a deterministic, chess-like strategy board game. No dice, no luck -- every outcome is the result of player decisions.
The board is an 11x11 grid with variable-height terrain tiles. Pieces cannot climb more than 1 unit of elevation per step, so terrain creates natural chokepoints, defensive positions, and flanking routes.
The game ends when a player's Commander is destroyed.
Pieces
Each piece has a distinct role. There are no duplicate movement patterns:
| Piece | Movement | Attack | Score |
|---|---|---|---|
| Commander | 1 tile, any direction | Same | 100 |
| Bomber | 1-4 tiles H/V, ignores elevation | Same | 5 |
| Missile | 1 tile any direction | 2-5 tiles diagonal (one-time use) | 4 |
| Air Defense | 1 tile any direction | Intercepts Bombers/Missiles/Artillery | 4 |
| Artillery | 1 tile any direction | 2-3 tiles H/V (ranged, reload) | 3 |
| Excavator | 1 tile any direction | Raises/lowers terrain | 3 |
| Tank | 1-2 tiles H/V | Same | 2 |
| Sniper | 1-2 tiles diagonal | Same | 2 |
| Infantry | 1 tile H/V | 1 tile diagonal | 1 |
Key interactions:
- Artillery fires over pieces and terrain but needs 3+ turns to reload between shots.
- Missiles are powerful diagonal strikers but are destroyed after a single use.
- Air Defense automatically intercepts Bombers, Missiles, and Artillery shells within 1 tile, sacrificing itself in the process.
- Bombers fly over friendly pieces and ignore elevation, but are vulnerable to Air Defense.
- Excavators reshape the terrain itself, creating walls or opening paths.
Movement Diversity
One of the core design goals is that horizontal/vertical and diagonal movement are balanced:
| Pattern | Combat Movers |
|---|---|
| H/V | Tank (1-2), Bomber (1-4), Artillery attack (2-3) |
| Diagonal | Sniper (1-2), Missile attack (2-5, one-use) |
| Any direction | Commander (1), Infantry (H/V move, diagonal attack) |
The AI
The AI uses a MiniMax search with Monte Carlo extensions. Here is how it works:
MiniMax Search
The AI builds a game tree by simulating moves for both players, alternating turns. At each leaf node, it evaluates the board state. The AI maximizes its own score and minimizes the opponent's, classic minimax.
AI (maximize)
-> Opponent (minimize)
-> AI (maximize)
-> evaluate position
Move Scoring
Each move is scored by combining four factors:
- Base Score The value of the piece destroyed (e.g. capturing a Bomber = 5.0 points).
- Commander Advance A small bonus for moving closer to the enemy Commander. Euclidean distance reduction divided by 10.
- Weaker Piece Bonus Encourages the AI to move cheaper pieces first (Infantry gets 0.23, Tanks/Snipers get 0.2, Bombers get 0.0). This prevents the AI from leading with its most valuable units.
- Noise Small random perturbation scaled inversely by depth, so the AI doesn't play the exact same game every time.
The player modifier flips the sign for opponent moves: the AI wants to maximize its own captures and minimize the opponent's.
Monte Carlo Extensions
At the bottom of the minimax tree, the AI occasionally continues searching with a random walk (5% probability). This helps it discover tactical sequences that a fixed-depth search would miss, like a sacrifice that leads to a bigger capture two moves later.
Attacks always trigger an additional search step so the AI can verify it won't immediately lose the attacking piece.
Apply / Undo
The AI doesn't copy the board for each branch. Instead, it applies moves directly to the board and undoes them when backtracking. This is significantly faster but requires every piece to correctly restore all state on undo, including things like Artillery reload timers.
Dev Log
A selection of development videos showing the game's progression:
- MiniMax AI
- Display turns & score
- Explosion effects
- Two-player mode, Artillery fire/reload, Missile one-time use
- Missile, Jet, Better terrain, Move vs attack highlighting
- Infantry, Highlight allowed moves
Running It
./gradlew desktop:dist
java -XstartOnFirstThread -jar desktop/build/libs/desktop-1.0.jar # macOS
java -jar desktop/build/libs/desktop-1.0.jar # Windows/Linux
Arrived
Ninja Turdle