▶ Live Experience: https://kayasax.github.io/Awale/
🚀 Microsoft Hackathon 2025 - An experimental journey into modern web development using AI-assisted "vibe coding" techniques. What happens when someone with zero TypeScript knowledge attempts to build a complete multiplayer game experience? This project is the answer!
The Challenge: Build a sophisticated web application using technologies I've never touched before The Method: AI-assisted "vibe coding" - learning through doing, guided by AI pair programming The Goal: Push the boundaries of what's possible with modern development tools and AI assistance
- Learning by Building: No tutorials, no courses - just dive in and solve real problems
- AI as Co-Pilot: Leverage GitHub Copilot and AI assistants for real-time guidance
- Iterative Discovery: Let the project evolve organically based on what works
- Modern Toolchain: Embrace the latest in TypeScript, React, WebSockets, Azure, and PWA technologies
Result: A fully functional online multiplayer Awale (African Mancala) game with lobby system, real-time gameplay, PWA capabilities, and Azure deployment - all built by someone who didn't know TypeScript existed 1 week ago! 🤯
- Authentic Awale Rules: Traditional African Mancala with proper capture logic and starvation prevention
- Stunning Visuals: Animated seed sowing, capture sparkles, delta badges, and thematic backgrounds
- Smart AI: Greedy strategy opponent for offline play
- Dual Themes: Dark modern and warm wood aesthetics
- Accessibility: Screen reader friendly, keyboard navigation, aria-live updates
- Real-time WebSocket Gameplay: Authoritative server with move validation
- Lobby System: See online players, send/receive game invitations
- Live Chat: Community interaction in the lobby
- Player Presence: Track who's available, in-game, or away
- Game Spectating: View ongoing games and player activity
- Progressive Web App: Offline play, installable, service worker caching
- Monorepo Architecture: Clean separation of engine, web client, and server
- Azure Container Apps: Scalable cloud deployment with WebSocket support
- Modern Build Pipeline: Vite dev server, esbuild production, automated CI/CD
- Type Safety: Full TypeScript throughout (learned on the job!)
🏗 Monorepo Structure (packages/)
├── core/ # Pure game engine + AI (The Brain)
├── shared/ # Common types & protocols (The Contract)
├── web/ # React PWA frontend (The Face)
├── server/ # Node.js WebSocket server (The Conductor)
└── bot/ # Legacy Teams bot (The Ghost of Plans Past)
- Original Vision: Microsoft Teams bot (blocked by enterprise restrictions)
- Pivot 1: Simple web game (too boring!)
- Pivot 2: PWA with offline play (getting warmer...)
- Final Form: Full multiplayer with lobby, chat, and Azure deployment (JACKPOT! 🎰)
# Just Node.js - we'll figure out the rest together!
node --version # v18+ recommended
npm --version # Comes with Node# Clone the experimental madness
git clone https://github.com/kayasax/Awale.git
cd Awale
# Install dependencies (trust the process)
npm install
# Launch development (prepare for magic)
npm run dev -w @awale/web
# Open http://localhost:5173 and witness the chaos!# Build optimized bundle
npm run build -w @awale/web
# Preview locally
npm run preview -w @awale/web
# Deploy to your favorite static host!Want to run your own multiplayer server? Here's the Azure magic:
# Create resource group
az group create -n awale-rg -l francecentral
# Build and deploy server
docker build -t awale-server -f packages/server/Dockerfile .
# ... (full deployment guide in original docs)# Start local multiplayer server
npm run dev -w @awale/server
# Frontend connects to localhost:8080
npm run dev -w @awale/web- AI Pair Programming: GitHub Copilot was genuinely helpful for TypeScript syntax and patterns
- Iterative Development: Small, testable changes made complex features achievable
- Modern Tooling: Vite, esbuild, and TypeScript created a surprisingly smooth developer experience
- Real-time Debugging: Console logging and browser dev tools were game-changers
- WebSocket State Management: Harder than expected to keep client/server in sync
- CSS Z-index Wars: Who knew pseudo-elements could be so troublesome?
- TypeScript Interfaces: Still learning when to use
typevsinterfacevsclass - Azure Deployment: Container Apps are powerful but the CLI dance is complex
- Monorepo Benefits: Shared types between frontend/backend prevented so many bugs
- Service Workers: PWA capabilities added with surprisingly little code
- React Hooks: useEffect cleanup patterns are crucial for WebSocket connections
- Git Workflow: Feature branches and semantic commits actually help!
- Stronger AI with minimax algorithm
- Tournament mode with brackets
- Replay system with move history
- Custom game variants and rules
- Spectator mode for live games
- Player rankings and statistics
- Private rooms and friend systems
- Mobile app using Capacitor or React Native
- Real-time testing with Playwright
- Performance monitoring with Application Insights
- Kubernetes deployment for scale
- GraphQL API for richer data queries
Microsoft Hackathon 2025 - For creating the perfect excuse to experiment with cutting-edge tech
AI Assistants - GitHub Copilot, Claude, and GPT for being patient teachers and debugging partners
Open Source Community - Every Stack Overflow answer, GitHub issue, and documentation page that saved the day
The Awale Game - Ancient African wisdom meets modern web technology
- Lines of Code: ~15,000+ (mostly TypeScript!)
- Development Time: 3 days!
- AI Assistance: ~80% of code had some AI input (vibe coding!)
- Bug Count: Lost count after 200 (learning experience!)
- Coffee Consumed: Immeasurable ☕
- Fun Had: Absolutely priceless 🎉
This project started as "let me try TypeScript" and somehow became a full-featured multiplayer game platform. The code probably violates several best practices, the architecture evolved organically (some might say chaotically), and I'm still not 100% sure how some parts work.
But it works! Players can join from around the world, chat in the lobby, start games, and have a genuinely fun experience. Sometimes the best way to learn is to just start building and figure it out as you go.
That's the magic of vibe coding. 🪄
MIT License - Feel free to learn from this experimental journey!
Found a bug? Have a suggestion? Want to see how NOT to write TypeScript?
Open an issue or submit a PR - all feedback welcome from fellow experimenters and experienced developers alike!
🧪 Testing & Development
# Run all tests
npm test
# Test specific package
npm test -w @awale/core- Dev: Vite handles module graph & fast HMR
- Prod: Custom
build-esbuild.cjsbundles optimizedapp.js - CI/CD: GitHub Actions for build, release, and Pages deployment
🌐 Full Azure Deployment Guide
az group create -n awale-rg -l francecentralACR_NAME=awaleacr$RANDOM
az acr create -n $ACR_NAME -g awale-rg --sku Basic --admin-enabled true
az acr login -n $ACR_NAMEIMG_TAG=v0.1.0
docker build -t $ACR_NAME.azurecr.io/awale-server:$IMG_TAG -f packages/server/Dockerfile .
docker push $ACR_NAME.azurecr.io/awale-server:$IMG_TAGaz extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App --wait
az containerapp env create -n awale-env -g awale-rg -l francecentral
FRONT_ORIGIN=https://kayasax.github.io
az containerapp create \
-n awale-server \
-g awale-rg \
--environment awale-env \
--image $ACR_NAME.azurecr.io/awale-server:$IMG_TAG \
--target-port 8080 \
--ingress external \
--transport auto \
--registry-server $ACR_NAME.azurecr.io \
--env-vars ALLOWED_ORIGIN=$FRONT_ORIGIN RATE_LIMIT_BURST=20 RATE_LIMIT_REFILL_MS=1000 \
--query properties.configuration.ingress.fqdn -o tsvFQDN=$(az containerapp show -n awale-server -g awale-rg --query properties.configuration.ingress.fqdn -o tsv)
curl https://$FQDN/health
# Configure frontend
VITE_AWALE_SERVER_WS=wss://$FQDN/ws npm run build -w @awale/web| Issue | Solution |
|---|---|
| WebSocket connection fails | Check ALLOWED_ORIGIN matches exactly |
| No game creation | Verify VITE_AWALE_SERVER_WS environment variable |
| Rate limiting | Adjust RATE_LIMIT_BURST setting |
🎮 Game Engine & Protocol
- 12-pit array: Positions 0-5 (player), 6-11 (AI/opponent)
- Capture logic: Final seed in opponent row with 2-3 seeds
- Starvation prevention: Must leave opponent with moves when possible
// Client → Server
{ type: "create", name: "Alice" }
{ type: "join", gameId: "abc123", name: "Bob" }
{ type: "move", gameId: "abc123", pit: 4 }
// Server → Client
{ type: "created", gameId: "abc123", playerToken: "..." }
{ type: "joined", gameId: "abc123", role: "guest", opponent: "Alice" }
{ type: "state", gameId: "abc123", version: 7, state: {...} }
{ type: "moveApplied", gameId: "abc123", captured: 3 }- Real-time presence: Track online/offline/in-game status
- Game invitations: Send/accept/decline game invites
- Chat system: Community interaction in lobby
- Player visibility: See all online players including those in games
| Variable | Purpose | Default |
|---|---|---|
PORT |
Listen port | 8080 |
ALLOWED_ORIGIN |
Restrict WebSocket Origin (optional) | (unset = allow) |
RATE_LIMIT_BURST |
Max immediate messages per connection | 20 |
RATE_LIMIT_REFILL_MS |
Token refill interval (ms) | 1000 |
STALE_DISCONNECT_MS |
Purge game after both players disconnected for this long | 300000 (5m) |
MAX_GAME_AGE_MS |
Hard cap to remove very old games | 3600000 (1h) |
npm run build -w @awale/core
npm run build -w @awale/shared
npm run build -w @awale/server
node packages/server/dist/index.js
curl http://localhost:8080/health
docker build -t awale-server:dev packages/server
docker run -p 8080:8080 awale-server:dev
- Mode selector (Play vs AI / Play Online)
- Create flow (display game code + share button)
- Join flow (enter code → live board sync)
- Reconnect handling (token reuse – future)
- Build & push image to ACR or Docker Hub.
- Deploy Azure Container App with ingress (external) on
wsspath/ws. - Configure env vars for origin + limits.
- Point frontend to server WebSocket URL (e.g.
wss://<yourapp>.azurecontainerapps.io/ws). - Future: add persistence (Redis or Table Storage) for reconnection & stats.
Backend core logic merged in feature branch. Frontend integration & documentation polishing in progress. Track tasks in repo TODO / issues.
The bot-era Azure Function is NOT used for realtime multiplayer. Deploy the new WebSocket server as a container.
Using francecentral for all resources (low latency from EU). Adjust names to be globally unique.
az group create -n awale-rg -l francecentralACR_NAME=awaleacr$RANDOM # choose a unique name if needed
az acr create -n $ACR_NAME -g awale-rg --sku Basic --admin-enabled true
az acr login -n $ACR_NAMEFrom repo root (Dockerfile in packages/server):
IMG_TAG=v0.1.0
docker build -t $ACR_NAME.azurecr.io/awale-server:$IMG_TAG -f packages/server/Dockerfile .
docker push $ACR_NAME.azurecr.io/awale-server:$IMG_TAGaz extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App --wait
az containerapp env create -n awale-env -g awale-rg -l francecentralFRONT_ORIGIN=https://kayasax.github.io
az containerapp create \
-n awale-server \
-g awale-rg \
--environment awale-env \
--image $ACR_NAME.azurecr.io/awale-server:$IMG_TAG \
--target-port 8080 \
--ingress external \
--transport auto \
--registry-server $ACR_NAME.azurecr.io \
--env-vars ALLOWED_ORIGIN=$FRONT_ORIGIN RATE_LIMIT_BURST=20 RATE_LIMIT_REFILL_MS=1000 \
--query properties.configuration.ingress.fqdn -o tsvNote the FQDN returned (e.g. awale-server.<hash>.francecentral.azurecontainerapps.io).
FQDN=$(az containerapp show -n awale-server -g awale-rg --query properties.configuration.ingress.fqdn -o tsv)
curl https://$FQDN/healthExpect {"status":"ok","games":0}.
Set WebSocket env variable for dev/prod builds:
VITE_AWALE_SERVER_WS=wss://$FQDN/ws
For GitHub Pages you can inject it at build time:
VITE_AWALE_SERVER_WS=wss://$FQDN/ws npm run build -w @awale/webThen deploy (existing Pages workflow will publish dist).
Open two browser windows of the GitHub Pages site:
- Create Online Game → copy code
- Join Online Game → paste code Moves should sync and captures broadcast.
NEW_TAG=v0.1.1
docker build -t $ACR_NAME.azurecr.io/awale-server:$NEW_TAG -f packages/server/Dockerfile .
docker push $ACR_NAME.azurecr.io/awale-server:$NEW_TAG
az containerapp update -n awale-server -g awale-rg --image $ACR_NAME.azurecr.io/awale-server:$NEW_TAGClients reconnect automatically (basic reconnect backoff already implemented).
- Add min / max replicas:
az containerapp scale update -n awale-server -g awale-rg --min-replicas 0 --max-replicas 3
- Add a simple concurrent connection rule (future): KEDA websocket scaling or HTTP RPS rule once metrics needed.
- Consider instrumentation (App Insights / OpenTelemetry) later.
az group delete -n awale-rg --no-wait -y| Symptom | Check |
|---|---|
| WebSocket fails immediately | ALLOWED_ORIGIN matches exact scheme + origin? Ingress FQDN correct? |
No created message |
Wrong VITE_AWALE_SERVER_WS or network blocked; verify /health works first |
| Rate limit errors | Lower burst or too many rapid clicks; adjust RATE_LIMIT_BURST |
| Stale games accumulate | Tune STALE_DISCONNECT_MS / MAX_GAME_AGE_MS |
This section finalizes the previously "Planned Outline" into actionable Azure CLI steps.