Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Nouveau Candidat au Club
    Affecter une valeur à une carte déjà distribuée aléatoirement
    Bonjour à tous,

    j'ai un projet à effectuer qui consiste en la conception d'un jeu de Belotte (et plus précisément la "Coinche") et je suis bloqué à une étape.

    (Petite remise en contexte : la coinche est un jeu de cartes se jouant à 4 joueurs chacun ayant 8 cartes reçues aléatoirement entre le 7 jusqu'a l'As, chaque carte correspond à un nombre de point qui détermine également sa "puissance" dans le jeu)
    Règle de la coinche si mon explication n'est pas claire : https://www.belote.com/regles-et-var...elote-coinche/

    Pour vous situer où j'en suis dans mon programme j'ai créer une structure "carte" me permettant d'y définir sa "couleur" "valeur" "nombre de point" ainsi qu'une variable "atout" 0 ou 1 pour simplifier par la suite la phase de jeu.

    J'ai réussi à générer et distribuer aléatoirement les cartes aux joueurs mais maintenant je suis bloqué à la phase dite "d'enchere" où le joueur doit choisir quelle couleur devient Atout ce qui va définir le nombre de points de cartes.

    A savoir que lors de la "création des cartes" je leur attribue le nombre de point de base (c'est à dire dans le cas où elles ne sont pas prisent comme Atout).

    Seulement une fois les cartes distribuées aléatoirement aux joueurs, j'arrive à modifier le nombre de points du Jeu de carte de base (celui que j'utilise pour générer les cartes et les distribuer) car elles sont rangées dans l'ordre mais je ne vois pas comment modifier les points dans les jeux des joueurs étant données qu'elles sont rangées aléatoirement.

    Merci pour votre aide je vous joins le fichier source.

  2. #2
    Nouveau Candidat au Club
    Je me demande si en donnant la même adresse des cartes des joueurs aux cartes du jeu cela pourrait fonctionner...qu'en pensez-vous?

  3. #3
    Expert éminent sénior
    Bonjour
    Citation Envoyé par El_Carlos Voir le message
    Je me demande si en donnant la même adresse des cartes des joueurs aux cartes du jeu cela pourrait fonctionner...qu'en pensez-vous?
    Bien évidemment (mais dans le bon sens => c'est le joueur qui reçoit l'adresse des cartes du jeu)
    Si ton joueur reçoit une "copie" de la carte, alors modifier la carte dans le jeu ne change rien à la carte du joueur.
    Mais si le joueur reçoit "l'adresse" de la carte, d'une part ça améliore les performances (vaut mieux recopier une adresse de 8 octets qu'une structure de 8000 octets) et d'autre part, toute modification de la carte du jeu se répercute chez le joueur.

    Toutefois tu t'y prends mal car une carte n'a pas de valeur en elle-même, elle n'en a que pour le joueur. Exemple un "9" c'est que dalle. Mais un "9" avec un "V" de suite ça change tout. C'est donc dans la partie du code dévolue au joueur qu'il te faudra coder l'algo d'analyse de la valeur de ses cartes.

    PS: je connais bien la belote contrée (règles strictes, c'est à dire pas de "contre" à la volée, ni d'annonce "sans-atout" ou "tout-atout" qui, elles, font partie de la belote "bridgée"), où j'ai déjà gagné des tournois de bistro avec mon père (ce qui ne signifie pas qu'on était des piliers de bistro !!!). Ton vrai souci sera de faire que le joueur 1 "comprenne" la valeur du jeu du joueur 3 (et inversement et idem joueur2 <=> joueur4) en fonction de leurs annonces respectives (80=AA, 90=(Vxx ou 9xxx) + As autre couleur, 100=V9Ax et surtout au-moins une carte dans toutes les autres couleurs (pour pouvoir aller chercher l'as éventuel du partenaire), 110=V9Ax et seulement 3 couleurs) ; et ensuite comprendre les réponses (+10=V ou 9, +20=V+A ou 9x+A, +10 second tour=A, etc.). J'avais à une époque pensé moi aussi à coder ce jeu mais j'y ai renoncé tellement c'est difficile (sauf si tu veux juste proposer un support de jeu à 4 joueurs humains là ça devient plus aisé).
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  4. #4
    Nouveau Candidat au Club
    Bonjour Sve@r,

    merci pour ta réponse rapide !

    Je t'avoue que les pointeurs sont réellement ma hantise j'ai du mal à comprendre se concept en pratique.

    Est-il possible de définir une adresse d'un tableau via un autre tableau (dans le style: &tab1[i] =&tab2[i];) sachant que mon tableau est défini selon une structure...? Car tu t'en doutes j'ai essayé cette "formule" mais ça n'a pas marché... (je précise je suis débutant cela fait seulement 2/3 mois que je code).

  5. #5
    Expert éminent sénior
    Citation Envoyé par El_Carlos Voir le message
    Je t'avoue que les pointeurs sont réellement ma hantise j'ai du mal à comprendre se concept en pratique.
    Ta peur du pointeur fait que tu perds déjà 50% de tes capacités intellectuelles pour le comprendre. Or cela n'a absolument rien de difficile.

    Prenons une variable de base int v=123. Cette variable se trouve positionnée à une adresse (par exemple 0x1000). Je vais écrire les adresses en commençant par "0x" pour bien les faire ressortir.
    Le C te permet de récupérer cette adresse. Cela se fait via l'opérateur "&" (&var). Cette adresse étant un nombre comme un autre, tu vas pouvoir la stocker. Toutefois (et c'est là le point à bien comprendre), le C a besoin de savoir que cette adresse correspond à un int. Et ce pour des raisons très simples: la mémoire est découpée en octets mais les types peuvent s'étendre sur plusieurs (un int fait 4 octets). Et donc pour que le C sache qu'à partir de 0x1000 il faut récupérer 4 octets pour reconstruire le nombre 123, il faut qu'il sache que l'adresse 0x1000 est celle d'un int.

    Donc cette déclaration se fait de la façon suivante: tu déclares une variable (exemple "pt") et tu lui dis "à la zone déréférencée par cette valeur il y a un int". Le déréférencement se fait par l'opérateur "*" donc ça s'écrit int* pt. Ca signifie alors 2 choses
    • la variable "pt" contient l'adresse d'un int (est de type "int étoile")
    • la case (et les suivantes) référencées ("*pt") par cette adresse représentent un int

    Ne reste plus qu'à lui donner l'adresse que tu veux y stocker (ici celle de "v") ce qui donne int *pt=&v. De là, tu peux obtenir le contenu soit par "v" (printf("%d\n", v)), soit par le déréférencement de "pt" (printf("%d\n", *pt)).
    Voilà. Quand tu comprends ça tu as fait la moitié du chemin.

    Ensuite l'autre moitié du chemin, c'est comprendre à quoi ça sert. Un des soucis du C, c'est qu'une variable dans une fonction est locale à la fonction. Et un paramètre de fonction ne reçoit qu'une copie de ce qu'on lui passe.
    Il s'ensuit que si tu écris
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void raz(int v) {
    	v=0;
    }
     
    int main() {
    	int v=123;
    	raz(v);
    	printf("%d\n", v)
    }

    Malgré le fait que j'ai mis le même nom "v", ce ne sont pas les mêmes variables. Et celle de la fonction n'est qu'une copie de ce qu'on lui passe. La copie est modifiée, mais pas l'original.

    Le pointeur sert à passer par dessus cette difficulté. Et ce pour une bonne raison: la mémoire d'un programme est unique pour tout le programme. La variable d'un bloc n'est connue que dans le bloc oui, mais la zone mémoire qu'elle utilise, elle, est accessible de partout pourvu qu'on connaisse l'adresse de cette zone.
    Donc si on veut que la fonction puisse modifier une variable qu'elle ne connait pas, il faut lui donner l'adresse de cette variable. Cette adresse étant toujours existante, la fonction pourra alors y aller et modifier la valeur qui s'y trouve.
    ce qui donne
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void raz(int *pt) {
    	(*pt)=0;
    }
     
    int main() {
    	int v=123;
    	raz(&v);
    	printf("%d\n", v);
    }

    Là j'envoie à la fonction l'adresse de ma variable à modifier (toujours 0x1000). La fonction reçoit cette adresse, la stocke dans un pointeur (credo à ne jamais oublier: une adresse est toujours stockée dans un pointeur), et bien que ce pointeur soit local, la zone mémoire qui se trouve à cette adresse elle reste globale.
    La fonction peut donc aller taper à cette adresse 0x1000 en utilisant l'opérateur de déréférencement d'adresse (l'étoile) et modifier la valeur (y mettre 0).

    Une des difficultés de compréhension, c'est que tantôt l'étoile représente un pointeur, tantôt une opération (le déréférencement). Pour s'en sortir c'est tout simple
    • quand on déclare une variable, l'étoile représente le fait que c'est un pointeur
    • quand on utilise une variable, l'étoile représente l'opération de déréférencement


    Une autre difficulté, c'est de bien savoir qu'on a un pointeur pour pouvoir le transmettre à un tiers si nécessaire.
    Exemple
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Fonction qui met une valeur dans un int
    void majValeur(int v, int *pt) {
    	(*pt)=v;
    }
     
    // Fonction qui met un int à 0
    void raz(int *pt) {
    	// Pour mettre à 0, autant appelerla mise à jour de valeur avec la valeur "0"
    	majValeur(0, ... ???);				// Oups, je veux que la fonction "majValeur" reçoive le pointeur reçu et je suis perdu !!!
    }
     
    int main() {
    	int v=123;
    	raz(&v);
    	printf("%d\n", v);
    }

    Donc là je veux que "majValeur" reçoive un pointeur. Bien souvent, le débutant se dit "la fonction reçoit un "int étoile", je lui passe "étoile pt" et écrit majValeur(0, *pt). Ce qui est une erreur. Tout simplement parce qu'il fait la confusion entre "l'étoile dans la déclaration" et "l'étoile dans l'action" dont j'ai parlé plus haut, et d'autre part parce que "étoile pt" c'est un int, pas un int étoile (autre credo à retenir par coeur: un type "truc étoile" ce n'est pas un type "truc")

    Ici "pt" est de type "int étoile", la fonction "majValeur" attend un "int étoile", je lui passe donc "pt", tout simplement => majValeur(0, pt).
    Ou alors, la fonction "majValeur" attend 0x1000 (l'adresse de "v"), cette adresse est stockée dans "pt", je lui passe donc "pt", tout simplement => majValeur(0, pt).


    Ensuite le bonus c'est que le pointeur sert à optimiser. Par exemple comme je l'ai dit, si tu as une structure à passer à une fonction (rappel, tout passage se fait toujours par copie), il vaut mieux passer l'adresse et copier l'adresse (8 octets à copier c'est que dalle) que passer la structure (peut-être 8 milliards d'octets) et copier cette structure (8 milliards d'octets à copier).
    Autre utilisation: chaque fois que tu utilises un indice de tableau (tab[i]) le C se place au début du tableau et effectue un calcul pour se décaler sur la bonne case. Si tu écris printf("le carre de %d c'est %d*%d=%u", tab[i], tab[i], tab[i], tab[i] * tab[i]) tu fais 5 fois ce décalage.
    Si maintenant tu crées un pointeur en plus et que tu le places toi sur la bonne case du tableau, tu pourras alors utiliser ce pointeur pour récupérer directement la bonne valeur sans faire faire ce calcul (enfin il faut toujours le faire une fois pour placer le pointeur au bon endroit mais ensuite, une fois placé, tu peux taper 50 fois dans la case sans obliger le C à faire 50 fois ce décalage)
    Ce qui donnera
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    int *pt=&tab[i];			// Je récupère l'adresse de tab[i] dans un pointeur
    printf("le carre de %d c'est %d*%d=%u", *pt, *pt, *pt, (*pt) * (*pt));			// J'affiche 5 fois la valeur qui se trouve à cette adresse, tout bêtement

    PS: accessoirement ici entre nous (les intervenants) il y a un petit schisme sur ce point car certains disent que l'optimiseur du compilateur peut voir ce genre d'instructions sur tab[i] et créer lui-même le pointeur qui va bien. Perso je dis "peut-être, pourquoi pas, mais ça n'empêche pas que le pointeur que moi je mets, au-moins je suis sûr qu'il y est". Ce sont des petites divergences d'opinion qui peuvent arriver mais qui n'ont rien de grave. Rien ne t'oblige à suivre aveuglément l'opinion de l'un ou de l'autre, tu as parfaitement le droit de choisir librement vers une direction et préférer le pointeur, ou t'orienter vers l'autre et utiliser l'indexation en faisant confiance au compilateur (ou alors 3° direction t'en as rien à carrer de l'optimisation et tu choisis l'indexation parce que c'est plus facile).

    Citation Envoyé par El_Carlos Voir le message
    Est-il possible de définir une adresse d'un tableau via un autre tableau
    Oui, tout comme tu peux stocker plein d'ints dans un tableau d'ints, tu peux stocker plein d'adresses dans un tableau d'adresses. Toutefois il faut que ce soient des adresses de même nature (*)
    Code c :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    // 3 variables quelconques
    int a=123;
    int b=456
    int c=789;
     
    int *tab[3]={&a, &b, &c};			// Tableau de 3 adresses contenant les adresses de ces 3 variables
    for (i=0; i < 3; i++)
    	printf("%d\n", *tab[i]);			// Affichage de chaque tab[i] déréférencé


    (*) on peut passer par dessus cette contrainte en utilisant le pointeur universel mais ce sera pour plus tard

    Citation Envoyé par El_Carlos Voir le message
    (dans le style: &tab1[i] =&tab2[i];) sachant que mon tableau est défini selon une structure...? Car tu t'en doutes j'ai essayé cette "formule" mais ça n'a pas marché...
    Evidemment. Tu peux copier une adresse (une valeur) dans une variable (donc sous la forme variable=adresse exactement comme tu écris variable=valeur) mais tu ne peux pas "forcer" une adresse en écrivant &var=valeur

    Citation Envoyé par El_Carlos Voir le message
    (je précise je suis débutant cela fait seulement 2/3 mois que je code).
    Ben je pense que tu t'es attaqué à un gros morceau qui nécessite de maitriser pas mal d'outils (structures, tableaux, et évidemment pointeurs) en plus de l'algorithmique pour évaluer informatiquement un jeu de 8 cartes...
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  6. #6
    Nouveau Candidat au Club
    Je dois t'avouer ne pas avoir compris entièrement ton explication (mais je pense que c'est normal), cependant je viens de faire un test avec des variables simples et cela à fonctionné et je pense avoir compris en partie le fonctionnement des pointeurs.

    Un grand merci déjà pour ton explication claire (et de prendre le temps) tu as déjà été un meilleur prof que celui que j'ai actuellement...

    Je vais continuer de tester avec les pointeurs et je te tiendrai au courant de si je réussi mon objectif.



    PS: oui on nous a donné ce projet il y a 2/3 semaines à faire (sachant que je n'avais JAMAIS joué à la belote Coinché et mon binome non plus il nous à déjà fallu comprendre les principes --') additionné au confinement qui a engendré de grosses difficultés pour les cours + un prof peu disponible et assez peu...disons clair dans ses explications, c'est difficile mais je tiendrai au courant de si on mène à bien le projet et je partagerai le code à la fin .