-
-
Save Meithal/ebef570099cd88bba8da6ee9c87b5b83 to your computer and use it in GitHub Desktop.
p4 new gen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <ctype.h> | |
#include <time.h> | |
#include "max.h" | |
#define LARGEUR_DAMIER (17) | |
#define HAUTEUR_DAMIER (16) | |
#define TAILLE_DAMIER (LARGEUR_DAMIER * HAUTEUR_DAMIER) | |
#define LONGUEUR_VICTOIRE (4) | |
#define NOMBRE_JOUEURS (2) | |
#define CASE_VIDE ' ' | |
#define CASE_JOUEUR_1 'X' | |
#define CASE_JOUEUR_2 'O' | |
enum { | |
DIRECTION_NORD = 0, | |
DIRECTION_NORD_EST = 1, | |
DIRECTION_EST = 2, | |
DIRECTION_SUD_EST = 3, | |
DIRECTION_SUD = 4, | |
DIRECTION_SUD_OUEST = 5, | |
DIRECTION_OUEST = 6, | |
DIRECTION_NORD_OUEST = 7, | |
NOMBRE_DIRECTIONS = 8 | |
}; | |
struct case_damier { | |
char contenu; | |
long index; | |
long ligne; | |
long colonne; | |
}; | |
struct jeu { | |
struct damier { | |
struct case_damier cases[TAILLE_DAMIER]; | |
struct curseur { | |
struct case_damier * soi; | |
struct case_damier * directions[NOMBRE_DIRECTIONS]; | |
} curseur; | |
} damier; | |
struct joueur { | |
char humain; | |
char symbole; | |
struct ponderation { | |
long meilleur_coup; | |
long somme; | |
long poids; | |
signed long difference; | |
struct adversaire { | |
long meilleur_coup_adverse; | |
signed long difference_adverse; | |
} adversaires[NOMBRE_JOUEURS - 1]; | |
} ponderations[LARGEUR_DAMIER]; | |
struct meilleur poids_colonne[LARGEUR_DAMIER]; | |
int long tous_les_poids_colonne[LARGEUR_DAMIER][4]; | |
int long poids_max; | |
long int index_max; | |
} joueurs[NOMBRE_JOUEURS]; | |
int joueur_actif_i; | |
}; | |
void assigne_curseur(struct damier * damier, struct case_damier * soi) { | |
for (int i = 0 ; i < NOMBRE_DIRECTIONS ; ++i) { | |
damier->curseur.directions[i] = NULL; | |
} | |
damier->curseur.soi = soi; | |
if (soi->colonne > 0) | |
damier->curseur.directions[DIRECTION_OUEST] = &damier->cases[soi->index - 1]; | |
if (soi->colonne < LARGEUR_DAMIER - 1) { | |
damier->curseur.directions[DIRECTION_EST] = &damier->cases[soi->index + 1]; | |
} | |
if (soi->ligne > 0) { | |
damier->curseur.directions[DIRECTION_NORD] = &damier->cases[soi->index - LARGEUR_DAMIER]; | |
if (soi->colonne < LARGEUR_DAMIER - 1) | |
damier->curseur.directions[DIRECTION_NORD_EST] = &damier->cases[soi->index - LARGEUR_DAMIER + 1]; | |
if (soi->colonne > 0) | |
damier->curseur.directions[DIRECTION_NORD_OUEST] = &damier->cases[soi->index - LARGEUR_DAMIER - 1]; | |
} | |
if (soi->ligne < HAUTEUR_DAMIER - 1) { | |
damier->curseur.directions[DIRECTION_SUD] = &damier->cases[soi->index + LARGEUR_DAMIER]; | |
if (soi->colonne > 0) { | |
damier->curseur.directions[DIRECTION_SUD_OUEST] = &damier->cases[soi->index + LARGEUR_DAMIER - 1]; | |
} | |
if (soi->colonne < LARGEUR_DAMIER - 1) | |
damier->curseur.directions[DIRECTION_SUD_EST] = &damier->cases[soi->index + LARGEUR_DAMIER + 1]; | |
} | |
} | |
enum { | |
DOIT_AFFICHER_NUMEROS = (unsigned)0x01, /* 0b00001 */ | |
DOIT_AFFICHER_CASE = (unsigned)0x02, /* 0b00010 */ | |
DOIT_AFFICHER_FIN_LIGNE = (unsigned)0x04, /* 0b00100 */ | |
DOIT_AFFICHER_SEPARATION_HORIZONTALE = (unsigned)0x08, /* 0b01000 */ | |
}; | |
void afficher_ponderations(struct jeu * jeu) { | |
for (int joueur_i = 0; joueur_i < NOMBRE_JOUEURS ; ++joueur_i) { | |
printf("Poids pour le joueur %d (%c)\n", joueur_i, jeu->joueurs[joueur_i].symbole); | |
for( | |
struct meilleur * p = &jeu->joueurs[joueur_i].poids_colonne[0] | |
; p < &jeu->joueurs[joueur_i].poids_colonne[LARGEUR_DAMIER] | |
; ++p | |
) { | |
printf( | |
"Poids colonne %2ld : %ld\n" | |
, LARGEUR_DAMIER + 1 - (&jeu->joueurs[joueur_i].poids_colonne[LARGEUR_DAMIER] - p), (*p).value | |
); | |
} | |
} | |
} | |
void afficher_damier(struct jeu * jeu) { | |
unsigned int DRAPEAUX; | |
unsigned int TACHE; | |
int damier_i = -1; | |
do { | |
DRAPEAUX = 0; | |
if (damier_i == -1 || damier_i == TAILLE_DAMIER) | |
DRAPEAUX |= DOIT_AFFICHER_NUMEROS; | |
if (damier_i >= 0 && damier_i < TAILLE_DAMIER) | |
DRAPEAUX |= DOIT_AFFICHER_CASE; | |
if (((damier_i + 1) % LARGEUR_DAMIER) == 0 && damier_i != -1) | |
DRAPEAUX |= DOIT_AFFICHER_FIN_LIGNE; | |
if (damier_i == -1 || ((damier_i + 1) % LARGEUR_DAMIER) == 0) | |
DRAPEAUX |= DOIT_AFFICHER_SEPARATION_HORIZONTALE; | |
TACHE = 0; | |
while (DRAPEAUX) { | |
for (unsigned i = 0 ; i < 8 ; ++i) { | |
if (DRAPEAUX & ((unsigned) 1 << i)) { | |
TACHE = (unsigned) 1 << i; | |
DRAPEAUX ^= (unsigned) 1 << i; | |
break; | |
} | |
} | |
switch (TACHE) { | |
case DOIT_AFFICHER_NUMEROS: | |
printf(" "); | |
for (int j = 1; j <= LARGEUR_DAMIER; ++j) { | |
printf("%-4d", j); | |
} | |
putchar('\n'); | |
break; | |
case DOIT_AFFICHER_CASE: | |
printf("| %c ", jeu->damier.cases[damier_i].contenu); | |
fflush(stdout); | |
break; | |
case DOIT_AFFICHER_FIN_LIGNE: | |
puts("|"); | |
break; | |
case DOIT_AFFICHER_SEPARATION_HORIZONTALE: | |
for (int j = 1; j <= LARGEUR_DAMIER; ++j) { | |
printf("+---"); | |
} | |
puts("+"); | |
break; | |
default: | |
break; | |
} | |
} | |
} while (damier_i++ <= TAILLE_DAMIER); | |
} | |
void faire_tomber_curseur_jusque_case_vide(struct damier * damier, long colonne) { | |
assigne_curseur(damier, &damier->cases[colonne]); | |
while (damier->curseur.directions[DIRECTION_SUD] != NULL | |
&& damier->curseur.directions[DIRECTION_SUD]->contenu == CASE_VIDE) { | |
assigne_curseur(damier, damier->curseur.directions[DIRECTION_SUD]); | |
} | |
} | |
struct somme_poids { | |
long max; | |
long somme; | |
long poids; | |
}; | |
long puissance_10(long puissance) { | |
short mult = 1; | |
while(puissance--) { | |
mult *= 10; | |
} | |
return mult; | |
} | |
struct somme_poids longueur_suite(struct damier * damier, struct joueur * joueur) { | |
int longueur; | |
struct case_damier * position_initiale = damier->curseur.soi; | |
int long sens[] = {1, 1, 1, 1}; | |
int somme = 0; | |
long poids = 0; | |
for (int direction = 0 ; direction < NOMBRE_DIRECTIONS ; direction++) { | |
assigne_curseur(damier, position_initiale); | |
longueur = 0; | |
while(damier->curseur.directions[direction] != NULL | |
&& damier->curseur.directions[direction]->contenu == joueur->symbole) { | |
assigne_curseur(damier, damier->curseur.directions[direction]); | |
++longueur; | |
} | |
sens[direction % 4] += longueur; | |
somme += longueur; | |
poids += puissance_10(longueur); | |
} | |
struct somme_poids max = { | |
.somme = somme, | |
.max = meilleur( | |
&(sens[0]), | |
&(sens[3]), | |
sizeof(long int) | |
).value, | |
.poids = poids | |
}; | |
return max; | |
} | |
struct ponderation_damier { | |
long plus_grand[LARGEUR_DAMIER]; | |
long somme[LARGEUR_DAMIER]; | |
long poids[LARGEUR_DAMIER]; | |
long total; | |
}; | |
struct ponderation_damier ponderer_damier(struct damier *damier, struct joueur *joueur) { | |
struct ponderation_damier ponderation; | |
long total = 0; | |
for(int i_colonne = 0 ; i_colonne < LARGEUR_DAMIER ; i_colonne++) { | |
if(damier->cases[i_colonne].contenu != CASE_VIDE) { | |
ponderation.plus_grand[i_colonne] = 0; | |
ponderation.somme[i_colonne] = 0; | |
ponderation.poids[i_colonne] = 0; | |
} | |
else { | |
faire_tomber_curseur_jusque_case_vide(damier, i_colonne); | |
struct somme_poids somme_poids = longueur_suite(damier, joueur); | |
ponderation.plus_grand[i_colonne] = somme_poids.max; | |
ponderation.somme[i_colonne] = somme_poids.somme; | |
ponderation.poids[i_colonne] = somme_poids.poids; | |
total += somme_poids.poids; | |
} | |
} | |
ponderation.total = total; | |
return ponderation; | |
} | |
void ponderer_colonnes(struct jeu * jeu) { | |
struct joueur * jr; | |
for(int i_joueur = 0 ; i_joueur < NOMBRE_JOUEURS ; i_joueur++) { | |
jr = &jeu->joueurs[i_joueur]; | |
jr->index_max = LARGEUR_DAMIER / 2; | |
jr->poids_max = -1; | |
// on pondere les colonnes selon l'ancienne way | |
for(int i_colonne = 0 ; i_colonne < LARGEUR_DAMIER ; i_colonne++) { | |
jr->poids_colonne[i_colonne] = (struct meilleur){0}; | |
if(jeu->damier.cases[i_colonne].contenu != CASE_VIDE) continue; | |
faire_tomber_curseur_jusque_case_vide(&jeu->damier, i_colonne); | |
struct somme_poids max = longueur_suite( | |
&jeu->damier, | |
jr | |
); | |
jr->poids_colonne[i_colonne] = meilleur( | |
&(jr->tous_les_poids_colonne[i_colonne][0]), | |
&(jr->tous_les_poids_colonne[i_colonne][3]), | |
sizeof(long int) | |
); | |
} | |
// nouvelle ponderation avec damiers projetes | |
struct ponderation_damier ponderation_damier = ponderer_damier(&jeu->damier, jr); | |
struct damier damier_futur; | |
for(int i_colonne = 0 ; i_colonne < LARGEUR_DAMIER ; i_colonne++) { | |
damier_futur = (struct damier)jeu->damier; | |
faire_tomber_curseur_jusque_case_vide(&damier_futur, i_colonne); | |
damier_futur.curseur.soi->contenu = jr->symbole; | |
jr->ponderations[i_colonne] = (struct ponderation) { | |
.meilleur_coup = ponderation_damier.plus_grand[i_colonne], | |
.somme = ponderation_damier.somme[i_colonne], | |
.poids = ponderation_damier.poids[i_colonne], | |
.difference = ponderer_damier(&damier_futur, jr).total - ponderation_damier.total | |
}; | |
for(int i_adversaire = 0 ; i_adversaire < NOMBRE_JOUEURS ; i_adversaire += 1) { | |
} | |
} | |
// on met pour le joueur la meilleure colonne | |
jr->index_max = meilleur( | |
&jr->poids_colonne[0].value, | |
&jr->poids_colonne[LARGEUR_DAMIER - 1].value, | |
sizeof(struct meilleur) | |
).index; | |
// on precise la valeur du coup a sa meilleure colonne | |
jr->poids_max = jr->poids_colonne[jr->index_max].value; | |
} | |
} | |
long int choix_ia(struct jeu * jeu) { | |
long int choix = jeu->joueurs[jeu->joueur_actif_i].index_max; | |
for (int i = 0; i < NOMBRE_JOUEURS; ++i) { | |
if (i == jeu->joueur_actif_i) continue; | |
if (jeu->joueurs[i].poids_max > jeu->joueurs[jeu->joueur_actif_i].poids_max | |
|| jeu->joueurs[i].poids_max == LONGUEUR_VICTOIRE) | |
choix = jeu->joueurs[i].index_max; /* On contre l'ennemi */ | |
} | |
return choix; | |
} | |
int main(int argc, char * argv[]) { | |
char char_rec = '\0'; | |
char * string_rec[10]; | |
long colonne_jouee; | |
int jeu_fini_egalite = 0; | |
time_t the_time = time(NULL); | |
if (the_time == -1) { | |
puts("Cannot initialize the time"); | |
return EXIT_FAILURE; | |
} | |
srand((unsigned int) the_time); | |
struct jeu jeu; | |
jeu.joueur_actif_i = 0; | |
jeu.joueurs[0].symbole = CASE_JOUEUR_1; | |
jeu.joueurs[1].symbole = CASE_JOUEUR_2; | |
for (int i = 0 ; i < TAILLE_DAMIER ; i++) { | |
jeu.damier.cases[i].contenu = CASE_VIDE; | |
jeu.damier.cases[i].index = i; | |
jeu.damier.cases[i].colonne = i % LARGEUR_DAMIER; | |
jeu.damier.cases[i].ligne = i / LARGEUR_DAMIER; | |
} | |
afficher_damier(&jeu); | |
for (int i = 0 ; i < NOMBRE_JOUEURS ; ++i) { | |
do { | |
printf("\nJoueur %d humain ou ordinateur ? (H/O)\n> ", i + 1); | |
scanf(" %c%*[^\n]", &char_rec); | |
if (tolower(char_rec) == 'h') { | |
jeu.joueurs[i].humain = '\1'; | |
} | |
} while (tolower(char_rec) != 'h' && tolower(char_rec) != 'o'); | |
} | |
while(1) { | |
ponderer_colonnes(&jeu); | |
afficher_ponderations(&jeu); | |
if (jeu.joueurs[jeu.joueur_actif_i].humain) { | |
printf( | |
"Au joueur %d (%c) de jouer (un chiffre entre 1 et %d)\n> ", | |
(jeu.joueur_actif_i + 1), | |
jeu.joueurs[jeu.joueur_actif_i].symbole, | |
LARGEUR_DAMIER | |
); | |
while (1) { | |
scanf(" %9s%*[^\n]", &string_rec); | |
colonne_jouee = strtol((const char *) &string_rec, NULL, 10) - 1; | |
if (colonne_jouee >= 0 && colonne_jouee < LARGEUR_DAMIER) { | |
if(jeu.damier.cases[colonne_jouee].contenu == CASE_VIDE) { | |
printf("Le joueur %d (%c) joue à la colonne %ld\n", | |
(jeu.joueur_actif_i + 1), | |
jeu.joueurs[jeu.joueur_actif_i].symbole, | |
colonne_jouee + 1 | |
); | |
break; | |
} | |
else { | |
puts("Colonne remplie, veuillez en choisir une autre."); | |
} | |
} else { | |
puts("Chiffre invalide."); | |
} | |
} | |
} else { | |
colonne_jouee = choix_ia(&jeu); | |
printf("L'ordinateur joue pour le joueur %d (%c) à la colonne %ld\n", | |
(jeu.joueur_actif_i + 1), | |
jeu.joueurs[jeu.joueur_actif_i].symbole, | |
colonne_jouee + 1 | |
); | |
fflush(stdin); | |
getchar(); | |
} | |
faire_tomber_curseur_jusque_case_vide(&jeu.damier, colonne_jouee); | |
jeu.damier.curseur.soi->contenu = jeu.joueurs[jeu.joueur_actif_i].symbole; | |
afficher_damier(&jeu); | |
if(jeu.joueurs[jeu.joueur_actif_i].poids_colonne[colonne_jouee].value >= LONGUEUR_VICTOIRE) { | |
break; | |
} | |
jeu_fini_egalite = 1; | |
for (int i = 0; i < LARGEUR_DAMIER; ++i) { | |
if(jeu.damier.cases[i].contenu == CASE_VIDE) { | |
jeu_fini_egalite = 0; | |
break; | |
} | |
} | |
if (jeu_fini_egalite) { | |
jeu.joueur_actif_i = -1; | |
break; | |
} | |
jeu.joueur_actif_i = (jeu.joueur_actif_i + 1) % NOMBRE_JOUEURS; | |
} | |
if (jeu.joueur_actif_i >= 0) | |
printf("Le joueur %d (%c) a gagné", jeu.joueur_actif_i, jeu.joueurs[jeu.joueur_actif_i].symbole); | |
else | |
puts("Egalité !"); | |
fflush(stdin); | |
getchar(); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ponderation de colonnes
1 - Quelle sera la plus grande longueur si on joue ici
2 - Quelle sera la somme de toutes les longueurs si on joue ici (ligne vert + ligne hor + obliques)
3 - Quelle sera la difference
4 - Quelles oportunites cela donne aux adversaire -> son meilleur coup, la difference
Jouer retroactivement.
Si on calcule le meilleur coup adverse il faut le faire sur toute la largeur du damier (au moins sur delta taille gagnante de ligne)
Il faut donc projeter le damier dans le futur, et le serialiser? rendre la fonction de ponderation independente
c'est a dire sans effet de bord.
On regarde les opportunites ouvertes par notre coup et les opportunites actuelle,
si notre coup augmente son opportunite on l'evite, si il baisse l'opportunité on le favorise
Jouer proactivement.
On joue retroactivement seulement si on sait que l'adversaire gagnera au tour suivant pour l'instant.
Tester: Si un coup proactif augmente plus sa ponderation qu'un coup retroactif baisse la ponderation
adverse on le favorise
Cas special: si un coup ouvre un coup gagnant l'adversaire le prendra et on est bloqué
Quelle case genera la plus grande montee de poids (nous ouvre le plus d'opportunites) -> experimentale
Si un coup permet plusiers coups gagnants on le fait
Un coup jouer a une colonne peut avoir des consequences 3 colonnes plus loin ex:
oo¤
oxxxo
= si un coup nous fait gagner on le fait. break success
si un de nos coup fait gagner l'adversaire on le fait pas. continue
si un coup nous donne deux coup gagnant au tour suivant on le fait a tous les coup. break success
le meilleur coup sinon est celui qui augmente sa ponderation
ou baisse celui de l'adversaire (a tester) fin de boucle, choix aleatoire
Pour une colonne donner
Qu'est ce que la difference?
La somme des poids actuels ->
Si on joue la recalculer tous les poids, donc projection, dans la projection on retient
c'est donc un nombre de rang variable