Licence CC BY-SA

TP

L’objectif maintenant va être de reprendre notre code et de placer des dictionnaires aux endroits adéquats. Notamment remplacer les multiples variables associées à un seul monstre par des dictionnaires représentant la hiérarchie de nos données.

Structurer les données

Un monstre devient alors une structure avec un nom et une liste d’attaques. Et une attaque se compose simplement d’un nombre de dégâts.

Dans le chapitre sur les dictionnaires, je présentais comment les utiliser pour former des données composites, avec un exemple correspondant à notre TP. En repartant de cet exemple on va pouvoir structurer facilement nos monstres et nos attaques.

monsters = {
    'pythachu': {
        'name': 'Pythachu',
        'attacks': ['tonnerre', 'charge'],
    },
    'pythard': {
        'name': 'Pythard',
        'attacks': ['jet-de-flotte', 'charge'],
    },
    'ponytha': {
        'name': 'Ponytha',
        'attacks': ['brûlure', 'charge'],
    },
}

attacks = {
    'charge': {'damages': 20},
    'tonnerre': {'damages': 50},
    'jet-de-flotte': {'damages': 40},
    'brûlure': {'damages': 40},
}

L’avantage de cette structure c’est que les attaques sont pleinement séparées des monstres, ce qui permet de ne pas les répéter quand elles sont partagées entre plusieurs monstres.

Le second avantage c’est qu’on est maintenant en mesure de définir précisément quel monstre peut utiliser quelle attaque, et donc d’avoir une meilleure validation à ce niveau.

Pour commencer le jeu, on demandera toujours aux deux joueurs de choisir leur monstre et d’indiquer les points de vie associés, mais on pourra maintenant vérifier qu’il s’agit d’un monstre que l’on connaît.

print('Monstres disponibles :')
for monster in monsters.values():
    print('-', monster['name'])

players = []

print('Joueur 1, quel monstre choisissez-vous ?')
name = input('> ').lower()
while name not in monsters:
    print('Monstre invalide')
    name = input('> ').lower()
pv = int(input('Quel est son nombre de PV ? '))
players.append({'id': '1', 'monster': name, 'pv': pv})

Le fait d’utiliser des dictionnaires pour représenter nos joueurs nous permet aussi de les stocker dans une liste plutôt que dans deux variables distinctes, et donc d’éviter les répétitions que l’on avait dans nos traitements (puisque l’on va pouvoir appliquer une boucle sur cette liste).

Solution

On retrouve maintenant la solution à ce TP, qui se rapproche de plus en plus d’un vrai système de combat.

monsters = {
    'pythachu': {
        'name': 'Pythachu',
        'attacks': ['tonnerre', 'charge'],
    },
    'pythard': {
        'name': 'Pythard',
        'attacks': ['jet-de-flotte', 'charge'],
    },
    'ponytha': {
        'name': 'Ponytha',
        'attacks': ['brûlure', 'charge'],
    },
}

attacks = {
    'charge': {'damages': 20},
    'tonnerre': {'damages': 50},
    'jet-de-flotte': {'damages': 40},
    'brûlure': {'damages': 40},
}

print('Monstres disponibles :')
for monster in monsters.values():
    print('-', monster['name'])

players = []

# Boucle pour créer 2 joueurs sans se répéter
for i in range(2):
    player_id = i + 1
    print('Joueur', player_id, 'quel monstre choisissez-vous ?')

    name = input('> ').lower()
    while name not in monsters:
        print('Monstre invalide')
        name = input('> ').lower()

    pv = int(input('Quel est son nombre de PV ? '))
    players.append({'id': player_id, 'monster': monsters[name], 'pv': pv})

print()
print(players[0]['monster']['name'], 'affronte', players[1]['monster']['name'])
print()

# Représente les tours de jeu, liste de couples (joueur, opposant)
turns = [
    (players[0], players[1]),
    (players[1], players[0]),
]

while players[0]['pv'] > 0 and players[1]['pv'] > 0:
    # On effectue les deux tours de jeu
    for player, opponent in turns:
        # Le joueur ne peut jouer que s'il n'est pas KO
        if player['pv'] > 0:
            print('Joueur', player['id'], 'quelle attaque utilisez-vous ?')
            for name in player['monster']['attacks']:
                print('-', name.capitalize(), -attacks[name]['damages'], 'PV')

            att_name = input('> ').lower()
            while att_name not in attacks:
                print('Attaque invalide')
                att_name = input('> ').lower()
            attack = attacks[att_name]

            opponent['pv'] -= attack['damages']

            print(
                player['monster']['name'],
                'attaque',
                opponent['monster']['name'],
                'qui perd',
                attack['damages'],
                'PV, il lui en reste',
                opponent['pv'],
            )

if players[0]['pv'] > players[1]['pv']:
    winner = players[0]
else:
    winner = players[1]

print('Le joueur', winner['id'], 'remporte le combat avec', winner['monster']['name'])

À l’exécution on a bien quelque chose d’assez clair sur le déroulement du jeu.

Monstres disponibles :
- Pythachu
- Pythard
- Ponytha
Joueur 1 quel monstre choisissez-vous ?
> Pythachu
Quel est son nombre de PV ? 100
Joueur 2 quel monstre choisissez-vous ?
> Ponytha
Quel est son nombre de PV ? 120

Pythachu affronte Ponytha

Joueur 1 quelle attaque utilisez-vous ?
- Tonnerre -50 PV
- Charge -20 PV
> tonnerre
Pythachu attaque Ponytha qui perd 50 PV, il lui en reste 70
Joueur 2 quelle attaque utilisez-vous ?
- Brûlure -40 PV
- Charge -20 PV
> charge
Ponytha attaque Pythachu qui perd 20 PV, il lui en reste 80
Joueur 1 quelle attaque utilisez-vous ?
- Tonnerre -50 PV
- Charge -20 PV
> charge
Pythachu attaque Ponytha qui perd 20 PV, il lui en reste 50
Joueur 2 quelle attaque utilisez-vous ?
- Brûlure -40 PV
- Charge -20 PV
> brulure
Attaque invalide
> brûlure
Ponytha attaque Pythachu qui perd 40 PV, il lui en reste 40
Joueur 1 quelle attaque utilisez-vous ?
- Tonnerre -50 PV
- Charge -20 PV
> tonnerre
Pythachu attaque Ponytha qui perd 50 PV, il lui en reste 0
Le joueur 1 remporte le combat avec Pythachu