Réaliser un morpion en C avec SDL
Un article de Games Creators Network.
Sommaire |
[modifier] Principe
Le jeu de morpion est certainement l'un des jeux le plus facile à programmer.
Le principe est le suivant : chaque joueur clique à tour de rôle sur une grille. L'un des joueurs est symbôlisé par un carré, l'autre par un rond. Le but est d'aligner trois symboles à l'horizontal, la verticale ou en diagonale.
[modifier] Préparation
Tout d'abord, commencez par créer un projet vide avec Dev-C++, puis configurez le projet.
Pour cela allez dans Projets > Options du projet et cliquez sur l'onglet Paramètres.
Vous obtiendrez la fenêtre suivante:
Remplissez le champ Editeur de liens avec la valeur suivante:
-lmingw32 -lSDLMain -lSDL
[modifier] Initialisation et libération de SDL
La première étape est d'inclure les fichiers d'en-tête qui nous seront nécessaires. Dans cet article, nous aurons besoin de sdl.h évidemment mais également de stdlib.h pour la fonction rand() que nous utiliserons à la fin de cet article.
#include <SDL/sdl.h> #include <stdlib.h>
Trois lignes sont nécessaires pour initialiser SDL :
- SDL_Init() initialise la SDL. On lui passe en paramètre la liste des sous-systèmes à initialiser, nous n'avons besoin que du module vidéo.
- SDL_SetVideoMode() prend de nombreux paramètre. Les trois premiers représente la taille et la résolution de la fenêtre. Quant au dernier paramètre, il indique que la surface doit être créée dans la mémoire vidéo et que nous utiliserons deux tampons afin d'éviter le scintillement à l'écran.
- La dernière ligne change le titre de notre fenêtre. NULL indique que la fenêtre se verra associé à l'icône par défaut.
SDL_Init(SDL_INIT_VIDEO); screen = SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF); SDL_WM_SetCaption("Morpion - Games Creators Network (http://www.games-creators.org)",NULL);
À la fin de notre programme, nous devrons appeler SDL_Quit() afin d'arrêter proprement l'utilisation de SDL.
SDL_Quit();
[modifier] Stocker le plateau de jeu
Notre plateau de jeu est composé de neuf cases. Ces cases peuvent avoir trois états :
- vide,
- occupée par un carré,
- occupée par un rond.
Chaque case sera décrite par le type suivant :
enum e_case { VIDE=0, CARRE, ROND };
Notre plateau de jeu sera donc un tableau de neuf cases déclaré de la façon suivante :
enum e_case plateau[9];
[modifier] Chargement et libération des ressources
Avant de commencer à travailler, nous devons charger les média utilisés par notre jeu. Dans notre cas, ils sont très peu nombreux :
- un fond d'écran,
- une image de carré,
- une image de rond.
Le chargement de ces images dans des surfaces SDL se fait de la façon suivante :
/* chargement des média */ fond=SDL_LoadBMP("fond.bmp"); carre=SDL_LoadBMP("carre.bmp"); rond=SDL_LoadBMP("rond.bmp");
Et leur libération est réalisée de cette manière :
/* Libération des média */ SDL_FreeSurface(fond); SDL_FreeSurface(carre); SDL_FreeSurface(rond);
[modifier] La boucle de gestion de messages : jouons à présent!
La boucle de messages teste deux événements :
- SDL_MOUSEBUTTONUP: ajoute un pion à la grille,
- SDL_QUIT: pour quitter le jeu.
while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_MOUSEBUTTONUP: /* on teste si l'utilisateur a bien cliqué dans le plateau de jeu */ if(event.button.x<100) break; if(event.button.x>295) break; if(event.button.y<100) break; if(event.button.y>295) break; /* Détermination de la case */ cx = (event.button.x-100)/65; cy = (event.button.y-100)/65; /* Si la case n'est pas vide, inutile de continuer */ if(plateau[cx+3*cy]!=VIDE) break; /* On place le nouveau pion */ plateau[cx+3*cy]=joueur; /* On change de joueur */ if(joueur==CARRE) joueur=ROND; else joueur=CARRE; break; case SDL_QUIT: isdone=1; break; } }
[modifier] Qui a gagné ?
Pour savoir qui a gagné, réalisons une fonction qui renvoie :
- VIDE si la partie n'est pas finie ou nulle,
- ROND ou CARRE si le vainqueur est respectivement les ronds ou les carrés.
enum e_case TestGagnant(const enum e_case *plateau) { int i; /* Teste les lignes */ for(i=0;i<3;i++) if( (plateau[(i*3)+0]==plateau[(i*3)+1]) && (plateau[(i*3)+0]==plateau[(i*3)+2]) ) if(plateau[(i*3)+0]!=VIDE) return plateau[(i*3)+0]; /* Teste les colonnes */ for(i=0;i<3;i++) if( (plateau[i]==plateau[3+i]) && (plateau[i]==plateau[6+i]) ) if(plateau[i]!=VIDE) return plateau[i]; /* Teste les diagonales */ if( (plateau[0]==plateau[4]) && (plateau[0]==plateau[8]) ) if(plateau[0]!=VIDE) return plateau[0]; if( (plateau[2]==plateau[4]) && (plateau[2]==plateau[6]) ) if(plateau[2]!=VIDE) return plateau[2]; return VIDE; }
Il ne reste plus qu'à concevoir une petite animation qui affiche partout des ronds/carrés selon le joueur qui a remporté la partie.
if(gagnant!=VIDE) { dest.w=dest.h=64; while(!isdone) { while(SDL_PollEvent(&event)) if(event.type==SDL_QUIT) isdone=1; dest.x=rand()%(640-64); dest.y=rand()%(480-64); if(gagnant==ROND) SDL_BlitSurface(rond, NULL, screen, &dest); else SDL_BlitSurface(carre, NULL, screen, &dest); SDL_Flip(screen); } }
[modifier] Démo : jeu de morpion
En emboîtant tous les éléments décris ci-dessus, on arrive au résultat suivant :
#include <SDL/sdl.h> #include <stdlib.h> enum e_case { VIDE=0, CARRE, ROND }; enum e_case TestGagnant(const enum e_case *plateau) { int i; /* Teste les lignes */ for(i=0;i<3;i++) if( (plateau[(i*3)+0]==plateau[(i*3)+1]) && (plateau[(i*3)+0]==plateau[(i*3)+2]) ) if(plateau[(i*3)+0]!=VIDE) return plateau[(i*3)+0]; /* Teste les colonnes */ for(i=0;i<3;i++) if( (plateau[i]==plateau[3+i]) && (plateau[i]==plateau[6+i]) ) if(plateau[i]!=VIDE) return plateau[i]; /* Teste les diagonales */ if( (plateau[0]==plateau[4]) && (plateau[0]==plateau[8]) ) if(plateau[0]!=VIDE) return plateau[0]; if( (plateau[2]==plateau[4]) && (plateau[2]==plateau[6]) ) if(plateau[2]!=VIDE) return plateau[2]; return VIDE; } int main(int argc, char **argv) { SDL_Surface *screen, *fond, *carre, *rond; SDL_Event event; SDL_Rect dest; int isdone; int cx, cy, i; enum e_case joueur, gagnant; enum e_case plateau[9]= {VIDE,VIDE,VIDE, VIDE,VIDE,VIDE, VIDE,VIDE,VIDE}; SDL_Init(SDL_INIT_VIDEO); screen = SDL_SetVideoMode(640,480,32,SDL_HWSURFACE|SDL_DOUBLEBUF); SDL_WM_SetCaption("Morpion - Games Creators Network (http://www.games-creators.org)",NULL); /* chargement des médias */ fond=SDL_LoadBMP("fond.bmp"); carre=SDL_LoadBMP("carre.bmp"); rond=SDL_LoadBMP("rond.bmp"); isdone=0; joueur=CARRE; gagnant=VIDE; while((!isdone)&&(gagnant==VIDE)) { while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_MOUSEBUTTONUP: if(event.button.x<100) break; if(event.button.x>295) break; if(event.button.y<100) break; if(event.button.y>295) break; /* Détermination de la case */ cx = (event.button.x-100)/65; cy = (event.button.y-100)/65; if(plateau[cx+3*cy]!=VIDE) break; plateau[cx+3*cy]=joueur; if(joueur==CARRE) joueur=ROND; else joueur=CARRE; break; case SDL_QUIT: isdone=1; break; } } /* Test du gagnant */ gagnant=TestGagnant(plateau); /* affichage fond */ SDL_BlitSurface(fond, NULL, screen, NULL); /* affichage de la carte */ dest.w=dest.h=64; for(i=0;i<9;i++) switch(plateau[i]) { case VIDE: break; case CARRE: dest.x=100+(i%3)*65, dest.y=100+(i/3)*65; SDL_BlitSurface(carre, NULL, screen, &dest); break; case ROND: dest.x=100+(i%3)*65, dest.y=100+(i/3)*65; SDL_BlitSurface(rond, NULL, screen, &dest); break; } /* inversion des tampons */ SDL_Flip(screen); } if(gagnant!=VIDE) { dest.w=dest.h=64; while(!isdone) { while(SDL_PollEvent(&event)) if(event.type==SDL_QUIT) isdone=1; dest.x=rand()%(640-64); dest.y=rand()%(480-64); if(gagnant==ROND) SDL_BlitSurface(rond, NULL, screen, &dest); else SDL_BlitSurface(carre, NULL, screen, &dest); SDL_Flip(screen); } } /* Libération des médias */ SDL_FreeSurface(fond); SDL_FreeSurface(carre); SDL_FreeSurface(rond); SDL_Quit(); return 0; }




