Les jeux sont déjà assez compliqué à programmer pour ce qui concerne leurs spécificités, il faut en plus programmer toute la machinerie qui permet de définir le nombre de joueurs, de savoir si c'est un joueur ordinateur ou humain, à chaque joueur de jouer, au programme principal de déceler au bon moment que le jeu est fini, de savoir qui a gagné, etc…
Bref, j'ai essayé de construire une trame toute prête qu'il est facile d'adapter au type de jeu qu'on veut.
Cette trame fonctionne en console Python, mais elle peut être reprise facilement dans le cadre d'un jeu graphique.
Voici les principes retenus:
Du fait de la programmation choisit, et en particulier grâce au fait qu'un joueur humain utilise les mêmes threads qu'un joueur ordinateur:
Le mieux pour comprendre comment ça marche est encore de l'essayer. Il ne demande qu'une console Python de type idle, et un copier-coller à partir du code ci-dessous.
Pour l'exemple donné dans le code ci-dessous:
Ces différents éléments sont bien entendu à supprimer lors de l'adaptation du code à un vrai jeu.
Le code proposé ne consomme pas beaucoup de ressources et, pour un jeu donné, ce seront surtout les éléments spécifiques au jeu (recherche du coup le meilleur) qui feront les délais de réponses.
Par exemple, si on retire ce qu'on a ajouté uniquement pour faire tourner la démo (les temporisations et les impressions):
Voilà le code proposé:
#!/usr/bin/python # -*- coding: utf-8 -*- import threading import time import random ############################################################################### class Joueur(threading.Thread): def __init__(self, nom, num, pion, typejoueur): threading.Thread.__init__(self) self.setName(nom) # nom du joueur. Ex: "joueur1", "joueur2", ... self.num = num # numéro du joueur. Ex: 0 pour joueur1, 1 pour joueur2, etc... self.pion = pion # forme de pion affecté self.typejoueur = typejoueur # type de joueur: 0 = ordinateur, 1 = humain self.stop = False # drapeau pour stopper le thread à la demande du programme principal def run(self): # accès aux variables globales global verrou # verrou d'accès aux variables globales global okjoue # drapeau donnée par le programme principal qui permet au joueur de jouer global cdcoups # compteur de coups global premier # désigne le numéro du joueur qui a joué en premier global nbjoueurs # nombre de joueurs du jeu while not self.stop: # tant que le jeu n'est pas terminé ##### => chaque joueur attend son tour pour jouer while True: # on prend le verrou d'accès aux variables globales verrou.acquire() if self.stop: # jeu terminé. on sort de la boucle, mais en conservant le blocage du verrou break if okjoue and (cdcoups+premier)%nbjoueurs==self.num: # = ça y est, on peut jouer, mais on conserve le verrou jusqu'à la fin du coup break # on libère le verrou pour que les autres joueurs accédent aussi aux variables globales verrou.release() ##### => le joueur en cours joue if not self.stop: if self.typejoueur==0: # c'est un joueur "ordinateur" qui joue time.sleep(0.1) print self.getName() + " joue son coup avec le pion: '" + self.pion + "'" else: # c'est un joueur "humain" qui joue time.sleep(0.1) print self.getName() + " joue son coup avec le pion: '" + self.pion + "'" ##### => fin du coup du joueur en cours # le joueur repasse la main au programme principal après chaque coup okjoue = False # on libère le verrou d'accès aux variables globales verrou.release() # et fin du thread si c'est demandé (sinon, attente du prochain coup) if self.stop: break def stopper(self): self.stop = True ############################################################################### print "Bonjour! En route pour le jeu!" ############################## => initialisation du jeu et des conditions de son démarrage # nombre de joueurs nbjoueurs = 2 # type de joueurs: 0=ordinateur, 1=humain; on doit avoir: len(typejoueurs)==nbjoueurs # par défaut: tous à 0 (=ordinateur joue contre lui-même): typejoueurs=[] for i in xrange(0,nbjoueurs): typejoueurs.append(0) # forme du pion affecté à chaque joueur. On doit avoir: len(pions)==nbjoueurs pions = ['O','X'] # if nbjoueurs>2: pions=[] for i in range(0,nbjoueurs): pions.append("%s" % (i+1)) # définir celui qui commence. Si None, fixer au hasard, sinon, donner le numéro du joueur premier = None if premier==None: premier = random.randint(0,nbjoueurs-1) print "=====> c'est joueur"+str(premier+1)+" qui commence" ############################## => initialisation du programme # création du verrou qui permettra le monopole d'accès aux variables globales (lecture-écriture) verrou = threading.Lock() # création du "compteur de coups" initialisé à -1 parce que c'est le programme principal qui commence cdcoups = -1 # drapeau initialisé à True pour que le programme principal reprenne la main après chaque coup # (initialisé à -1 parce que c'est le programme principal qui commence) okjoue = False # creation de la liste des joueurs (NB: le joueur numéro 0 est appelé "joueur1") joueurs = [] for i in xrange(0,nbjoueurs): j = Joueur("joueur%d" % (i+1), i, pions[i], typejoueurs[i]) j.setDaemon(True) joueurs.append(j) # lancement de tous les threads des joueurs for i in xrange(0,nbjoueurs): joueurs[i].start() ############################## # surveillance du jeu et attente condition de fin de partie tps=time.time() while True: # attente ququ'un joueur ait joué while True: verrou.acquire() if not okjoue: cdcoups+=1 # on incrémente le compteur de coups du coup qui vient d'être joué # on sort de la boucle, mais le verrou reste bloqué pendant la surveillance verrou.release() # détection du départ d'un nouveau tour numéro ((cdcoups//nbjoueurs)+1) par (cdcoups%nbjoueurs==0) ch="" if cdcoups%nbjoueurs==0: print print u"=====> début du tour " + str((cdcoups//nbjoueurs)+1) # affichage de la grille # Après chaque coup, examiner si les conditions d'arrêt du jeu sont atteintes # ici, arrêt du jeu au bout de 5 secondes if (time.time()-tps)>5: verrou.release() break # permet au joueur suivant de jouer okjoue = True verrou.release() # et on boucle pour attendre jusqu'à ce que le joueur suivant ait joué ############################# # fin du jeu print print "fin du jeu" # arrêt de tous les threads for i in xrange(0,nbjoueurs): joueurs[i].stopper() # attente jusqu'à ce que tous les threads soient terminés for i in xrange(0,nbjoueurs): joueurs[i].join() verrou.acquire() print "fin du thread " + joueurs[i].getName() verrou.release() print u"A bientôt pour un prochain jeu!"
Voilà ce que ce code génère comme sortie:
Bonjour! En route pour le jeu! =====> c'est joueur2 qui commence =====> début du tour 1 joueur2 joue son coup avec le pion: 'X' joueur1 joue son coup avec le pion: 'O' =====> début du tour 2 joueur2 joue son coup avec le pion: 'X' joueur1 joue son coup avec le pion: 'O' =====> début du tour 3 joueur2 joue son coup avec le pion: 'X' joueur1 joue son coup avec le pion: 'O' =====> début du tour 4 joueur2 joue son coup avec le pion: 'X' joueur1 joue son coup avec le pion: 'O' ... ... ... =====> début du tour 18 joueur2 joue son coup avec le pion: 'X' joueur1 joue son coup avec le pion: 'O' =====> début du tour 19 joueur2 joue son coup avec le pion: 'X' joueur1 joue son coup avec le pion: 'O' =====> début du tour 20 joueur2 joue son coup avec le pion: 'X' joueur1 joue son coup avec le pion: 'O' =====> début du tour 21 fin du jeu fin du thread joueur1 fin du thread joueur2 A bientôt pour un prochain jeu!
Il est facile d'adapter cette trame à des jeux comme le morpion (tic-tac-toe) ou le gomoku.
Au fur et à mesure que je l'adapterai à d'autres jeux, je le modifierai.
En fait, à chaque fois qu'on a plusieurs joueurs qui jouent chacun à leur tour avec des conditions d'arrêt du jeu et de désignation de vainqueur, la trame peut être adaptée.
Amusez-vous bien!