Connexion élèves

Choisir le(s) module(s) à installer :

Projets et mini-projets

Les compétences que nous allons vous faire travailler cette année sont les suivantes :

Un point important du travail du projet est de mettre par écrit les différentes étapes d'un projet informatique, soit la compétence décomposer.

Vous trouverez ici un document vous donnant quelques indications pour bien mener votre démarche de projet; lisez attentivement ces conseils avant de démarrer.

Une grille d'évaluation possible montrant les différentes compétences attendues pourrait être la suivante :

Grille d'évaluation projets
Image non contractuelle...

"Random Street" (sur une idée d'Adrien WILLM)

Présentation générale

On souhaite écrire un programme qui permet de générer « aléatoirement » le dessin d’une rue de 4 immeubles.
On utilisera pour cela le module Turtle de Python.

random street
random street

Les contraintes dans la première phase de développement sont les suivantes :

Détail des fonctions à réaliser et organisation du travail

Les Outils

Pour réaliser ce programme vous aurez besoin d'utiliser un nombre aléatoire. Le programme ci-dessous permet d'afficher un entier aléatoire compris entre 0 et 140.

			
import random
print(random.randint(0,140))						
			

Vous disposez également d'un mémento turtle (cliquer sur les mots précédents).

Fonctions à utiliser

Afin de faciliter votre travail, deux fonctions ont déjà été programmées :

			
def trait (x1,y1,x2,y2):
    '''
    x1, y1 : coordonnées du début du trait
    x2, y2 : coordonnées de la fin du trait
    trace un trait de (x1,y1) jusqu'a (x2,y2),
     le pointeur et renvoyé à sa position initiale à la fin du tracé
    '''
    penup()
    xdep,ydep=position() #sauvegarde de la position initiale
    setposition(x1,y1)
    pendown()
    setposition(x2,y2)
    penup()
    setposition(xdep,ydep)#reprise de la position de départ

def rectangle(x,y,w,h,r,v,b):
    '''
    x, y : coordonnées du centre de la base de rectangle
    r,v,b : composantes rvb de la couleur de remplissage (comprises entre 0 et 255)
    w : largeur du rectangle
    h : hauteur du rectangle
    Trace le rectangle correspondant,
    le pointeur et renvoyé à sa position initiale à la fin du tracé
    '''
    colormode(255)
    fillcolor(r,v,b)
    penup()
    xdep,ydep=position() #sauvegarde de la position initiale
    setposition(x-w/2,y)
    pendown()
    begin_fill()
    trait (x-w/2,y,x+w/2,y)
    trait (x+w/2,y,x+w/2,y+h)
    trait (x+w/2,y+h,x-w/2,y+h)
    trait (x-w/2,y+h,x-w/2,y)
    end_fill()
    setposition(xdep,ydep)#reprise de la position de départ	
			

Organisation du travail

Vous devez donc maintenant vous répartir les fonctions suivantes et les coder. Les contraintes sont les suivantes :

Pour ce premier projet nous allons utiliser utiliser une méthode de projet par étape ou "Run".
Au bout de chaque "Run" vous devrez rendre aux chefs de projet (M. Le Bourg et M. Perrod) votre code fonctionnel, agrémenté de tests, via l'ENT.

méthode de projet
méthode de projet par Run

Le Run 0 correspond à la présentation de ce projet par le chef de projet, qui doit s'assurer que tout le monde à compris l'objectif du Run 1.
Vous devez renvoyer, avant de commencer le travail :

Run 1

			
def toit(x, y_sol, niveau):
	'''
	tracé du toit plat
	x : abscisse du centre de l'étage
	y_sol: ordonnée du sol
	niveau : numéro de l'étage en partant de 0 (RDC)
	'''
	
def porte(x,y_sol):
	'''
	x est l'abscisse du centre de la porte
	y_sol est l'ordonnée du sol du niveau de la porte
	'''
	
def fenetre(x,y):
	'''
	x est l'abscisse du centre de la fenêtre
	y est l'ordonnée du sol du niveau de la fenêtre
	'''
		
def etage(x, y_sol, niveau, r,v,b):
	'''
	tracé d'un étage avec ses trois fenêtres
	x : abscisse du centre de l'étage
	y_sol : ordonnée du sol du la rue
	niveau : numéro de l'étage en partant de 0 ( RDC)
	r,v,b : composantes rvb de la couleur de remplissage (comprises entre 0 et 255)
	'''
				

Run 2

			
def sol(y_sol):
	'''
	tracé de la ligne de sol
	y_sol : ordonnée du sol du la rue
	'''
	
def rdc(x, y_sol) :
	'''
	tracé du rez de chaussée avec sa porte et ses deux fenêtres
	x : abscisse du centre de l'immeuble
	y_sol : ordonnée du sol du la rue
	'''
	
def facade(x, y_sol, nbre_etages, r,v,b):
	'''
	tracé du rdc et des étages
	x : abscisse du centre de la façade
	y_sol : ordonnée du sol du la rue
    r,v,b : composantes rvb de la couleur de remplissage (comprises entre 0 et 255)
	nbre_etages : nombre d'étages
	'''
			

Run 3

				
def immeuble(x, y_sol,nbre_etages,r,v,b)
	'''	
	tracé complet d'un immeuble de n étages avec le toit
	x : abscisse du centre de l'étage
	y_sol : ordonnée du sol du la rue
	nbre_etages : nombre d'étages
   r,v,b : composantes rvb de la couleur de remplissage (comprises entre 0 et 255)
   '''
			

Run 4

			
def randomstreet(y_sol)
	'''
	tracé de la rue avec ses 4 immeubles			
	y_sol : ordonnée du sol du la rue
	'''	
			

Approfondissement du projet

Si vous avez réussi à boucler le projet, bravo !
Voici une nouvelle série de contraintes, vous pouvez bien sur repartir de votre projet précédent et développer une nouvelle version. Vous pouvez également ajouter ces bonus progressivement, en choisissant ceux qui vous semblent les plus abordables.
A vous cette fois de vous répartir le travail et de déterminer :

Les contraintes dans la deuxième phase de développement (Bonus) sont les suivantes :

Ce qui peut donner ce genre de rue :

random street
random color street

Le jeu de la Petite Soeur

Principe

On tire au sort N cartes (on prendra N = 10 ici) parmi les cartes disponibles qu'on pose face visible entre les 2 joueurs. Dans un premier temps, vous jouerez contre un adversaire humain, puis vous verrez si vous pouvez écrire une "IA" qui vous permette de jouer contre l'ordinateur...

A tour de rôle, chaque joueur ne peut prendre qu'une des cartes situées à l'une des deux extrémités.
A chaque carte est associée une valeur : l'As (peut importe la couleur) vaut 1 point, le 2 vaut ... 2 ! Ainsi de suite, le valet vaut 11 points, la dame 12 points et le roi 13 points.
Une fois que les N cartes ont été ramassées, le jeu est fini et on compte les points. Le score le plus grand désigne le gagnant !

Toute la stratégie est donc de prendre à chaque fois la "bonne" carte, celle qui à la fin permettra d'obtenir le score le plus élevé, quitte à ne pas prendre à chaque fois la carte de plus grande valeur, ce qui peut en effet laisser ensuite au joueur adverse une carte d'encore plus grande valeur...

Jeu de cartes

Codage du jeu

Dans un premier temps, l'affichage de la partie se fera uniquement dans la console, par exemple sous la forme :


...

C'est au tour du Joueur 2:
[("As","coeur"), ("7","pic"), ("8","carreau"), ("D","trèfle"), ("R","pic"), ("R","trèfle"), ("A","carreau"), ("9","coeur"), ("10","pic"), ("V","trèfle")]
Vous prenez la carte à gauche (G) ou à droite (D) ? D

C'est au tour du Joueur 1:
[("As","coeur"), ("7","pic"), ("8","carreau"), ("D","trèfle"), ("R","pic"), ("R","trèfle"), ("A","carreau"), ("9","coeur"), ("10","pic")]
Vous prenez la carte à gauche (G) ou à droite (D) ? G

C'est au tour du Joueur 2:
[("7","pic"), ("8","carreau"), ("D","trèfle"), ("R","pic"), ("R","trèfle"), ("A","carreau"), ("9","coeur"), ("10","pic")]
Vous prenez la carte à gauche (G) ou à droite (D) ? G
...

Fin de la partie :
Joueur 1 : 10 pts
Joueur 2 : 15 pts
Joueur2 gagne !
			

Contraintes

Votre code devra être le plus modulaire possible, c'est à dire découpé en fonctions, qui n’exécuteront qu'une seule tache.

Par exemple il y aura une fonction pour tirer la liste des N cartes aléatoirement, une fonction pour afficher les cartes en jeu à chaque tour, une fonction pour savoir si un joueur a gagné, etc...

Une étape de réflexion préalable sur papier est donc indispensable avant de se jeter sur les claviers !

La liste des cartes en jeu sera codée avec un tableau de taille fixe : on ne pourra pas "raccourcir" ou "allonger" ce tableau au cours du jeu; il faut donc que vous trouviez la méthode pour gérer le fait, qu'à chaque tour de jeu, il y a une carte en moins dans la liste.

Ressources

Le code ci-dessous vous donne un tableau de 32 tuples décrivant les cartes d'un jeu de 32 cartes.

A vous de réfléchir à la manière de "piocher" N cartes au hasard dans ce tableau ( il existe une fonction Python pour ça...)


jeu_cartes = [('7','Carreau'), ('8','Carreau'),('9','Carreau'), ('10','Carreau'),('V','Carreau'),('D','Carreau'),('R','Carreau'),('A','Carreau'),('7','Coeur'),('8','Coeur'),('9','Coeur'), ('10','Coeur'),('V','Coeur'),('D','Coeur'),('R','Coeur'),('A','Coeur'),('7','Pique'),('8','Pique'),('9','Pique'), ('10','Pique'),('V','Pique'),('D','Pique'),('R','Pique'),('A','Pique'),('7','Trèfle'),('8','Trèfle'),('9','Trèfle'), ('10','Trèfle'),('V','Trèfle'),('D','Trèfle'),('R','Trèfle'),('A','Trèfle')]

shuffle(jeu_cartes)  # mélange aléatoirement les éléments du tableau jeu_cartes			
			

Prolongement

Vous trouverez ici une archive avec un script Python de base permettant l'affichage des cartes, et les images de ces cartes.

Vous compléterez le script à l'endroit indiqué sans modifier les autres instructions.

Pour afficher les cartes encore en jeu, vous disposez d'une nouvelle fonction affiche_cartes, qui remplacera celle que vous avez écrite précédemment; étudiez la docstring et les annotations de type de cette fonction pour l'utiliser correctement.

Vous pouvez aussi réfléchir à une possibilité de jeu contre l'ordinateur : une stratégie simple à coder serait que ce dernier prenne toujours la plus grande des cartes parmi les deux possibilités.

Solution possible

Un code possible pour ce jeu peut se trouver ici :

Le jeu de la bataille

Le projet

Vous avez bien entendu déjà joué à la Bataille fermée : on partage les cartes d'un jeu entre les joueurs ( deux le plus souvent ); à chaque tour chaque joueur pose à l'endroit la carte du dessus de son tas. Si sa carte a la plus grande valeur, il ramasse toutes les cartes posées et les place en dessous de son tas.
En cas d'égalité, il y a "bataille" : on place une carte à l'envers sur la précédente, puis on en pose une autre à l'endroit; on recommence tant qu'il n'y a pas de gagnant pour le tour.

Les cartes par valeur croissante sont : 7, 8, 9, 10, Valet, Dame, Roi, As ( symbolisés par la suite par : 7, 8, 9, D, J, Q, K , A )

Le gagnant est celui qui a ramassé toutes les cartes du jeu.

Jeu de cartes

En vous limitant à un jeu de 32 cartes, vous allez coder un jeu de Bataille fermée entre deux joueurs gérés par l'ordinateur.
A chaque tour de jeu devront être indiquées les cartes posées, et le nombre de cartes restant dans le tas de chaque joueur.


	JEU DE LA BATAILLE
	
	Tour 1 :
	L'ordinateur pose : D Carreau
	Vous posez : A Coeur
	
	Vous gagnez le tour !
	Vous avez 17 cartes. L'ordinateur a 15 cartes. Pas de gagnant pour la partie.
	
	Tour 2 :
	L'ordinateur pose : A Trèfle
	Vous posez : 7 Pique
	
	L'ordinateur gagne le tour !
	Vous avez 16 cartes. L'ordinateur a 16 cartes. Pas de gagnant pour la partie.
	
	Tour 3 :
	L'ordinateur pose : K Pique
	Vous posez : K Coeur
	
	BATAILLE !
	
	................
			

Organisation du travail

Pour ce projet nous allons utiliser utiliser une méthode de projet par étape ou "Run".
Au bout de chaque "Run" vous devrez rendre aux chefs de projet (M. Le Bourg et M. Perrod) votre code fonctionnel, agrémenté de tests, via l'ENT.

méthode de projet
méthode de projet par Run

Le Run 0 correspond à la présentation de ce projet par le chef de projet, qui doit s'assurer que tout le monde à compris l'objectif du Run 1.

Vous devez renvoyer, avant de commencer le travail :

Par la suite, chaque Run correspondra à la réalisation de différentes fonctions, qui seront codées par chacun des membres du groupe, puis renvoyées ensuite au chef de projet.

Bien entendu, le code devra être le plus clair possible ( noms des variables bien choisis, commentaires dans le code,...), et les docstrings des fonctions, ainsi que les annotations de type, complétées.
Des tests devront apparaître dans le code après chaque Run pour montrer que vous avez bien testé le bon fonctionnement de vos fonctions.

Attention, ces fonctions nécessiteront souvent plusieurs paramètres, et elles renverront souvent plusieurs résultats.

Faites donc bien attention à la manière de leur transmettre des arguments, et à celle de récupérer les résultats qu'elles renvoient.

Pour simplifier l'écriture de ces fonctions, on pourra se servir du caractère mutable des tableaux ( mais pas des tuples !), qui évite de les passer en paramètre ou à les renvoyer : une mutation de ces tableaux dans les fonctions se "répercutera" dans le programme principal ( et dans les autres fonctions ), pour peu que ces tableaux aient été initialisés dans le programme principal ( on dit que les tableaux sont des variables globales à tout le script ).

Une réflexion préalable sur papier sera très souvent profitable avant tout codage des fonctions...

Structures de données utilisées

Vous verrez en Terminale que le plus important dans un projet logiciel n'est pas le code lui-même, mais bien la manière dont on va représenter les données dans la mémoire de l'ordinateur.

Une organisation de données s'appelle une structure de données; nous allons vous les indiquer pour ce projet, mais par la suite, ce sera à vous d'y réfléchir pour que cette représentation soit la plus simple et efficace à manipuler.

Codage des cartes

Une carte sera représentée par un tuple de deux éléments ( deux chaînes de caractères ) : ("valeur de la carte", "couleur").

Le tableau ci-dessous contient les 32 tuples d'un jeu de cartes complet :

			
jeu_complet = [("7", "Carreau"), ("8", "Carreau"), ("9", "Carreau"),("D", "Carreau"),("J", "Carreau"), ("Q", "Carreau"), ("K", "Carreau"), ("A", "Carreau"), ("7", "Coeur"), ("8", "Coeur"), ("9", "Coeur"),("D", "Coeur"),("J", "Coeur"), ("Q", "Coeur"), ("K", "Coeur"), ("A", "Coeur"), ("7", "Pique"), ("8", "Pique"), ("9", "Pique"),("D", "Pique"),("J", "Pique"), ("Q", "Pique"), ("K", "Pique"), ("A", "Pique"), ("7", "Trèfle"), ("8", "Trèfle"), ("9", "Trèfle"),("D", "Trèfle"),("J", "Trèfle"), ("Q", "Trèfle"), ("K", "Trèfle"), ("A", "Trèfle")] 
			

Codage des jeux de chaque joueur

Le jeu de chaque joueur sera représenté en mémoire de l'ordinateur par un tableau de taille fixe de 32 éléments; chaque élément pourra "contenir" une carte dans le jeu d'un joueur ( donc, un tuple ).
Le "dessous" du jeu correspondra au premier élément du tableau ( indice 0 ), et le "dessus" sera repéré par une variable spéciale appelée un pointeur, dont la valeur correspondra à l'indice de l'élément qui correspond à la "carte du dessus" du tas du joueur.

Lorsque le joueur jouera une carte, le pointeur "sommet" sera décrémenté d'une position : le joueur perd donc si "sommet" arrive à -1 ( = plus de cartes ! ).

Inversement, lorsque le joueur gagne une ( ou plusieurs ) carte(s), le pointeur "sommet" sera incrémenté, après que les cartes gagnées ont été insérées au début du tableau.

Pour indiquer qu'un élément du tableau ne contient aucune carte ( c'est à dire pour les éléments du tableau de l'indice "sommet" + 1 jusqu'à la fin du tableau ), l'élément prendra la valeur None.

