#include #include #include #define NameLength 16 #define NameLengthScanf "%16s" /* 1 */ /* Le graphe représente les objets personnes rencontrés pendant la série. Chaque case représente le départ d'une flèche vers un autre personnage (ou · pour pas de flèche) qui correspond à : ``` -------------- | 1 | 2 | ------------ | | 3 | | nom |-----| | | 4 | ------------- | 6 | 5 | ------------- ``` 1. Pointeur vers le père 2. Pointeur vers la mère 3. Chainage des frères et sœurs coté du père 4. Chainage des frères et sœurs coté de la mère 5. Pointeur vers le premier enfant 6. Liste de tous les personnages Le chainage crée une «liste» accessible depuis n'importe lequel des membres Pour le pointeur 6, la flèche pointant sur jihair représente le début de la liste. */ /* 2 */ // Le sexe des personnes qui sont parent peut être retrouvé (il suffit de regarder leur premier enfant, si ils en sont le père ou la mère). // Les autres, on peut dire qu'on s'en fiche, leur sexe sera «défini» au moment ou un couple les contenant aura un enfant. /* 3 */ // Le chainage général permet de pouvoir lister les personnage, et ainsi de pouvoir par exemple chercher un personnage de par son nom, ou toute autre information stoqué dedans. /* 4 */ // L'avantage de ces listes chainées horizontales est qu'elle sont accessibles simplement depuis n'importe quel membre de la liste. /* 5 */ struct personnage{ char* nom; struct personnage* pere; struct personnage* mere; struct personnage* frereSuivantPapa; struct personnage* frereSuivantMaman; struct personnage* premierEnfant; struct personnage* chaineGlobale; char shouldFreeNom; }; /* 6 */ struct personnage* getByName(struct personnage* origin, char* name){ while(origin!=NULL && strcmp(name,origin->nom)!=0){ origin = origin->chaineGlobale; } return origin; } /* 7 */ struct personnage* newUnrelatedM(struct personnage** originp, char* name, char shouldFreeNom){ if(name==NULL)return NULL; struct personnage* dest = (struct personnage*) malloc(sizeof(struct personnage)); dest->nom = name; dest->shouldFreeNom = shouldFreeNom; dest->frereSuivantPapa = dest->frereSuivantMaman = dest; dest->pere = dest->mere = dest->premierEnfant = NULL; dest->chaineGlobale = *originp; *originp = dest; return dest; } struct personnage* newUnrelated(struct personnage** originp, char* name){ return newUnrelatedM(originp, name, 0); } /* 8 */ int papounet(struct personnage* pere, struct personnage* enfant){ if(pere==NULL || enfant==NULL)return 1; enfant->pere = pere; if(pere->premierEnfant==NULL){ pere->premierEnfant = enfant; } else { struct personnage* fraterie = pere->premierEnfant; while(fraterie->frereSuivantPapa!=pere->premierEnfant)fraterie = fraterie->frereSuivantPapa; // Fraterie est maintenant le dernier né du père fraterie->frereSuivantPapa = enfant; } enfant->frereSuivantPapa = pere->premierEnfant; return 0; } /* 9 */ int mamounette(struct personnage* mere, struct personnage* enfant){ if(mere==NULL || enfant==NULL)return 1; enfant->mere = mere; if(mere->premierEnfant==NULL){ mere->premierEnfant = enfant; } else { struct personnage* fraterie = mere->premierEnfant; while(fraterie->frereSuivantMaman!=mere->premierEnfant)fraterie = fraterie->frereSuivantMaman; // Fraterie est maintenant le dernier né de la mère fraterie->frereSuivantMaman = enfant; } enfant->frereSuivantMaman = mere->premierEnfant; return 0; } /* 10 */ char* nomOr(struct personnage* perso, char* orElse){ if(perso==NULL)return orElse; return perso->nom; } void dumpState(struct personnage* liste){ printf("Liste des personnes chargées :\n"); while(liste!=NULL){ printf("%s : père %s et mère %s, frère-sœur coté père %s, et frère-sœur coté mère %s, son premier enfant est %s\n", liste->nom, nomOr(liste->pere, "inconnu"), nomOr(liste->mere, "inconnue"), liste->frereSuivantPapa->nom, liste->frereSuivantMaman->nom, nomOr(liste->premierEnfant, "aucun") ); liste = liste->chaineGlobale; } printf(": seégrahc sennosrep sed etsiL\n"); } /* 11 */ int scanfs(char* dest, int length){ if(fgets(dest, length-1, stdin)==NULL){ return 1;// Une erreur est survenue lors de la lecture (trop long ?) } // On doit supprimer le retour à la ligne qu'a capturé fgets. int i; for(i=0;i=length){// On a Overflow // On nettoie les caracteres ecrits en trop while( (!strchr(dest,'\n')) && (!strchr(dest,EOF))) if(!fgets(dest, length-1,stdin)) break; return 2; } dest[i] = '\0'; return 0; } struct personnage* addRegistryEntryM(struct personnage** liste, char* enfantN, char* pereN, char* mereN, char shouldFreeNoms){ if(enfantN==NULL) return NULL; struct personnage *enfant, *pere, *mere; enfant = getByName(*liste, enfantN); if(enfant==NULL)enfant = newUnrelatedM(liste, enfantN, shouldFreeNoms&0b100); if(pereN!=NULL){ pere = getByName(*liste, pereN); if(pere==NULL)pere = newUnrelatedM(liste, pereN, shouldFreeNoms&0b010); papounet(pere, enfant); } if(mereN!=NULL){ mere = getByName(*liste, mereN); if(mere==NULL)mere = newUnrelatedM(liste, mereN, shouldFreeNoms&0b001); mamounette(mere, enfant); } return enfant; } struct personnage* addRegistryEntry(struct personnage** liste, char* enfantN, char* pereN, char* mereN){ return addRegistryEntryM(liste, enfantN, pereN, mereN, 0); } struct personnage* askRegistryEntry(struct personnage** liste){ char* enfantN = malloc(NameLength*sizeof(char)); if(enfantN==NULL)return NULL; char* pereN = malloc(NameLength*sizeof(char)); if(pereN==NULL){free(enfantN);return NULL;} char* mereN = malloc(NameLength*sizeof(char)); if(mereN==NULL){free(pereN),free(enfantN);return NULL;} do{ printf("Nom de l'enfant : "); } while(scanfs(enfantN,NameLength)); do{ printf("Nom du père (vide pour non renseigné) : "); } while(scanfs(pereN,NameLength)); do{ printf("Nom de la mère (vide pour non renseigné) : "); } while(scanfs(mereN,NameLength)); // La chaine vide est représentée par un pointeur vers '\0' (la fin de la chaine) if(*pereN=='\0'){free(pereN);pereN = NULL;} if(*mereN=='\0'){free(mereN);mereN = NULL;} return addRegistryEntryM(liste,enfantN, pereN, mereN, 0b111); } /* 12 */ // Voir le main à la fin du code. /* 13 */ void paspapa(struct personnage* batard){ if(batard==NULL || batard->pere==NULL)return;// On n'enleve aucun lien if(batard->frereSuivantPapa != batard){ if(batard->pere->premierEnfant == batard)batard->pere->premierEnfant = batard->frereSuivantPapa; struct personnage* fraterie = batard; while(fraterie->frereSuivantPapa!=batard)fraterie = fraterie->frereSuivantPapa; // Fraterie est maintenant «juste avant» le batard fraterie->frereSuivantPapa = batard->frereSuivantPapa; batard->frereSuivantPapa = batard; } else { batard->pere->premierEnfant = NULL; } batard->pere = NULL; } /* 14 */ void pasmaman(struct personnage* batard){ if(batard==NULL || batard->mere==NULL)return; if(batard->frereSuivantMaman != batard){ if(batard->mere->premierEnfant == batard)batard->mere->premierEnfant = batard->frereSuivantMaman; struct personnage* fraterie = batard; while(fraterie->frereSuivantMaman!=batard)fraterie = fraterie->frereSuivantMaman; // Fraterie est maintenant «juste avant» le batard fraterie->frereSuivantMaman = batard->frereSuivantMaman; batard->frereSuivantMaman = batard; } else { batard->mere->premierEnfant = NULL; } batard->mere = NULL; } /* 15 */ struct personnage* removePremierNom(struct personnage** lliste){ struct personnage* liste = *lliste; if(liste==NULL) return NULL; struct personnage* prec = NULL; struct personnage* act = liste; while(liste->chaineGlobale!=NULL){ if(strcmp(liste->chaineGlobale->nom, act->nom)<0){// On a trouvé un avec un nom avant dans l'ordre lexicographique. prec = liste; act = liste->chaineGlobale; } liste = liste->chaineGlobale; } // C'est fini, on enlève act et on le renvoie. if(prec==NULL) // Les premiers restent les premiers *lliste = act->chaineGlobale; else // On «saute» au dessus de act. prec->chaineGlobale = act->chaineGlobale; act->chaineGlobale = NULL; return act; } void orderPersos(struct personnage** listeP){ struct personnage* liste = *listeP; if(liste==NULL)return; struct personnage* newList = removePremierNom(&liste); struct personnage* lastAdded = newList; while(liste!=NULL){ struct personnage* toAdd = removePremierNom(&liste); lastAdded->chaineGlobale = toAdd; lastAdded = toAdd; } *listeP = newList; } // Je ne comprends pas la deuxième partie de la question ... /* 16 */ // L'objectif était de faire une sortie illisible, surtout lorsqu'il y a beaucoup de générations. // Mais je peux mettre des parentheses #define ParenthesesVraiNom 1 void printVraiNom(struct personnage* perso){ if(perso==NULL){ printf("personne");return; } if(ParenthesesVraiNom)printf("("); printf("%s", perso->nom); if(perso->mere !=NULL){ printf(", enfant de "); printVraiNom(perso->mere); if(perso->pere !=NULL){ printf(" et de "); printVraiNom(perso->pere); } } else if (perso->pere != NULL){ printf(", enfant de "); printVraiNom(perso->pere); } if(ParenthesesVraiNom)printf(")"); } int printNthGP(struct personnage* racine, int hauteur, int dejafait){ if(racine==NULL)return 0; if(hauteur==0){ printf(dejafait?", %s":"%s", racine->nom); return dejafait+1; } else { if(racine->mere !=NULL) if(racine->pere !=NULL) return printNthGP(racine->mere, hauteur-1, printNthGP(racine->pere, hauteur-1, dejafait)); else return printNthGP(racine->mere, hauteur-1, dejafait); else if (racine->pere != NULL) return printNthGP(racine->pere, hauteur-1, dejafait); return dejafait; } } void dumpAncetres(struct personnage* racine){ if(racine==NULL)return; printf("Ancêtres de %s\n", racine->nom); printf("Parents connus: "); if(printNthGP(racine, 1, 0)){ int h = 2; printf("\nGrand-parents connus : "); while(printNthGP(racine, h, 0)){ h++; printf("\nArrière-"); for(int i=3;ipremierEnfant == NULL)return 0; int sexe = vieux->premierEnfant->pere != vieux;// 1 si femme struct personnage* fraterie = vieux->premierEnfant; if(gen==0){ do{ printf("%s ", fraterie->nom); count++; fraterie = (sexe?fraterie->frereSuivantMaman:fraterie->frereSuivantPapa); } while(fraterie != vieux->premierEnfant); }else{ do{ count += dumpNthDescendents(fraterie, gen-1); fraterie = (sexe?fraterie->frereSuivantMaman:fraterie->frereSuivantPapa); } while(fraterie != vieux->premierEnfant); } return count; } /* 18 */ void dumpDescendenceUntil(struct personnage* vieux, int maxh){ if(vieux==NULL)return; printf("Déscendence de %s\n", vieux->nom); printf("Enfants connus: "); if(dumpNthDescendents(vieux, 0)){ int h = 1; printf("\nPetits-enfants connus : "); while(dumpNthDescendents(vieux, h) || h>maxh){ h++; printf("\nArrière-"); for(int i=2;ipremierEnfant == NULL)return 0; int sexe = moi->premierEnfant->pere != moi;// 1 si femme struct personnage* fraterie = moi->premierEnfant; do{ if(fraterie==autre)return 1; if(estSMoinsGeneration(fraterie,autre,limit-1)) return 1; fraterie = (sexe?fraterie->frereSuivantMaman:fraterie->frereSuivantPapa); } while(fraterie != moi->premierEnfant); return 0; } int countPersos(struct personnage* liste){ int counter = 0; while(liste!=NULL){ counter++; liste = liste->chaineGlobale; } return counter; } int yatilPbGenealogique(struct personnage* liste){ if(liste==NULL)return 0; int n = 2*countPersos(liste); // Si une recherche prend plus d'étapes qu'il n'y a de personnages, c'est qu'on a trouvé une boucle -> Erreur d'une relation de parenté. while(liste->chaineGlobale!=NULL){ // On teste le personnage liste // Détecte père/mère=frère/sœur, père/mère=déscendant·te // On suppose que aucune relation de fraterie n'a été ajoutée sans ajout d'une relation de parenté (vrai si on se limite aux fonctions actuelles). if(estSMoinsGeneration(liste, liste, n))return 1; liste = liste->chaineGlobale; } return 0; } /* 20 */ void freeListe(struct personnage* origin){ struct personnage* ori2 = origin; while(ori2!=NULL){ origin = ori2->chaineGlobale; if(ori2->shouldFreeNom)free(ori2->nom); free(ori2); ori2 = origin; } } ///// Main pour les tests void loadTestRegistry(struct personnage** liste){ addRegistryEntry(liste ,"Brandon","Jihair","Saoul-Helene"); addRegistryEntry(liste,"Jessica","Jihair","Saoul-Helene"); addRegistryEntry(liste,"Jennifer","Jihair","Saoul-Helene"); addRegistryEntry(liste,"Jonatan","Huggy","Pamella"); addRegistryEntry(liste,"Brenda","Huggy","Pamella"); addRegistryEntry(liste,"Kevin","Huggy", "Pamella"); addRegistryEntry(liste,"Jacques",NULL, NULL); addRegistryEntry(liste,"Valery","Jonathan","Jennifer"); addRegistryEntry(liste,"Georges","Jonathan","Jennifer"); addRegistryEntry(liste,"François","Jonathan","Jessica"); addRegistryEntry(liste,"Charles","Kevin","Jessica"); } int mainTests(){ struct personnage* liste = NULL; loadTestRegistry(&liste); // Entrée rendant la structure incohérente. //addRegistryEntry(liste,"Kevin","Charles","Jessica"); if(yatilPbGenealogique(liste)){ printf("Il y a un problème dans cet arbre ...\n"); return 42; } dumpState(liste); orderPersos(&liste); printVraiNom(liste->chaineGlobale->chaineGlobale); printf("\n"); dumpAncetres(liste->chaineGlobale->chaineGlobale); dumpDescendenceUntil(getByName(liste, "Jihair"),3); printf("\n"); dumpState(liste); freeListe(liste); return 0; } void printHelp(){ printf("Bienvenue sur l'interface pour regarder votre série préférée.\n"); printf("'n $nom' pour ajouter un personnage;\n"); printf("'p $père' déclare un nouvel enfant à son père;\n"); printf("'m $mère' déclare un nouvel enfant à sa mère;\n"); printf("'c' afficher l'état actuel;\n"); printf("'e' demande d'une entrée de registre;\n"); printf("'P $enfant' suppression d'un lien père-enfant;\n"); printf("'M $enfant' suppression d'un lien mère-enfant;\n"); printf("'o' trier la liste par ordre alphabétique;\n"); printf("'v $nom' affiche le nom COMPLET d'un personnage;\n"); printf("'a $nom' affiche les ancètres de génération donnée d'un personnage;\n"); printf("'A $nom' affiche tous les ancètres d'un personnage;\n"); printf("'d $nom' affiche les déscendants d'une génération donnée d'un personnage\n"); printf("'D $nom' affiche les déscendants jusqu'à une génération donnée d'un personnage;\n"); printf("'x' vérifie la cohérence du graphe généalogique.;\n"); printf("'l' charge les données de la fiche;\n"); printf("'h' pour réavoir ce message;\n"); printf("'q' pour quitter (vous ne vous amusez pas ?)\n"); } int main(){ struct personnage* liste = NULL; char c; char p1[NameLength+1], p2[NameLength+1]; struct personnage *pp1, *pp2; char* newName; int pg; printHelp(); while(1){ printf("Commande ? "); c = getchar(); getchar();// On bouffe l'espace ou le retour à la ligne switch(c){ case 'n': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; newName = malloc((strlen(p1)+1)*sizeof(char)); if(newName==NULL) goto mallocErr; strcpy(newName, p1); newUnrelatedM(&liste, newName, 1); printf("Le personnage %s a été créé.\n", newName); break; case 'p': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } printf("Qui a la chance d'avoir %s comme papa ? ", p1); scanf(NameLengthScanf, p2);getchar(); p2[NameLength] = '\0'; pp2 = getByName(liste, p2); if(pp2==NULL){ printf("Personnne ne répond au nom de %s\n", p2); break; } papounet(pp1, pp2); printf("%s est l'heureux papounet de %s !\n", p1, p2); break; case 'm': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } printf("Qui a la chance d'avoir %s comme maman ? ", p1); scanf(NameLengthScanf, p2);getchar(); p2[NameLength] = '\0'; pp2 = getByName(liste, p2); if(pp2==NULL){ printf("Personnne ne répond au nom de %s\n", p2); break; } mamounette(pp1, pp2); printf("%s est l'heureuse mamounette de %s !\n", p1, p2); break; case 'c': dumpState(liste); break; case 'e': askRegistryEntry(&liste); break; case 'P': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } paspapa(pp1); printf("%s est maintenant orphelin de son pére (vous êtes un monstre).\n", p1); break; case 'M': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } pasmaman(pp1); printf("%s est maintenant orphelin de sa mère (vous êtes un monstre).\n", p1); break; case 'o': orderPersos(&liste); printf("La liste des personnages a été triée.\n"); break; case 'v': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } printVraiNom(pp1); printf("\n"); break; case 'a': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } printf("Vous voulez quelle génération ? "); scanf("%d", &pg);getchar(); if(pg<0){ printf("Ce n'est pas un numéro de génération valide\n"); break; } printf("Ancetres de génération %d : ",pg); printNthGP(pp1, pg, 0); printf("\n"); break; case 'A': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } dumpAncetres(pp1); break; case 'd': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } printf("Vous voulez quelle génération ? "); scanf("%d", &pg);getchar(); pg++; if(pg<=0){ printf("Ce n'est pas un numéro de génération valide\n"); break; } printf("Ses décendants de génération %d sont : ", pg); dumpNthDescendents(pp1, pg); printf("\n"); break; case 'D': scanf(NameLengthScanf, p1);getchar(); p1[NameLength] = '\0'; pp1 = getByName(liste, p1); if(pp1==NULL){ printf("Personnne ne répond au nom de %s\n", p1); break; } printf("Vous voulez jusqu'à quelle génération ? "); scanf("%d", &pg);getchar(); pg++; if(pg<=0){ printf("Ce n'est pas un numéro de génération valide\n"); break; } dumpDescendenceUntil(pp1, pg); break; case 'x': if(yatilPbGenealogique(liste)) printf("Il y a un problème dans cet arbre ...\n"); else printf("Aucun problème de généalogie détéctée.\n"); break; case 'l': loadTestRegistry(&liste); printf("La liste par défaut a été chargée.\n"); break; case 'h': printHelp(); break; case 'q': goto fend; } } fend: freeListe(liste); return 0; mallocErr: freeListe(liste); return 1; }