This project has been created as part of the 42 curriculum by jmondon, hgamiz-g, chanin and mjeannin.
ft_transcendence is a full-stack multiplayer RPG centered on progression at 42: your academic progress drives character customization and power. The social web layer is intentionally minimal, focused on identity, friends, and lightweight communication to support the game experience. Players authenticate with their 42 Intra accounts and use their project progress to build and personalize characters across two game modes.
- 42 OAuth2 + Local Authentication — Log in via 42 Intra OAuth2; once validated, a local email/password can also be set. Protected with JWT (HttpOnly cookies), CSRF tokens, rate limiting, and account lockout.
- User Profiles & Accounts — Lightweight profile management linked to 42 Intra data (login, avatar, cursus level, coalition, approved projects).
- Friends System — Core social layer for connecting players.
- Real-time Chat — WebSocket-powered messaging for basic coordination.
- Multiplayer RPG Core — Turn-based RPG with five character classes (Fighter, Ranger, Rogue, Mage, Barbarian), stat system (STR/DEX/CON/INT), skill system (Melee/Ranged/Magic), a talent tree, spell casting with cooldowns and buffs, and a weapon system.
- Three Game Modes — Local PvE (player vs AI), plus multiplayer in two variants: free-for-all and team-based.
- Progress-Driven Customization — 42 project scores become customization points, making school progress the engine of character growth.
- Real-time Monitoring Dashboard — Live event stream (SSE) visualizing system activity across all microservices.
- Microservices Architecture — Fully containerized, each domain in its own service with an isolated database.
- Nginx Reverse Proxy — SSL/TLS termination on port 8443, routing HTTP and WebSocket traffic.
| Login | Full Name | Role(s) | Responsibilities |
|---|---|---|---|
hgamiz-g |
Héctor Gámiz | PO / Developer | Game desing, backend micro-services and game-logic developer, task distribution |
mjeannin |
Matteo Jeannin | Tech Lead / Developer | Architecture design, tech stack, infrastructure, front-end development |
jmondon |
Juan Mondón | PM / Developer | Project management, OAuth2 flow, database schema |
chanin |
Chris Hanin | Developer | 3D renderer, front-end developer |
- Task Distribution: Core project tasks, micro-services and frontend/backend
- Meetings: weekly meetings for project priorities, daily stand-ups. Depending on team members availability they were held physically and sometimes via Discord.
- Project Management Tool: Jira for tasks and feature priorities.
- Communication: Discord server with dedicated channels for different aspects of the project (Frontend, backend, api, git, game-dev).
- Version Control Strategy: Feature branches, small commits, pull requests and code reviews.
| Technology | Version | Purpose |
|---|---|---|
| React | 18.3.1 | UI framework |
| TypeScript | 5.6 | Type safety |
| Vite | 5.4 | Build system & hot-reload |
| React Router | v7 | Client-side routing |
| Tailwind CSS | 3.4 | Utility-first styling |
Three.js + @react-three/fiber |
0.182 | 3D game rendering |
@react-three/drei |
9.117 | Three.js helpers |
| Framer Motion | 11 | UI animations |
| Socket.io-client | 4.7 | WebSocket client |
| Recharts | 2.15 | Monitoring charts |
| Technology | Version | Purpose |
|---|---|---|
| Node.js | 20 LTS | Runtime |
| NestJS | 10 | Framework for Auth, Users, Player, Chat, Friends, Health, Game services |
| TypeORM | 0.3 | ORM for PostgreSQL |
| Socket.io | 4 | WebSocket server (Chat, Game) |
| Express | 4 | Lightweight services (Monitor) |
| Nginx | Alpine | Reverse proxy + SSL termination |
API Gateway — Custom NestJS HTTP+WebSocket proxy that routes all client traffic to the appropriate microservice.
| Service | Database | Rationale |
|---|---|---|
Accounts (users) |
PostgreSQL 16 | Relational data, JSONB for flexible 42 profile data, UNIQUE constraints |
| Player | PostgreSQL 16 | Relational structure for character stats, JSONB for talents/inventory/spells |
| Friends | PostgreSQL 16 | Relational join tables for friendship graph |
| Chat | PostgreSQL 16 | Message history and channel management |
Why PostgreSQL? Chosen for ACID compliance, excellent JSONB support (used for flexible 42 API data and game item arrays), mature TypeORM integration, and the fact that all services follow the Database-per-Service pattern — each service owns and exclusively accesses its own database instance, ensuring domain isolation and independent scalability.
- Docker & Docker Compose — Every service is containerized. Three isolated Docker networks (frontend-net, backend-net, service-net) enforce strict communication boundaries.
- Nginx — SSL/TLS termination, HTTP and WebSocket reverse proxy on port 8443.
- Makefile — Top-level task runner for building, starting, and cleaning the stack.
- Microservices over monolith — Design choice. Enables teams to develop, deploy, and scale each domain (auth, game, chat, friends) independently.
- NestJS — Provides a structured, opinionated framework with built-in support for WebSockets, guards, pipes, interceptors, and DI, reducing boilerplate in each service.
- Database-per-Service — Prevents tight coupling between domains; a schema migration in Player does not affect the Chat service.
- JSONB columns —
profile_data(accounts) andtalents/inventory/spells(player) use JSONB so game data can evolve without SQL migrations, while still being queryable via PostgreSQL's GIN indexes. - JWT in HttpOnly cookies + CSRF — Prevents XSS token theft while CSRF tokens guard against cross-site request forgery.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | Internal universal identifier |
ft_id |
int |
UNIQUE, NOT NULL | 42 Intra user ID |
login |
varchar(50) |
UNIQUE, NOT NULL | 42 login (e.g. jdoe) |
email |
varchar(255) |
UNIQUE, NOT NULL | Institutional email |
image_url |
varchar(500) |
NULLABLE | Avatar URL from 42 API |
profile_data |
jsonb |
NOT NULL, default {} |
Flexible 42 API data (name, level, coalition, projects, scores…) |
password |
varchar(255) |
NULLABLE, hidden | bcrypt hash — set after OAuth2 first login; excluded from normal queries |
registration_complete |
boolean |
NOT NULL, default false |
True once local password is set |
failed_login_attempts |
int |
NOT NULL, default 0, hidden |
Consecutive failed local-auth attempts (brute-force protection) |
locked_until |
timestamptz |
NULLABLE, hidden | Account lockout expiry timestamp |
customization_points |
int |
NOT NULL, default 0 |
Total accumulated customization points |
status_message |
varchar(280) |
NULLABLE | User-set status message |
custom_avatar |
text |
NULLABLE | Base64 or URL of user-uploaded avatar |
privacy_settings |
jsonb |
NOT NULL | Visibility toggles: showLevel, showProjects, showStats, showFriends, showStatus |
last_seen_at |
timestamptz |
NULLABLE, INDEX | Last activity timestamp |
created_at |
timestamptz |
NOT NULL | Auto-generated |
updated_at |
timestamptz |
NOT NULL | Auto-updated |
last_login_at |
timestamptz |
NULLABLE | Last successful login timestamp |
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | Character UUID |
user_id |
uuid |
NOT NULL, INDEX | Logical ref to user_accounts.id (no FK — separate DB) |
name |
varchar(50) |
NOT NULL, INDEX | Character name |
class_name |
enum |
NOT NULL, INDEX | fighter | mage | rogue | ranger | barbarian |
str |
int |
NOT NULL, default 5 |
Strength (1–10) |
dex |
int |
NOT NULL, default 5 |
Dexterity (1–10) |
int |
int |
NOT NULL, default 5 |
Intelligence (1–10) |
con |
int |
NOT NULL, default 5 |
Constitution (1–10) |
melee |
int |
NOT NULL, default 0 |
Melee skill (0–5) |
ranged |
int |
NOT NULL, default 0 |
Ranged skill (0–5) |
magic |
int |
NOT NULL, default 0 |
Magic skill (0–5) |
max_hp |
int |
NOT NULL, default 25 |
15 + con × 2 |
current_hp |
int |
NOT NULL, default 25 |
Current HP |
movement |
int |
NOT NULL, default 10 |
6 + DEX_mod |
magic_points |
int |
NOT NULL, default 10 |
Current MP pool |
talent_points |
int |
NOT NULL, default 10 |
Available talent points |
max_customization_points |
int |
NOT NULL, default 20 |
Max customization points ever earned |
current_customization_points |
int |
NOT NULL, default 20 |
Remaining customization points to spend |
talents |
jsonb |
NOT NULL, default [] |
Array of unlocked talent IDs |
inventory |
jsonb |
NOT NULL, default [] |
Array of owned item IDs |
spells |
jsonb |
NOT NULL, default [] |
Array of known spell IDs |
equipped_talents |
jsonb |
NOT NULL, default [] |
Active equipped talent IDs |
equipped_spells |
jsonb |
NOT NULL, default [] |
Active equipped spell IDs |
equipped_weapons |
jsonb |
NOT NULL, default [] |
Active equipped weapon IDs |
equipped_items |
jsonb |
NOT NULL, default [] |
Active equipped item IDs |
equipped_potion |
varchar |
NULLABLE | Equipped potion ID |
created_at |
timestamptz |
NOT NULL | Auto-generated |
updated_at |
timestamptz |
NOT NULL | Auto-updated |
Note: The relationship between
user_accountsandcharactersis logical, not a foreign key — the two tables live in separate databases. Referential integrity is enforced at the application layer. A user may have multiple characters of any class.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | Friendship record UUID |
requester_id |
uuid |
NOT NULL, INDEX | User who sent the friend request |
addressee_id |
uuid |
NOT NULL, INDEX | User who received the request |
status |
enum |
NOT NULL, default pending |
pending | accepted | rejected | blocked |
created_at |
timestamptz |
NOT NULL | Auto-generated |
updated_at |
timestamptz |
NOT NULL | Auto-updated |
Constraint:
UQ_friendship_pair— unique on(requester_id, addressee_id)prevents duplicate rows. Composite index on(addressee_id, status)for efficient inbox queries.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | Conversation UUID |
user1_id |
uuid |
NOT NULL, INDEX | Participant with lower UUID (enforced for uniqueness) |
user2_id |
uuid |
NOT NULL, INDEX | Participant with higher UUID |
created_at |
timestamptz |
NOT NULL | Auto-generated |
updated_at |
timestamptz |
NOT NULL | Auto-updated |
Constraint:
UQ_conversation_pair— unique on(user1_id, user2_id). The pair is always stored withuser1_id < user2_idto guarantee a single row per pair regardless of who initiates.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
uuid |
PK | Message UUID |
conversation_id |
uuid |
NOT NULL, INDEX, FK → conversations.id CASCADE |
Parent conversation |
sender_id |
uuid |
NOT NULL | Sending user's ID |
content |
text |
NOT NULL | Message body |
is_read |
boolean |
NOT NULL, default false |
Read receipt flag |
created_at |
timestamptz |
NOT NULL | Auto-generated (send timestamp) |
| # | Feature | Owner(s) | Description |
|---|---|---|---|
| 1 | OAuth2 Login (42 Intra) | jmondon | Full OAuth2 code-exchange flow with 42 API, sets HttpOnly JWT cookie |
| 2 | Local email/password auth | jmondon | Post-OAuth2 password registration, bcrypt hashing, rate limiting, account lockout |
| 3 | CSRF protection | jmondon | HMAC-SHA256 CSRF tokens validated on all state-changing requests |
| 4 | JWT refresh flow | jmondon | Sliding-window refresh tokens with cookie rotation |
| 5 | User profiles | jmondon | Avatar, display name, 42 profile data (level, coalition, projects) |
| 6 | Friends system | jmondon | Send/accept/reject friend requests, block users |
| 7 | Real-time chat | jmondon / chanin | WebSocket messaging, private channels, friend-gated rooms |
| 8 | Character creation | mjeannin / hgamiz-g | Choose from 5 RPG classes; stats auto-set from class template |
| 9 | Character stats & derived values | hgamiz-g | AC = 10 + DEX_mod; MaxHP = 15 + CON×2; attack bonuses by skill |
| 10 | Turn-based combat engine | hgamiz-g | 1d20 attack rolls, damage (1d6 + stat mod), initiative (DEX_mod), crits/fumbles |
| 11 | Spell casting / Special abilities system | hgamiz-g | MP cost validation, target resolution, cooldowns, area effects |
| 12 | Buff/debuff system | hgamiz-g | Duration tracking, stat recomputation on expiry, periodic effects |
| 13 | Talent tree | hgamiz-g | Class-specific talents unlocked via 42 project score points |
| 14 | Weapon system | hgamiz-g | Weapon types mapped to class skills, equip/unequip logic |
| 15 | 42-progress → game progression | hgamiz-g | Project scores converted to customization points (2 + floor((mark−80)/10)) |
| 16 | API Gateway | mjeannin | HTTP + WebSocket proxy, service registry, CORS, security headers |
| 17 | Real-time monitor dashboard | mjeannin | SSE event stream, event creation, statistics charts, type filtering |
| 18 | Service health checks | mjeannin | Health endpoint across all services, exposed through gateway |
| 19 | Nginx SSL reverse proxy | mjeannin | TLS termination on :8443, HTTP and WS routing, self-signed cert for dev |
| 20 | Inter-service authentication | hgamiz-g | Shared HMAC secret (x-service-secret header) on all internal calls |
| 21 | Docker containerisation | mjeannin | Per-service Dockerfiles, multi-stage builds, hot-reload in dev |
| 22 | 3D frontend rendering | chanin | Three.js scene via @react-three/fiber for the game viewport |
| 23 | Animated UI | mjeannin | Firefly particle background, Framer Motion transitions |
| 24 | AI Opponents | chanin | Local game mode (player vs IA) |
The ft_transcendence subject scores modules as: Major = 2 pts, Minor = 1 pt. Total required: 14 pts minimum.
| # | Module | Type | Points | Owner(s) | Implementation Notes |
|---|---|---|---|---|---|
| 1 | Use a framework for both the frontend and backend (React + NestJS) | Major | 2 | mjeannin / hgamiz-g | All stateful microservices (Auth, Users, Player, Chat, Friends, Health) built with NestJS 10. Frontend design using React. |
| 2 | Implement real-time features using WebSockets or similar technology (Websockets) | Major | 2 | jmondon / chanin | Chat across the platform and in-game |
| 3 | Allow users to interact with other users | Major | 2 | jmondon / chanin | Chat system, profile system and friends system |
| 4 | Use an ORM for the database (PostgreSQL) | Minor | 1 | jmondon / hgamiz-g | Database-per-Service pattern; 4 independent PostgreSQL 16 instances managed via TypeORM |
| 5 | Standard user management, authentication | Major | 2 | jmondon | Users can update their profile information, upload an avatar, add other users as friends and see their online status. Users have a profile page displaying their information |
| 6 | Implementing a remote authentication (42 OAuth2) | Minor | 1 | jmondon | 42 Intra API OAuth2 code-exchange, token validation, auto user creation |
| 7 | AI Opponent for games | Major | 2 | chanin | Users can play local games against an AI |
| 8 | Implement a complete web-based game where users can play against each other | Major | 2 | whole team | The core of the project. Turn based rpg with focus on team strategy |
| 9 | Remote players — Enable two players on separate computers to play the same game in real-time | Major | 2 | whole team | Disconnection/Reconnection handlers, smooth user experience from different computers |
| 10 | Multiplayer game (more than two players) | Major | 2 | whole team | Multiple concurrent independent game rooms, each supporting up to 6 players each by design for game mechanics flow, but can be extended if needed. Finished rooms are cleaned up automatically after 30 s. |
| 11 | Implement advanced 3D graphics (Three.js) | Major | 2 | chanin | 3D environment, characters and animations. Camera movement system. |
| 12 | Game customization options | Minor | 1 | hgamiz-g / mjeannin | Character customization based on 42 progress, character customization shop and in-game special abilities |
| 13 | Spectator mode | Minor | 1 | chanin | Users can join any game as spectators, watch it in real-time and use the game chat |
| 14 | Backend as microservices | Major | 2 | team | Separated responsabilities among different services, each can run on its own. REST APIs for communication. |
| 15 | Music and sound effects | Major | 2 | chanin | Music and sound effects during game, volume controls on the interface |
| 16 | Custom-made design system | Minor | 1 | mjeannin | Reusable components, including a proper color palette, typography, and icons |
Total: 27 pts
- Designed and implemented the full game mechanics layer in JavaScript/TypeScript: character classes, TurnManager, d20 combat system, spell casting with cooldowns and area effects, buff/debuff system, and talent tree
- Built and maintained five backend microservices (lobby, game, character, coalition, customization) with their full REST API surface
- Integrated backend services with PostgreSQL (via TypeORM) and with the frontend (game state polling, action dispatch, combat log)
- Designed the multi-game, multi-player infrastructure to support concurrent game rooms with independent state
- Defined the 42-progress → customization-points conversion system, linking academic scores to in-game character growth
- Challenges: The game logic went through several major refactoring cycles as new features (team modes, spectators, spell system, talent tree) were added mid-development. The main difficulty was keeping the combat engine consistent and reliable while being consumed simultaneously by the game frontend, the lobby service, the character service, and the database layer — each with different data contracts and timing requirements.
- Architected and deployed a multi-database infrastructure using PostgreSQL and TypeORM, ensuring strict data isolation between microservices (Accounts, Player, Chat, and Auth).
- Developed a Hybrid Authentication System: Integrated a full OAuth2 code-exchange flow with the 42 API, complemented by a mandatory Bcrypt-hashed email/password registration to meet security standards.
- Engineered Advanced Security Layers: Implemented HttpOnly JWT cookie rotation, sliding-window refresh tokens, and HMAC-SHA256 CSRF protection for all state-changing requests.
- Built Real-time Communication Infrastructure: Developed a platform-wide WebSocket system supporting private messaging and friend-gated rooms.
- User & Social Ecosystem: Designed and implemented the full-stack logic for User Profiles (syncing 42 API data like levels and coalitions) and a comprehensive Friends System (requests, blocking, and real-time status).
- Challenges: The primary difficulty was orchestrating the "Post-OAuth" registration flow. Balancing the requirement for local password security (hashing/salting) with the exclusivity of 42 Intra students required a complex middleware logic to validate identity before allowing credential creation. Additionally, maintaining real-time consistency across multiple isolated database instances required a rigorous synchronization strategy between the Gateway and the backend microservices.
- Led the frontend implementation and overall visual direction of the site, defining the design system (color palette, typography, icons) and reusable UI components.
- Built the character-creation frontend flow, including class selection and the customization UI tied to 42 progress.
- Implemented the API Gateway (HTTP + WebSocket proxy), service registry, and security headers; wired health checks for all services.
- Delivered the infrastructure glue: Dockerfiles and Compose wiring, Nginx SSL reverse proxy on
:8443, and animated UI polish (Framer Motion, background effects). - Responsible for maintaining codebase consistency across the project, defining and sustaining the technical stack, and managing the organization of the GitHub repository.
- Participated on game design.
- Challenges: The main difficulty was keeping the frontend experience cohesive while integrating multiple backend services through the gateway, and ensuring the customization/creation flow remained responsive and consistent with backend constraints (auth state, profile sync, and character data) without breaking the site's overall visual coherence.
- Built the entire 3D game frontend using React Three Fiber and Three.js: game scene with configurable camera, shadow-mapped lighting, OrbitControls, and WebGL crash recovery
- Implemented 3D character rendering with KayKit Adventurers models, 10 animation states (idle, walk, attack, death, etc.) with transitions, and weapon attachment to hand bones
- Created the medieval arena environment (castle walls, towers, torches with animated flame, banners, props, trees) and the game board with multi-layer cell highlighting (movement, attack, team)
- Developed the full AI opponent system for local mode
- Designed and implemented the sound system mapping 18 game events to audio files, random variant selection, master volume control, and SFX mute toggle
- Built the spectator mode with dedicated join/leave lifecycle
- Implemented front-end-side game engine for local play: d20 combat, Manhattan distance movement, buff tick-down, talent passives, AP system, and spell casting
- Created all in-game UI components: HUD (action bar, turn banner), initiative roll animation, class selection, lobby screen, game overlays, combat log, sidebars, player stats panel, and action panel
- Challenges: The main difficulty was keeping the 3D rendering layer synchronized with both the local game engine and the remote server state. Animation timing had to be carefully sequenced (walk → pause → attack → hit reaction) so that AI turns were visually readable, while multiplayer state polling required a versioning system to prevent stale server responses from overwriting the results of the player's own actions. Additionally, skeleton retargeting across different model rigs (Medium vs Large for Barbarian) and reliable hand-bone detection for weapon attachment (10+ bone-naming conventions across asset packs) demanded extensive trial-and-error.
| Requirement | Minimum Version | Notes |
|---|---|---|
| Docker | 24.x | docker --version |
| Docker Compose | v2 (plugin) | docker compose version |
| GNU Make | 4.x | make --version |
| 42 OAuth2 app credentials | — | Register at profile.intra.42.fr/oauth/applications |
| OpenSSL | any | For generating secrets |
Node.js is not required on the host — all builds happen inside Docker. Install it locally only if you want IntelliSense (
make install-local).
git clone https://github.com/<org>/ft_transcendence.git
cd ft_transcendencecp env_example .envOpen .env and fill in every value:
IMPORTANT: for all the features to work, use your IP and NOT localhost
# 42 OAuth2 — obtain from https://profile.intra.42.fr/oauth/applications
FT_CLIENT_ID=<your_client_id>
FT_CLIENT_SECRET=<your_client_secret>
FT_REDIRECT_URI=https://<YOUR_IP_OR_HOSTNAME>:8443/api/auth/callback
# JWT — generate with: openssl rand -base64 128
JWT_SECRET=<long_random_secret>
JWT_EXPIRATION=7d
# App URL
FRONTEND_URL=https://<YOUR_IP_OR_HOSTNAME>:8443
# Inter-service secret — generate with: openssl rand -hex 32
INTERNAL_SERVICE_SECRET=<32_hex_chars>
# CSRF secret — generate with: openssl rand -hex 32
CSRF_SECRET=<32_hex_chars>
# Cookie settings (keep defaults for local dev)
COOKIE_SECURE=true
COOKIE_SAMESITE=laxSelf-signed certificate: Nginx ships a self-signed cert for local development. Your browser will show a security warning — click "Advanced → Proceed" (or add the cert to your trust store).
42 OAuth2 redirect URI: The application registered in 42 Intra must have
https://<YOUR_IP_OR_HOSTNAME>:8443/api/auth/callbacklisted as an allowed redirect URI.
make allThis runs docker compose up --build -d for every service and then installs local Node.js dependencies for editor IntelliSense.
The stack takes ~60–90 seconds on first build (downloading base images + installing npm packages inside containers). Subsequent starts are faster.
docker compose ps # all containers should show "Up"
curl -k https://localhost:8443/api/health # should return {"status":"ok"}Open https://localhost:8443 in your browser.
| Command | Description |
|---|---|
make all |
Build images and start all containers (background) |
make restart |
Stop and restart without rebuilding |
make clean |
Stop all containers (docker compose down) |
make fclean |
Full teardown: containers, images, volumes, node_modules |
make install-local |
Install node_modules locally (for VS Code IntelliSense) |
docker compose logs -f <service> |
Tail logs for a specific service |
docker compose exec <service> sh |
Open a shell in a running container |
- Game state via REST polling — The game service is REST-only for game state; the frontend polls every 500 ms. Real-time chat, however, is fully WebSocket-based (Socket.io) across the whole platform.
- Game room player cap — Each game room supports up to 6 players (
MAX_PLAYERS = 6ingame.instance.ts), but can be modified. - Self-signed TLS — Browser trust warning on first visit; acceptable for local dev/evaluation.
The visual assets for the game created by Kay Lousberg used in this project are distributed under the Creative Commons CC0 1.0 Universal license. These assets are part of the public domain and may be used, modified, and redistributed without restriction or attribution. They have been included in this project in accordance with the terms of the CC0 license.
- React documentation
- Vite documentation
- NestJS documentation
- TypeORM documentation
- Three.js documentation
- Tailwind CSS documentation
- Socket.io documentation
- Docker documentation
- PostgreSQL 16 documentation
- 42 API documentation
- OAuth 2.0 RFC 6749
- JWT RFC 7519
- Microservices with NestJS — official guide
- Database-per-Service pattern — microservices.io
- CSRF prevention cheat sheet — OWASP
- D&D 5e SRD (combat mechanics inspiration)
- Three.js journey — Bruno Simon
- Framer Motion docs
AI assistance was used throughout the project for the following purposes:
| Task | Tool used | Parts of the project |
|---|---|---|
| Test scripts | Copilot | Game-logic |
| Drafting technical documentation | Copilot | Use guides for API endpoints to share across the team |
| Code checks | Copilot | Backend |
| Generating game balance numbers | ChatGPT | Game design |
AI was not used as a black box: all generated code was reviewed, tested, and integrated by team members who understand it fully.