Skip to content

AI: opponent first integration#63

Open
lisambet wants to merge 39 commits intomainfrom
61-ai-opponent-integration
Open

AI: opponent first integration#63
lisambet wants to merge 39 commits intomainfrom
61-ai-opponent-integration

Conversation

@lisambet
Copy link
Collaborator

@lisambet lisambet commented Feb 3, 2026

This pull request introduces a new RL-trained Pong AI service and integrates it throughout the stack, enabling users to play against an AI opponent. It adds new backend endpoints for AI interaction, updates the gateway to proxy AI-related requests, and enhances the frontend to support AI game sessions. Additionally, it includes supporting infrastructure updates, such as Docker and service orchestration, and some dependency upgrades.

Pong AI Service Integration

  • Added a new pong-ai-service to both docker-compose.yml and dev-docker-compose.yml, including build context, environment variables, healthcheck, and model volume mounting. [1] [2]
  • Added a Dockerfile and a README.md for the Pong AI service, describing setup, training, API, and usage. [1] [2]

Backend API Enhancements for RL/AI

  • Implemented new endpoints in the game backend for RL/AI integration: /rl/reset, /rl/step, and /rl/state for game session management and step-wise control. [1] [2] [3]
  • Extended the GameState interface and game engine to expose ball velocity, needed for RL environments. [1] [2]

Gateway and Proxy Updates

  • Added a new controller and route registration in the gateway to proxy /pong-ai/join-game requests to the Pong AI service, supporting both GET and POST. [1] [2]
  • Updated gateway constants to allowlist new RL endpoints for rate limiting and CORS.

Frontend (Game UI) Enhancements

  • Added a "Create game with AI" button and implemented the workflow to create a session, invite the AI, and display status/logs to the user. [1] [2] [3]

Infrastructure and Miscellaneous

  • Updated Makefile to support building, shell access, and logs for the new Pong AI service. [1] [2] [3]
  • Upgraded several dependencies in package.json and package-lock.json for improved compatibility and security. [1] [2] [3] [4] [5]

These changes collectively enable a user to play against a reinforcement learning-based AI opponent in Pong, with all necessary backend, proxy, and frontend support.

Francois Petit and others added 24 commits January 7, 2026 16:16
- bypass auth check for /health in UM
- undo eslint fix in gateway
- Add pong-ai-service to docker-compose and dev-compose
- Fix healthcheck to use GET instead of HEAD
- Add environment variables to .env
- Update requirements.txt with missing dependencies
fix: kept these chages for later
ai-opponent integration
@lisambet lisambet linked an issue Feb 3, 2026 that may be closed by this pull request
13 tasks
@lisambet lisambet changed the title 61 ai opponent integration AI: opponent first integration Feb 3, 2026
@lisambet
Copy link
Collaborator Author

lisambet commented Feb 3, 2026

@copilot

Copy link

Copilot AI commented Feb 3, 2026

@lisambet I've opened a new pull request, #64, to work on those changes. Once the pull request is ready, I'll request review from you.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated 13 comments.

Comments suppressed due to low confidence (1)

srcs/gateway/src/controllers/health.controller.ts:64

  • In healthAllHandler, results[name] is used inside the services.map(...) callback, but name is no longer defined after switching from Object.entries(SERVICES) to Object.values(SERVICES). This will either fail TypeScript compilation or throw a ReferenceError at runtime, and also means your results object is never populated under the intended service keys. You likely want to restore iteration over [name, service] entries or derive a key (e.g. the map key or serviceKey) and use that consistently for both success and error branches.
        const res = await fetch(`https://${service.host}:${service.port}/health`, fetchOptions);
        results[name] = res.status === 200 ? 'healthy' : 'unhealthy';
      } catch (error) {
        results[name] = `unhealthy (error: ${(error as Error).message})`;
      }

Comment on lines 99 to 103
self.model_path = model_path
self.model: Optional[PPO] = None
self.sessions: Dict[str, GameSession] = {}
self.load_model()

Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIService can leave self.model as None when load_model doesn’t find or fails to load a checkpoint, but downstream GameSession instances created from this service call get_ai_action() which blindly does self.model.predict(...). This will raise an AttributeError at runtime as soon as the RL WebSocket/session endpoints try to use the model. Consider either preventing session creation when no model is loaded, or explicitly handling the missing-model case (e.g. by failing fast with a clear error or initializing a fresh model) instead of passing None into GameSession.

Copilot uses AI. Check for mistakes.
Comment on lines 66 to 70
"ball_x": float(self.env.ball_x),
"ball_y": float(self.env.ball_y),
"ball_vx": float(self.env.ball_vx),
"ball_vy": float(self.env.ball_vy),
"ai_paddle_y": float(self.env.ai_paddle_y),
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GameSession.step() assumes the environment exposes attributes like player_paddle_y, ball_x, ball_y, ball_vx, and ball_vy, but the PongEnv implementation in this service only returns observations via _convert_state and never defines these attributes. As written, any call to step() will raise AttributeError on the PongEnv instance, so the /session/create and /ws/game/{session_id} endpoints cannot function correctly. You should either adapt GameSession to work with PongEnv’s API (e.g. operate purely on the observation vector) or extend PongEnv to maintain and expose the fields GameSession expects.

