Aller au contenu

Devoir sur la Programmation Orientée Objet 2025-2026

(Sujet Baccalauréat NSI du 11 mai 2022 - modifié)

Les participants à un jeu de LaserGame sont répartis en équipes et s'affrontent dans ce jeu de tir, revêtus d'une veste à capteurs et munis d'une arme factice émettant des infrarouges.

Les ordinateurs embarqués dans ces vestes utilisent la programmation orientée objet pour modéliser les joueurs. La classe Joueur est définie comme suit :

class Joueur:
    def __init__(self, pseudo, identifiant, equipe):
        self.pseudo = pseudo
        self.equipe = equipe
        self.id = identifiant
        self.nb_de_tirs_emis = 0
        self.liste_id_tirs_recus = []
        self.est_actif = True

    def tire(self):
        '''méthode déclenchée par l'appui sur la gachette'''
        if self.est_actif == True:
            self.nb_de_tirs_emis = self.nb_de_tirs_emis + 1

    def est_determine(self):
        '''methode qui renvoie True si le joueur réalise un grand nombre de tirs'''
        return self.nb_de_tirs_emis > 500

    def subit_un_tir(self, id_recu):
        '''méthode déclenchée par les capteurs de la veste'''
        if self.est_actif == True:
            self.est_actif = False
            self.liste_id_tirs_recus.append(id_recu)

Question 1

Donner une instruction qui permet de déclarer un objet joueur1, instance de la classe Joueur, correspondant à un joueur dont le pseudo est "Sniper", dont l'identifiant est 319 et qui est intégré à l'équipe "A".

Corrigé
joueur1 = Joueur("Sniper", 319, "A")

Question 2

La méthode subit_un_tir réalise les actions suivantes :

Lorsqu'un joueur actif subit un tir capté par sa veste, l'identifiant du tireur est ajouté à l'attribut liste_id_tirs_recus et l'attribut est_actif prend la valeur False (le joueur est désactivé). Il doit alors revenir à son camp de base pour être de nouveau actif.

a. Écrire la méthode redevenir_actif qui rend à nouveau le joueur actif uniquement s'il était précédemment désactivé.

b. Écrire la méthode nb_de_tirs_recus qui renvoie le nombre de tirs reçus par un joueur en utilisant son attribut liste_id_tirs_recus.

Corrigé

a.

def redevenir_actif(self):
    if self.est_actif == False:
        self.est_actif = True
