Voici un exemple de projet qu'il est possible de développer en 1ère NSI : le jeu du Tic-Tac-Toe (ou morpion) n'a que très peu d'intérêt en lui-même (sauf peut-être pour de jeunes enfants) mais bien entendu ici nous recherchons un intérêt pédagogique pour l'apprentissage des langages Python ou Javascript.
Ce jeu a d'abord été créé en mode console en Python, puis adapté en version graphique Web en adaptant le code Python en Javascript. L'utilisation d'un canvas permet de gérer l'interface de jeu graphique.
Lancer le jeu pour une démo.
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Tic Tac Toe</title>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h1>Jeu du Tic-Tac-Toe</h1>
<div id="jeu">
<h4 id="status">Cliquer sur Commencer pour démarrer la partie</h4>
<canvas id="plateau">
<p>Votre navigateur ne prend pas en charge les Canvas HTML5... Dommage !</p>
</canvas>
<p><button onclick="partie()" id="bouton">Commencer</button></p>
</div>
<script src="script.js"></script>
</body>
</htmL>
"use strict";
// paramètres du jeu
const DIM = 3;
const VIDE = 0;
const CASE = 150;
const RAYON = 0.7 * CASE/2;
const JOUEURS = new Map([[1 , ['blue', 'bleu'] ], [-1 , ['green', 'vert']]]); // dictionnaire
const NB_COUPS_MAX = DIM * DIM;
//****** fin paramètres
//* Variables globales : pour faciliter la communication au seind des fonctions
let status = document.getElementById('status');
let bouton = document.getElementById('bouton');
let canvas = document.querySelector('#plateau');
let plateau = canvas.getContext('2d'); // conserve une référence (Context) à la zone graphique pour 1 graphe 2D
const largeur = canvas.width = CASE * DIM;
const hauteur = canvas.height = CASE * DIM;
let joueur = 1;
let grille = [];
let nb_coups = 0;
//******* fin variables globales
function caseCliquee(evt){
// récupère la position du clic sur le plateau et appelle la fonction jouer si la case est libre
let x = evt.offsetX; // Position de la souris par rapport à l'élément appelant
let y = evt.offsetY;
let c = Math.trunc(x / CASE);
let l = Math.trunc(y / CASE);
if (grille[l][c] == VIDE){
jouer(l, c);
}
}
function dessinerPion(l, c, couleur){
// Dessine un pion dans la case (l, c) de la bonne couleur
plateau.fillStyle = couleur;
plateau.beginPath();
plateau.arc(CASE/2 + c*CASE, CASE/2 + l*CASE, RAYON, 0, 2*Math.PI, true);
plateau.fill();
}
function jouer(l, c){
// moteur principal du jeu
nb_coups++;
let couleur = JOUEURS.get(joueur)[0];
grille[l][c] = joueur; // maj de la grille
dessinerPion(l, c, couleur);
afficherGrille(); // retour console facultatif
if (gagnant()){
canvas.removeEventListener("click", caseCliquee);
status.textContent = "Gagnant : joueur "+ JOUEURS.get(joueur)[1]+" !!!";
bouton.textContent = "Rejouer";
bouton.setAttribute("onclick", "partie()");
return
}
if (nb_coups == NB_COUPS_MAX){
canvas.removeEventListener("click", caseCliquee);
status.textContent = "Fin du jeu ! Match nul...";
bouton.textContent = "Rejouer";
bouton.setAttribute("onclick", "partie()");
return
}
joueur = - joueur; // changement de joueur
}
function partie(){
// Initialisation d'une nouvelle partie
nb_coups = 0;
bouton.textContent = "Partie en cours...";
bouton.removeAttribute("onclick");
status.textContent = "Cliquer sur une case pour jouer";
grille = creerGrille(DIM, VIDE);
canvas.addEventListener("click", caseCliquee);
dessinerPlateau();
afficherGrille(); // retour console facultatif
}
function dessinerPlateau(evt) {
plateau.clearRect(0, 0, largeur, hauteur); // nettoie tout le canvas
plateau.strokeStyle = 'red';
for (let i = 0; i < DIM; i++) {
plateau.beginPath();
plateau.moveTo(0, CASE * i);
plateau.lineTo(CASE * DIM, CASE * i);
plateau.stroke();
plateau.beginPath();
plateau.moveTo(CASE * i, 0);
plateau.lineTo(CASE * i, CASE * DIM);
plateau.stroke();
}
plateau.strokeStyle = 'black';
plateau.strokeRect(0, 0, largeur, hauteur);
}
function creerGrille(dim = 3, vide = 0) {
// Renvoie une grille carrée de dimension 'dim', remplie de 'vide'
let grille = [];
for (let i = 0; i < dim; i++) {
let ligne = [];
for (let j = 0; j < dim; j++) {
ligne.push(vide);
}
grille.push(ligne);
}
return grille
}
function afficherGrille(){
// Rendu graphique en console de la grille
console.log(grille);
}
dessinerPlateau();
function gagnant(){
// Renvoie true si le joueur gagne (sinon false)
// ligne gagnante ?
for (let l =0 ; l < DIM; l++){
let c = 0;
while (c < DIM && grille[l][c] == joueur){
c++;
}
if (c == DIM){
return true;
}
}
// colonne gagnante ?
for (let c =0 ; c < DIM; c++){
let l = 0;
while (l < DIM && grille[l][c] == joueur){
l++;
}
if (l == DIM){
return true;
}
}
// diagonale 1 gagnante ?
let n = 0;
while (n < DIM && grille[n][n] == joueur){
n++;
}
if (n == DIM){
return true;
}
// diagonale 2 gagnante ?
let m = 0;
while (m < DIM && grille[m][DIM - 1 - m] == joueur){
m++;
}
if (m == DIM){
return true;
}
return false; // le joueur n'a pas gagné
}
#!/usr/bin/env python
# coding: utf-8
# Version Tkinter
import tkinter as tk
def creer_grille(dim=3, vide=0):
""" Renvoie une grille carrée de dimension 'dim', remplie de 'vide' """
return [[vide for i in range(dim)] for j in range(dim)]
def gagnant():
""" Renvoie True si le joueur gagne (sinon False) """
# ligne gagnante ?
for l in range(DIM):
c = 0
while c < DIM and grille[l][c] == joueur:
c = c + 1
if c == DIM:
return True
# colonne gagnante ?
for c in range(DIM):
l = 0
while l < DIM and grille[l][c] == joueur:
l = l + 1
if l == DIM:
return True
# diagonale 1 gagnante ?
n = 0
while n < DIM and grille[n][n] == joueur:
n = n + 1
if n == DIM:
return True
# diagonale 2 gagnante ?
n = 0
while n < DIM and grille[n][DIM - 1 - n] == joueur:
n = n + 1
if n == DIM:
return True
return False # le joueur n'a pas gagné
def afficher_grille():
""" Rendu graphique en console de la grille """
print("État de la grille :")
for l in range(DIM):
for c in range(DIM):
print(str(grille[l][c]).rjust(5), end='')
print()
print()
def dessiner_grille():
""" Dessine une grille vierge dans le Canvas plateau """
plateau.delete(tk.ALL)
for i in range(1, DIM):
plateau.create_line(0, CASE*i, CASE*DIM, CASE*i, fill='red')
plateau.create_line(CASE*i, 0,CASE*i, CASE*DIM, fill='red')
def dessiner_pion(l, c, couleur):
""" Dessine un pion dans le Canvas plateau """
plateau.create_oval(CASE*c+CASE//2-RAYON, CASE*l+CASE//2-RAYON, CASE*c+CASE//2+RAYON, CASE*l+CASE//2+RAYON, fill=couleur, outline=couleur)
def partie(*evt):
""" Initialise une partie """
global grille, joueur, nb_coups, nb_coups_max # ca facilite la communication pour toutes les fonctions
info.set("Cliquer sur une case libre pour jouer")
rejouer.configure(state=tk.DISABLED)
grille = creer_grille(DIM, VIDE)
nb_coups = 0
nb_coups_max = len(grille) ** 2
joueur = 1 # 1er joueur
afficher_grille() # retour console facultatif
dessiner_grille()
plateau.bind('<Button>', jouer_clic)
def jouer(l, c):
""" Joue un pion et met la grille à jour """
global grille, joueur, nb_coups
grille[l][c] = joueur # maj de la grille
couleur = JOUEUR[grille[l][c]][0]
dessiner_pion(l, c, couleur)
afficher_grille() # retour console facultatif
nb_coups = nb_coups + 1
if gagnant():
plateau.unbind('<Button>')
rejouer.configure(state=tk.NORMAL)
print(f"Bravo {joueur}, tu as gagné !") # retour console facultatif
info.set(f"Joueur {JOUEUR[joueur][1]} a gagné !")
return
if nb_coups == nb_coups_max:
plateau.unbind('<Button>')
rejouer.configure(state=tk.NORMAL)
print("Match nul...") # retour console facultatif
info.set(f"Match nul...")
return
joueur = - joueur
def jouer_clic(evt):
""" Convertit le clic en une case : ligne, colonne """
l, c = evt.y // CASE, evt.x // CASE
if grille[l][c] == VIDE: # on vérifie que le joueur a cliqué sur une case libre
jouer(l, c)
# paramètres du jeu
DIM = 3
VIDE = 0
CASE = 150
RAYON = 0.7 * CASE//2
JOUEUR = {-1 : ('blue', 'bleu'), 1 : ('green', 'vert')}
######################
jeu = tk.Tk()
jeu.title("Jeu Tic Tac Toe")
tk.Button(jeu, text="Quitter", command=jeu.destroy).pack(side=tk.BOTTOM)
rejouer = tk.Button(jeu, text="Rejouer", command=partie, state=tk.DISABLED)
rejouer.pack(side=tk.BOTTOM)
info = tk.StringVar()
tk.Label(jeu, textvariable=info).pack()
plateau = tk.Canvas(jeu, width=CASE*DIM, height=CASE*DIM, bg='white')
plateau.pack()
partie()
jeu.mainloop()