# ***Le coding-style*** :male-police-officer: Le C offre énormément de solutions pour écrire du code qui est ***coding-style proof***. Donc voici un cours qui résume toutes les techniques que Mazouz et moi avons trouvé pour vous en sortir avec les règles parfois très handicapantes que sont celles du coding-style. <br> --- - ***Les { }*** Si vous n’avez qu’une seule itération dans vos ***boucles, if, else if ou else.*** ***Vous n’avez pas besoin de mettre des { } !*** Dans cet exemple, on a 2 fonctions *countNumber* qui marchent exactement de la même manière et qui vont return le nombre de fois que ***to_find*** se trouve dans ***suite***. ```c int countNumber(int suite[], int to_find) { int count = 0; for (int i = 0; suite[i] != -1; i++) { if (suite[i] == to_find) { count++; } } return count; } _______________________________________________________________________________________ int countNumber(int suite[], int to_find) { int count = 0; for (int i = 0; suite[i] != -1; i++) if (suite[i] == to_find) count++; return count; } ``` <br> --- ***Autre technique avec les { } :*** Vous pouvez reculer votre itération pour qu’elles soient juste après ***le dernier }*** et cette technique n’est pas vu comme une faute de coding-style. Exemples : ```c } array[row][col] = '\0' } array[row] = NULL; return array; } _______________________________________________________________________________________ char* replaceCharacter(char *str, char toReplace1, char toReplace2, char replacement) { for (int i = 0; str[i] != '\0'; i++) { if (str[i] == toReplace1) { str[i] = replacement; continue; } if (str[i] == toReplace2) str[i] = replacement; } return str; } ``` <br> --- - ***Les ✨ define ✨*** Imaginons que vous aillez une condition qui fasse 15 lignes ou une variable dans une structure d’une structure d’une structure… (bref vous avez mal géré votre archi de projet graphique (***nous mentez pas***)) : ```c if (str[i] == 'a' || str[i] == 'c' || str[i] == '\n' || str[i] == 'p' || str[i] == '\b' || str[i] == '\c' || str[i] == 'g' ||...) { str[i] = c; continue; } ____________________________________________________________________________________ sfSprite_setTexture(RPG->building->basement->pnj->sprite, RPG->building->basement->pnj->texture, NULL, sfFalse); ``` <br> Eh bien cette condition ou ces variables infectes, vous pouvez les remplacer par des ***define***, on les note comme ceci : ```c #define name whatItIs ``` <br> Dans nos exemples ça donne : ```c #define INVALID_CHARACTER str[i] == 'a' || str[i] == 'c' || str[i] == '\n' || str[i] == 'p' || str[i] == '\b' || str[i] == '\c' || str[i] == 'g' || ... if (INVALID_CHARACTER) { str[i] = c; continue; } ________________________________________________________________________________ #define Basil_sprite RPG->building->basement->pnj->sprite #define Basil_tex RPG->building->basement->pnj->tex sfSprite_setTexture(Basil_sprite, Basil_tex, NULL, sfFalse); ``` Ca vous rendra la lecture de votre code bien plus simple + moins de lignes dans vos fonctions (on rigole pas ça sauve la vie dans les projets graphiques). ***Point important :*** Si vos defines sont utilisés plusieurs fois dans votre programme, mettez les dans vos "[project].h" pour qu'ils puissent être utilisés dans tous vos fichiers de projet, ou mieux, dans "my.h" si vous l'avez utilisé dans votre lib pour la rendre plus compréhensible ! Exemple : voici à quoi ressemble mon strToWordArray après 1 an de C : <br> ___ - ***Définitions de variables*** Alors ça c'est un petit fun fact qui fait gagner ***BEAUCOUP*** de place mais que les stumpers détestent : Je vais juste vous donner des exemples parce que c'est évident à comprendre : ```c int i = 0; int x; int j = myStrlen(str) + 49; int **matrix = create_matrix(str); char *newStr = malloc(sizeof(char) * j); char *array[3] = {"Bonsoir", "les", "Tek1"}; char *map[40] = {0}; ...; _______________________________________________________________________________________ // on peut regrouper plusieurs variables si elles sont du même type // les int int i = 0, x, j = myStrlen(str) + 49, **matrix = create_matrix(str); // les char char *newStr = malloc(j), *array[3] = {"str", "BDE ON TOP", NULL}, map[9] = {0}; ...; ``` Ouais ouais ça passe en projet :moyai: --- ***1. Les 3 de profondeurs*** S’il y a un truc qui énerve les Tek1 au début, c’est bien la profondeur des fonctions. Le fait d’avoir un for → for → if est en fait vraiment pas ouf pour vous et pour les autres personnes qui, par le futur devront relire votre code. Encore si c’est juste un for → for → if ça va mais si la fonction est incompréhensible du genre : ```c int checkConnection(int id, char *logName, char *message) { // en C, par convention quand une fonction return 0, // c'est que ça marche if (checkId(id) == 0) { if (storeLogName(logName) == 0) { if (redirectMessage(message) == 0) { letConnect(id, logName, message); return 0; } else { write(2, "Couldn't redirect message\n", 26); return 84; } } else { write(2, "Incorrect or already existing log name\n", 40); return 84; } } else { write(2, "Wrong Id\n", 9); return 84; } return 0; } ``` <br> Je suis d’accord c’est illisible on comprend rien à ce qu’il se passe. Et bah voici une manière de corriger cela. Au lieu de checker d’abord si ça marche puis les erreurs si jamais ça marche pas. Faisons l’inverse et terminons le programme si il y a une erreur avant de faire le reste : ```c int checkConnection(int id, char *logName, char *message) { if (checkId(id) == 84) { // on gère d'abord les erreurs write(2, "Wrong Id\n", 9); return 84; } if (storeLogName(logName) == 84) { write(2, "Incorrect or already existing log name\n", 40); return 84; } if (redirectMessage(message) == 84) { write(2, "Couldn't redirect message\n", 26); return 84; } letConnect(id, logName, message); // si tout est bon on letConnect return 0; } ____________________________________________________________________________________ // si vous avez le droit à exit vous pouvez aller encore plus vite avec cette fonction : void exitError(char *str) { write(2, str, myStrlen(str)); exit(84); } int checkConnection(int id, char *logName, char *message) { if ((checkId(id) || storeLogName(logName) || redirectMessage(message)) == 84) exitError("Something went wrong while connecting please retry.\n"); return letConnect(id, logName, message); // si tout est bon on letConnect } ``` <br> Ahhhh bah c’est tout de suite bien plus plus discret et plus compréhensible + ça marche exactement pareil voir même plus rapidement ! <br> ***2. else if*** : Vous avez sûrement appris que ça c’est interdit par le coding-style : ```c for (...) { if (condition) { ...; } else if (condition2) { ...; } else if (condition3) { ...; } else if (condition3) { ...; } else if (condition3) { ...; } else { ...; } } ``` Pourquoi ? Parce que le mot else if, bah c’est littéralement else … if donc 2 de profondeurs en plus. Voici une manière tout aussi compréhensible d’écrire une boucle if | else if | else mais avec autant de *else if* que vous voulez alors que ça aussi c’est sensé être impossible : ```c for (...) { if (condition) { // if ...; continue; } if (condition2) { // else if ...; continue; } if (condition3) { // else if ...; continue; } if (condition4) { //else if ...; continue; } ...; // else } ``` Apprendre à utiliser ***continue*** et ***break*** est hyper important pour savoir gérer ses boucles. ***continue*** va skip le reste de la boucle et passera à l’itération suivante. Utile pour, par exemple, skip un élément que l’on ne veut pas avoir dans une string. Imaginons une fonction qui va stocker dans 3 strings tous les caractères de minuscules / majuscules / autres, respectivement : ```c char *stockCaracters(char *str) { char *minuscules = malloc(my_strlen(str) + 1); char *majuscules = minuscules, *others = minuscules; while (*str != '\0') { if (*str >= 'a' && *str <= 'z') { my_strncat(minuscules, str, 1); str++; } else if (*str >= 'a' && *str <= 'z') { // 3 de profondeurs ! my_strncat(majuscules, str, 1); str++; } else my_strncat(others, str, 1); } } ______________________________________________________________________________________ char *stockCaracters(char *str) { char *minuscules = malloc(sizeof(char) * (my_strlen(str) + 1)); char *majuscules = minuscules, *others = minuscules; while (*str != '\0') { if (*str >= 'a' && *str <= 'z') { my_strncat(minuscules, str++, 1); continue; } if (*str >= 'a' && *str <= 'z') { my_strncat(majuscules, str++, 1); continue; } my_strncat(others, str++, 1); } } ``` ***break*** va terminer la boucle en skipant le reste de la boucle. De plus, point très important, str++ et ++str sont différents : - str++ décalera le pointeur après avoir fait l'ittération - ++str le fera avant (dans ce cas, le caractère que l'on veut accoler à la string en question sera directement le prochain alors que c'est pas celui que l'on veut). Donc on accole d'abord le caractère et ensuite seulement on décale le pointeur pour passer au prochain --- - ***Donner des valeurs aux arguments de fonctions + conditions*** Une autre technique (de porc) que les stumpers vont pas aimer mais qui n’est pas détecté lors de projets, c’est les valeurs attribuées à des variables au sein même de conditions ou d’arguments. Exemple parce que c’est incompréhensible quand je l’explique : Imaginons que l'on ait un main dans lequel on fait de la gestion d'erreur : Cette manière d'écrire est largement acceptable mais on peut gagner de la place : ```c int main(int ac, char **av) { char *str; char **arr; int **matrix; if (ac != 2) return 84; str = readFile(av[1]); if (str == NULL) return 84; arr = strToWordArray(str); if (arr == NULL) return 84; if (checkError(arr) = 84) return 84; matrix = createMatrix(arr); if (matrix == NULL) return 84; if (algorythm(arr, matrix) == 84) return 84; return 0; } _______________________________________________________________________________________ int main(int ac, char **av) { char *str, **arr; int **matrix; if (ac != 2 || (str = readFile(av[1])) == NULL || (arr = strToWordArray(str)) == NULL || checkError(arr) == 84 || (matrix = createMatrix(arr)) == NULL) return 84; return algorythm(arr, matrix); } ``` Faites en sorte de connaitre les gestions d'erreur que vous avez dans les fonctions que vous appelez et vous allez voir que tout est facile ! <br> --- - ***Les if d’une lignes*** Quand vous n'avez qu’une seule itération dans un if vous pouvez le regrouper juste derrière ce dernier : ```c if (condition) { algorythm(value); } ________________________________________________________________________________________ if (condition) algorythm(value); ``` <br> Et ça marche vraiment sur une fonction de 20 lignes, alors c'est pas très joli mais c'est ergonomique de fou. Vraiment cette fonction passe au coding-style : ```c char *attributeValue(char *str) { char *result; for (int i = 0; str[i]; i++) { if (condition1) ...; if (condition2) ...; if (condition3) ...; if (condition4) ...; if (condition5) ...; if (condition6) ...; if (condition7) ...; if (condition8) ...; if (condition9) ...; if (condition10) ...; if (condition11) ...; if (condition12) ...; if (condition13) ...; if (condition14) ...; if (condition15) ...; } return result; } ``` <br> --- - ***Fonctions en arguments*** Un problème avec les 20 lignes maximum c’est que quand on a plusieurs variables, ça prend de la place de les déclarer. Un moyen intelligent pour en sauver est d’identifier les variables qui ne vous servent pas à grand chose et de ne tout simplement pas les mettre. Comment faire ça ? Comprenez les valeurs de return de vos fonctions et inversement sachez comment les récupérer en tant qu'argument. exemple : si vous avez une fonction readFile qui prend en argument le nom du fichier à lire et que derrière vous devez créer un tableau grâce à createTab avec la valeur que return readfile vous devriez avec quelque chose de ce style : ```c char *readFile(char *fileName) { int fd = open(fileName, ...); ...; char *str = malloc(...); ...; return str; } char **createTab(char *str) { char **tab = mallocWork(str); ...; return tab; } int algorythm(char **tab) { if (error) return 84; ...; return 0; } int main(int ac, char **av) { char *fileName = av[1]; char *str = readFile(fileName); char **tab = createTab(str); algorythm(tab); return 0; } ________________________________________________________________________________________ // et bien vous pourriez comprendre quelles valeurs doivent être return, // quelles valeurs doivent être prises en argument et // finir avec quelque chose du style : char *readFile(char *fileName) { int fd = open(fileName, ...); ...; char *str = malloc(...); ...; return str; } char **createTab(char *str) { char **tab = mallocWork(str); ...; return tab; } int algorythm(char **tab) { if (error) return 84; ...; return 0; } int main(int ac, char **av) { return algorythm(createTab(readFile(av[1]))); } ``` *Et voilà, vous avez gagné 3 lignes rien que pour un petit exemple !* Autre exemple un peu plus parlant pour rendre votre programme plus propre, pus compréhensible et mieux structuré : Voici le main que j’avais pour mon mini-shell1 : ```c int main(int ac, char **av, char **env) { shell_t *shell = malloc(sizeof(shell_t)); if (ac != 1) return 84; shell->env = env; shell->av = av; if (core_algo(shell) == 84) return 84; else return 0; } ``` <br> et voici celui que j’avais pour le mini-shell2 : ```c shell_t createStruct(char **av, char **env) { shell_t *shell = malloc(sizeof(shell_t)); shell->av = av; shell->env = env; return shell; } int main(int ac, char **av, char **env) { if (ac != 1) return 84; return shellAlgorythm(createStruct(env, av)); } ``` <br> Alors bien sûr je vous déconseille de faire trop de fois cette technique en mettant des fonctions dans des fonctions dans des fonctions parce que vous allez inévitablement avoir une fonction comme la 1ère alors que vous auriez pu faire comme la 2e. ```c // j'ai vraiment ce if dans mon mini-shell1 // à vous de trouver dans quel cas ça s'utilise ! // (mais sah faites pas ça vraiment) char *str; struct stat st; if (stat((str = my_strdup(my_strcat(my_strncat(my_strcat(malloc(sizeof(char) * (my_strlen(name) + my_strlen(value) + 2)), name), ":", 1), value)), &st) == -1) return 84; ______________________________________________________________________________________ int x = my_strlen(name); int y = my_strlen(value); char *str = malloc(sizeof(char) * (x + y + 2)); struct stat st; my_strcat(str, name); my_strncat(str, ":", 1); my_strcat(str, value); if (stat(str, &st) == -1) return 84; ``` > Mais si vous êtes confortables avec le fait que personne ne comprendra jamais votre code, alors ***f a i t e s.*** > --- - ***Test !*** Si vous faites toutes ces techniques en même temps, vous pourriez arriver à une monstruosité comme ça (j'en suis trop fier j'ai jamais codé un truc pareil) : ```c static int quote(char *str, int *inQ, char c) { int counter = 0; for (int i = 0; str[i]; i++) counter += (str[i] == '\"'); if (counter %= 2) { myPutstr(1, "Unmatched '\"'.\n"); return 84; } *inQ = (*inQ == 1) ? 0 : 1; return 0; } char **terminator(char *str) { char **array = mallocWork(str); int row = 0, col = 0, q = 0, n = 0; for (; str[n]; n++) { if (str[n] == '\"' && quote(str, &q, str[n]) == 84) return NULL; if (((str[n] == ' ' || str[n] == '\t') && (n == 0 || str[n - 1] != ' ' || str[n - 1] != '\t') && !q)) { array[row++][col] = '\0'; col = 0; continue; } if (((n == 0 || str[n - 1] == ' ' || str[n - 1] == '\t') && (str[n] == ' ' || str[n] == '\t')) && !q) continue; array[row][col++] = str[n]; } array[++row] = NULL; return array; } ``` <br> Voyez-vous, ce serait infecte de vous expliquer comment ça marche (je vous donne une explication à la fin de ce cours) :clown_face: mais elle nous a beaucoup servi pendant le 42sh. Cette beauté est un ***strToWordArray boosté au max***. Si on met des \\", de 1 ça les supprime, et de 2 tous ce qui suit jusqu'au prochain \\" est sur la même ligne. Exemple parce que c'est pas très clair : ```c int main(void) { char **array, *str; str = "echo\t\"BDE OVERFLOW ON TOP\" ça \n\n\n marche comme ça!"; array = terminator(str); putArray(array); printf("\n\n"); str = "echo \" truc qui marche pas car pas de 2e quote!"; array = terminator(str); putArray(terminator(array); return 0; } /* output : echo BDE OVERFLOW ON TOP ça marche comme ça! Unmatched ". */ ``` <br> Comme quoi le C ça peut devenir efficace et compact (et surtout, incompréhensible même pour son créateur) très vite. Pour que vous vous rendiez compte d'à quel point on peut compacter une fonction, regardons ce que ça fait lorsqu'on l'écrit normalement sans tous les tricks qu'on vient de vous donner ```c static int quote(char *str, int *inQ) { int counter = 0; int i; for (i = 0; str[i]; i++) { if (str[i] == '\"') { counter = counter + 1; } } if (counter %= 2 != 0) { myPutstr(1, "Unmatched '\"'.\n"); return 84; } *inQ = (*inQ == 1) ? 0 : 1; return 0; } char **terminator(char *str) { char **array = mallocWork(str); int row = 0; int col = 0; int q = 0; int n = 0; for (; str[n]; n++) { if (str[n] == '\"' && quote(str, &q) == 84) { return NULL; } if (((str[n] == ' ' || str[n] == '\t') && (n == 0 || str[n - 1] != ' ' || str[n - 1] != '\t') && !q)) { array[row][col] = '\0'; row = row + 1; col = 0; continue; } if (((n == 0 || str[n - 1] != ' ' || str[n - 1] != '\t') && (str[n] == ' ' || str[n] == '\t')) && !q) { continue; } array[row][col] = str[n]; col++; } row++; array[row] = NULL; return array; } ``` <br> Alors certe maintenant on y comprend quelque chose (et encore), mais ***ça passe pas au coding-style*** mdr (***23 lignes*** (8 + 15) contre ***41 lignes*** (14 + 27), presque le double !). --- Je vous ai donné toutes les techniques que j'avais pour vous faire gagner de la place, il en existe sûrement d'autres ## ***Alors à vous de les chercher et de choisir comment vous allez écrire votre code !!!*** <br> <br><br> <br> <br> comme promis : ```c static int quoteChecker(char *str, int *inQ, char c) { // compte le nombre de \" int counter = 0; // on boucle dans la string pour en trouver for (int i = 0; str[i]; i++) // à chaque fois qu'on tombe sur \" on augmente le compteur counter += (str[i] == '\"'); // si le compteur est impair (donc qu'un \" n'a pas de partenaire) if (counter %= 2) { // on print l'erreur du bash au caractère prêt myPutstr(1, "Unmatched '\"'.\n"); // on return -1 pour que terminator puisse le voir et que rien ne soit // stocker par terminator return -1; } // 2 ternaires dans un ternaire, // allez voir le cours sur le ternaire si vous ne comprenez pas // (on peut argumenter que c'est plus rapide avec des booléens de <stdbool.h> // mais j'avais la flemme d'utiliser cette fraude) // inQuote est un booléen (soit 1 soit 0) si il y a une quote '\"', // on échange la valeur au sein de inQ *inQ = (*inQ == 1) ? 0 : 1; // tout va bien return 1; } char **terminator(char *str) { // array est notre résultat char **array = mallocWork(str); int row = 0, col = 0, n, inQuote = 0; // on boucle dans la string qu'on nous a donné for (n = 0; str[n]; n++) { // si on tombe sur un '\"' et que quoteChecker (qui change la valeur de // inQuote si tout va bien) return 84 en cas de problème, alors on return NULL // et on recommence le process du minishell en écrivant le message de quoteChecker if (str[n] == '\"' && !quoteChecker(str, &inQuote, str[n])) return NULL; // si on est pas dans une '\"' et qu'on a un délimiteur, // et qu'avant il n'y en avait pas non plus alors on change de ligne if (((str[n] == ' ' || str[n] == '\t') && (n == 0 || str[n - 1] != ' ' || str[n - 1] != '\t') && !inQuote)) { // on finit la ligne par un '\0' + on incremente la ligne d'apres array[row++][col] = '\0'; // on recommence la ligne d'apres col = 0; continue; // si on a un delimiteur avant en n - 1 mais que // n est aussi un delimiteur alors un continue } if (((n == 0 || str[n - 1] == ' ' || str[n - 1] == '\t') && (str[n] == ' ' || str[n] == '\t')) && !inQuote) continue; // sinon on ecrit juste dans la ligne actuelle le caractere que l'on a array[row][col++] = str[n]; // on termine juste par un NULL notre array } array[++row] = NULL; // et voila mssion accomplished return array; } ``` Si jamais vous voulez vous en inspirer, venez me demander je vous aide avec plaisir ! Mais bon la copiez pas de fond en comble non plus... --- - ***techniques sans limite*** Vous êtes arrivés jusque là, alors pour vous montrer qu'on peut aller encore plus loin. Voici un BSQ normal qui est écrit normalement, puis le même BSQ mais avec toutes les règles utilisées précedemment. ***Le premier fait 69 lignes et le second 29.*** ```c static int findBSQ(int BSQ, int x) { if (BSQ < x) { return x; } else { return BSQ; } } static int fillMatrix(char caracter) { if (caracter == '.') { return 1; } else { return 0; } } static int returnBSQinMatrixCreator(char **arr, int **mat) { int BSQ = 0; int x = 0; int i = 0; int standardSize = myStrlen(arr[0]); for (; arr[i]; i++) { if (myStrlen(arr[i]) != standardSize) { exitError("Wrong Map\n"); } mat[i] = malloc(sizeof(int) * (standardSize + 1)); for (x = 0; arr[i][x] != '\0'; x++) { mat[i][x] = fillMatrix(arr[i][x]); } mat[i][x] = -1; } mat[i] = NULL; for (i = 1; mat[i]; i++) { for (x = 1; mat[i][x] != -1; x++) { int a = mat[i - 1][x]; int b = mat[i][x - 1]; int c = mat[i - 1][x - 1]; mat[i][x] = (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); BSQ = findBSQ(BSQ, mat[i][x]); } } return BSQ; } int main(int ac, char **av) { if (ac != 2) { exitError("You didn't provided a file\n"); } char *str = readFile(av[1]); char **arr = strToWordArray(str); if (isExisting(arr, '.') == -1) return putarray(arr); for (int i = 0; str[i]; i++) { if (str[i] != '.' && str[i] != '\n' && str[i] != '#') { exitError("Wrong Map\n"); } } int **mat = malloc(sizeof(int *) * (arrlen(arr) + 1)); int i = 0; int x = 0; int BSQ = returnBSQinMatrixCreator(arr, mat); for (; mat[i]; i++) { x = intExisting(mat[i], BSQ); if (x != -1) { break; } } if (x == -1) { putarray(arr); return 0; } int squareRow = i; int squareCol = x; for (; i > (squareRow - BSQ); i--) { for (x = squareCol; x > (squareCol - BSQ); x--) { arr[i][x] = 'X'; } } return putarray(arr); } _____________________________________________________________________________________ #define WRONG (str[i] != '.' && str[i] != '\n' && str[i] != '#') static int returnBSQinMatrixCreator(char **arr, int **mat) { int BSQ = 0, x = 0, i = 0, standardSize = myStrlen(arr[0]); for (; arr[i]; i++) { if (myStrlen(arr[i]) != standardSize) exitError("Wrong Map\n"); mat[i] = malloc(sizeof(int) * (standardSize + 1)); for (x = 0; arr[i][x] != '\0'; x++) mat[i][x] = (arr[i][x] == '.') ? 1 : 0; mat[i][x] = -1; } mat[i] = NULL; for (i = 1; mat[i]; i++) { for (x = 1; mat[i][x] != -1; x++) { int a = mat[i - 1][x], b = mat[i][x - 1], c = mat[i - 1][x - 1]; mat[i][x] = (a < b) ? ((a < c) ? a : c) : ((b < c) ? b : c); BSQ = (mat[i][x] > BSQ) ? mat[i][x] : BSQ; } } return BSQ; } int main(int ac, char **av) { if (ac != 2) exitError("Something's wrong with arguments\n"); char *str = readFile(av[1]), **arr = strToWordArray(str); if (isExisting(arr, '.') == -1) return putarray(arr); for (int i = 0; str[i]; i++) if (WRONG) exitError("Wrong Map\n"); int **mat, i = 0, x = 0, BSQ = returnBSQinMatrixCreator(arr, (mat = malloc(sizeof(int *) * (arrlen(arr) + 1)))); for (i; mat[i]; i++) if ((x = intExisting(mat[i], BSQ)) != -1) break; if (x == -1) ? return putarray(map); int squareRow = i, squareCol = x; for (; i > (squareRow - BSQ); i--) for (x = squareCol; x > (squareCol - BSQ); x--) arr[i][x] = 'X'; return putarray(arr); } ``` ***40 LIGNES EN MOINS ????*** (*J'ai utilisé du ternaire alors si vous savez pas ce que c'est parce que vous aviez la flemme dans la lecture de l'explication de ***terminator*** allez voir le cours dessus*) Il y a sûrement encore moyen de rendre le code plus condensé mais je vous déconseille fortement de faire cette pratique car le code est illisble, c'est juste fun si vous n'avez que ça à faire. ***Fun fact j'ai pas testé le code, il y a 99% de chances que ca segv :sunglasses:***