````
**b.**
```python
def nb_de_tirs_recus(self):
    return len(self.liste_id_tirs_recus)

Question 3

Lorsque la partie est terminée, les participants rejoignent leur camp de base respectif où un ordinateur, qui utilise la classe Base, récupère les données.

La classe Base est définie par :

  • ses attributs :
  • equipe : nom de l'équipe (str). Par exemple, "A",
  • liste_des_id_de_l_equipe qui correspond à la liste (list) des identifiants connus des joueurs de l'équipe,
  • score : score (int) de l'équipe, dont la valeur initiale est 1000 ;

  • ses méthodes :

  • est_un_id_allie qui renvoie True si l'identifiant passé en paramètre est un identifiant d'un joueur de l'équipe, False sinon,
  • incremente_score qui fait varier l'attribut score du nombre passé en paramètre,
  • collecte_information qui récupère les statistiques d'un participant passé en paramètre (instance de la classe Joueur) pour calculer le score de l'équipe.
def collecte_information(self, participant):
    if participant.equipe == self.equipe:  # test 1
        for id in participant.liste_id_tirs_recus:
            if self.est_un_id_allie(id):  # test 2
                self.incremente_score(-20)
            else:
                self.incremente_score(-10)

a. Indiquer le numéro du test (test 1 ou test 2) qui permet de vérifier qu'en fin de partie un participant égaré n'a pas rejoint par erreur la base adverse.

b. Décrire comment varie quantitativement le score de la base lorsqu'un joueur de cette équipe a été touché par le tir d'un coéquipier.

Corrigé

a. Le test 1 permet de vérifier qu'un participant égaré n'a pas rejoint par erreur la base adverse.

b. Le score de la base diminue de 20 points lorsqu'un joueur de cette équipe a été touché par le tir d'un coéquipier.

Question 4

On souhaite accorder à la base un bonus de 40 points pour chaque joueur particulièrement déterminé (qui réalise un grand nombre de tirs).

Compléter la méthode collecte_information

Corrigé
def collecte_information(self, participant):
    if participant.equipe == self.equipe:  # test 1
        for id in participant.liste_id_tirs_recus:
            if self.est_un_id_allie(id):  # test 2
                self.incremente_score(-20)
            else:
                self.incremente_score(-10)
        if participant.est_determine():  # test 3
            self.incremente_score(40)

Question 5

Pour améliorer le système, on souhaite suivre les statistiques de précision des joueurs. On définit le ratio de précision d'un joueur comme étant le rapport entre le nombre de tirs émis par ce joueur et le nombre de joueurs adverses distincts qu'il a touchés.

a. Expliquer pourquoi il est nécessaire d'ajouter un nouvel attribut à la classe Joueur pour pouvoir calculer ce ratio de précision. Proposer un nom et un type pour cet attribut.

b. Modifier la méthode tire pour qu'elle prenne désormais en paramètre optionnel id_cible (l'identifiant du joueur visé, avec la valeur par défaut None). Cette méthode doit mettre à jour le nouvel attribut créé en (a.) uniquement si le tir a effectivement touché un joueur adverse (c'est-à-dire si id_cible n'est pas None et n'est pas déjà présent dans la liste).

c. Écrire la méthode ratio_precision qui renvoie le ratio de précision du joueur. Cette méthode doit renvoyer 0 si le joueur n'a touché aucun adversaire distinct, et doit gérer le cas où le joueur n'a émis aucun tir.

Corrigé

a. Il est nécessaire d'ajouter un attribut pour stocker les identifiants des joueurs adverses distincts touchés par le joueur. On peut nommer cet attribut liste_id_adversaires_touches et le type sera une liste (list).

b.

def tire(self, id_cible=None):
    '''méthode déclenchée par l'appui sur la gachette'''
    if self.est_actif == True:
        self.nb_de_tirs_emis = self.nb_de_tirs_emis + 1
        if id_cible is not None and id_cible not in self.liste_id_adversaires_touches:
            self.liste_id_adversaires_touches.append(id_cible)

c.

def ratio_precision(self):
    if self.nb_de_tirs_emis == 0:
        return 0
    nb_adversaires_touches = len(self.liste_id_adversaires_touches)
    if nb_adversaires_touches == 0:
        return 0
    return self.nb_de_tirs_emis / nb_adversaires_touches

Question 6

On souhaite maintenant implémenter un système de classement des joueurs. Pour cela, on crée une classe Partie qui gère l'ensemble des joueurs et des bases.

La classe Partie possède les attributs suivants :

  • liste_joueurs : liste de tous les joueurs (instances de Joueur)
  • dictionnaire_bases : dictionnaire où les clés sont les noms d'équipes et les valeurs sont les instances de Base correspondantes

a. Écrire la méthode meilleur_tireur_equipe de la classe Partie qui prend en paramètre le nom d'une équipe (str) et qui renvoie le pseudo du joueur de cette équipe ayant émis le plus grand nombre de tirs. Si plusieurs joueurs sont ex-aequo, la méthode renvoie le pseudo du premier joueur rencontré dans la liste. Si l'équipe n'a aucun joueur, la méthode renvoie None.

b. Écrire la méthode joueur_le_plus_vise de la classe Partie qui renvoie un tuple contenant le pseudo du joueur ayant reçu le plus de tirs et le nombre de tirs reçus. En cas d'égalité, renvoyer le premier joueur rencontré.

c. On souhaite détecter les comportements suspects. Écrire la méthode detecter_tirs_fratricides_excessifs de la classe Partie qui renvoie une liste contenant les identifiants des joueurs ayant tiré sur leurs coéquipiers plus de 5 fois.

Corrigé

a.

def meilleur_tireur_equipe(self, nom_equipe):
    meilleur_tireur = None
    max_tirs = -1
    for joueur in self.liste_joueurs:
        if joueur.equipe == nom_equipe:
            if joueur.nb_de_tirs_emis > max_tirs:
                max_tirs = joueur.nb_de_tirs_emis
                meilleur_tireur = joueur.pseudo
    return meilleur_tireur

b.

def joueur_le_plus_vise(self):
    joueur_vise = None
    max_tirs_recus = -1
    for joueur in self.liste_joueurs:
        nb_tirs_recus = len(joueur.liste_id_tirs_recus)
        if nb_tirs_recus > max_tirs_recus:
            max_tirs_recus = nb_tirs_recus
            joueur_vise = joueur.pseudo
    return (joueur_vise, max_tirs_recus)

c.

def detecter_tirs_fratricides_excessifs(self):
    suspects = []
    for joueur in self.liste_joueurs:
        tirs_fratricides = 0
        for id in joueur.liste_id_tirs_recus:
            if id in [j.id for j in self.liste_joueurs if j.equipe == joueur.equipe]:
                tirs_fratricides += 1
        if tirs_fratricides > 5:
            suspects.append(joueur.id)
    return suspects