program jeuxPuissance4(input, output);
uses crt, graph;
const L = 6;
      C = 7;
      N = 4;
      police =0 ;
      dir = 0;
      size = 1 ;
      fond = 7 ;
      trait = 15;
      sizepion = 4;

type couleur= (blanc, rouge,nulle);
     grille = array[-1..L+1,-1..C+1]of couleur;
     tabChoix = record {pour la machine}
                 nbChoix:integer;
                 elems:array[1..C] of integer;
                 end;

var gd, gm:integer;
    ms,xmax,ymax, pasX,pasY:integer;
    fin : boolean;
    touche:char;
    lg,col:integer;
    finPartie,abondant:boolean;
    g:grille;
    joueur,gagnant:couleur;
    nbCoupsBlanc,nbCoupsRouge:integer;
    choix:integer;
    ch:string;


     procedure ajouterChoix(var p: tabChoix; cl:integer);
     begin
        p.nbChoix:=p.nbChoix+1;
        p.elems[p.nbChoix]:=cl;
     end;


     procedure poserPion(lg,col, x,y:integer;joueur:couleur);
     begin

          case joueur of
          blanc: begin
                      setcolor(15);
                      circle(col*X+X div 2,(lg-1)*Y+Y div 2, X div sizepion);
                      setFillStyle(1,15);
                      FloodFill(col*X+X div 2,(lg-1)*Y+Y div 2, 15);
                 end;
          rouge: begin
                      setcolor(4);
                      circle(col*X+X div 2,(lg-1)*Y+Y div 2, X div sizepion);
                      setFillStyle(1,4);
                      FloodFill(col*X+X div 2,(lg-1)*Y+Y div 2, 4);
                      setcolor(15);
                 end;
          end;
     end;

     procedure effacerPion(lg, col,x,y:integer;joueur:couleur);
     begin

          case joueur of
          blanc: begin
                      setcolor(1);
                      circle(col*X+X div 2,(lg-1)*Y+Y div 2, X div sizepion);
                      setFillStyle(1,fond);
                      FloodFill(col*X+X div 2,(lg-1)*Y+Y div 2, 1);
                      setcolor(fond);
                      circle(col*X+X div 2,(lg-1)*Y+Y div 2, X div sizepion);
                      setcolor(15);
                 end;
          rouge: begin
                      setcolor(1);
                      circle(col*X+X div 2,(lg-1)*Y+Y div 2, X div sizepion);
                      setFillStyle(1,fond);
                      FloodFill(col*X+X div 2,(lg-1)*Y+Y div 2, 1);
                      setcolor(fond);
                      circle(col*X+X div 2,(lg-1)*Y+Y div 2, X div sizepion);
                      setcolor(15);
                 end;
          end;
     end;



      function estfile(g:grille;i,j:integer):boolean;

       begin
       estfile := ( (g[i,j-2]= joueur) and (g[i,j-1]=joueur) and (g[i,j+1]=joueur) {and (g[i,j+2]=joueur)}) or
                  ( (g[i-2,j]= joueur) and (g[i-1,j]=joueur) and (g[i+1,j]=joueur) {and (g[i+2,j]=joueur)}) or
                  ( (g[i-2,j-2]=joueur) and (g[i-1,j-1] =joueur) and (g[i+1,j+1]=joueur) {and (g[i+2,j+2]=joueur)}) or
                  ( (g[i+2, j-2]=joueur) and (g[i+1,j-1] =joueur) and (g[i-1,j+1]=joueur) {and (g[i-2,j+2]=joueur)} );
       end;

     procedure jouer(var g:grille;lg,col,x,y:integer;joueur:couleur;choix:integer);
     var i:integer;
         c:integer;
     begin
          g[lg,col]:=joueur;
          case joueur of
          blanc: c:=15;
          rouge: c:=4;
          end;
          case choix of
          1: begin
                  effacerPion(1,col,x,y,joueur);
                  for i:=2 to lg
                  do begin
                     poserPion(i,col,x,y,joueur);
                     delay(100);
                     effacerPion(i,col,x,y,joueur);
                     end;
                  poserPion(lg+1,col,x,y,joueur);
             end;
          2:poserPion(lg+1,col,x,y,joueur);
          end;

     end;

     procedure numeroterGrille(X,Y:integer);
     var i:integer;
         num:string[2];
     begin
          setColor(8);
          for i:= 1 to L do
          begin
               str(L-i+1,num);
               OutTextXY(X div 2,Y div 2+ Y*(i),num);
               end;

          for i:= 1 to C do
          begin
               str(i,num);
               OutTextXY(X +X div 2 + X*(i-1),Y div 2+ Y*(L+1),num);
          end;

     end;

     procedure tracerGrille(pasX,pasY:integer);
     var x0,y0, x1, y1,i:integer;
     begin

          setBkColor(fond);
          setcolor(trait);
          x0:=pasX;y0:=pasY;
          x1:=pasX*(C+1); y1:=pasY;

          for i:=0 to L do
            line(x0,y0+pasY*i,x1,y1+pasY*i);
          x0:=pasX;y0:=pasY;
          x1:=pasX; y1:=pasY*(L+1);
          for i:=0 to C do
            line(x0+pasX*i,y0,x1+pasX*i,y1);
          numeroterGrille(pasX,pasY);
          setcolor(1);
          OutTextXY( 500,460 , 'ESC: pour quitter');
     end;

     function max(a,b:integer):integer;
     begin
          if (a>b) then max:=a else max:=b;
     end;

     procedure tailleFile(g:grille;cl:integer;var lgfb,lgfr:integer);
     var fh,fh1,fh2,i,j:integer;
         trouve:boolean;
     begin
          lg:=0;trouve:=false;
           repeat
                 lg:=lg+1;
                 trouve:=g[lg,cl]<>nulle
           until trouve or (lg=L);
           if (trouve)
           then lg:=lg-1;

           lgfr:=0;lgfb:=0;
           {file horizontale}
           fh1 := 0;i:=cl-1;
           if (g[lg,i]<>nulle)
           then begin
                     fh1:=fh1+1;
                     while ((i>=1) and (g[lg,i] = g[lg,i-1]))
                     do begin
                             i:=i-1;
                             fh1:=fh1+1;
                        end;
                 end;

           fh2:=0; i:=cl+1;
           if (g[lg,i]<>nulle)
           then begin
                     fh2:=fh2+1;
                     while (i<=C) and (g[lg,i] = g[lg,i+1])
                     do begin
                             i:=i+1;
                             fh2:=fh2+1;
                        end;
                 end;

           if (g[lg,cl-1]<>nulle)
           then begin
                     case (g[lg,cl-1]) of
                     blanc: lgfb:=fh1;
                     rouge: lgfr:=fh1;
                     end;
                end ;
           if (g[lg,cl+1]<>nulle)
           then begin
                  if (g[lg,cl-1]=g[lg,cl+1])
                  then begin
                            case (g[lg,cl+1]) of
                            blanc: lgfb:=lgfb+fh2;
                            rouge: lgfr:=lgfr+fh2;
                            end;
                       end
                  else begin
                            case (g[lg,cl+1]) of
                            blanc:  lgfb:=max(lgfb,fh2);
                            rouge:  lgfr:=max(lgfr,fh2);
                            end;
                            {case (g[lg,cl-1]) of
                            blanc:  lgfb:=max(lgfb,fh1);
                            rouge:  lgfr:=max(lgfr,fh1);
                            end;}
                       end;
                end;

           {file verticale}
           fh:=0; i:=lg+1;
           if (g[i,cl]<>nulle)
           then begin
                     fh:=fh+1;
                     while ((i<=L) and (g[i,cl] = g[i+1,cl]))
                     do begin
                             i:=i+1;
                             fh:=fh+1;
                        end;
                     case (g[lg+1,cl]) of
                     blanc: lgfb:=max(fh,lgfb);
                     rouge: lgfr:=max(fh,lgfr);
                     end;
                end;
           {diagonale1}
           fh1 := 0;i:=lg-1;j:=cl-1;
           if (g[i,j]<>nulle)
           then begin
                     fh1:=fh1+1;
                     while ((i>=1) and (j>=1) and (g[i,j] = g[i-1,j-1]))
                     do begin
                             i:=i-1;j:=j-1;
                             fh1:=fh1+1;
                        end;
                 end;

           fh2:=0; i:=lg+1; j:=cl+1;
           if (g[i,j]<>nulle)
           then begin
                     fh2:=fh2+1;
                     while ((i<=L) and (j<=C) and (g[i,j] = g[i+1,j+1]))
                     do begin
                             i:=i+1; j:=j+1;
                             fh2:=fh2+1;
                        end;
                 end;

           if (g[lg-1,cl-1]=g[lg+1,cl+1])
           then begin
                     fh:=fh1+fh2;
                     case (g[lg+1,cl+1]) of
                     blanc: lgfb:=max(fh,lgfb);
                     rouge: lgfr:=max(fh,lgfr);
                     end;
                end
           else begin
                     case (g[lg+1,cl+1]) of
                     blanc: lgfb:=max(fh2,lgfb);
                     rouge: lgfr:=max(fh2,lgfr);
                     end;
                     case (g[lg-1,cl-1]) of
                     blanc: lgfb:=max(fh1,lgfb);
                     rouge: lgfr:=max(fh1,lgfr);
                     end;
                end;

           {diagonale2}
           fh1 := 0;i:=lg+1;j:=cl-1;
           if (g[i,j]<>nulle)
           then begin
                     fh1:=fh1+1;
                     while ((i<=L) and (j>=1) and (g[i,j] = g[i+1,j-1]))
                     do begin
                             i:=i+1;j:=j-1;
                             fh1:=fh1+1;
                        end;
                 end;

           fh2:=0; i:=lg-1; j:=cl+1;
           if (g[i,j]<>nulle)
           then begin
                     fh2:=fh2+1;
                     while ((i>=1) and (j<=C) and (g[i,j] = g[i-1,j+1]))
                     do begin
                             i:=i-1; j:=j+1;
                             fh2:=fh2+1;
                        end;
                 end;
           if (g[lg-1,cl+1]=g[lg+1,cl-1])
           then begin
                     fh:=fh1+fh2;
                     case (g[lg-1,cl+1]) of
                     blanc: lgfb:=max(fh,lgfb);
                     rouge: lgfr:=max(fh,lgfr);
                     end;
                end
           else begin
                     case (g[lg-1,cl+1]) of
                     blanc: lgfb:=max(fh2,lgfb);
                     rouge: lgfr:=max(fh2,lgfr);
                     end;
                     case (g[lg+1,cl-1]) of
                     blanc: lgfb:=max(fh1,lgfb);
                     rouge: lgfr:=max(fh1,lgfr);
                     end;
                end;

     end;

     procedure choisirCaseMachine(var col:integer;joueur:couleur; pasX,pasY:integer;g:grille;var abondant:boolean);
     var cl, colBMax,colRMax, lgfb,lgfr ,lgfBMax,lgfRMax: integer;
         pcb,pcr:tabChoix;
     begin
          pcb.nbChoix:=0;pcr.nbChoix:=0;
          lgfb:=0;lgfr:=0;lgfBMax:=0; lgfRMax:=0; colBMax:=1; colRMax:=1;
          cl:=1;
          while(cl<=C) do
          begin
             tailleFile(g,cl,lgfb,lgfr);
             if (lgfb > lgfBMax)
             then begin
                    pcb.nbChoix:=0;
                    ajouterChoix(pcb,cl);
                    lgfBMax:=lgfb;
                  end
             else if (lgfb=lgfBMax)
                  then ajouterChoix(pcb,cl);

             if (lgfr > lgfRMax)
             then begin
                    pcr.nbChoix:=0;
                    ajouterChoix(pcr,cl);
                    lgfRMax:=lgfr;
                  end
             else if (lgfr=lgfRMax)
                  then ajouterChoix(pcr,cl);
             cl:=cl+1;
          end;
          colRMax:=pcr.elems[random(pcr.nbChoix)+1];
          colBMax:=pcb.elems[random(pcb.nbChoix)+1];
          if (lgfRMax=N-1)
          then col := colRMax
          else if (lgfBMax= N-1)
               then col := colBMax
               else if (lgfRMAX = N-2)
                    then col:=colRMax
                    else if (lgfBMax = N-2)
                         then col :=colBMax
                         else if (lgfRMax = N-3)
                              then col:=colRMax
                              else if (lgfBMAX= N-3)
                                   then col:=colBMax
                                   else col := random(C)+1;
     end;

     procedure choisirCaseHumain(var col:integer;joueur:couleur; pasX,pasY:integer;g:grille;var abondant:boolean);
     var fin:boolean;
         touche:char;
     begin
          fin := false;
          col:= C div 2;
          poserPion(1, col,pasX,pasY,joueur);
          repeat
                touche:= readkey;
                case ord(touche) of
                75: begin {gauche}
                          if(col<>1) then
                          begin
                               effacerPion(1,col,pasX,pasY,joueur);
                               col:=col-1;
                               poserPion(1, col,pasX,pasY,joueur);
                          end;
                    end;
                77: begin {droite}
                          if(col<>C) then
                          begin
                               effacerPion(1,col,pasX,pasY,joueur);
                               col:=col+1;
                               poserPion(1,col,pasX,pasY,joueur);
                          end;
                    end;

                32: begin {valider}
                          fin:=true;
                          effacerPion(1,col,pasX,pasY,joueur);
                    end;
                27: begin
                         abondant:=true;
                         fin:=true;
                    end;
                end;

          until fin;
     end;


     procedure initGrille(var g:grille);
      var i, j: integer;
     begin
          for i:=-1 to L+1 do
              for j:=-1 to C+1 do
                  g[i,j]:= nulle;
     end;

     function rechercherLigne(g:grille;col:integer):integer;
     var lg:integer;
          trouve:boolean;
     begin
      lg:=0;trouve:=false;
      repeat
           lg:=lg+1;
           trouve:=g[lg,col]<>nulle
      until trouve or (lg=L);
      if (trouve)
      then rechercherLigne:=lg-1
      else rechercherLigne:=L;
     end;

     function coupvalideBlanc(lg,col:integer;g:grille;nbCoupsBlanc:integer): boolean;
     begin
      if (lg=0)
      then coupValideBlanc:=false
      else if(lg=L)
           then coupValideBlanc := (g[lg,col]=nulle)
           else if (nbCoupsBlanc>0)
                then coupValideBlanc:= ((g[lg,col]=nulle) and (g[lg+1,col]<>nulle))
                else coupValideBlanc:= (lg=L);
     end;

     function coupValideRouge(lg,col:integer;g:grille;nbCoupsrouge:integer): boolean;
     begin
      if(lg=L)
      then coupValideRouge := (g[lg,col]=nulle)
      else if (nbCoupsBlanc>0)
           then coupValideRouge:= ( (g[lg,col]=nulle) and (g[lg+1,col]<>nulle))
           else coupValideRouge:= (lg=L);
     end;



     function testerFinPartie(g:grille;nbCb,nbCr:integer;joueur:couleur):boolean;
     var i,j:integer;
         fin:boolean;
     begin
          fin:=false;{(nbCb+nbCr= L*C);}
          if (not fin)
          then begin
                    i:=1;
                    while (not fin and (i <= L)) do
                    begin
                          j:=1;
                          while (not fin and (j<=C)) do
                          begin
                               if(g[i,j]=joueur)
                               then fin := estfile(g,i,j);

                               j:=j+1;
                          end;
                          i:=i+1;
                    end;

               end;
          testerFinPartie:=fin;
     end;

     procedure afficherResultat(gagnant:couleur);
     var c:integer;
     begin
          case joueur of
          blanc: c:=15;
          rouge: c:=4;
          nulle: c:=1;
          end;
          SetTextStyle(police, dir, size);
          setcolor(c);
          case gagnant of
          blanc : OutTextXY(310,460 , 'Blanc  gagne !');
          rouge : OutTextXY(310, 460, 'Rouge  gagne !');
          nulle:  OutTextXY(310,460 , 'Match nul : Quelle partie!');
          end;
     end;


    procedure afficherTexte(joueur:couleur; ch:string);
    VAR c:integer;
    begin
     case joueur of
     blanc: c:=15;
     rouge: c:=4;
     end;
     SetTextStyle(police, dir, size);
     setcolor(c);
     case joueur of
     blanc : OutTextXY( 310,460 , 'Blanc '+ch);
     rouge : OutTextXY(310,460, 'Rouge '+ch);
     end;
    setcolor(15);
    end;

    procedure effacerTexte(joueur:couleur; ch:string);
    begin
     SetTextStyle(police, dir, size);
     setcolor(fond);
     case joueur of
     blanc : OutTextXY(310,460 , 'Blanc '+ch);
     rouge : OutTextXY(310,460, 'Rouge '+ch);
     end;
    setcolor(15);
    end;

     function quiCommence:couleur;
     var choix:integer;
     begin
          randomize;
          choix:=random(2);
          case choix of
          0: quiCommence:=blanc;
          1: quiCommence:=rouge;
          end;
     end;

     function menu:integer;
     var ch:integer;
     begin
       writeln('Pour jouer avec un(e) amie, tapez <1> ?');
       writeln('Pour jouer avec la machine (pion rouge), tapez <2> ?');
       writeln('Pour quiter, tapez <3> ? ');
       write('votre choix ? '); readln(ch);
       menu := ch;
     end;

     procedure jeux(choix:integer);
     begin
     InitGraph(gd,gm,'../BGI');
     xmax:=getmaxx;
     ymax:=getmaxy;
     pasX:=xmax div (C+1);
     pasY:=ymax div (L+2);
     tracerGrille(pasX,pasY);
     initGrille(g);

     finPartie:=false;
     abondant:=false;
     gagnant:=nulle;
     nbCoupsBlanc:=0;
     nbCoupsRouge:=0;
     joueur:=quiCommence;
     afficherTexte(joueur,'commence');
     delay(1000);
     effacerTexte(joueur,'commence');

     repeat

           if(joueur= blanc)
           then begin
                     afficherTexte(joueur,'joue');
                     choisirCaseHumain(col,joueur,pasX,pasY,g,abondant);
                     lg:=rechercherLigne(g,col);
                     if(not abondant and (lg<>0))
                     then begin
                               nbCoupsBlanc:=nbCoupsBlanc+1;
                               jouer(g,lg,col,pasX,pasY,joueur,choix);
                               finPartie:=testerFinPartie(g,nbCoupsBlanc,nbCoupsRouge,joueur);
                               if(finPartie)
                               then  gagnant :=blanc
                               else  begin
                                          effacerTexte(joueur,'joue');
                                          joueur:=rouge;
                                     end;
                          end;
                 end
           else  begin
                     afficherTexte(joueur,'joue');
                     case choix of
                     1: choisirCaseHumain(col,joueur,pasX,pasY,g,abondant);
                     2: choisirCaseMachine(col,joueur,pasX,pasY,g,abondant);
                     end;
                     setcolor(1);
                     str(col, ch);
                     outtextxy(10, 10,'==');
                     lg:=rechercherLigne(g,col);
                     if(not abondant and (lg<>0))
                     then begin
                               nbCoupsRouge:=nbCoupsRouge+1;
                               jouer(g,lg,col,pasX,pasY,joueur,choix);

                               finPartie:=testerFinPartie(g,nbCoupsBlanc,nbCoupsRouge,joueur);
                               if(finPartie)
                               then  gagnant :=rouge
                               else  begin
                                          effacerTexte(joueur,'joue');
                                          joueur:=blanc;
                                     end;
                          end;
                 end;
     if (nbCoupsBlanc+nbCoupsRouge= L*C)
     then finPartie:=true;
     until finPartie or abondant;
     if(not(abondant))
     then begin
          effacerTexte(joueur,'joue');
          afficherResultat(gagnant) ;
          readln;
     end;
     CloseGraph;

     end;


begin
     clrscr;
     repeat
     choix:= menu;
     case choix of
     1,2 : jeux(choix);
     3 : fin :=true;
     end;
until fin;

end.