Nous voulons programmer un tic-tac-toe en réseau.
Le projet est déjà entamé. Vous devez télécharger ce code
puis l'enrichir.
Le projet est organisé en 4 packages:
Il contient:
Regardez attentivement ces classes que vous utiliserez.
Nous avons les conventions suivantes:
Il contient:
Un objet Joueur, a une couleur, connait la configuration des pions, l'arbitre, ainsi qu'un objet de type LecteurPosition. Si cet objet est du sous-type LecteurPositionDeFlux, alors le joueur est en attente d'une chaine de caractères sur un flux d'entrée contenant la position qu'il doit jouer.
Un utilisateur peut donc transmettre par le flux d'entrée standard son déplacement à l'objet Joueur qui le représente.
Pour transmettre une position sur le flux d'entrée standard, un utilisateur doit respecter la convention suivante:
i,j
(abscisse, virgule, ordonnée). Par exemple 1,2
.NB. la méthode toString
des objets de type Position
respecte cette convention.
Il contient pour l'instant:
A l'avenir, nous voulons aussi avoir:
Il est vide! Créez vite une première application TicTacToeShell
, dans laquelle
les deux utilisateurs donnent la position de leur nouvelle marque, sous forme textuelle,
dans le shell.
Dans la méthode main
:
EnsembleDeMarques
),LecteurPositionDeFlux
),Arbitre
et Joueur
),Vous pouvez jouer à l'aveugle: la grille n'est pas affichée!
La classe EnsembleDeMarques
(le modèle) ne connait pas la classe VueShell
(la vue).
On veut pourtant qu'elles puissent communiquer entre elles, pour que la vue soit mise à jour après la modification des données (ajout ou suppression d'un pion sur une position).
En même temps, on ne veut pas que le modèle dépende d'une vue particulière et on voudrait avoir la possibilité d'offrir aisément zéro, une ou plusieurs vues sur le modèle.
Pour réaliser cela, nous allons mettre en oeuvre le principe Observable/Observer.
Notre modèle (EnsembleDeMarques
) doit être observable (et observé par la vue).
Pour être observable, il doit dériver de la classe Observable.
Il hérite notamment des méthodes
setChanged()
: indique que l'état du modèle à changernotifyObservers(Object arg)
: avertit les objets qui observent le modèleclearChanged()
: oublie le changementque vous devez appeler, dans l'ordre, à la fin de la méthode ajouterMarque
,
pour demander à la vue de procéder à une mise à jour de l'affichage.
Notre vue (VueShell
) doit observer notre modèle et déclencher l'affichage à
chaque fois que le modèle est mis à jour après l'ajout d'une nouvelle marque.
Elle doit satisfaire l'interface Observer,
c'est-à-dire posséder une méthode update(Observable o, Object arg)
(appelée par notifyObservers(Object arg)
).
C'est dans cette méthode que vous devez déclencher l'affichage.
L'objet o
est l'objet observable qui appelle update
(EnsembleDeMarques
).
Le paramètre arg
permet d'ajouter une information pour la mise à jour; par exemple,
la position à laquelle à été ajouté la nouvelle marque.
TicTacToeShell
§Dans la méthode main
:
VueShell
),EnsembleDeMarques
)
avec la méthode addObserver(Observer o)
, héritée de Observable.Vous pouvez commencer à jouer!
Jusqu'à maintenant, les deux utilisateurs jouent sur la même machine.
Nous allons maintenant faire deux applications: TicTacToeShellServeur
et TicTacToeShellClient
.
Les deux applications auront toutes deux, un ensemble de marques, deux joueurs synchronisés par un arbitre, ainsi qu'une vue. Cependant, l'un des deux joueurs lira ses positions depuis la machine distante, tandis que l'autre enverra ses positions sur la machine distante.
Ainsi, seuls des positions, sous forme textuelle, seront communiquées.
LecteurPositionDeFlux
le flux d'entrée du socket. Testez cette partie avec telnet
.JoueurTransmetteur
qui dérive de Joueur
et qui redéfinit la
méthode jouer
de façon à, une fois une position choisie,
l'écrire sur le flux de sortie du socket. Testez encore avec telnet
.Une fois que vous avez un serveur qui fonctionne, écrire
l'application TicTacToeShellClient
est un jeu d'enfant,
car le code est parfaitement symétrique au code du serveur.
Vous pouvez maintenant jouer depuis deux machines différentes. Mais l'interface du jeu n'est pas confortable.
Nous voulons créer une interface graphique pour notre jeu.
Le package Sortie du projet sera enrichi:
VueGraphique
satisfaisant les interfaces Runnable
et Observer
,La package App sera enrichi de nouvelles applications:
TicTacToeGraphique
pour jouer à deux sur la même machine,La conception est libre, mais si vous ne savez pas quoi faire, vous pouvez:
Case
dérivant de JButton
avec un champs contenant la position correspondante.VueGraphique
, organisez les cases dans un panel (JPanel
) sous forme tabulaire (GridLayout
).Vous pouvez commencer par tester votre affichage en donnant les positions par le shell. Mais comment lire les positions directement sur l'interface graphique ?
Maintenant que nous avons une interface graphique, nous voulons aussi que les utilisateurs puissent prescrire leurs déplacements par des actions (clics) sur l'interface graphique.
CaseListener
.
Une instance de cette classe devrait récupérer la position
de la case choisie (et c'est pourquoi il est utile qu'une case
connaisse sa position).LecteurPositionDeVueGraphique
qui satisfait
l'interface LecteurPosition
. La vue, comme le listener, doivent
connaitre le lecteur afin de lui transmettre la position de la case
choisie.