Académique Documents
Professionnel Documents
Culture Documents
Algorithmique Antipolis
& Programmation Deug MIAS-MI 1
2002–2003
TP N 3
Procédures et fonctions
Buts :
– Manipuler des objets complexes
– Tableaux à deux dimensions
– Usage des sentinelles
– Utilisation des constantes
– Apprendre à modifier un programme
1 Le jeu « Puissance 4 »
Ce jeu se joue à deux joueurs sur une grille rectangulaire supposée verticale. Chacun à
leur tour les joueurs posent un pion en haut d’une colonne non pleine : ce pion glisse vers le bas jusqu’à
être arrêté par un autre pion ou par le bas de la grille. Le gagnant est le premier qui aligne 4 de ses pions,
horizontalement, verticalement ou en diagonale.
Voici un exemple de partie en cours :
1 2 3 4 5 6 7
. . . . . . .
. . . . . . .
. . . . . . .
. . o x o . .
. o x o x . .
x x x o o . .
C’est à X de jouer. Il peut gagner en jouant en colonne 5 mais, s’il ne le voit pas et commet l’erreur
de jouer en colonne 2, c’est O qui pourra gagner en jouant lui aussi en colonne 2 !
Nous avons programmé pour vous un gestionnaire de partie pour ce jeu : un programme qui demande
alternativement à deux joueurs humains 1 quel coup ils jouent, affiche l’état de la partie et détermine qui
gagne. Ce programme, trop long et trop complexe pour que vous l’écriviez vous- même, vous est fourni
dans les Documents de cours et il est reproduit à la fin de cette feuille de TP. Votre premier travail
sera de le comprendre, le second sera d’y faire quelques modifications pour en améliorer la
programmation ou la qualité.
2 Comprendre un programme
2.1 Objets complexes
Les objets définis par la classe instanciable Power4Game sont, comme leur nom l’indique, des
parties en cours de « Puissance 4 ». Le constructeur sera chargé de créer la partie dans son état initial
(personne n’a encore joué) et la classe exportera une seule méthode d’instance, l’ordre de « jouer la
partie » ! Regardez la classe Power4 pour voir comment la classe Power4Game est utilisée et
vérifiez que toutes les autres méthodes de Power4Game sont déclarées private.
1. Programmer un jeu « humain contre ordinateur » n’est pas encore de votre niveau : attendez la fin du semestre !
Les attributs d’une partie en cours seront, très naturellement, l’état de la grille de jeu (les coups déjà
joués), les noms des joueurs (pour l’affichage des dialogues), celui dont c’est le tour de jouer et le
résultat provisoire (la partie est-elle finie? y a-t-il un gagnant ?).
3 Modifier un programme
À présent que vous avez à peu près compris comment marche ce programme, nous allons voir si vous
êtes capables de lui faire subir quelques améliorations ou modifications mineures. Les exercices proposés
sont tous indépendants et ils doivent être réalisés sans bouleverser le programme de façon importante.
a. Modifiez le programme pour que, quand il affiche l’état de la partie, il dise aussi à qui c’est le tour de
jouer.
b. La convention qui a été choisie est de numéroter les lignes de bas en haut. Ce n’est pas la convention
usuelle en informatique. Modifiez le programme pour qu’elles soient numérotées de haut en bas.
c. Les lignes 98 et 99 sont un peu violentes car elles peuvent interrompre la partie pour une simple faute
de frappe. Modifiez le programme pour que, si un joueur joue dans une colonne pleine, on lui signale son
erreur et on lui demande un autre choix.
d. La méthode playable est un peu lourde puisqu’elle réexamine toutes les têtes de colonne à
chaque coup alors qu’elles ne changent pas souvent. Modifiez le programme pour que la fin de partie par
impossibilité de jouer (partie nulle) soit décelée plus économiquement.
e. (difficile) On change les règles en déclarant qu’un alignement de quatre pions est aussi ga- gnant s’il
est réalisé selon la marche du cavalier aux échecs (c’est-à-dire selon une pente 2, –2, 1/2 ou –1/2).
Modifiez le programme pour tenir compte de cette nouvelle règle. Faites-le de telle sorte qu’on puisse
revenir très facilement aux anciennes règles.
3. Il aurait même été encore plus propre de les déclarer private.
f. (difficile) Il n’est pas facile de repérer les alignements. Modifiez le programme de telle sorte que,
quand un joueur gagne, l’alignement qu’il vient de compléter soit mis en majuscules.
g. (programme d’imitation, facultatif) Le jeu japonais appelé gomoku se joue avec les règles de notre «
morpion » (un joueur a les X, l’autre les O, ils jouent chacun leur tour en posant leur pion sur une
intersection d’un quadrillage, le premier qui aligne cinq a gagné) à la seule différence que la zone de jeu
est un carré .
Dans la variante la plus jouée, appelée gomoku ninuki, une règle de prise est ajoutée : quand un
joueur, au moment où il joue, prend deux pions adverses en tenaille avec un autre de ses pions, ils sont
considérés comme prisonniers et retirés du jeu (par exemple, si X joue sur le point dans
la position X O O . , la situation devient X . . X ; en revanche, O peut sans danger jouer
sur le point dans une position X O . X ). La prise peut être multiple et, comme l’alignement,
elle se fait dans n’importe quelle direction. Le gagnant est le premier qui a aligné cinq ou fait dix
prisonniers. Programmez le jeu de base ou la variante.
1 import unsa.Console ;
2
3 public class Power4Game {
4
5 //
CONSTANTES
6 // joueurs
7 final static int PLAYER_1 = 0 ;
8 final static int PLAYER_2 = 1 ;
9 final static int EMPTY = 2 ;
10 final static char[] SYMBOL = new char[] { ’ x ’ , ’ o ’ , ’ . ’ } ;
11 // coordonnées
12 final static int GRID_WIDTH = 9 ; // 7 + sentinelles
13 final static int GRID_HEIGHT = 8 ; // 6 + sentinelles
14 final static int FIRST_COL = 1 ;
15 final static int LAST_COL = GRID_WIDTH - 2 ;
16 final static int TOP_LINE = GRID_HEIGHT - 2 ;
17 final static int BOTTOM_LINE = 1 ;
18 // directions
19 final static int H = 0 ; // horizontalement
20 final static int V = 1 ; // verticalement
21 final static int[] HOR = new int[] {1, 0} ; // horizontale
22 final static int[] VER = new int[] {0, 1} ; // verticale
23 final static int[] UP = new int[] {1, 1} ; // diagonale montante
24 final static int[] DOWN = new int[] {1, -1} ; // diagonale descendante
25 final static int[][] DIRECTION = new int[][] {HOR, VER, UP, DOWN} ;
26
27 // Attributs
28 private int[][] grid ; // grille de jeu
29 private String[] name ; // noms des joueurs
30 private int player ; // qui doit jouer
31 private int result ; // vainqueur ou partie nulle (si EMPTY)
32
33 // Variable d’instance
34 private int impactLine ; // ligne atteinte par le dernier pion joué
35
37 public Power4Game (String[] name) { // noms des joueurs
38 // tout mettre à vide, y compris les sentinelles !
39 grid = new int[GRID_WIDTH][GRID_HEIGHT] ;
40 for (int i = 0 ; i < GRID_WIDTH ; i++) {
41 for (int k = 0 ; k < GRID_HEIGHT ; k++)
42 grid[i][k] = EMPTY ;
43 }
44 this.name = name ;
45 player = PLAYER_1 ;
46 result = EMPTY ;
47 }
48
49 // Fonctions
50 private boolean isFree (int column) {
51 return grid[column][TOP_LINE] == EMPTY ;
52 }
53 private boolean playable () {
54 // cherche s’il existe une colonne libre
55 int column = FIRST_COL ;
56 while (column <= LAST_COL) {
57 if (isFree(column))
58 return true ;
59 column++ ;
60 }
61 return false ;
62 }
63 private boolean isWinning (int column, int line) {
64 // cherche s’il existe une direction alignant 4
65 int i = 0 ;
66 while (i < DIRECTION.length) {
67 if (isWinning(column, line, DIRECTION[i]))
68 return true ;
69 i++ ;
70 }
71 return false ;
72 }
73 private boolean isWinning (int column, int line, int[] dir) {
74 int nextColumn = column + dir[H] ; int nextLine = line + ;
dir[V]
75 int forward = 0 ; // nombre de cases du joueur vers l’avant
76 while (grid[nextColumn][nextLine] == player)
77 { nextColumn += dir[H] ; nextLine += dir[V] ; forward+
+ ;
78 }
79 // reculer
80 nextColumn = column - dir[H] ; nextLine = line - dir[V] ;
81 int backward = 0 ; // nombre de cases du joueur vers l’arrière
82 while (grid[nextColumn][nextLine] == player) {
83 nextColumn -= dir[H] ; nextLine -= dir[V] ; backward++ ;
84 }
85 return backward + 1 + forward >= 4 ;
86 }
87
88 // Procédures
89 private void nextPlayer () {
90 // préférer un test clair à une astuce fondée sur les valeurs
91 // arbitraires des constantes comme ‘player = 1 - player‘
92 if (player == PLAYER_1)
93 player = PLAYER_2 ;
94 else
95 player = PLAYER_1 ;
96 }
98 if (!isFree(column))
99 throw new RuntimeException(" colonne p l e i n e " ) ;
100 impactLine = TOP_LINE ; // on sait qu’elle est libre
101 while (impactLine > BOTTOM_LINE && grid[column][impactLine - 1] == EMPTY)
102 impactLine-- ;
103 // on est en bas ou au-dessus d’un pion
104 grid[column][impactLine] = player ;
105 }
106
107 // Affichage
108 private void printBoard () {
109 // numéros des colonnes
110 for (int column = FIRST_COL ; column <= LAST_COL ; column++)
111 System.out.print(" " + column + " ") ;
112 System.out.println() ;
113 // état de la partie
114 for (int line = TOP_LINE ; line >= BOTTOM_LINE ; line--) {
115 for (int column = FIRST_COL ; column <= LAST_COL ; column++)
116 System.out.print(" " + SYMBOL[grid[column][line]] + " ") ;
117 System.out.println() ;
118 }
119 }
120
121 // Jeu d’une partie
122 public void playGame () {
123 while (result == EMPTY && playable()) {
124 printBoard() ;
125 int column = Console.readInt(name[player] + " joue en colonne ?") ;
126 play(column) ; // la variable ‘impactLine‘ a reçu une valeur
127 if (isWinning(column, impactLine))
128 result = player ;
129 else
130 nextPlayer() ;
131 }
132 printBoard() ;
133 if (result == EMPTY)
134 System.out.println(" p a r t i e n u l l e ") ;
135 else
136 System.out.println(name[result] + " gagne" ) ;
137 }
138 }