Le jeu d'un joueur à une phase du jeu donnée pourrait donc correspondre au tableau suivant :


jeu_1 = [("7", "Carreau"), ("Q", "Pique"), ("A", "Coeur"), ("J", "Trèfle"), ("K", "Coeur"), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]			
			

( Dans ce jeu, le pointeur "sommet" a donc la valeur 4. )

Le travail à faire

Run 1

A cette étape, vous écrirez les fonctions de création des jeux, et de manipulation des tableaux représentant chaque jeu.

  1. Écrire une fonction genere_jeux() qui renvoie les deux tableaux représentant les tas de 32 cartes initiaux de chacun des deux joueurs.

    
    def genere_jeux()->(list, list):
            
        ......
            
        return jeu1, jeu2					
    					

    Le début de ces tableaux devra être rempli avec la moitié des cartes d'un jeu complet ( soit 16 cartes ).

    Vous utiliserez le tableau jeu_complet ci-dessus et la fonction shuffle() du module random qui permet de mélanger aléatoirement les éléments d'un tableau :

    
    from random import shuffle		
    				
    jeu_complet = [.....]	
    
    shuffle(jeu_complet)  # mélange aléatoirement les éléments du tableau cartes
    					
  2. Écrire une fonction jouer_carte(), qui permet d'enlever une carte du dessus du jeu d'un joueur afin de la "jouer".

    
    def jouer_carte(jeu: list, sommet: int)->(tuple, int):
        
        .....
        
        return carte_jouee, sommet 
        				

    Attention, cette fonction doit quand même prendre comme paramètre le jeu dans lequel piocher la carte.

    Cette fonction devra renvoyer la carte jouée, ainsi que la valeur modifiée du pointeur de "sommet" du jeu duquel la carte a été jouée.

  3. Écrire une fonction inserer_carte() qui permet d'insérer une carte en dessous du jeu d'un joueur lorsqu'il l'a gagnée.

    
    def inserer_carte(jeu: list, carte: tuple, sommet: int)->int:
        
        .......
        
        return sommet
        				

    Attention, cette fonction doit quand même prendre comme paramètre le jeu dans lequel insérer la carte.

    Cette fonction renverra la valeur modifiée du pointeur de "sommet" du jeu dans lequel la carte a été insérée..

    Bien réfléchir à l'algorithme à suivre pour insérer un élément au début d'un tableau.

  4. Écrire une fonction comparer_carte() qui permet de comparer la valeurs de deux cartes l'une par rapport à l'autre.

    
    def comparer_cartes(c1:tuple, c2:tuple)->(tuple, ...):
        
        ......
        
        return carte, .....
            			

    Cette fonction devra renvoyer la plus petite des cartes ( ou la plus grande, un choix est à faire ), ou alors une information indiquant que les deux cartes sont de même valeur.

    Bien réfléchir à la manière de comparer la valeur de deux cartes, on ne peut pas simplement comparer deux tuples...et on ne peut pas non plus comparer des chaînes de caractères ( enfin si, on peut, mais ça ne donnera pas forcément le résultat recherché...). Il va falloir réfléchir...

