Partage
  • Partager sur Facebook
  • Partager sur Twitter

[algo, tous langages] Parties de morpion

Compter toutes les parties possibles

    17 juin 2010 à 1:16:16

    Bonjour,

    Toto joue au morpion contre Titi. Toto commence toujours. Combien de parties différentes peuvent-ils faire au maximum ? Bien sûr, une partie s'arrête dès qu'il y a un alignement de trois pions de même couleur ou qu'on ne peut plus poser de pion. Quand je dis toutes les parties possibles, ça veut dire toutes, y compris une partie où Toto ne verrait pas qu'il a la possibilité de gagner.

    Ce nombre de parties est connu et il vaut 256188255168, cf. Tic-tac-toe et il peut même se calculer à la main (mais c'est pas évident a priori).

    Je vous invite à poster un code dans le langage de votre choix (y compris les langages des autres forums : C, Java et C++) et qui soit capable de calculer le nombre de parties possibles. Vous programme doit aussi calculer :

    -- le nombre de parties gagnées par Toto,
    -- le nombre de parties gagnées par Titi,
    -- le nombre de parties nulles.



    • Partager sur Facebook
    • Partager sur Twitter
      17 juin 2010 à 7:13:16

      Bonjour,

      Un premier essai en C. Pas dans la finesse. :-°
      #include <stdio.h>
      
      #define SZ  3
      
      enum {VIDE, TOTO, TITI};
      struct partie
      {
          int grille[SZ][SZ];
          int victoires[2];
          int partiesNulles;
          int pions;
      };
      
      
      
      int testerLignes(struct partie *p)
      {
          int i, j;
          for(i = 0; i < SZ; i++)
          {
              for(j = 1; p->grille[i][0] && j < SZ; j++)
                  if(p->grille[i][j] != p->grille[i][0])
                      break;
              if(j == SZ)
                  return p->grille[i][0];
          }
      
          return 0;
      }
      
      
      int testerColonnes(struct partie *p)
      {
          int i, j;
          for(i = 0; i < SZ; i++)
          {
              for(j = 1; p->grille[0][i] && j < SZ; j++)
                  if(p->grille[j][i] != p->grille[0][i])
                      break;
              if(j == SZ)
                  return p->grille[0][i];
          }
          return 0;
      }
      
      
      int testerDiagonale1(struct partie *p)
      {
          int i;
          for(i = 1; p->grille[0][0] && i < SZ; i++)
              if(p->grille[i][i] != p-> grille[0][0])
                  return 0;
          return p->grille[0][0];
      }
      
      
      int testerDiagonale2(struct partie *p)
      {
          int i;
          for(i = 1; p->grille[SZ - 1][0] && i < SZ; i++)
              if(p->grille[SZ - 1 - i][i] != p->grille[SZ - 1][0])
                  return 0;
          return p->grille[SZ - 1][0];
      }
      
      
      int evaluer(struct partie *p)
      {
          int i;
          int res;
      
          int(*f[])(struct partie *p) =
          {
              testerLignes,
              testerColonnes,
              testerDiagonale1,
              testerDiagonale2
          };
      
          for(i = 0; i < sizeof f / sizeof *f; i++)
          {
              res = f[i](p);
              if(res)
              {
                  p->victoires[res - 1]++;
                  return 1;
              }
          }
      
          if(p->pions == SZ * SZ)
              p->partiesNulles++;
      
          return 0;
      }
      
      void jouer(struct partie *p, int joueurCourant)
      {
          int i, j;
      
          if(evaluer(p))
              return;
      
          for(i = 0; i < SZ; i++)
              for(j = 0; j < SZ; j++)
                  if(p->grille[i][j] == 0)
                  {
                      p->grille[i][j] = joueurCourant;
                      p->pions++;
                      jouer(p, 1 - joueurCourant + 2);
                      p->grille[i][j] = VIDE;
                      p->pions--;
                  }
      }
      
      
      int main(void)
      {
          struct partie p = {0};
      
          jouer(&p, TOTO);
      
          printf("Toto    ->  %d\n", p.victoires[0]);
          printf("Titi    ->  %d\n", p.victoires[1]);
          printf("Nulles  ->  %d\n", p.partiesNulles);
          printf("Total   ->  %d\n", p.victoires[0] +
                                      p.victoires[1] +
                                      p.partiesNulles);
      
          return 0;
      }
      

      Toto    ->  131184
      Titi    ->  77904
      Nulles  ->  46080
      Total   ->  255168
      
      Process returned 0 (0x0)   execution time : 0.500 s
      Press any key to continue.
      • Partager sur Facebook
      • Partager sur Twitter
      Zeste de Savoir, le site qui en a dans le citron !
        17 juin 2010 à 8:59:20

        Citation : GurneyH


        Pas dans la finesse. :-°



        Sûr que brute force dans la finesse, c'est pas évident ;) Bon, je trouve ton code très bien, d'ailleurs, j'avais fait à peu près la même chose ;) mais en Python (*):

        import psyco
        psyco.full()
        
        class Joueur:
            def __init__(self):
                self.gain = 0
                self.nulle = 0
        
        class Partie:
            voisins = {1: ((2, 3), (4, 7), (5, 9)), 2: ((1, 3), (5, 8)),
                       3: ((2, 1), (5, 7), (6, 9)), 4: ((7, 1), (5, 6)),
                       5: ((9, 1), (2, 8), (3, 7), (4, 6)), 6: ((3, 9), (5,
                       4)), 7: ((4, 1), (5, 3), (8, 9)), 8: ((2, 5), (9, 7)),
                       9: ((5, 1), (5, 8), (6, 3))}
        
            def __init__(self, *joueurs):
                self.joueur = joueurs[0]
                self.non_joueur = joueurs[1]
                self.plateau = dict.fromkeys(range(1, 10), None)
                self.libres = range(1, 10)
        
            def jouer(self, c):
                self.plateau[c] = self.joueur
                self.libres.remove(c)
        
            def retirer(self, c):
                self.plateau[c] = None
                self.libres.append(c)
        
            def gagner(self, c):
                for v in Partie.voisins[c]:
                    if self.plateau[v[0]] == self.plateau[v[1]] == self.joueur:
                        return 1
                return 0
        
        def f(p):
            libres = p.libres[:]
            joueur = p.joueur
            non_joueur = p.non_joueur
            if libres:
                for c in libres:
                    p.jouer(c)
                    if p.gagner(c):
                        joueur.gain += 1
                    else:
                        (p.joueur, p.non_joueur) = (p.non_joueur, p.joueur)
                        f(p)
                    p.joueur = joueur
                    p.non_joueur = non_joueur
                    p.retirer(c)
            else:
                p.joueur.nulle += 1
                p.non_joueur.nulle += 1
        
        (j1, j2) = (Joueur(), Joueur())
        p = Partie(j1, j2)
        
        f(p)
        z = (j1.gain, j2.gain, j1.nulle)
        zz = '%-15s : %6s\n'
        print (zz * 4)[:-1] % ('Titi gagne', z[0], 'Toto gagne', z[1],
                               'Parties nulles', z[2], 'Nombre parties', sum(z))
        


        Le temps est néanmoins catastrophique par rapport à ton code en C :


        candide@~$ time python partiesMorpion.py 
        Titi gagne      : 130644
        Toto gagne      :  76584
        Parties nulles  :  48960
        Nombre parties  : 256188
        
        real    0m5.339s
        user    0m5.320s
        sys     0m0.016s
        
        candide@~$ time ./x
        Toto    ->  131184
        Titi    ->  77904
        Nulles  ->  46080
        Total   ->  255168
        
        real    0m0.049s
        user    0m0.048s
        sys     0m0.000s
        candide@~$



        EDIT : juste un point : on peut quand même substantiellement améliorer le temps d'exécution en tenant compte au lancement de l'algo des symétries du problème (on fera un peu moins bien que si on divisait le temps par 3)


        (*) EDIT Le code contient une coquille rectifiée ICI
        • Partager sur Facebook
        • Partager sur Twitter
          17 juin 2010 à 17:01:48

          J'ai fait un test en Ada pour changer un peu, je ne maitrise pas tellement le langage donc le code est assez long et redondant je trouve (y'a certainement moyen de faire mieux en étant connaisseur mais c'est vrai qu'étant habitué au C, y'a certaines chose que je suis incapable de remplacer en Ada :p )
          with Ada.Text_IO;
          use Ada.Text_IO;
          
          procedure Morpion is
          
             Nc : constant Integer := 3;
          
             type Joueur is (VIDE, TOTO, TITI);
             type Une_Grille is array (1..Nc,1..Nc) of Joueur;
          
             type Partie is record
               Grille : Une_Grille;
               Toto_Gagne, Titi_Gagne, Match_Nul : Integer;
             end record;
          
             function Test_Ligne(G : Une_Grille) return Joueur;
             function Test_Colone(G : Une_Grille) return Joueur;
             function Test_Diagonale(G : Une_Grille) return Joueur;
             function Match_Nul(G : Une_Grille) return Boolean;
             procedure Evaluer(P : in out Partie; Fin : out Boolean);
             procedure Jouer(P : in out Partie; J1 : Joueur);
          
             function Test_Ligne(G : Une_Grille) return Joueur is
             begin
                for N in G'Range(1) loop
                   if G(N,1) /= VIDE and G(N,1) = G(N,2) and G(N,2) = G(N,3) then
                      return G(N,1);
                   end if;
                end loop;
                return VIDE;
             end Test_Ligne;
          
             function Test_Colone(G : Une_Grille) return Joueur is
             begin
                for N in G'Range(2) loop
                   if G(1,N) /= VIDE and G(1,N) = G(2,N) and G(2,N) = G(3,N) then
                      return G(1,N);
                   end if;
                end loop;
                return VIDE;
             end Test_Colone;
          
             function Test_Diagonale(G : Une_Grille) return Joueur is
                C : Joueur := G(2,2);
             begin
                if C /= VIDE then
                   if (C = G(1,1) and C = G(3,3)) or (C = G(1,3) and C = G(3,1)) then
                     return C;
                   end if;
                end if;
                return VIDE;
             end Test_Diagonale;
          
             function Match_Nul(G : Une_Grille) return Boolean is
             begin
                for I in G'Range(1) loop
                   for J in G'Range(2) loop
                      if G(I,J) = VIDE then
                         return FALSE;
                      end if;
                   end loop;
                end loop;
                return TRUE;
             end Match_Nul;
          
             procedure Evaluer(P : in out Partie; Fin : out Boolean) is
                Gagnant : Joueur := VIDE;
             begin
                Gagnant := Test_Ligne(P.Grille);
                if (Gagnant = TOTO) then
                   P.Toto_Gagne := P.Toto_Gagne + 1;
                   Fin := TRUE;
                   return ;
                elsif (Gagnant = TITI) then
                   P.Titi_Gagne := P.Titi_Gagne + 1;
                   Fin := TRUE;
                   return ;
                end if;
          
                Gagnant := Test_Colone(P.Grille);
                if (Gagnant = TOTO) then
                   P.Toto_Gagne := P.Toto_Gagne + 1;
                   Fin := TRUE;
                   return ;
                elsif (Gagnant = TITI) then
                   P.Titi_Gagne := P.Titi_Gagne + 1;
                   Fin := TRUE;
                   return ;
                end if;
          
                Gagnant := Test_Diagonale(P.Grille);
                if (Gagnant = TOTO) then
                   P.Toto_Gagne := P.Toto_Gagne + 1;
                   Fin := TRUE;
                   return ;
                elsif (Gagnant = TITI) then
                   P.Titi_Gagne := P.Titi_Gagne + 1;
                   Fin := TRUE;
                   return ;
                end if;
          
                Fin := Match_Nul(P.Grille);
          
                if Fin then
                   P.Match_Nul := P.Match_Nul + 1;
                end if;
          
             end Evaluer;
          
             procedure Jouer(P : in out Partie; J1 : Joueur) is
                Fin : Boolean;
                J2 : Joueur;
             begin
                Evaluer(P, Fin);
          
                if Fin then
                   return ;
                end if;
          
                if J1 = TOTO then
                   J2 := TITI;
                else
                   J2 := TOTO;
                end if;
          
                for I in P.Grille'Range(1) loop
                   for J in P.Grille'Range(2) loop
                      if P.Grille(I,J) = VIDE then
                         P.Grille(I,J) := J1;
                         Jouer(P,J2);
                         P.Grille(I,J) := VIDE;
                      end if;
                   end loop;
                end loop;
             end Jouer;
          
             P : Partie := ((others => (others => VIDE)),0,0,0);
          begin
             Jouer(P, TOTO);
             Put_Line("Victoire de Toto : " & Integer'Image(P.Toto_Gagne));
             Put_Line("Victoire de Titi : " & Integer'Image(P.Titi_Gagne));
             Put_Line("Match nul : " & Integer'Image(P.Match_Nul));
             Put_Line("Total : " & Integer'Image(P.Toto_Gagne + P.Titi_Gagne + P.Match_Nul));
          end Morpion;

          Désolé pour vos yeux mais il n'y a pas de balise Ada :euh:

          Edit : Modification d'une partie du code

          Edit 2 : Voici mes résultats :
          Victoire de Toto :  131184
          Victoire de Titi :  77904
          Match nul :  46080
          Total :  255168
          
          real    0m0.094s
          user    0m0.088s
          sys     0m0.004s
          • Partager sur Facebook
          • Partager sur Twitter
            17 juin 2010 à 18:30:30

            Citation : candide

            candide@~$ time python partiesMorpion.py 
            Titi gagne      : 130644
            Toto gagne      :  76584
            Parties nulles  :  48960
            Nombre parties  : 256188
            
            real    0m5.339s
            user    0m5.320s
            sys     0m0.016s
            
            candide@~$ time ./x
            Toto    ->  131184
            Titi    ->  77904
            Nulles  ->  46080
            Total   ->  255168
            
            real    0m0.049s
            user    0m0.048s
            sys     0m0.000s
            candide@~$

            Note que tes résultats sont faux. :-°
            Tu t'es trompé aussi dans ton premier post, ce n'est pas 256188 mais 256168.

            Quand à moi, j'essaye de trouver quelque chose d'autre que le brute force. :D
            • Partager sur Facebook
            • Partager sur Twitter
              17 juin 2010 à 18:50:06

              Moi j'avais fait y'a pas longtemps un truc qui joue au morpion avec un minmax, le modifier pour compter les parties doit pas être dur...
              Code ici : http://quentin.alwaysdata.net/bettle-ml/beetle.html
              • Partager sur Facebook
              • Partager sur Twitter
                17 juin 2010 à 18:55:57

                Citation : Pouet_forever

                Citation : candide

                candide@~$ time python partiesMorpion.py 
                Titi gagne      : 130644
                Toto gagne      :  76584
                Parties nulles  :  48960
                Nombre parties  : 256188
                
                real    0m5.339s
                user    0m5.320s
                sys     0m0.016s
                
                candide@~$ time ./x
                Toto    ->  131184
                Titi    ->  77904
                Nulles  ->  46080
                Total   ->  255168
                
                real    0m0.049s
                user    0m0.048s
                sys     0m0.000s
                candide@~$


                Note que tes résultats sont faux. :-°
                Tu t'es trompé aussi dans ton premier post, ce n'est pas 256188 mais 256168.

                Quand à moi, j'essaye de trouver quelque chose d'autre que le brute force. :D


                Dans le lien donné par Candide il est indiqué 255168 (les résultats correspondent avec ceux obtenus par GurneyH).
                • Partager sur Facebook
                • Partager sur Twitter
                  17 juin 2010 à 20:23:27

                  Il y a 2 exécutions, la première de candide et la seconde de GurneyH. :-°

                  candide@~$ time python partiesMorpion.py 
                  Titi gagne      : 130644
                  Toto gagne      :  76584
                  Parties nulles  :  48960
                  Nombre parties  : 256188
                   
                  real    0m5.339s
                  user    0m5.320s
                  sys     0m0.016s
                  
                  candide@~$ time ./x
                  Toto    ->  131184
                  Titi    ->  77904
                  Nulles  ->  46080
                  Total   ->  255168
                  
                  real    0m0.049s
                  user    0m0.048s
                  sys     0m0.000s
                  candide@~$
                  • Partager sur Facebook
                  • Partager sur Twitter
                    17 juin 2010 à 20:40:32

                    Citation : Pouet_forever

                    Il y a 2 exécutions, la première de candide et la seconde de GurneyH. :-°

                    candide@~$ time python partiesMorpion.py 
                    Titi gagne      : 130644
                    Toto gagne      :  76584
                    Parties nulles  :  48960
                    Nombre parties  : 256188
                     
                    real    0m5.339s
                    user    0m5.320s
                    sys     0m0.016s
                    
                    candide@~$ time ./x
                    Toto    ->  131184
                    Titi    ->  77904
                    Nulles  ->  46080
                    Total   ->  255168
                    
                    real    0m0.049s
                    user    0m0.048s
                    sys     0m0.000s
                    candide@~$

                    Oui je sais je ne disais pas ça contre toi (ni contre personne d'ailleurs), je corrigeais juste le fait que tu avais dis 256168 au lieu de 255168 :)
                    • Partager sur Facebook
                    • Partager sur Twitter
                      17 juin 2010 à 20:47:31

                      Tu as dû mal lire. ;)

                      Citation : candide

                      Ce nombre de parties est connu et il vaut 256188

                      • Partager sur Facebook
                      • Partager sur Twitter
                        17 juin 2010 à 21:00:00

                        Citation : Pouet_forever

                        Tu as dû mal lire. ;)

                        Citation : candide

                        Ce nombre de parties est connu et il vaut 256188


                        En fait Candide a écrit 256188, tu as corrigé en 256168 et je t'ai corrigé en 255168 :

                        Citation

                        When winning combinations are considered, there are 255,168 possible games. Assuming that X makes the first move every time:


                        Mais bon on a un peu dérivé hors de l'exercice là :p
                        • Partager sur Facebook
                        • Partager sur Twitter
                          18 juin 2010 à 1:35:06

                          Citation : Pouet_forever

                          Citation : candide

                          candide@~$ time python partiesMorpion.py 
                          Titi gagne      : 130644
                          Toto gagne      :  76584
                          Parties nulles  :  48960
                          Nombre parties  : 256188
                          
                          real    0m5.339s
                          user    0m5.320s
                          sys     0m0.016s
                          
                          candide@~$ time ./x
                          Toto    ->  131184
                          Titi    ->  77904
                          Nulles  ->  46080
                          Total   ->  255168
                          
                          real    0m0.049s
                          user    0m0.048s
                          sys     0m0.000s
                          candide@~$


                          Note que tes résultats sont faux. :-°



                          Hum, comme l'impression que ça t'a fait plaisir ... Celui qui ne propose pas de code ne risque pas de se tromper ...

                          Bon l'erreur était pas très dure à trouver, une banale coquille dans ma liste de voisins alignés, j'ai écrit


                          Citation : candide

                          9: ((5, 1), (5, 8), (6, 3))}
                          





                          et il fallait écrire :

                          9:((5,1), (7,8), (6,3))}
                          


                          Hier c'était la journée des coquilles puisqu'effectivement j'avais mal recopié le nombre fourni par Wikipedia et il y avait une autre coquille dans mon code (inversion de Toto et Titi dans l'affichage)


                          Donc le code rectifié :

                          class Joueur:
                              def __init__(self):
                                  self.gain=0
                                  self.nulle=0
                          
                          class Partie:
                              voisins={
                              1:((2,3), (4,7), (5,9)), 
                              2:((1,3), (5,8)),
                              3:((2,1), (5,7), (6,9)), 
                              4:((7,1), (5,6)),
                              5:((9,1), (2,8), (3,7), (4,6)),
                              6:((3,9), (5,4)),
                              7:((4,1), (5,3), (8,9)),
                              8:((2,5), (9,7)),
                              9:((5,1), (7,8), (6,3))
                              }
                              
                              def __init__(self, *joueurs):
                                  self.joueur=joueurs[0]
                                  self.non_joueur=joueurs[1]
                                  self.plateau=dict.fromkeys(range(1,10), None)
                                  self.libres=range(1,10)
                          
                              def jouer(self, c):
                                  self.plateau[c]=self.joueur
                                  self.libres.remove(c)
                              
                              def retirer(self, c):
                                  self.plateau[c]=None
                                  self.libres.append(c)
                          
                              def gagner(self, c):
                                  for v in Partie.voisins[c]:
                                      if self.plateau[v[0]]==self.plateau[v[1]]==self.joueur:
                                          return 1
                                  return 0
                          
                          def f(p):
                              libres=p.libres[:]
                              joueur=p.joueur
                              non_joueur=p.non_joueur
                              if libres:
                                  for c in libres:
                                      p.jouer(c)
                                      if p.gagner(c):
                                          joueur.gain+=1
                                      else:
                                          p.joueur, p.non_joueur= p.non_joueur, p.joueur
                                          f(p)
                                      p.joueur=joueur
                                      p.non_joueur=non_joueur
                                      p.retirer(c)
                              else:
                                  p.joueur.nulle+=1
                                  p.non_joueur.nulle+=1
                                  
                          j1, j2= Joueur(), Joueur()
                          p=Partie(j1,j2)
                          
                          f(p)
                          z=(j1.gain,j2.gain, j1.nulle)
                          zz="%-15s : %6s\n"
                          print (zz*4)[:-1] %("Toto gagne",z[0], "Titi gagne",z[1], "Parties nulles", z[2],"Nombre parties", sum(z))
                          


                          candide@~$ python partiesMorpion.py 
                          Toto gagne      : 131184
                          Titi gagne      :  77904
                          Parties nulles  :  46080
                          Nombre parties  : 255168
                          candide@~$






                          Citation : Pouet_forever


                          Quand à moi, j'essaye de trouver quelque chose d'autre que le brute force. :D



                          A moins de refaire la démonstration proposée par un lien de l'article de Wikipédia, je doute que tu puisses échapper à brute force. Après, implémenter autrement une recherche exhaustive, c'est certainement possible bien que la récursivité me paraisse encore le moyen le plus simple.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            18 juin 2010 à 8:49:50

                            Citation : candide

                            Hum, comme l'impression que ça t'a fait plaisir ... Celui qui ne propose pas de code ne risque pas de se tromper ...


                            Tout le monde fait des erreurs, les bons et les moins bons. Ya pas vraiment de satisfaction à dire qu'un code ne fonctionne pas, seulement la manière de le dire.

                            Ce que je pensais faire c'est un 'semi' brute force. Comme on travaille sur un carré, est-ce que c'est nécessaire de calculer le nombre de coups qu'on fait à chaque coin ?
                            On calcule le nombre de coups possibles à chaque coin et on multiplie par 4, même chose pour les cases du milieu/côtés et un calcul avec celle du milieu.
                            Ce qui revient à calculer 3 cases et pas 9. :)
                            Après, je me suis pas penché vraiment dessus pour savoir si ça fonctionne, mais ça me paraît cohérent. :)
                            Qu'est-ce que t'en penses ?
                            • Partager sur Facebook
                            • Partager sur Twitter
                              18 juin 2010 à 10:46:42

                              Citation : Pouet_forever


                              Ce que je pensais faire c'est un 'semi' brute force. Comme on travaille sur un carré, est-ce que c'est nécessaire de calculer le nombre de coups qu'on fait à chaque coin ?
                              On calcule le nombre de coups possibles à chaque coin et on multiplie par 4, même chose pour les cases du milieu/côtés et un calcul avec celle du milieu.
                              Ce qui revient à calculer 3 cases et pas 9. :)




                              On dirait que tu veux utiliser les symétries du carré, effectivement il y a trois cases à examiner : la centrale, un angle et un côté. Mais une fois cela dit, tout reste à faire.


                              Apparemment, des gens se sont déjà penchés sur un calcul à la mano, cf. ICI par exemple, et comme tu peux le voir, sans être mathématiquement très complexe, c'est une discussion plutôt technique. Donc, dans ce problème, la force bestiale a tout son sens.

                              Il y a une autre méthode que le backtracking que Gurney et moi avons proposé (pour Holt, je ne sais pas quel est son algo) : on liste les factorielle 9 permutations au sens mathématique (toutes les placements possibles des 9 jetons sur le plateau en ne tenant pas compte des alignements) et on teste chacune d'elle si elle contient un alignement de trois jetons monocolores. La seule difficulté technique est qu'une configuration de jeu peut se retrouver dans plusieurs permutations mais cette difficulté se résout assez facilement. A mon avis cette méthode est plus facile à intellectualiser et à coder que la récursivité que j'ai trouvé quand même assez technique à mettre en oeuvre. Mais cette méthode est sans doute plus longue à écrire que la récursivité.
                              • Partager sur Facebook
                              • Partager sur Twitter
                                18 juin 2010 à 11:45:18

                                Bonjour,

                                Hier j'ai essayé d'utiliser la propriété suivante :

                                Citation : Wikipedia

                                There is a game that is isomorphic to tic-tac-toe, but on the surface appears completely different. Two players in turn say a number between one and nine. A particular number may not be repeated. The game is won by the player who has said three numbers whose sum is 15. Plotting these numbers on a 3×3 magic square shows that the game exactly corresponds with tic-tac-toe, since three numbers will be arranged in a straight line if and only if they total 15.


                                A priori on se ramène à un problème sur un carré magique. L'algorithme naïf doit avoir la même complexité mais peut-être que cette propriété peut amener à une astuce de calcul non triviale.

                                • Partager sur Facebook
                                • Partager sur Twitter
                                Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
                                  18 juin 2010 à 12:47:28

                                  Citation : iNaKoll

                                  Citation : Wikipedia

                                  There is a game that is isomorphic to tic-tac-toe, but on the surface appears completely different. Two players in turn say a number between one and nine. A particular number may not be repeated. The game is won by the player who has said three numbers whose sum is 15. Plotting these numbers on a 3×3 magic square shows that the game exactly corresponds with tic-tac-toe, since three numbers will be arranged in a straight line if and only if they total 15.




                                  C'est astucieux mais je ne vois pas en quoi ça nous permet de résoudre plus simplement le problème.


                                  J'en profite pour donner un code C :

                                  #include <stdio.h>
                                  
                                  #define N 3
                                  #define VIDE 2
                                  
                                  typedef
                                    struct {
                                    int t[N][N];
                                    int j;
                                    int gains[2];
                                    int nulles;
                                    int remplies;
                                  } Partie;
                                  
                                  int gagner(Partie * p)
                                  {
                                    int (*t)[N] = p->t;
                                  #define LIG(i) (t[i][0]!=VIDE && t[i][0]==t[i][1] && t[i][0]==t[i][2])
                                  #define COL(j) (t[0][j]!=VIDE && t[0][j]==t[1][j] && t[0][j]==t[2][j])
                                    return LIG(0) || LIG(1) || LIG(2) || COL(0) || COL(1) || COL(2)
                                      || (t[0][0] != VIDE && t[0][0] == t[1][1] && t[0][0] == t[2][2])
                                      || (t[0][2] != VIDE && t[0][2] == t[1][1] && t[0][2] == t[2][0]);
                                  }
                                  
                                  void compter(Partie * p)
                                  {
                                    int (*t)[N] = p->t, j = p->j;
                                    int lin, col;
                                  
                                    for (lin = 0; lin < N; lin++)
                                      for (col = 0; col < N; col++)
                                        if (t[lin][col] == VIDE)
                                          {
                                            t[lin][col] = p->j;
                                            p->remplies++;
                                            if (gagner(p))
                                              (p->gains)[j]++;
                                            else if (p->remplies < N * N)
                                              {
                                                p->j = 1 - p->j;
                                                compter(p);
                                                p->j = 1 - p->j;
                                              }
                                            else
                                              (p->nulles)++;
                                            p->remplies--;
                                            t[lin][col] = VIDE;
                                          }
                                  }
                                  
                                  int main(void)
                                  {
                                    Partie p = { 0 };
                                    int i;
                                    int *z = &(p.t[0][0]);
                                  
                                    for (i = 0; i < N * N; i++)
                                      z[i] = VIDE;
                                    compter(&p);
                                    printf("%d\n", p.gains[0] + p.gains[1] + p.nulles);
                                    return 0;
                                  }
                                  


                                  255168
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    18 juin 2010 à 13:59:46

                                    Peut-être en travaillant sur la somme des points de chaque joueur, en trouvant d'autres règles ou en appliquant des propriétés des carrés magiques existantes. En mathématiques, se donner un problème équivalent permet parfois de trouver de meilleurs solutions. L'avantage ici c'est que l'on ne travaille plus qu'avec des ensembles de nombres.

                                    Joueur 1 :
                                    Tirage de 5 nombres : {1 parmi 9, 1 parmi 7, 1 parmi 5, 1 parmi 3, 1 parmi 1}
                                    Calculer la probabilité que la somme de 3 nombres parmi ces 5 soit égale à 15

                                    Joueur 2 :
                                    Tirage de 4 nombres : {1 parmi 8, 1 parmi 6, 1 parmi 4, 1 parmi 2}
                                    Calculer la probabilité que la somme de 3 nombres parmi ces 4 soit égale à 15

                                    On comprend qu'il y a un certain couplage entre ces deux probabilités. Une fois exprimé correctement, ce couplage doit permettre de trouver une expression analytique du résultat.
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                    Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
                                      18 juin 2010 à 15:37:26

                                      Il y a une méthode qui me vient à l'esprit mais je n'ai pas trop réfléchi sur son utilité et ses performances, la voici :

                                      Nous avons notre jeu qui se compose de la manière suivant :

                                      1 | 2 | 3
                                      __|___|___
                                      4 | 5 | 6
                                      __|___|___
                                      7 | 8 | 9

                                      à partir de là on peut trouver les combinaisons gagnantes, qui sont les suivantes :

                                      123 | 456 | 789 | 147 | 258 | 369 | 159 | 357

                                      considérons maintenant que l'on mette notre jeu sur une seule ligne et transcrivons nos combinaisons gagnantes en chaines binaires :

                                      123 456 789 = 111 111 111

                                      123 = 111 000 000
                                      456 = 000 111 000
                                      789 = 000 000 111
                                      147 = 100 100 100
                                      258 = 010 010 010
                                      369 = 001 001 001
                                      159 = 100 010 001
                                      357 = 001 010 100

                                      Nous obtenons donc 8 masques qui déterminent les cas de victoires.

                                      Il suffirait donc peut-être de parcourir toutes les parties et de retranscrire sous forme de chaîne binaire à chaque étape l'état des pions des deux joueurs (J1 et J2), et de faire un ET avec ces 8 masques (Mx), dès que J1&Mx = Mx ou J2&Mx = Mx, on a un gagnant.
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        18 juin 2010 à 16:03:14

                                        J'avais pensé à ça aussi mais le problème c'est qu'il faut toujours générer toutes les parties (et leurs masques). Une fois que le masque d'une partie est créé le test est peut-être plus rapide mais honnêtement je ne pense pas que ça apporte grand chose au final. C'est toujours de la force brute et au mieux on améliore la complexité d'un certain facteur constant.
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                        Inkamath on GitHub - Interpréteur d'expressions mathématiques. Reprise du développement en cours.
                                          18 juin 2010 à 17:10:47

                                          Citation : Monkey D Mat


                                          à partir de là on peut trouver les combinaisons gagnantes, qui sont les suivantes :

                                          123 | 456 | 789 | 147 | 258 | 369 | 159 | 357



                                          C'est un petit peu vague tout ça.


                                          Citation : Monkey D Mat


                                          considérons maintenant que l'on mette notre jeu sur une seule ligne et transcrivons nos combinaisons gagnantes en chaines binaires :



                                          Tu te perds dans des détails d'implémentation (la cerise). Revenons à l'essentiel (le gâteau) : quel est précisément ton algorithme de dénombrements des parties ?
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            19 juin 2010 à 11:11:45

                                            il doit y avoir moyen de faire marcher l'algo de monkey:
                                            tu génère toutes les parties sous forme linéaire et tu applique le masque.

                                            Pour les générer sous forme linéaire, c'est le nombre de permutation (prenons 0, 1 et 2 pour case libre, j1 et j2) sur une liste d'au moins trois 1 et deux 2 avec toujours comme condition que le nombre de 1 est égal au nombre de 2 plus ou moins un.

                                            ça nous donne quelque chose comme
                                            nbJ1 = 3; 
                                            nbJ2 = 2;
                                            tant que nbJ1 + nbJ2 <= 9
                                                générer toute les permutation avec nbJ1 1 et nbJ2 2
                                                appliquer le masque et compter le nombres de solution gagnante
                                                   si nbJ1<nbJ2  //c'est au joueur deux de jouer
                                                nbJ2++
                                                   sinon        //c'est au joueur 1
                                                nbJ1++
                                            fin tant que
                                            afficher résultat
                                            fin

                                            mais ça reste du brute force...
                                            on peut peut-être améliorer la recherche des solution gagnante avec le masque. événtuellement recoder un strcmp optimisé pour notre problème.

                                            admettons que notre jeu soit représenté par un tableau d'int et qu'on ait n solution de longueur 9:
                                            int check(int* tab)
                                            {
                                               int res = 0;
                                               i=0;
                                               while(i<n)
                                               {
                                                   res |= (tab[0] & sol[i][0]) & (tab[1] & sol[i][1]) & (tab[2] & sol[i][2]) & (tab[0] & sol[i][2])....;//ça fait pas très C ce ... mais vous avez compris
                                                   i++;
                                               }
                                            }
                                            


                                            Hedi

                                            edit: il y a peut être moyen de faire la même chose avec des bits et on gagnerai du temps sur la comparaison. mais je me demande si on en perdrai pas plus sur la génération des permutations
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              19 juin 2010 à 13:31:28

                                              Voilà mon code basé sur ce que j'ai dit plus haut :

                                              #include <stdio.h>
                                              #include <stdlib.h>
                                              #include <string.h>
                                              
                                              #define N       3
                                              #define MAX_P   9
                                              
                                              typedef struct _partie {
                                                char tab[N][N];
                                                int toto, titi, nul;
                                                int nb_pions;
                                              } partie;
                                              
                                              enum JOUEURS {
                                                RIEN, TOTO, TITI
                                              };
                                              
                                              int verifie(char (*tab)[N]) {
                                                int i;
                                                
                                                for (i = 0; i < N; i++) {
                                                  if (tab[i][0] != RIEN &&
                                                      tab[i][0] == tab[i][1] && tab[i][1] == tab[i][2])
                                                    return tab[i][0];
                                                  if (tab[0][i] != RIEN &&
                                                      tab[0][i] == tab[1][i] && tab[1][i] == tab[2][i])
                                                    return tab[0][i];
                                                }
                                                if (tab[0][0] != RIEN &&
                                                    tab[0][0] == tab[1][1] && tab[1][1] == tab[2][2])
                                                  return tab[0][0];
                                                if (tab[2][0] != RIEN &&
                                                    tab[2][0] == tab[1][1] && tab[1][1] == tab[0][2])
                                                  return tab[2][0];
                                                return RIEN;
                                              }
                                              
                                              int jouerCoup(partie * p, int tour, int lig, int col) {
                                                if (p->tab[lig][col] != RIEN)
                                                  return 0;
                                                p->tab[lig][col] = tour;
                                                p->nb_pions++;
                                                return 1;
                                              }
                                              
                                              void delCoup(partie * p, int lig, int col) {
                                                p->tab[lig][col] = RIEN;
                                                p->nb_pions--;
                                              }
                                              
                                              int evalue(partie * p, int ret) {
                                                int res = 1;
                                                
                                                if (p->nb_pions == MAX_P && ret == RIEN)
                                                  p->nul++;
                                                else if (ret == TOTO)
                                                  p->toto++;
                                                else if (ret == TITI)
                                                  p->titi++;
                                                else
                                                  res = 0;
                                                return res;
                                              }
                                              
                                              void jouerParties(partie * p, int tour) {
                                                int lig, col;
                                                
                                                if (p->nb_pions > 4) {
                                                  int tmp = verifie(p->tab);
                                                  if (evalue(p, tmp))
                                                    return;
                                                }
                                                
                                                for (lig = 0; lig < N; lig++) {
                                                  for (col = 0; col < N; col++) {
                                                    if (!jouerCoup(p, tour, lig, col))
                                                      continue;
                                                    jouerParties(p, (tour == TOTO ? TITI : TOTO));
                                                    delCoup(p, lig, col);
                                                  }
                                                }
                                              }
                                              
                                              #define N_COUPS 3
                                              
                                              void jouer3Coups(partie * p) {
                                                int coups[N_COUPS][2] = { { 0, 0 }, { 1, 0 }, { 1, 1 } };
                                                int mult[N_COUPS] = { 4, 4, 1 };
                                                partie p3;
                                                int i;
                                                
                                                for (i = 0; i < N_COUPS; i++) {
                                                  memset(&p3, 0, sizeof p3);
                                                  jouerCoup(&p3, TOTO, coups[i][0], coups[i][1]);
                                                  jouerParties(&p3, TITI);
                                                  p->toto += (p3.toto * mult[i]);
                                                  p->titi += (p3.titi * mult[i]);
                                                  p->nul += (p3.nul * mult[i]);
                                                }
                                              }
                                              
                                              int main(void) {
                                                int total;
                                                partie p;
                                                
                                                memset(&p, 0, sizeof p);
                                                /* jouerParties(&p, TOTO); */
                                                jouer3Coups(&p);
                                                total = p.toto + p.titi + p.nul;
                                                
                                                printf("Toto  : %d\n", p.toto);
                                                printf("Titi  : %d\n", p.titi);
                                                printf("Null  : %d\n", p.nul);
                                                printf("Total : %d\n", total);
                                                return EXIT_SUCCESS;
                                              }
                                              

                                              $ ./pouet 
                                              Toto  : 131184
                                              Titi  : 77904
                                              Null  : 46080
                                              Total : 255168
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                                19 juin 2010 à 15:35:44

                                                Citation : Pouet_forever

                                                Voilà mon code basé sur ce que j'ai dit plus haut :



                                                Oui mais ce n'était pas très clair et ton code non commenté et assez compliqué ne m'a pas permis d'éclaircir la question. Pour éviter les ambiguïtés, j'invite chaque personne à donner deux versions de son code :

                                                (1) une version propre
                                                (2) la version propre augmentée de commentaire

                                                On s'attachera à commenter en quoi la logique de l'algorithme transparait dans le code et non pas à commenter l'implémentation (qui dépend du langage).

                                                Voici donc mon code (en langage C) commenté :


                                                /*--------------- RECHERCHE DU NOMBRE TOTAL DE PARTIES DE MORPION RÉALISABLES ---------*/
                                                /* 
                                                   L'algorithme est un backtracking qui examine tous les coups possibles
                                                   depuis le plateau vide. Autrement dit, au début, toto peut joueur en case 
                                                   1, 2, 3, etc jusqu'à 9. Puis en fonction de ce qu'a joué toto, titi peut 
                                                   joueur en case 2,3, etc et ainsi de suite.
                                                
                                                
                                                   L'algorithme est implémentée par une fonction récursive appelée
                                                   compter(). On appelle état du jeu, la connaissance -- de l'état du
                                                   plateau (sans l'ordre de placement) -- du joueur j qui a le trait. Le
                                                   principe de recherche du nb total de parties est le suivant : à partir
                                                   d'un état donné E du jeu, l'ensemble de tous les états suivants
                                                   possibles est obtenu en (a) considérant la totalité des états
                                                   résultant d'un _seul_ coup du joueur j (b) considérant la totalité des
                                                   suites de parties possibles à partir du coup joué par j.
                                                
                                                   Cette recherche s'implémente en une boucle (on boucle sur les positions
                                                   trouvées en (a)) et à chaque étape de la boucle, on comptabilise les
                                                   scores de suite de la partie (ce qui appelle récursivement la fonction
                                                   compter()). Mais pour que la partie ait une suite, il faut s'assurer dans
                                                   la boucle que (1): j n'a pas gagné (2): j n'a pas joué la dernière
                                                   case du jeu et si on est dans le cas (1) ou (2), il faut mettre à jour
                                                   les scores et passer à la position suivante de jeu de j. Ceci explique
                                                   l'implémentation de la fonction compter(). */
                                                
                                                #include <stdio.h>
                                                
                                                /* Le côté du carré */
                                                #define N 3
                                                
                                                /* case du plateau non occupée */
                                                #define VIDE 2
                                                
                                                /* Toutes les parties évoluent dans une même structure Partie */
                                                typedef
                                                  struct {
                                                  /* Le plateau */
                                                  int t[N][N];
                                                  /* Le joueur qui a le trait (toto=0 ou titi=1) */
                                                  int j;
                                                  /* gain[j] est le nombre de parties gagnées par j */
                                                  int gains[2];
                                                
                                                  /* Le nombre de parties nulles */
                                                  int nulles;
                                                
                                                  /* Le nombre de cases occupées. Quand ce nombre vaut N*N, le plateau ext
                                                     complètement rempli. */
                                                  int remplies;
                                                } Partie;
                                                
                                                /* Retourne 1 si il y a un alignement de 3 jetons du même joueur, retourne 0 
                                                   sinon Effectue la vérification dans chacune des 3 lignes, chacune des 3
                                                   colonnes et chacune des 2 diagonales */
                                                int gagner(Partie * p)
                                                {
                                                  int (*t)[N] = p->t;
                                                
                                                #define LIG(i) (t[i][0]!=VIDE && t[i][0]==t[i][1] && t[i][0]==t[i][2])
                                                #define COL(j) (t[0][j]!=VIDE && t[0][j]==t[1][j] && t[0][j]==t[2][j])
                                                  return LIG(0) || LIG(1) || LIG(2) || COL(0) || COL(1) || COL(2)
                                                    || (t[0][0] != VIDE && t[0][0] == t[1][1] && t[0][0] == t[2][2])
                                                    || (t[0][2] != VIDE && t[0][2] == t[1][1] && t[0][2] == t[2][0]);
                                                }
                                                
                                                
                                                /* Fonction récursive qui compte le nombre de parties en fonction du score
                                                   (gain de toto, gain de titi, partie nulle). */
                                                void compter(Partie * p)
                                                {
                                                  /* t pointe vers la première ligne du plateau de la partie courante */
                                                  int (*t)[N] = p->t, j = p->j;
                                                
                                                  int lin, col;
                                                
                                                  /* On scanne le plateau ligne par colonne à la recherche d'une case vide */
                                                  /* Pour chaque case vide on procède de la même façon. */
                                                  for (lin = 0; lin < N; lin++)
                                                    for (col = 0; col < N; col++)
                                                
                                                      /* On a trouvé une case vide */
                                                      if (t[lin][col] == VIDE)
                                                        {
                                                          /* le joueur qui a le trait s'y place */
                                                          t[lin][col] = p->j;
                                                          /* Mise à jour du nb de cases occupées */
                                                          p->remplies++;
                                                          /* On regarde si le nouveau plateau est gagnant pour j */
                                                          if (gagner(p))
                                                            /* Si oui, on met à jour son total de gain */
                                                            (p->gains)[j]++;
                                                          /* si le coup de j n'est pas gagnant et il reste des cases libres
                                                             sur le plateau */
                                                          else if (p->remplies < N * N)
                                                            {                   /* La partie continue : */
                                                              /* On change de joueur */
                                                              p->j = 1 - p->j;
                                                              /* on calcule tous les scores de toutes les parties possibles
                                                                 à partir de la nouvelle position */
                                                              compter(p);
                                                              /* On se prépare à recommencer le décompte pour les autres
                                                                 cases où leur joueur initial pouvait jouer (donc on revient 
                                                                 au joueur initial) */
                                                              p->j = 1 - p->j;
                                                            }
                                                          /* Si le coup n'est pas gagnant et qu'il ne reste aucune case libre 
                                                           */
                                                          else
                                                            /* La partie est nulle */
                                                            (p->nulles)++;
                                                          /* on retire du plateau le jeton joué par j */
                                                          p->remplies--;
                                                          t[lin][col] = VIDE;
                                                        }
                                                }
                                                
                                                int main(void)
                                                {
                                                  Partie p = { 0 };
                                                  int i;
                                                
                                                  int *z = &(p.t[0][0]);
                                                
                                                  /* Le plateau au départ est vide */
                                                  for (i = 0; i < N * N; i++)
                                                    z[i] = VIDE;
                                                  /* On lance le décompte des parties. Le joueur 0 commence */
                                                  compter(&p);
                                                  /* On affiche le nombre total de parties jouables */
                                                  printf("%d\n", p.gains[0] + p.gains[1] + p.nulles);
                                                  return 0;
                                                }
                                                
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  19 juin 2010 à 18:27:29

                                                  Voilà le code commenté :

                                                  /* Calcul du nombre de parties possibles du morpion */
                                                  /* Le principe est de faire un backtraking sur seulement 3 positions.
                                                     On joue la partie en (a), celle en (b) et celle en (e).
                                                      (a) | (b) | (c)
                                                      (d) | (e) | (f)
                                                      (g) | (h) | (i)
                                                   Si on 'tourne' le plateau, jouer en (c) revient à jouer en (a),
                                                   jouer en (i) revient à jouer en (a) et jouer en (g) revient à jouer en (a).
                                                   Ce qui fait qu'au lieu de jouer à 4 positions, on joue à une seule
                                                   et on multiplie le résultat par 4 (qui correspond à (a), (c), (i) et (g)).
                                                   Même chose pour (b), (f), (h), (d).
                                                   Pour (e) il n'y a qu'une seule possibilité.*/
                                                   
                                                  #include <stdio.h>
                                                  #include <stdlib.h>
                                                  #include <string.h>
                                                  
                                                  #define N       3
                                                  #define MAX_P   9
                                                  
                                                  /* Structure contenant l'état de la partie :
                                                    - tab : tableau du jeu
                                                    - toto, titi, nul : Nombres de parties gagnées ou nulles
                                                    - nb_pions : Nombre de pions présents sur le plateau */
                                                  typedef struct _partie {
                                                    char tab[N][N];
                                                    int toto, titi, nul;
                                                    int nb_pions;
                                                  } partie;
                                                  
                                                  /* Constantes pour définir les cases du plateau */
                                                  enum JOUEURS {
                                                    RIEN, TOTO, TITI
                                                  };
                                                  
                                                  /* Fonction qui vérifie si quelqu'un a gagné et retourne celui qui
                                                    a gagné ou le cas échéant RIEN pour dire qu'une partie est nulle. */
                                                  int verifie(char (*tab)[N]) {
                                                    int i;
                                                    
                                                    for (i = 0; i < N; i++) {
                                                      /* Teste les lignes */
                                                      if (tab[i][0] != RIEN &&
                                                          tab[i][0] == tab[i][1] && tab[i][1] == tab[i][2])
                                                        return tab[i][0];
                                                      /* Teste les colonnes */
                                                      if (tab[0][i] != RIEN &&
                                                          tab[0][i] == tab[1][i] && tab[1][i] == tab[2][i])
                                                        return tab[0][i];
                                                    }
                                                    /* Diagonale */
                                                    if (tab[0][0] != RIEN &&
                                                        tab[0][0] == tab[1][1] && tab[1][1] == tab[2][2])
                                                      return tab[0][0];
                                                    /* Diagonale */
                                                    if (tab[2][0] != RIEN &&
                                                        tab[2][0] == tab[1][1] && tab[1][1] == tab[0][2])
                                                      return tab[2][0];
                                                    return RIEN;
                                                  }
                                                  
                                                  /* Joue un coup.
                                                   Retourne 0 si le coup ne peut pas être joué, 1 sinon. */
                                                  int jouerCoup(partie * p, int tour, int lig, int col) {
                                                    if (p->tab[lig][col] != RIEN)
                                                      return 0;
                                                    p->tab[lig][col] = tour;
                                                    p->nb_pions++;
                                                    return 1;
                                                  }
                                                  
                                                  /* 'Supprime' un coup. */
                                                  void delCoup(partie * p, int lig, int col) {
                                                    p->tab[lig][col] = RIEN;
                                                    p->nb_pions--;
                                                  }
                                                  
                                                  /* Evalue la partie pour savoir qui a gagné (ou pas).
                                                    - ret : retour de la fonction evalue qui dit qui a gagné.
                                                    - res : Retourne 1 si la partie est gagnée ou nulle, 0 sinon.
                                                   */
                                                  int evalue(partie * p, int ret) {
                                                    int res = 1;
                                                    
                                                    /* Si on a joué tous les pions et que personne n'a gagné */
                                                    if (p->nb_pions == MAX_P && ret == RIEN)
                                                      p->nul++;
                                                    else if (ret == TOTO)
                                                      p->toto++;
                                                    else if (ret == TITI)
                                                      p->titi++;
                                                    else
                                                      res = 0;
                                                    return res;
                                                  }
                                                  
                                                  /* Fonction récursive qui joue tous les coups possibles. */
                                                  void jouerParties(partie * p, int tour) {
                                                    int lig, col;
                                                    
                                                    /* Pour qu'il y ai une ligne il faut au moins 3 pions d'un joueur.
                                                       Il faut donc qu'au moins 2 pions de chaque joueur soit posé. */
                                                    if (p->nb_pions > 4) {
                                                      /* Si la partie est gagnée ou nulle on termine la fonction */
                                                      int tmp = verifie(p->tab);
                                                      if (evalue(p, tmp))
                                                        return;
                                                    }
                                                    
                                                    /* On parcours toutes les cases */
                                                    for (lig = 0; lig < N; lig++) {
                                                      for (col = 0; col < N; col++) {
                                                        /* Si le coup n'est pas jouable, on passe */
                                                        if (!jouerCoup(p, tour, lig, col))
                                                          continue;
                                                        /* Appel récursif que le prochain joueur joue */
                                                        jouerParties(p, (tour == TOTO ? TITI : TOTO));
                                                        /* On 'supprime' le coup joué précédement */
                                                        delCoup(p, lig, col);
                                                      }
                                                    }
                                                  }
                                                  
                                                  #define N_COUPS 3
                                                  
                                                  /* Fonction qui joue les 3 coups définis plus haut */
                                                  void jouer3Coups(partie * p) {
                                                    /* Lise des coups à jouer */
                                                    int coups[N_COUPS][2] = { { 0, 0 }, { 1, 0 }, { 1, 1 } };
                                                    /* Liste des facteurs multiplicateurs à appliquer aux résultats */
                                                    int mult[N_COUPS] = { 4, 4, 1 };
                                                    /* Variable pour la partie en cours */
                                                    partie p3;
                                                    int i;
                                                    
                                                    for (i = 0; i < N_COUPS; i++) {
                                                      memset(&p3, 0, sizeof p3);
                                                      /* On joue le coup */
                                                      jouerCoup(&p3, TOTO, coups[i][0], coups[i][1]);
                                                      /* Et on fait un backtraking sur le reste */
                                                      jouerParties(&p3, TITI);
                                                      /* On ajoute les valeurs trouvées aux résultats */
                                                      p->toto += (p3.toto * mult[i]);
                                                      p->titi += (p3.titi * mult[i]);
                                                      p->nul += (p3.nul * mult[i]);
                                                    }
                                                  }
                                                  
                                                  int main(void) {
                                                    int total;
                                                    partie p;
                                                    
                                                    memset(&p, 0, sizeof p);
                                                    jouer3Coups(&p);
                                                    total = p.toto + p.titi + p.nul;
                                                    
                                                    printf("Toto  : %d\n", p.toto);
                                                    printf("Titi  : %d\n", p.titi);
                                                    printf("Null  : %d\n", p.nul);
                                                    printf("Total : %d\n", total);
                                                    return EXIT_SUCCESS;
                                                  }
                                                  

                                                  Au niveau des perfs c'est pas trop mal non plus. :)

                                                  $ time ./candide 
                                                  255168
                                                  
                                                  real        0m0.058s
                                                  user        0m0.047s
                                                  sys        0m0.002s
                                                  pouet$ time ./gurney 
                                                  Toto    ->  131184
                                                  Titi    ->  77904
                                                  Nulles  ->  46080
                                                  Total   ->  255168
                                                  
                                                  real        0m0.084s
                                                  user        0m0.078s
                                                  sys        0m0.002s
                                                  pouet$ time ./pouet 
                                                  Toto  : 131184
                                                  Titi  : 77904
                                                  Null  : 46080
                                                  Total : 255168
                                                  
                                                  real        0m0.030s
                                                  user        0m0.027s
                                                  sys        0m0.002s
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    19 juin 2010 à 19:02:55

                                                    Citation : Pouet_forever


                                                    /* Calcul du nombre de parties possibles du morpion */
                                                    /* Le principe est de faire un brute force sur seulement 3 positions.
                                                       On joue la partie en (a), celle en (b) et celle en (e).
                                                        (a) | (b) | (c)
                                                        (d) | (e) | (f)
                                                        (g) | (h) | (i)
                                                     Si on 'tourne' le plateau, jouer en (c) revient à jouer en (a),
                                                     jouer en (i) revient à jouer en (a) et jouer en (g) revient à jouer en (a).
                                                     Ce qui fait qu'au lieu de jouer à 4 positions, on joue à une seule
                                                     et on multiplie le résultat par 4 (qui correspond à (a), (c), (i) et (g)).
                                                     Même chose pour (b), (f), (h), (d).
                                                     Pour (e) il n'y a qu'une seule possibilité.*/
                                                    




                                                    OK, je crois que ça correspond à ce que j'avais compris : à cause de la symétrie du problème, tu limites juste ta brute force (*) à l'initialisation de l'algorithme.

                                                    Tu as apporté une petite optimisation en n'évaluant le plateau que si le nb de pièces est > 4.

                                                    Jusqu'à présent, personne n'a écrit une méthode vraiment nouvelle.


                                                    Du point de vue du code,
                                                    -- les commentaires rendent le code vraiment plus lisible et l'algo plus compréhensible
                                                    -- est-ce volontaire de nommer ton enum (JOUEURS) alors que tu n'utilises pas ce nom ?
                                                    -- est-ce que ton instruction continue est vraiment nécessaire ? Moi j'aurais écrit :

                                                    if (jouerCoup(p, tour, lig, col))
                                                                {
                                                                  /* Appel récursif que le prochain joueur joue */
                                                                  jouerParties(p, (tour == TOTO ? TITI : TOTO));
                                                                  /* On 'supprime' le coup joué précédement */
                                                                  delCoup(p, lig, col);
                                                                }
                                                    


                                                    je pense que c'est logiquement équivalent et ça me semble plus simple (l'instruction continue est assez d'utilisation et donc beaucoup de codeurs de niveau intermédiaire ne sont pas à l'aise avec cette instruction).






                                                    (*) je préfère ici le terme de backtracking qui est plus précis tout en sous-entendant lui aussi "brute force" -- au passage c'est un anglicisme et que l'on pourrait traduire par "méthode bestiale".

                                                    Citation : Pouet_forever


                                                    au niveau des perfs c'est pas trop mal non plus. :)



                                                    c'est vrai mais je vais te dire, en écrivant mon code C, je n'avais absolument pas de préoccupation de rapidité en tête.


                                                    Tu pourrais gagner du temps (et moi aussi d'ailleurs) en faisant l'économie de vérifications inutiles comme j'ai fait dans mon code Python avec la méthode gagner(). Là tu testes tout le plateau alors qu'en fait tu peux te limiter au dernier coup joué.
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      19 juin 2010 à 19:17:16

                                                      Citation : candide

                                                      -- est-ce volontaire de nommer ton enum (JOUEURS) alors que tu n'utilises pas ce nom ?


                                                      C'est volontaire, parce que j'aime bien donner un nom à un enum. Après c'est vrai que c'est pas utile, mais moi j'aime bien. =)

                                                      Citation : candide

                                                      -- est-ce que ton instruction continue est vraiment nécessaire ?


                                                      Non, mais c'est parce que j'ai modifié le code en cours de route et c'est resté. ^^
                                                      Je n'aime pas avoir trop de retrait donc en général je préfère supprimer les cas qui sont les plus courts. :)
                                                      Mais bon, ton écriture est plus lisible je te l'accorde.

                                                      Je modifie le nom en backtracking. ^^
                                                      Pour les performances je ne m'en souciait pas non plus trop, mais c'est parce que tu as lancé le truc au début alors... :D
                                                      Pour un morpion je ne sais pas si c'est vraiment utile de ne tester que le dernier coup joué. Pour un reversi, puissance 4 ou autre je trouve ça plus utile. :)
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        19 juin 2010 à 21:21:26

                                                        Citation : Pouet_forever

                                                        Pour un reversi, puissance 4



                                                        Tiens : le nombre de parties possibles au Puissance 4 ? Le problème c'est qu'il y en a certainement beaucoup, sans doute trop pour une recherche exhaustive.


                                                        EDIT : Number of possible positions for n men on a standard 7 X 6 board of Connect-Four. La réponse ne semble donc pas encore connue.
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter

                                                        [algo, tous langages] Parties de morpion

                                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                        • Editeur
                                                        • Markdown