ARCADIANS Clone — Classic BBC Micro Game

ARCADIANS Clone — Classic BBC Micro Game

A Galaxian-style retro game demo on OpenShift

Faithful clone of the 1982 Acornsoft Arcadians (BBC Micro) — itself a clone of Namco's Galaxian.
Features swooping dive-bombing aliens, authentic BBC palette, extracted original intro music,
and a PostgreSQL high score table. Built as a full Kubernetes demo stack on OpenShift.


Stack

Component Technology
Game Vanilla HTML5 Canvas + Web Audio
API FastAPI (Python 3.12)
Database PostgreSQL 16 StatefulSet
Backup Kanister non-exclusive BP
Registry Harbor (harbor.apps.openshift2.lab.home)
Platform OpenShift (rke2-prod)
Namespace retro-game

Directory Structure

arcadians/
├── README.md
├── game/
│   ├── index.html       # Full game — Canvas renderer, Web Audio, score UI
│   ├── nginx.conf       # Proxies /api/ → backend service
│   └── Dockerfile       # nginx:alpine serving static HTML
├── api/
│   ├── main.py          # FastAPI — GET /api/scores, POST /api/scores, GET /healthz
│   ├── requirements.txt # fastapi, uvicorn, asyncpg, pydantic
│   └── Dockerfile       # python:3.12-slim
└── k8s/
    ├── 00-namespace.yaml           # retro-game namespace
    ├── 01-postgresql.yaml          # StatefulSet + headless + ClusterIP + Secret
    ├── 02-api.yaml                 # FastAPI Deployment + Service
    ├── 03-frontend.yaml            # nginx Deployment + Service + OpenShift Route
    └── 04-kanister-actionset.yaml  # Manual backup trigger

Game Features

  • Galaxian mechanics — aliens hold formation sweeping side to side, groups peel off
    and dive-bomb the player in curved paths, shooting as they go
  • Two alien types — Charger (top rows, high value) and Convoy (formation fillers)
  • Authentic scoring — Charger diving 100pts, Convoy diving 80pts, formation kills 20-50pts
  • BBC Micro palette — black, red, green, yellow, blue, magenta, cyan, white only
  • Intro music — extracted via FFT analysis from original Arcadians BBC Micro audio
  • Sound effects — Web Audio synthesised: formation march tick, shoot, dive warble,
    alien explosions, layered player death boom with debris animation
  • Player death animation — ship fragments with expanding debris cloud over 12 frames
  • Mission briefing — "WE ARE THE ARCADIANS!" screen between waves
  • Hi-score table — top 8 scores with initials and wave reached, persisted in PostgreSQL
  • Wave progression — aliens speed up, shoot faster, dive more aggressively each wave

Controls

Key Action
/ Z Move left
/ X Move right
SPACE Fire
Any key Start intro music (title screen)
SPACE Skip intro / start game

Build & Push Images

# Game frontend
cd game
docker build -t harbor.apps.openshift2.lab.home/retro-game/arcadians-game:latest .
docker push harbor.apps.openshift2.lab.home/retro-game/arcadians-game:latest

# API backend
cd ../api
docker build -t harbor.apps.openshift2.lab.home/retro-game/arcadians-api:latest .
docker push harbor.apps.openshift2.lab.home/retro-game/arcadians-api:latest

Deploy to OpenShift

# Apply manifests in order
oc apply -f k8s/00-namespace.yaml
oc apply -f k8s/01-postgresql.yaml

# Wait for PostgreSQL to be ready before deploying the API
oc -n retro-game rollout status statefulset/arcadians-postgresql

oc apply -f k8s/02-api.yaml
oc apply -f k8s/03-frontend.yaml

# Check all rollouts
oc -n retro-game rollout status deployment/arcadians-api
oc -n retro-game rollout status deployment/arcadians-game

# Get the route URL
oc -n retro-game get route arcadians

OpenShift SCC Note

If the PostgreSQL pod fails to start due to SCC restrictions, grant anyuid to the
default service account:

oc adm policy add-scc-to-user anyuid -z default -n retro-game

For a cleaner approach using a dedicated service account:

oc create sa arcadians-sa -n retro-game
oc adm policy add-scc-to-user anyuid -z arcadians-sa -n retro-game

Then add serviceAccountName: arcadians-sa to the StatefulSet pod spec in 01-postgresql.yaml.


PostgreSQL & Kanister Backup

The StatefulSet is labelled to match the postgres-non-exclusive-backup Kanister blueprint
already deployed in kasten-io. This blueprint uses the PostgreSQL 15+ non-exclusive backup
API (pg_backup_start / pg_backup_stop).

Key label and naming conventions the blueprint requires:

app.kubernetes.io/instance: arcadians          # used to build PGHOST
Secret name: arcadians-postgresql              # must be {{ instance }}-postgresql
Secret key:  postgres-password

To trigger a manual backup, set your Kanister profile name in 04-kanister-actionset.yaml
then apply:

oc apply -f k8s/04-kanister-actionset.yaml -n kasten-io
oc -n kasten-io get actionset arcadians-postgresql-backup -w

ArgoCD Integration

Push the repo to Gitea and create an ArgoCD Application pointing at the k8s/ directory:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: arcadians
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://YOUR_GITEA/YOUR_ORG/arcadians.git
    targetRevision: HEAD
    path: k8s
  destination:
    server: https://kubernetes.default.svc
    namespace: retro-game
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

API Reference

Method Path Description
GET /api/scores Top 10 scores ordered by score desc
POST /api/scores Submit a new score
GET /healthz Liveness/readiness health check

POST request body:

{ "initials": "JTT", "score": 12340, "wave": 5 }

The scores table is created automatically on API startup if it does not exist.


Architecture Notes

The nginx frontend container proxies all /api/ requests to the arcadians-api ClusterIP
service, so the game only needs a single exposed Route. The API connects to PostgreSQL using
the arcadians-postgresql ClusterIP service within the same namespace — no external database
exposure required.

The entire stack is self-contained within the retro-game namespace and can be torn down
cleanly with oc delete namespace retro-game.