Bonjour tout l'monde!
Je suis nouveau ici et je viens à la recherche d'une précieuse aide.
Dans le cadre de mon cursus scolaire, je dois programmer le célèbre jeu Abalone en C, à l'aide des quelques notions qui nous ont été fournies (tableaux, pointeurs, entrées-sorties...). Pour ceux qui ne connaissent pas, les règles se trouvent ici: http://www.joupi.com/templates/1/docum [...] alone_639.pdf
Pour ce qui est du tableau à initialiser pas trop de problème, je m'en suis sorti (heureusement!). Par contre, j'ai plusieurs idées pour programmer les déplacements mais aucune ne me parait plus simple (voire réalisable) que les autres.
A l'aide d'un scanf/printf, l'utilisateur rentre les coordonnées de la boule qu'il souhaite déplacer. Ensuite, il indique une direction (nord-ouest, nord-est...) et une fonction sera créer pour chaque direction demandée. Pour un déplacement vers l'est par exemple, je souhaite effectuer un comptage vers la droite, depuis la boule sélectionnée jusqu'à une case vide ou un bord du tableau de jeu. Ensuite, un compteur s'incrémente en comptant le nombre de boules blanches et noires, puis le programme détermine si le déplacement est possible en fonction des règles de force (3 boules contre 2, 3 boules contre 1 etc..).
Voici la solution qui me paraissait la moins gourmande en temps de programmation mais je ne vois même pas par où commencer.
L'autre solution à laquelle j'avais pensé était d'utiliser 6 pointeurs différents pour les six directions possibles de déplacements, mais là encore la manière de coder ça est assez floue.
Je cherche donc quelqu'un qui soit susceptible de m'aider, et non un code tout fait auquel je ne comprendrai rien.
Merci d'avance.
Bonsoir,
je te conseillerais de modéliser le plateau par un graphe, chaque position étant un noeud, chaque déplacement possible un arc. Pour chaque noeud tu peux ensuite définir un valeur (vide, boule blanche, boule noire)
Cela te permet d'appliquer assez simplement ta solution (par contre pas une fonction pour chaque direction, un paramètre suffira)
Pour ma part, je ne suis pas partisan du graphe mais du tableau 2d.
La plateau est hexagonal, mais on peut aisément imaginer des cases en plus qui feraient qu'il est en forme de parallélogramme.(en rouge sur mon croquis)
ainsi, par la suite, les cases du tableau sont comme je les ai commencé.
Il suffira ensuite de flagger les cases des zones rouges comme interdites, puis tu pourras te déplacer simplement aux cases adjascentes (6 dans abalone) de cette façon :
- x+1,y
- x-1,y
- x,y+1
- x,y-1
- x+1,y+1
- x-1,y-1
Il faut différentier deux choses : la façon dont un mouvement est représenté dans ton programme et la façon dont l'utilisateur entre un mouvement que ce soit par une ligne de commande (chaine caractère), une interface graphique, de la télépathie, etc... Remarques que la même distinction est aussi à faire entre le plateau en interne et la représentation de celui-ci pour l'utilisateur.
Le but ici est de réfléchir à l'architecture fondamental du programme donc l'interface utilisateur, osef (dans un premier temps). Comment noter les coups de manière abstraites ? Je serais d'avis de regarder comment les joueurs d'abalone notent leur partie.
Edit : Tu verras que l'idée de Fvirtman est exactement celle adopté par la communauté des joueurs, il y a bien des case interdites (comme A7 ou I2 par exemple).
Il y a deux cas à différencier : les mouvements en-ligne et les mouvements de coté. Pour les mouvements en ligne, il faut deux couples de coordonnées (départ/arrivée de la bille qui pousse), pour les mouvements latéraux il faut trois couples de coordonnées (départ des billes encadrante et arrivé de l'une d'elle). Une structure pour noter un coup pourrait donc être :
Évidement, il faudra une structure représentant le plateau. Ensuite viendra le moment de coder une fonction qui vérifie qu'un coup est valide par rapport au plateau de jeu, une autre fonction pour appliquer le mouvement, une fonction de test de victoire ( qui compte les billes éliminées), etc...
include<stdio.h>
#define NMAX 11
/* Fonction qui initialise le tableau avec des 0 partout */
void plat_pre_init (int tab[][NMAX])
{
int i, j;
for (i=0;i<NMAX;i++)
for (j=0;j<NMAX;j++)
tab[i][j]=0;
}
/* Dans la fonction d'initialisation du tableau, on codera en premier lieu les couleurs des boules et les différents status des cases (vides,etc...) par les numéros suivant: 0=cases injouable, 1=cases vides, 2=cases rouges, 3=cases bleues, 4=cases du bord */
void plat_init (int tab[][NMAX])
{
int i,j;
/*Création des bords du plateau de jeu, c'est-à-dire des cases où les boules seront éjectées si elles tombent dessus */
for(i=0;i<6;i++)
{
tab[i][0]=4;
}
for(j=0;j<6;j++)
{
tab[0][j]=4;
}
for(j=5;j<=NMAX-1;j++)
{
tab[10][j]=4;
}
for(i=5;i<=NMAX-1;i++)
{
tab[i][10]=4;
}
tab[1][6]=4;
tab[2][7]=4;
tab[3][8]=4;
tab[4][9]=4;
tab[6][1]=4;
/* Placement des boules bleues au début de la partie */
for(j=1;j<6;j++)
{
tab[1][j]=3;
tab[2][j]=3;
tab[2][6]=3;
}
for(j=3;j<6;j++)
{
tab[3][j]=3;
}
/* Placement des boules rouges au début de la partie */
for(j=5;j<10;j++)
{
tab[9][j]=2;
tab[8][j]=2;
tab[8][4]=2;
}
for(j=5;j<8;j++)
{
tab[7][j]=2;
}
}
void affiche_plat(int tab[][NMAX])
{
int i, j;
for(i=0; i<NMAX; i++)
{
for(j=0; j<NMAX; j++)
printf("%3d", tab[i][j]);
printf("\n");
}
}
int main(int argc, char *argv[]){
int tab[NMAX][NMAX];
plat_pre_init(tab);
plat_init(tab);
affiche_plat(tab);
return 0;
}
Voilà comment j'ai codé l'affichage du tableau, à priori la représentation correspond à celle que proposée par Fvirtman. En effet pour l'instant l'interface graphique n'a que très peu d'importance, elle sera réalisée si il reste du temps.
J'avais pensé à l'utilisation de toutes ces structures mais je ne suis pas très à l'aise avec ça. Je ne vois même pas comment mettre mon tableau de jeu réalisé ci-dessus à l'intérieure d'une struct. Je vais essayer de réfléchir à ça. En attendant, voilà c'que j'avais commencé à faire pour les déplacements vers l'EST:
int R,B,vide,bord,var_ech,rapport[2];
R=0;
B=0;
rapport={B,R};
//le joueur aura précédemment sélectionner les coordonnées ij de la boule à déplacer.
for(;tab[i][j]==vide || tab[i][j]==bord;j++)
{
if(tab[i][j]==rouge)
R=R+1;
if(tab[i][j]==bleue)
B=B+1;
}
return rapport;
//cas où il y a 1 boule contre une autre
if (rapport[0]==0 && rapport[1]==1)
{
var_ech=*tab[i][j];
*tab[i][j]=*tab[i][j+1];
*tab[i][j+1]=var_ech;
}
Voici une première partie de ce que je pensais utliser comme manière de coder un déplacement, mais je vais réfléchir aux structures.
Merci pour votre aide, je vous tiens au courant de l'avancement.
J'ai l'impression que ton bord n'est pas complet. Et de toute manière pourquoi noter les bords différemment des cases interdites ? Il suffit de 3 valeurs (bille bleu, bille rouge, case libre) + 1 valeur spécial pour le vide. De plus comme la position initial ne change jamais, autant la marquer en dur dans le code.
Quelque chose comme ça :
int main (int argc, char *argv[])
{
/* on codera en premier lieu les couleurs des boules et les différents
status des cases (vides,etc...) par les numéros suivant:
0=cases injouable,
1=cases vides,
2=cases rouges,
3=cases bleues,
( 4=cases du bord, distinction inutile avec les case injouables )
*/
int tab[NMAX][NMAX] = {
{0, 0, 0, 0, 2, 2, 2, 2, 2},
{0, 0, 0, 2, 2, 2, 2, 2, 2},
{0, 0, 1, 1, 2, 2, 2, 1, 1},
{0, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 0},
{1, 1, 3, 3, 3, 1, 1, 0, 0},
{3, 3, 3, 3, 3, 3, 0, 0, 0},
{3, 3, 3, 3, 3, 0, 0, 0, 0}
};
affiche_plat (tab);
return 0;
}
On peut aussi se débrouiller pour avoir un affichage plus agréable (c'est toujours plus confortable)
// pour avoir des choses plus facile à lire que des chiffres
char caseToChar(int case_plat)
{
switch(case_plat)
{
case 0: // vide intersidéral
return ' ';
break;
case 1: // case libre
return '.';
break;
case 2: // bille rouge
return 'r';
break;
case 3: // bille bleu
return 'b';
break;
default: // quesaco ?
printf ("Code case invalide\n");
exit(EXIT_FAILURE);
break;
}
}
void affiche_plat_joli (int tab[][NMAX])
{
int i, j;
for (i = 0; i < NMAX; i++)
{
// affiche les espaces necessaire en début de ligne
for (j = 0; j < abs (NMAX / 2 - i); ++j)
putchar (' ');
for (j = 0; j < NMAX; j++)
{
if (tab[i][j] == 0)
continue; // on n'affiche pas les case interdites
printf ("%c ", caseToChar(tab[i][j]) );
}
printf ("\n");
}
}
Effectivement, l'affichage plus compréhensible avec votre méthode.
En fait, les cases que j'avais désignées comme des "bords" m'aident dans la programmation des déplacements.
Je pense avoir réussi à coder les déplacements Est (E), Ouest (O) et Sud-Ouest (SO) mais je ne suis pas sûr que ça soit la meilleure méthode.
Voici mon code:
1 #include<stdio.h>
2 #define bord 4
3 #define vide 0
4 #define rouge 3
5 #define bleue 2
6 #define NMAX 11
7
8 void deplacement_ligne_rouge(long choix, int tab[][NMAX], int i, int j)
9 {
10 int rouge, bleue, bord, vide, var_ech1, var_ech2, sumito[2],R,B;
11 sumito={R,B}
12 R=0;
13 B=0;
14 switch (choix)
15 {
16 case E:
17 for(;tab[i][j]==vide || tab[i][j]==bord;j++)
18 {
19 if(tab[i][j]==rouge)
20 R=R+1;
21
22 if(tab[i][j]==bleue)
23 B=B+1;
24 }
25 return sumito;
26
27 /* Une boule rouge face à une case vide */
28 if(sumito[0]==1 && sumito[1]==0)
29 {
30 var_ech1=*tab[i][j];
31 *tab[i][j]=*tab[i][j+1];
32 *tab[i][j+1]=var_ech1;
33 }
34
35 /* Deux boules rouges face à une case vide */
36 if(sumito[0]==2 && sumito[1]==0)
37 {
38 var_ech1=*tab[i][j];
39 *tab[i][j]=*tab[i][j+2];
40 *tab[i][j+2]=var_ech1;
41 }
42
43 /* Trois boules rouges face à une case vide */
44 if(sumito[0]==3 && sumito[1]==0)
45 {
46 var_ech1=*tab[i][j];
47 *tab[i][j]=*tab[i][j+3];
48 *tab[i][j+3]=var_ech1;
49 }
50
51 /* Deux boules rouges contre une boule bleue */
52 if(sumito[0]==2 && sumito[1]==1)
53 {
54 if(tab[i][j+3]==vide)
55 {
56 var_ech1=*tab[i][j+2];
57 *tab[i][j+2]=*tab[i][j+3];
58 *tab[i][j+3]=var_ech1;
59
60 var_ech2=*tab[i][j];
61 *tab[i][j]=*tab[i][j+2];
62 *tab[i][j+2]=var_ech2;
63 }
64
65 else if(tab[i][j+3]==bord)
66 {
67 tab[i][j+2]=vide;
68 var_ech=*tab[i][j];
69 *tab[i][j]=*tab[i][j+2];
70 *tab[i][j+2]=var_ech;
71 }
72
73 /* Trois boules rouges contre une boule bleue */
74 if(sumito[0]==3 && sumito[1]==1)
75 {
76 if(tab[i][j+4]==vide)
77 {
78 var_ech1=*tab[i][j+3];
79 *tab[i][j+3]=*tab[i][j+4];
80 *tab[i][j+4]=var_ech1;
81
82 var_ech2=*tab[i][j];
83 *tab[i][j]=*tab[i][j+3];
84 *tab[i][j+3]=var_ech2;
85 }
86
87 else if(tab[i][j+4]==bord)
88 {
89 tab[i][j+3]=vide;
90 var_ech1=*tab[i][j];
91 *tab[i][j]=*tab[i][j+3];
92 *tab[i][j+3]=var_ech1;
93 }
94
95 /* Trois boules rouges contre deux boules bleues */
96 if(sumito[0]==3 && sumito[1]==2)
97 {
98 if(tab[i][j+5]==vide)
99 {
100 var_ech1=*tab[i][j+3];
101 *tab[i][j+3]=*tab[i][j+5];
102 *tab[i][j+5]=var_ech1;
103
104 var_ech2=*tab[i][j];
105 *tab[i][j]=*tab[i][j+3];
106 *tab[i][j+3]=var_ech2;
107 }
108
109 else if(tab[i][j+5]==bord)
110 {
111 tab[i][j+3]==vide;
112 var_ech1=*tab[i][j];
113 *tab[i][j]=*tab[i][j+3];
114 *tab[i][j+3]=var_ech1;
115 }
116 break;
117
118
119 case O:
120 for(;tab[i][j]==vide || tab[i][j]==bord;j--)
121 {
122 if(tab[i][j]==rouge)
123 R=R+1;
124
125 if(tab[i][j]==bleue)
126 B=B+1;
127 }
128 return sumito;
129
130 /* Une boule rouge face à une case vide */
131 if(sumito[0]==1 && sumito[1]==0)
132 {
133 var_ech1=*tab[i][j];
134 *tab[i][j]=*tab[i][j-1];
135 *tab[i][j-1]=var_ech1;
136 }
137
138 /* Deux boules rouges face à une case vide */
139 if(sumito[0]==2 && sumito[1]==0)
140 {
141 var_ech1=*tab[i][j];
142 *tab[i][j]=*tab[i][j-2];
143 *tab[i][j-2]=var_ech1;
144 }
145
146 /* Trois boules rouges face à une case vide */
147 if(sumito[0]==3 && sumito[1]==0)
148 {
149 var_ech1=*tab[i][j];
150 *tab[i][j]=*tab[i][j-3];
151 *tab[i][j-3]=var_ech1;
152 }
153
154 /* Deux boules rouges contre une boule bleue */
155 if(sumito[0]==2 && sumito[1]==1)
156 {
157 if(tab[i][j-3]==vide)
158 {
159 var_ech1=*tab[i][j-2];
160 *tab[i][j-2]=*tab[i][j-3];
161 *tab[i][j-3]=var_ech1;
162
163 var_ech2=*tab[i][j];
164 *tab[i][j]=*tab[i][j-2];
165 *tab[i][j-2]=var_ech2;
166 }
167
168 else if(tab[i][j-3]==bord)
169 {
170 tab[i][j-2]=vide;
171 var_ech=*tab[i][j];
172 *tab[i][j]=*tab[i][j-2];
173 *tab[i][j-2]=var_ech;
174 }
175
176 /* Trois boules rouges contre une boule bleue */
177 if(sumito[0]==3 && sumito[1]==1)
178 {
179 if(tab[i][j-4]==vide)
180 {
181 var_ech1=*tab[i][j-3];
182 *tab[i][j-3]=*tab[i][j-4];
183 *tab[i][j-4]=var_ech1;
184
185 var_ech2=*tab[i][j];
186 *tab[i][j]=*tab[i][j-3];
187 *tab[i][j-3]=var_ech2;
188 }
189
190 else if(tab[i][j-4]==bord)
191 {
192 tab[i][j-3]=vide;
193 var_ech1=*tab[i][j];
194 *tab[i][j]=*tab[i][j-3];
195 *tab[i][j-3]=var_ech1;
196 }
197
198 /* Trois boules rouges contre deux boules bleues */
199 if(sumito[0]==3 && sumito[1]==2)
200 {
201 if(tab[i][j-5]==vide)
202 {
203 var_ech1=*tab[i][j-3];
204 *tab[i][j-3]=*tab[i][j-5];
205 *tab[i][j-5]=var_ech1;
206
207 var_ech2=*tab[i][j];
208 *tab[i][j]=*tab[i][j-3];
209 *tab[i][j-3]=var_ech2;
210 }
211
211
212 else if(tab[i][j-5]==bord)
213 {
214 tab[i][j-3]==vide;
215 var_ech1=*tab[i][j];
216 *tab[i][j]=*tab[i][j-3];
217 *tab[i][j-3]=var_ech1;
218 }
219 break;
220
221
222 case SO:
223 for(;tab[i][j]==vide || tab[i][j]==bord;i++)
224 {
225 if(tab[i][j]==rouge)
226 R=R+1;
227
228 if(tab[i][j]==bleue)
229 B=B+1;
230 }
231 return sumito;
232
233 /* Une boule rouge face à une case vide */
234 if(sumito[0]==1 && sumito[1]==0)
235 {
236 var_ech1=*tab[i][j];
237 *tab[i][j]=*tab[i+1][j];
238 *tab[i+1][j]=var_ech1;
239 }
240
241 /* Deux boules rouges face à une case vide */
242 if(sumito[0]==2 && sumito[1]==0)
243 {
244 var_ech1=*tab[i][j];
245 *tab[i][j]=*tab[i+2][j];
246 *tab[i+2][j]=var_ech1;
247 }
248
249 /* Trois boules rouges face à une case vide */
250 if(sumito[0]==3 && sumito[1]==0)
251 {
252 var_ech1=*tab[i][j];
253 *tab[i][j]=*tab[i+3][j];
254 *tab[i+3][j]=var_ech1;
255 }
256
257 /* Deux boules rouges contre une boule bleue */
258 if(sumito[0]==2 && sumito[1]==1)
259 {
260 if(tab[i+3][j]==vide)
261 {
262 var_ech1=*tab[i+2][j];
263 *tab[i+2][j]=*tab[i+3][j];
264 *tab[i+3][j]=var_ech1;
265
266 var_ech2=*tab[i][j];
267 *tab[i][j]=*tab[i+2][j];
268 *tab[i+2][j]=var_ech2;
269 }
270
271 else if(tab[i+3][j]==bord)
272 {
273 tab[i+2][j]=vide;
274 var_ech=*tab[i][j];
275 *tab[i][j]=*tab[i+2][j];
276 *tab[i+2][j]=var_ech;
277 }
278
279 /* Trois boules rouges contre une boule bleue */
280 if(sumito[0]==3 && sumito[1]==1)
281 {
282 if(tab[i+4][j]==vide)
283 {
284 var_ech1=*tab[i+3][j];
285 *tab[i+3][j]=*tab[i+4][j];
286 *tab[i+4][j]=var_ech1;
287
288 var_ech2=*tab[i][j];
289 *tab[i][j]=*tab[i+3][j];
290 *tab[i+3][j]=var_ech2;
291 }
292
293 else if(tab[i+4][j]==bord)
294 {
295 tab[i+3][j]=vide;
296 var_ech1=*tab[i][j];
297 *tab[i][j]=*tab[i+3][j];
298 *tab[i+3][j]=var_ech1;
299 }
300
301 /* Trois boules rouges contre deux boules bleues */
302 if(sumito[0]==3 && sumito[1]==2)
303 {
304 if(tab[i+5][j]==vide)
305 {
306 var_ech1=*tab[i+3][j];
307 *tab[i+3][j]=*tab[i+5][j];
308 *tab[i+5][j]=var_ech1;
309
310 var_ech2=*tab[i][j];
311 *tab[i][j]=*tab[i+3][j];
312 *tab[i+3][j]=var_ech2;
313 }
314
315 else if(tab[i+5][j]==bord)
316 {
317 tab[i+3][j]==vide;
318 var_ech1=*tab[i][j];
319 *tab[i][j]=*tab[i+3][j];
320 *tab[i+3][j]=var_ech1;
321 }
322 break;
Le code parait un peu lourd, je pense qu'il y a une manière de généraliser chaque déplacement mais pour l'instant je ne m'intéresse pas à l'optimisation.
Pouvez-vous m'en dire plus sur la manière dont réaliser les déplacements avec les structs?
Bonsoir à tous!
Depuis une semaine, le projet a bien avancé: malgré les conseils donnés, j'ai continué sans utiliser de structures (avec lesquelles je ne suis vraiment pas à l'aise) mais bien un tableau en dur.
J'en suis désormais à la mise en place d'une I.A. pour Abalone. Je me suis renseigné sur l'algorithme minmax utilisé dans la plupart des jeux (j'essayerai ensuite l'élagage alpha-beta si tout se passe bien).
J'ai lu le tuto du site à ce propos, et plusieurs problèmes se posent à moi.
1/ J'aimerai copier mon tableau de jeu de manière à ce qu'une fonction simule le coup actuel.
2/ Je ne vois pas dutout quelle fonction d'évaluation utiliser pour donner du "poids" au coup joué.
Voilà en gros les deux soucis majeurs que j'ai.
Si quelqu'un a une idée à ce propos, je prends!
Bonne soirée!
Bonsoir,
pour copier le tableau, il suffit de faire deux boucles imbriquées.
pour la partie IA, je pense qu'il est possible, au moins dans un premier temps, de chercher à maximiser la distance entre tes boules et le bord:
pour chaque boule, tu calcules la distance la plus courte pour qu'elle soit sortie(sans prêter attention aux autres boules) et tu additionnes toutes les valeurs obtenues. (C'est une fonction assez basique, il y a moyen de faire beaucoup mieux, mais je pense, au moins que commencer que ça peut suffire)
Ensuite éventuellement, tu peux soustraire le même calcul effectué sur les boules adverses
Effectivement, pour copier le tableau je devrais pas avoir plus de soucis que ça.
Pour l'IA, c'que j'pensais faire c'est de déterminer des "cercles" sur le plateau qui délimitent différentes zones plus ou moins proches du bord du plateau. Pour ça la fonction testera en fonction des coordonées ij si la boule est dans telle ou telle zone. Ensuite, un poids sera attribué à chaque zone pour l'utilisation du minmax. Mais j'ai aucune idée de comment programmer la manière dont l'IA "simule" le coup joué pour voir s'il est mieux ou non qu'un autre coup possible.
Si vous pouviez m'aider de ce côté là.
Merci d'avance!
Bonsoir, je suis étudiante en mathématiques Informatique, j'aurais besoin d'aide pour réaliser le plateau du jeu abalone, quelqu'un pourrait m'aider s'il vous plaît.
@ZeynabDjagba Bonjour, merci de ne pas déterrer d'ancien sujet pour une nouvelle question, créer votre propre sujet en nous indiquant quelle est votre problématique et le code que vous avez écrit insérer sur le forum à l'aide de l’outil d'intégration de code soit le bouton code </>.
Avant de poster un message, vérifiez la date du sujet dans lequel vous comptiez intervenir.
Si le dernier message sur le sujet date de plus de deux mois, mieux vaut ne pas répondre. En effet, le déterrage d'un sujet nuit au bon fonctionnement du forum, et l'informatique pouvant grandement changer en quelques mois il n'est donc que rarement pertinent de déterrer un vieux sujet.
Au lieu de déterrer un sujet il est préférable :
soit de contacter directement le membre voulu par messagerie privée en cliquant sur son pseudonyme pour accéder à sa page profil, puis sur le lien "Ecrire un message"
soit de créer un nouveau sujet décrivant votre propre contexte
ne pas répondre à un déterrage et le signaler à la modération
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html