Copilot uses AI. Check for mistakes.
Comment on lines 203 to 212

return {
"status": "success",
"session_id": session_id,
"message": "AI player joined the game",
"paddle": "right"
}

except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This join_game handler has two except Exception as e blocks in a row, and the return after raise HTTPException is unreachable, which makes the control flow confusing and redundant. The second except will never be hit, and the dead return path suggests an earlier implementation that wasn’t fully cleaned up. It would be clearer and less error-prone to keep a single except block here and remove the unreachable return code.

Suggested change
return {
"status": "success",
"session_id": session_id,
"message": "AI player joined the game",
"paddle": "right"
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

Copilot uses AI. Check for mistakes.

def load_model(self):
try:
import os
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import of module os is redundant, as it was previously imported on line 5.

Suggested change
import os

Copilot uses AI. Check for mistakes.
Comment on lines 211 to 212
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This except block handling Exception is unreachable; as this except block also handles Exception.

Suggested change
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

Copilot uses AI. Check for mistakes.
@@ -1 +1 @@
import { Vector2 } from './game.vector.js';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove import
ESLint extension in VSCode can be set up with "Fix all auto-fixable problems"

Suggested change

'/api/users/health',
'/api/game/health',
'/api/block/health',
'/api/game/create-session',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je pense pas que cette route devrait etre dans les PUBLIC_ROUTES. Creer une game session necessite de l'utilisateur qu il soit enregistré.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ces displayProvider.ts sont amené a disparaitre au profit de l'implementation REACT

Copy link
Owner

@codastream codastream left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hâte de le voir s'entrainer et fonctionner avec game.
Il faudrait probablement régler avant de fusionner certains points

  • adapter le service en wss
  • la cohérence des données renvoyées par env et la manière dont elles sont lues dans server
  • éviter d'ajouter des erreurs ESLint : unused imports, no any, ...

Ainsi que

  • l'architecture API : pourquoi get et post pour join-game ? utiliser un préfixe plus court ?
  • le découpage des classes (faire une pour la connexion)
  • éviter d'avoir trop de code en dur pour les url, ports, actions, ...
  • vérifier les deps (requests, pygame) + fixer les versions
  • validation des données entrantes (pydantic ?)

Et quelques optimisations pour maintenant ou plus tard

  • timeout et resiliency patterns (reconnexion, ..)
  • normalisation des valeurs

@@ -1,2 +1,2 @@
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { logger } from '../utils/logger.js';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tu veux que je supprimes import { logger } from '../utils/logger.js'; n'est-ce pas ?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export async function healthAllHandler(req: FastifyRequest) {

});

// POST version - accepts sessionId in body
app.post('/join-game', async (request, reply) => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quel est l'intérêt d'avoir 2 routes pour join game ? POST est peut-être plus approprié

"message": "AI is already in this game"
}

# Create AI player
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

il faudrait trouver un mécanisme pour supprimer les AIPlayer
bloc finally dans webSocket game selon Gemini

self.model = None

def create_session(self, session_id: str) -> GameSession:
if session_id in self.sessions:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if session_id in self.sessions:
if self.model is None:
raise RuntimeError("No model")
if session_id in self.sessions:

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

je ne sais pas comment fonctionnent les checkpoints. il doit y avoir moyen de reduire leur nombre

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c'est fait!

Makefile Outdated
$(D_COMPOSE) up -d --build $(GAME_SERVICE_NAME)
block:
$(D_COMPOSE) up -d --build $(BK_SERVICE_NAME)
pong:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ai ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right!

lisambet and others added 9 commits February 8, 2026 10:29
Makefile
docs/pong-ai.md
srcs/dev-docker-compose.yml
srcs/docker-compose.yml
srcs/pong-ai/Dockerfile
srcs/pong-ai/ai_player.py
srcs/pong-ai/requirements.txt
Co-authored-by: Francois <multicompte+github@outlook.fr>
Co-authored-by: Francois <multicompte+github@outlook.fr>
Co-authored-by: Francois <multicompte+github@outlook.fr>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Owner

@codastream codastream left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

si on utilise pong_500000, peut-on supprimer les autres checkpoints ?
il y a quelques erreurs de linter qui sont faciles a supprimer (unused vars)

import { handleClientMessage } from '../service/game.communication.js';
import { GameSettings } from '../core/game.types.js';
import { WebSocket } from 'ws';
import { WS_CLOSE } from '../core/game.state.js';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

newGameSession,
healthCheck,
gameSettings,
resetGame,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
resetGame,

healthCheck,
gameSettings,
resetGame,
stepGame,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stepGame,

gameSettings,
resetGame,
stepGame,
getGameState,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
getGameState,

@@ -0,0 +1,41 @@
import { FastifyInstance } from 'fastify';
import { proxyRequest, webSocketProxyRequest } from '../utils/proxy.js';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { proxyRequest, webSocketProxyRequest } from '../utils/proxy.js';
import { proxyRequest } from '../utils/proxy.js';

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

est-ce qu'un build multistage builder + runner ferait gagner de la place ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ça fera ~200-300MB économisés,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI opponent integration

4 participants

Comments