Run 2

Le Run 2 va être consacré uniquement à l'écriture d'une fonction partie() qui simulera une partie de Bataille, mais sans y inclure pour l'instant la gestion des batailles elles-mêmes.

Cette fonction n'aura aucun paramètre, et ne renverra qu'une chaîne de caractères indiquant le joueur qui a gagné la partie.


def partie()->str:
    
    ......
    
    return joueur_gagnant    
        			

Il faudra dans la fonction initialiser toutes les variables nécessaires.

La fonction s'exécutera tant qu'un des deux joueurs n'a pas perdu; elle fera appel aux autres fonctions codées au Run 1, et réalisera les affichages qui permettront de suivre le déroulement de la partie.

Pour tester votre fonction : suivez le déroulement d'une partie, et observer le nombre de cartes restantes à chaque joueur; vous devez voir évoluer ces deux valeurs de manière logique : si le joueur 1 gagne le tour, son compteur augmente, et celui du joueur 2 diminue ( et vide et versa ).
Tel quel, le script doit normalement s'arrêter lorsque l'un des deux compteurs atteint 0 ( mais l'autre joueur n'aura pas alors 32 cartes tant que la gestion des batailles n'est pas faite...)

Run 3

Il sera consacré à l'écriture d'une fonction de gestion des situations de batailles.

La fonction bataille() devra, lorsqu'elle est appelée, gérer la pose de cartes de la part des joueurs tant que la situation de bataille perdure ( c'est à dire tant qu'ils posent des cartes de même valeur ).
Elle renverra alors :

  • les valeurs des pointeurs de "sommet" pour les deux jeux
  • un tableau contenant les cartes ayant été posées pendant la bataille
  • une indication sur le joueur ayant gagnée la bataille

def bataille(carte1: tuple, carte2: tuple, sommet1: int, sommet2: int)->(int, int, list, str):
    
    .....

    return sommet1, sommet2, cartes_gagnees, joueur_gagnant			
			
			
			

On rappelle comment se déroule une bataille :

  1. on part donc de la situation où les deux cartes de même valeur ont été posées
  2. chaque joueur pose sur sa carte une autre carte face dessous ( retournée )
  3. chaque joueur pose à nouveau sur son tas une autre carte, face dessus cette fois.
  4. on compare ces deux dernières cartes : si elles sont encore égales, on recommence à l'étape 2, sinon un des joueurs a gagné la bataille

Ce n'est pas évident, réfléchissez bien à l'algorithme à utiliser pour gérer ces situations ( on ne connaît pas a priori le nombre de cartes qui seront posées par chaque joueur lors de la bataille ).

Une fois tout ceci fait, vous pouvez lancer une partie de bataille, et voir qui gagne à la fin !!

Run 4

Votre jeu fonctionne ? Très bien ! Maintenant, si vous voulez un affichage un peu plus agréable des cartes, vous pouvez utiliser cette archive.
Elle permet un affichage graphique des cartes posées par les deux joueurs dans une fenêtre.

Téléchargez cette archive et décompressez-la dans le dossier de votre choix : un sous-dossier nommé Bataille y sera créé.

L'archive contient les images des 32 cartes d'un jeu, ainsi que l'image d'un dos de carte.

  • dans le dossier Bataille, vous trouverez également un script nommé bataille_gui.py; il est à compléter avec votre propre script, à l'endroit qui y est indiqué :
    
    	##########################
    	#     VOTRE SCRIPT       #
    	#          ICI           #
    	##########################
    					
  • Vous disposez alors de deux fonctions supplémentaires :
    • affiche_cartes() : permet d'afficher les cartes des deux joueurs dans la fenêtre.
      Lisez la docstring de cette fonction pour en comprendre l'utilisation.
    • clic_souris() : fonction éxécutée automatiquement lors d'un clic sur le bouton gauche de la souris.
      Cette fonction permet de ne déclencher certains évènements que lorsque le joueur cliquera, ce qui évite que le jeu "défile" rapidement et permet donc d'introduire une temporisation dans son déroulement.
      Vous pouvez mettre toutes les instructions nécessaires dans cette fonction, et éventuellement y faire appel à la fonction précédente.
  • attention à bien laisser, comme indiqué, les deux dernières instructions tout à la fin du script

Attention, la fenêtre où s'affiche les cartes risque de "passer derrière" celle de Pyzo : réduisez la fenêtre de ce dernier de façon à avoir les deux en même temps sur le bureau.

Puissance 4

Voila un projet qui met en jeu, comme beaucoup de choses en informatique, des capacités d'abstraction pour représenter en mémoire d'un ordinateur le plateau d'un jeu de société et permettre de jouer à ce jeu.

Un grand classique qu'on ne présente plus...Vous coderez un jeu à deux joueurs jouant successivement.

Dans un premier temps, vous n'utiliserez pour l'affichage du plateau de jeu et pour l'interaction avec les utilisateurs que la console.


Entrez la colonne joueur Jaune : 2

1	2	3	4	5	6	7	
x	x	x	x	x	x	x
x	x	x	x	x	x	x	
x	x	J	x	x	x	x
x	x	R	J	R	x	x
x	J	R	R	J	x	x
R	J	J	J	R	R	x			
			

Dans un deuxième temps, une fois que votre jeu fonctionnera, vous utiliserez une interface graphique pour pouvoir notamment contrôler le jeu à la souris : un clic sur une colonne de la grille doit faire tomber le pion du joueur, alternativement rouge ou jaune.

Puissance 4

Principe

Le "plateau de jeu" peut être vu comme une grille de 6 lignes et 7 colonnes; dans chaque "case" de cette grille peut être affichée un rond rouge ou jaune.

Grille Puissance 4

Le plateau de jeu sera donc représenté dans le script par un tableau de tableaux de 6 lignes de 7 colonnes, soit autant de "cases" qu'il y a dans la grille du jeu; chaque élément de ce tableau de tableaux peut contenir une valeur qui code le "contenu" de la "case" correspondante de la grille, par exemple :

Ce tableau de tableaux permettra de garder en mémoire l'état de la grille du jeu et de pouvoir gérer celle-ci, l'afficher, déterminer si un joueur gagne, etc...


grille = [
	[0, 0, 0, 0, 0, 0, 0],
	[0, 0, 0, 0, 0, 0, 0],	
	[0, 0, 2, 0, 0, 0, 0],
	[0, 0, 1, 2, 1, 0, 0],
	[0, 2, 1, 1, 2, 0, 0],
	[1, 2, 2, 2, 1, 1, 0]]			
			

Le script demandera à tour de rôle à chaque joueur d'indiquer la colonne ( de 1 à 7 ) dans laquelle il souhaite placer un pion; le script déterminera alors comment modifier le tableau de tableaux pour représenter le coup joué.

A chaque pion joué, le script devra également mettre à jour l'affichage du plateau de jeu, et bien entendu déterminer si le coup est gagnant.

Run n°1 : création de la grille de jeu et fonction d'affichage

Deux fonctions à coder, ce qui peut se faire en parallèle par deux personnes différentes :

  1. Écrire une fonction genere_grille qui crée le tableau de tableaux représentant la grille du jeu, l'initialise et renvoie ce tableau.
    
    def genere_grille():
    	'''Fonction qui initialise la grille du jeu
    	Entrée :
    		aucune
    	Sortie :
    		le tableau de tableaux représentant la grille du jeu initialisée
    	'''
    	pass
    			
    	return grille		
    					
  2. écrire une fonction affiche_grille qui permet d'afficher le jeu dans la console en faisant apparaître le "contenu" de chaque "case" conformément aux informations stockées dans le tableau de tableaux passé en paramètre.
    
    def affiche_grille(grille):
    	'''Fonction qui affiche la grille du jeu.
    	'x' = case vide, 'R' = pion rouge, 'J' = pion jaune ( par exemple ).
    	Les numéros de colonne de 1 à 7 doivent apparaître au dessus de la grille.
    	Entrée :
    		grille = le tableau de tableau représentant la grille du jeu
    	Sortie :
    		aucune
    	'''
    	pass
    					
    					
  3. Bien entendu, il faudra tester chacune de ces fonctions. A vous de réfléchir aux tests à réaliser.

Run n°2 : un joueur joue

  1. après avoir réfléchi au problème, écrire sur papier l'algorithme à suivre pour savoir si une colonne est pleine, et si non, à déterminer le numéro de la ligne où un pion pourrait y être placé en "tombant" de puis le haut de la colonne.
    Travailler sur un exemple de grille pour bien comprendre le problème.
  2. écrire une fonction est_jouable qui :
    • prend comme paramètre le tableau de tableau représentant la grille du jeu, et un entier représentant le numéro d'une colonne.
    • détermine si un pion peut être placé dans cette colonne ( c'est à dire si cette colonne n'est pas pleine ), et dans l'affirmative détermine le numéro de la ligne à laquelle le pion se placera en "tombant" depuis le haut de la grille.
    • renvoie ce numéro de ligne si le pion peut être placé, ou la valeur -1 si la colonne est pleine.
    
    def est_jouable(grille, colonne):
    	"""Fonction qui détermine si un pion peut être placé dans une colonne donnée.
    	Entrées :
    		grille = le tableau de tableau représentant la grille du jeu
    		colonne = le numéro de la colonne où placer un pion
    	Sorties :
    		le numéro de la ligne où le pion se placera, -1 si la colonne est pleine
    	"""					
    	pass
    	
    	return ligne			
    				
    				
  3. Écrire une fonction place_pion qui prend comme paramètres :

    • le tableau de tableaux représentant la grille du jeu
    • le joueur qui souhaite placer un pion
    • le numéro de colonne dans laquelle un joueur souhaite placer son pion

    Elle utilisera la fonction est_jouable pour savoir si le coup est possible, et renverra :

    • le tableau de tableaux modifié par le coup joué.
    • l'indice de la ligne où le pion est "tombé"
    
    def place_pion(grille, joueur, colonne):
    	'''Fonction qui place le pion d'un joueur dans une case de la grille
    	Entrée :
    		grille = le tableau de tableaux représentant la grille du jeu
    		joueur = le numéro du joueur
    		colonne = le numéro de colonne dans laquelle le joueur souhaite placer un pion 
    	Sortie :
    		le tableau de tableaux modifié par le placement du pion
    		ligne = l'indice de la ligne où le pion a été posé
    	'''
    	pass
    	
    	
    	return grille, ligne
    					
    				

Run n°3 : un joueur gagne ?

Voila le cœur du problème : comment le script va-t-il pouvoir déterminer si, après qu'il a placé un pion, le joueur qui vient de jouer est gagnant ?

Pour bien comprendre le problème, nous vous proposons de le décomposer en sous-problèmes plus simples :

Il y a plusieurs méthodes pour traiter ces problèmes; nous vous en proposons une ici, mais rien ne vous empêche de développer la vôtre, à condition bien sûr qu'il ne s'agisse pas d'un copier-coller depuis le web. Pensez à expliquer votre démarche !

Vérification d'un alignement sur une ligne

Nous utiliserons la méthode dite "par force brute", aussi dite "naïve", qui consiste à parcourir toute la ligne où un joueur vient de poser un pion, et à compter, en partant du début de la ligne, le nombre de pions de la couleur du joueur placés les uns à la suite des autres :

Parcours par ligne

fonction alignement_ligne(grille, ligne, joueur):
	nb_pion ← 0
	pour chaque case de la ligne :
			si la case contient un pion de la couleur du joueur :
				incrémenter nb_pion	
				si nb_pion vaut 4:
					renvoyer VRAI
			sinon ( si la case est vide ou contient un pion de l'autre joueur ) :
				nb_pion ← 0
	renvoyer FAUX ( on n'a pas compté 4 pions successifs )
			

L'idée est que, lors du parcours d'une ligne, lorsqu'on rencontre un pion de la couleur du joueur qui vient de poser un pion, on incrémente un compteur.
On teste alors si on a incrémenté 4 fois le compteur, ce qui signifie avoir trouvé un alignement de 4 pions : dans ce cas, on sort de la fonction en renvoyant un résultat indiquant que le joueur est gagnant ( un booléen True par exemple ).

Si on rencontre lors du parcours une case vide ou un pion de la couleur de l'autre joueur, alors on remet à zéro le compteur, et on recommence le comptage...

Écrire une fonction alignement_ligne, qui permet de déterminer si une suite de 4 pions d'une même couleur se trouve sur une ligne de la grille.

Cette fonction prend comme paramètres le tableau de tableaux représentant la grille du jeu, l'indice de la ligne où un pion vient d'être joué, le numéro du joueur, et renvoie une information indiquant si le joueur est gagnant ou pas.


def alignement_ligne(grille, ligne, joueur)- :
	'''Fonction qui détermine si une suite de 4 pions de la couleur d'un joueur donné se trouve sur une des lignes de la grille.
	Entrée :
		grille = le tableau de tableau représentant la grille du jeu
		ligne = l'indice de la ligne sur laquelle le joueur vient de poser un pion
		joueur = la valeur identifiant un joueur
	Sortie :
		une information indiquant si le joueur est gagnant ou pas
	'''
	pass
						
			

Vérification d'un alignement sur une colonne

Le principe est le même sauf que, vous vous en doutez, la recherche se fait sur une même colonne...

Parcours par colonne.

Écrire une fonction alignement_colonne qui permet de savoir si une suite de 4 pions d'une même couleur se trouve sur une colonne de la grille.

Cette fonction prend comme paramètres le tableau de tableaux représentant la grille du jeu, l'indice de la colonne où un pion vient d'être joué, le numéro du joueur, et renvoie une information indiquant si le joueur est gagnant ou pas.


def alignement_colonne(grille, colonne, joueur):
	'''Fonction qui détermine si une suite de 4 pions de la couleur d'un joueur donné se trouve sur une des colonnes de la grille.
	Entrée :
		grille = le tableau de tableau représentant la grille du jeu
		colonne = l'indice de la colonne sur laquelle le joueur vient de poser un pion
		joueur = la valeur identifiant un joueur
	Sortie :
		une information indiquant si le joueur est gagnant ou pas
	'''
	pass
	
	
		
			

Vérification d'un alignement sur une diagonale

Ah, là c'est un peu plus compliqué...Et il faut en plus distinguer les diagonales descendantes ( de haut en bas vers la droite ) des ascendantes ( de bas en haut vers la droite )...

Alignement sur une diagonale descendante

L'idée : toujours avec l'approche "force brute", nous allons, en partant de sa case la plus en haut à gauche, parcourir la diagonale descendante jusqu'en bas de la grille, et compter sur cette diagonale le nombre de pions successifs de la couleur du joueur, en utilisant le même principe que précédemment.

Parcours diagonale descendante

Tout le problème est de savoir à quelle case commencer le parcours de la diagonale : cette case peut être n'importe où sur la première colonne OU la première ligne...

D'où l'idée : depuis la case où le pion du joueur vient d'être placé, on va tout d'abord "remonter" à la case de départ, en décrémentant les indices de ligne et de colonne ( valeurs passées en paramètres à la fonction ), jusqu'à arriver à la première colonne OU à la première ligne.


fonction alignement_descendant(grille, ligne, colonne, joueur):
	1. Détermination de la case de départ de la diagonale
	tant qu'on n'est pas "remonté" à la première colonne ou à la première ligne:
		décrémenter l'indice de ligne et celui de colonne ( on "remonte" ainsi case par case )
		
	2. "Descente" le long de la diagonale et comptage des pions alignés
	tant qu'on n'est pas "descendu" jusqu'à la dernière ligne ou la dernière colonne:
		faire le comptage des pions du joueur comme précédemment
		si on a compté 4 pions alignés:
			renvoyer VRAI
			
		incrémenter l'indice de ligne et celui de colonne ( on "descend" ainsi case par case )
		
	renvoyer FAUX
			 
			 

Attention, il faut bien réfléchir aux tests à faire dans les boucles, notamment lors de la "redescente" le long de la diagonale : il faut prendre garde à bien compter le pion sur la dernière case de la diagonale...

traduire l'algorithme précédent en une fonction Python alignement_descendant qui :

  • prend comme paramètre le tableau de tableau représentant la grille, les indices de ligne et de colonne où le pion a été posé, et le numéro du joueur
  • renvoie une information indiquant si le joueur est gagnant ou pas.

def alignement_descendant(grille, ligne, colonne, joueur):
	'''Fonction qui détermine si une suite de 4 pions d'une même couleur se trouve sur une diagonale descendante de la grille.
	Entrée :
		grille = le tableau de tableau représentant la grille du jeu
		ligne, colonne = indices de la case où un pion vient d'être posé
		joueur = le numéro du joueur qui vient de poser le pion
	Sortie :
		une information indiquant si le joueur est gagnant ou pas
	'''
	pass
	
				
					
Alignement sur une diagonale ascendante

Enfin, il faut vérifier si un alignement de 4 pions d'une même couleur se trouve sur une diagonale ascendante de la grille.

Parcours des diagonales ascendantes
  1. en vous inspirant du précédent, réfléchir sur papier à l'algorithme à utiliser pour compter les pions d'une couleur sur une diagonale ascendante.
  2. traduire alors l'algorithme précédent en une fonction Python alignement_ascendant qui :
    • prend comme paramètre le tableau de tableau représentant la grille, les indices de ligne et de colonne où un pions vient d'être posé, et le numéro du joueur
    • renvoie une information indiquant si le joueur est gagnant ou non.
    
    def alignement_ascendant(grille, ligne, colonne, joueur):
    	'''Fonction qui détermine si une suite de 4 pions d'une même couleur se trouve sur une diagonale ascendante de la grille.
    	Entrée :
    		grille = le tableau de tableau représentant la grille du jeu
    		ligne, colonne = indices de la case où un pion vient d'être posé
    		joueur = le numéro du joueur qui vient de poser le pion
    	Sortie :
    		une information indiquant si le joueur est gagnant ou pas
    	'''
    	pass
    			
    					

Run n°4 : le jeu !

Vous avez maintenant les fonctions nécessaires pour gérer une partie entre deux joueurs humains.

  1. écrire l'algorithme à suivre pour, en utilisant les fonctions précédentes, simuler le déroulement d'une partie de puissance 4.
  2. En utilisant les fonctions écrites précédemment, compléter un script qui permet à deux joueurs de disputer une partie de Puissance 4.

Run n°5 : interface graphique

Votre jeu fonctionne ? Très bien ! Il serait sans doute plus convivial avec un affichage plus joli...

Dans ce script, vous trouverez des fonctions qui utilisent le module Tkinter pour gérer l'affichage d'une grille et des pions d'un Puissance 4.

A vous de compléter ce script avec votre propre code en "l'intégrant" dans l'interface graphique.

  • au lancement du script, la grille du jeu s'affiche, mais c'est tout !
  • il est indispensable que votre programme soit placé dans la partie indiquée du script.
  • vous disposez de deux fonctions déjà écrites :
    • une fonction affiche_pion(ligne, colonne, couleur) pour afficher un pion en indiquant sa ligne et sa colonne dans la grille, et sa couleur ( codée comme ci-dessus )
    • une fonction clic() qui détermine le numéro ( de 0 à 6 ) de la colonne de la grille sur laquelle un joueur a cliqué.

    Attention, cette dernière fonction ne peut pas être appelée directement par votre script : elle est en réalité appelée automatiquement dès que Python détecte un clic souris.
    Vous pouvez par contre très bien la compléter avec votre propre code !

La logique de cette interface graphique est donc un peu différente de la programmation Python telle que vous l'avez faite jusqu'à maintenant : on parle ici de programmation évènementielle, car certaines portions du code ne s'exécuteront que si certains évènements ( ici, un clic-droit ) se produisent.
On n'a donc plus vraiment cette exécution "linéaire" du script où les instructions s’exécutent les unes à la suite des autres dans l'ordre où elles ont été écrites.

Il faudra tenir compte de ce fait pour intégrer votre code à cette interface graphique.

Run n°6 éventuel : améliorations de l'interface

Le jeu est encore "semi-graphique" puisque des informations s'affichent encore dans la console.

Si vous avez du temps, vous pouvez vous pencher de manière plus approfondi sur le fonctionnement du module Tkinter pour améliorer l'interface graphique du jeu : indication du joueur dont c'est le tour et indication du joueur gagnant directement dans l'interface, bouton pour relancer le jeu, etc...

De nombreuses ressources sont disponibles sur le web; vous pouvez par exemple consulter cette page.

Vous pouvez également essayer d'améliorer l'algorithme de détection de gagnant : après tout, ce n'est sans doute pas nécessaire de parcourir toutes les lignes/colonnes/diagonales pour détecter un alignement de 4 pions : on peut simplement compter les pions autour de celui qui vient d'être posé...

Plus délicat, vous pouvez vous intéresser à coder un jeu contre l'ordinateur : une "intelligence artificielle" basique serait une qui joue au hasard à chaque tour...très facile à battre, mais facile à programmer également !
D'autres IA plus élaborées existent bien entendu, mais elles dépassent de loin le cours de Première NSI...

Correction complète du projet

Le code de la version "console" est ici.

Pour une version jouable en ligne, c'est ici.

Projet de fin d'année : un jeu d'action, le Pong !

D'après Wikipedia :

Pong est un des premiers jeux vidéo d'arcade et le premier jeu vidéo d'arcade de sport. Il a été imaginé par l'Américain Nolan Bushnell et développé par Allan Alcorn, et la société Atari le commercialise à partir de novembre 1972. Bien que d'autres jeux vidéo aient été inventés précédemment, comme Computer Space, Pong est le premier à devenir populaire.

Le jeu est inspiré du tennis de table en vue de dessus, et chaque joueur s'affronte en déplaçant la raquette virtuelle de haut en bas, de façon à garder la balle dans le terrain de jeu. Le joueur peut changer la direction de la balle en fonction de l'endroit où celle-ci tape sur la raquette, alors que sa vitesse augmente graduellement au cours de la manche. Un score est affiché pour la partie en cours et des bruitages accompagnent la frappe de la balle sur les raquettes.

Pong
Là je vous parle d'un temps que les moins de 45 ans ne peuvent pas connaître, à l'époque on savait faire du vrai jeu ma p'tite dame, du rustique, du plein de cambouis, un rond et deux rectangles et on s'amusait une après-midi entière et on était pas plus malheureux, ah c'est pas comme maint'nant avec leur smartphaune, leur Wazzap et leur Amstragram...

L'objectif est donc de coder un jeu de Pong contrôlé au clavier entre deux adversaires, en utilisant le module d'interface graphique Tkinter déjà rencontré au projet Puissance 4.

Fichier fourni

Comme ressource de base, téléchargez ce fichier, que vous décompresserez dans votre zone personnelle, et qui contient un répertoire dans lequel se trouvent deux scripts Python :

Étudier la docstring des fonctions du module pong_utils pour comprendre leur fonctionnement.

Ce module est importé au début du script pong.py, dans lequel vous disposez donc de ses fonctions utilitaires.

Principe du code

Le script pong.py est structuré en plusieurs parties :

Travail à faire

A vous de développer votre code pour permettre à deux joueurs de disputer une partie de Pong...

Commencer modestement : une balle qui bouge et rebondit, puis une raquette qui se déplace, puis deux raquettes, une balle qui rebondit sur les raquettes, etc...
Beaucoup de questions sont déjà à se poser : comment déplacer la balle ? Les raquettes ? De quelles variables a-t-on besoin ? etc...

Passez ensuite aux raffinements : gestion d'un score, d'un "game over", des sons, de la couleur,....

Rappel : lien vers la page de ressources de Tkinter.

Team working !