A fantasy tower defense game built with Python and Pygame.
Defend the castle, place towers with limited gold, survive escalating waves, and review polished end-of-session battle stats.
- Project by: Thanutdit Jiravichalert
- Game Genre: Strategy, Tower Defense
Kingdom's Last Stand is a single-player wave-based tower defense game. The player defends a castle by placing Archer, Mage, and Cannon towers on a map to stop 10 escalating waves of enemies. Each wave enemies grow stronger and faster, and every gold coin spent matters. After each session, all gameplay data is saved to a CSV file and can be reviewed in an interactive Statistics screen with 6 data visualizations.
Clone this project:
git clone https://github.com/thanutdit-ku/GameYear1Project.git
cd GameYear1ProjectCreate and activate a Python virtual environment, then install dependencies:
Mac:
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtWindows:
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txtAfter activating the virtual environment, run the game:
Mac:
python3 main.pyWindows:
python main.pyThe test suite covers StatsTracker and the game's statistics logic. No display or Pygame window is required to run tests.
Mac:
python3 -m pytest tests/ -vWindows:
python -m pytest tests/ -vTo run a specific test file:
Mac:
python3 -m pytest tests/test_stats_tracker.py -v
python3 -m pytest tests/test_game_stats.py -vWindows:
python -m pytest tests/test_stats_tracker.py -v
python -m pytest tests/test_game_stats.py -vExpected output:
101 passed in 0.47s
| Test File | What It Covers |
|---|---|
tests/test_stats_tracker.py |
Recording kills/damage/gold, CSV writes, history, generate_report |
tests/test_game_stats.py |
Player stat aggregation, efficiency calculation, edge cases |
Tests also run automatically on every push via GitHub Actions.
- Launch the game — run
main.py - Enter your Commander name in the text box on the Home screen
- Select a map — choose from 6 maps, each with a unique enemy path
- Click Start Campaign to begin
- Place towers — click a tower in the Armory panel (or press
1/2/3), then click a green tile on the map - Click Start Wave to release enemies
- Earn gold by defeating enemies, then spend it on new towers or upgrades
- Right-click a placed tower to cycle its targeting mode (First / Last / Strongest / Closest)
- Press
Fand click a tower to sell it - Press
Pto pause,ESCto clear selection - Survive all 10 waves to achieve Victory
- From the Home screen, click Statistics to review all session data across 6 graphs
- 3 tower types with distinct roles:
Archer— fast attacks with 20% critical hit chanceMage— slows enemies by 50% for 2 seconds on hitCannon— area splash damage in a 50px radius
- 4 targeting modes per tower — First, Last, Strongest, Closest (right-click to cycle)
- 9 enemy types — Slime (splits on death), Goblin, Bat, SwordShield, Orc, Spider, DarkKnight, Boss Dragon
- 10 waves with HP scaling (×1.20 per wave) and speed scaling (×1.10 per wave)
- Boss waves — Dragon with 750 HP appears on Wave 5 and Wave 10
- 6 playable maps — Royal Road, Twin Bend, Southern Pass, Stone Spiral, Ember Valley, Frost Crossing
- Automatic CSV data recording — every wave saved to
data/game_stats.csv - Interactive Statistics screen with 6 visualizations:
- Summary Table (Mean, Median, Std Dev, Min, Max)
- Leaderboard
- Gold vs Wave scatter plot
- Damage Efficiency bar chart
- Survival Curve line chart
- Wave Survival Heatmap
- No have
- Tower sell refund is a flat rate; a percentage-based refund was planned but not implemented
- The Statistics screen has no filter by map or player — all sessions are always aggregated together
| Library | Version | Purpose | License |
|---|---|---|---|
| Pygame | 2.6+ | Game engine, rendering, input | LGPL |
| matplotlib | 3.7+ | In-game statistical charts | PSF/BSD |
| pandas | 2.0+ | CSV loading and summary statistics | BSD |
| Python | 3.10+ | Language | PSF |
All sprites are used under their respective free/open licenses. Frame counts and sheet dimensions are as loaded in-game.
| Tower | File(s) | Format | Details | Source |
|---|---|---|---|---|
| Archer Tower | assets/images/towers/archer/Idle/ |
10 individual PNGs (1024×1024) | Idle animation — 10 frames | 2D Animated Archer Spritesheet — OpenGameArt |
| Archer Tower | assets/images/towers/archer/Shoot_Stand/ |
22 individual PNGs (1024×1024) | Shoot animation — 22 frames | 2D Animated Archer Spritesheet — OpenGameArt |
| Archer Tower | assets/images/towers/archer/Jump/ |
22 individual PNGs (1024×1024) | Jump animation — 22 frames | 2D Animated Archer Spritesheet — OpenGameArt |
| Archer Tower | assets/images/towers/archer/Run_Idle/ |
22 individual PNGs (1024×1024) | Run-idle animation — 22 frames | 2D Animated Archer Spritesheet — OpenGameArt |
| Mage Tower | assets/images/enemies/dark_knight/mage/AttackMighty1–6.png |
6 individual PNGs (32×32, scaled 2×) | Cast animation — 6 frames | Pixel Art Assets 5 — greenpixels (itch.io) |
| Cannon Tower | assets/images/enemies/dark_knight/Cannon/cannon-sheet.png |
Sprite sheet 512×64 | 8 frames × 64×64 px in a single row | Cannon Gun Sprite Animated — OpenGameArt |
| Enemy | File(s) | Format | Details | Source |
|---|---|---|---|---|
| Slime / MiniSlime | assets/images/enemies/slime/slime-move-0–3.png |
4 individual PNGs (32×25) | Walk animation — 4 frames, MiniSlime uses same frames scaled down | Pixel Art Animated Slime — rvros (itch.io) |
| Wolf | assets/images/enemies/goblin/howl.png |
Single PNG (64×64) | Static sprite, no animation | LPC Wolf Animation — OpenGameArt |
| Bat | assets/images/enemies/goblin/bat.png |
Sprite sheet (128×128) | Directional fly animation | Bat Sprite — OpenGameArt |
| SwordShield | assets/images/enemies/swordshield/walk.png |
Sprite sheet 336×42 | 8 frames × 42×42 px — walk cycle | Knight Sprite — lionheart963 (itch.io) |
| SwordShield | assets/images/enemies/swordshield/idle.png |
Sprite sheet 168×42 | 4 frames × 42×42 px — idle cycle | Knight Sprite — lionheart963 (itch.io) |
| Orc | assets/images/enemies/orc/orc_walk.png |
Sprite sheet 320×320 | 10 columns × 32×32 px per frame — walk cycle, displayed at 48×48 | Animated Orcs — OpenGameArt |
| Spider | assets/images/enemies/spider/walk.png |
Sprite sheet 640×320 | 4 rows (up/left/down/right) × 10 cols × 64×64 px — directional walk | LPC Spider — OpenGameArt |
| DarkKnight | assets/images/enemies/dark_knight/FreeKnight/_Idle.png |
Sprite sheet 1200×80 | 10 frames × 120×80 px — idle animation | Fantasy Knight — aamatniekss (itch.io) |
| DarkKnight | assets/images/enemies/dark_knight/FreeKnight/_Run.png |
Sprite sheet 1200×80 | 10 frames × 120×80 px — run animation | Fantasy Knight — aamatniekss (itch.io) |
| DarkKnight | assets/images/enemies/dark_knight/FreeKnight/_Death.png |
Sprite sheet 1200×80 | 10 frames × 120×80 px — death animation | Fantasy Knight — aamatniekss (itch.io) |
| Dragon Boss | assets/images/enemies/boss/dragon.png |
Sprite sheet 432×512 | 3 cols × 4 rows × 144×128 px — directional walk (N/E/S/W) | Flying Dragon Rework — OpenGameArt |