%%%
% Kakuro
%%%
\def\filedateKakuro{2025/05/27}%
\def\fileversionKakuro{0.1a}%
\message{-- \filedateKakuro\space v\fileversionKakuro}%
%
\setKVdefault[Kakuro]{CouleurCase=LightGray,TLargeur=5,THauteur=5,Solution=false,Largeur=2em,Aide=false,CouleurSolution=Black,Leger=false,ListeNombres={},Taille={},Graine=1,Creation=false,ValeurMax=9,ValeurMin=1,Template={},Vide=false,Produit=false}%

\newlength\PfCKakuro%

\NewDocumentCommand\Kakuro{o m}{%
  \useKVdefault[Kakuro]%
  \setKV[Kakuro]{#1}%
  \ifemptyKV[Kakuro]{Taille}{}{%
    \setKV[Kakuro]{THauteur=\useKV[Kakuro]{Taille}}%
    \setKV[Kakuro]{TLargeur=\useKV[Kakuro]{Taille}}%
  }%
  \setlength{\PfCKakuro}{\useKV[Kakuro]{Largeur}+\tabcolsep}%
  \ifboolKV[Kakuro]{Creation}{%
    \edef\PfCKKListeTemplateDeux{%
      -1%
    }%
    \edef\PfCKKListeTemplateTrois{%
      -1§%
      4§%
      0,8§%
      2,6%
    }%
    \edef\PfCKKListeTemplateQuatre{%
      0,4,11,15§%
      0,1,14,15§%
      0,1,4,11,14,15§%
      2,3,7,8,12,13§%
      2,3,12,13§%
      0,3,12,15%
    }%
    \edef\PfCKKListeTemplateCinq{%
      0,3,4,9,12,15,20,21,24§%
      0,3,4,5,12,19,20,21,24§%
      0,1,4,9,12,15,20,23,24§%
      0,1,4,5,12,19,20,23,24§%
      0,4,5,12,19,20,24§%
      3,4,9,12,15,20,21§%
      0,3,4,12,20,21,24§%
      2,10,14,22%
    }%
    \edef\PfCKKListeTemplateSix{%
      2,3,12,13,17,18,22,23,32,33§%
      0,1,2,5,6,12,15,20,23,29,30,33,34,35§%
      2,3,12,16,17,18,19,23,32,33§%
      0,1,4,5,11,14,15,20,21,24,30,31,34,35§%
      0,3,14,17,18,21,32,35§%
      0,1,4,5,6,11,14,15,20,21,24,29,30,31,34,35§%
      2,3,9,12,13,17,18,22,23,26,32,33§
      0,5,11,14,15,20,21,24,30,35§%
      2,3,8,12,16,17,18,19,23,27,32,33§%
      0,3,6,14,21,29,32,35§%
      0,1,14,17,18,21,34,35§%
      0,3,9,17,18,26,32,35§%
      0,4,5,11,14,15,20,21,24,30,31,35§
      2,3,12,13,16,17,18,19,22,23,32,33}%
    \setsepchar{§}\ignoreemptyitems%
    \IfStrEqCase{\useKV[Kakuro]{Taille}}{%
      {2}{\readlist*\PfCKakuroTemplateFacile{\PfCKKListeTemplateDeux}}%
      {3}{\readlist*\PfCKakuroTemplateFacile{\PfCKKListeTemplateTrois}}%
      {4}{\readlist*\PfCKakuroTemplateFacile{\PfCKKListeTemplateQuatre}}%
      {5}{\readlist*\PfCKakuroTemplateFacile{\PfCKKListeTemplateCinq}}%
      {6}{\readlist*\PfCKakuroTemplateFacile{\PfCKKListeTemplateSix}}%
    }%
    \reademptyitems%
    \ifnum\useKV[Kakuro]{Template}>\PfCKakuroTemplateFacilelen\relax%
      Le template choisi n'est pas disponible. Pour la taille choisie (\useKV[Kakuro]{Taille}), il y a \num{\PfCKakuroTemplateFacilelen} template\ifnum\useKV[Kakuro]{Taille}>2\relax s\fi{} disponible\ifnum\useKV[Kakuro]{Taille}>2\relax s\fi.%
    \else%
      \ifemptyKV[Kakuro]{Template}{%
        \ChoixAlea{1}{\PfCKakuroTemplateFacilelen}{\PfCKakuroChoixTemplate}%
      }{\edef\PfCKakuroChoixTemplate{\useKV[Kakuro]{Template}}}%
      \BuildKakuroCreation{\useKV[Kakuro]{Taille}+2}{\PfCKakuroTemplateFacile[\PfCKakuroChoixTemplate]}{\useKV[Kakuro]{ListeNombres}}%
    \fi%
  }{%
  \setsepchar[*]{,*/}\reademptyitems%
  \readlist*\ListeCasesKK{#2}%
  \ifemptyKV[Kakuro]{ListeNombres}{}{%
    \setsepchar{,}\reademptyitems%
    \xdef\ListeAvantNombres{\useKV[Kakuro]{ListeNombres}}%
    \readlist*\ListeKakuroNombres{\ListeAvantNombres}%
  }%
  \reademptyitems%
  \savecomparemode%
  \comparestrict%
  \begin{NiceTabular}{*{\useKV[Kakuro]{TLargeur}}{m{\useKV[Kakuro]{Largeur}}}}[hvlines]%
    \xintFor* ##1 in {\xintSeq{0}{\fpeval{\useKV[Kakuro]{THauteur}-1}}}\do{%
      \xintFor* ##2 in {\xintSeq{1}{\useKV[Kakuro]{TLargeur}}}\do{%
        \rule{0pt}{\PfCKakuro}%
        \StrCompare{\ListeCasesKK[\fpeval{\useKV[Kakuro]{TLargeur}*##1+##2},1]}{*}[\PfCTestBlack]%
        \xintifboolexpr{\PfCTestBlack==0}{%
          \Block[fill=black]{1-1}{}%
        }{%
          \xintifboolexpr{\listlen\ListeCasesKK[\fpeval{\useKV[Kakuro]{TLargeur}*##1+##2}]==2}{%
            \Block[fill=\useKV[Kakuro]{CouleurCase}]{1-1}{\diagbox{\ListeCasesKK[\fpeval{\useKV[Kakuro]{TLargeur}*##1+##2},1]}{\ListeCasesKK[\fpeval{\useKV[Kakuro]{TLargeur}*##1+##2},2]}}%
          }{%
            \Block{1-1}{\ifboolKV[Kakuro]{Solution}{\Large\color{\useKV[Kakuro]{CouleurSolution}}\num{\ListeCasesKK[\fpeval{\useKV[Kakuro]{TLargeur}*##1+##2},1]}}{%
                \ifemptyKV[Kakuro]{ListeNombres}{}{%
                  \xintFor* ##3 in{\xintSeq{1}{\ListeKakuroNombreslen}}\do{%
                    \Block{1-1}{\xintifboolexpr{\ListeCasesKK[\fpeval{\useKV[Kakuro]{TLargeur}*##1+##2},1]==\ListeKakuroNombres[##3]}{\Large\color{\useKV[Kakuro]{CouleurSolution}}\num{\ListeCasesKK[\fpeval{\useKV[Kakuro]{TLargeur}*##1+##2},1]}}{}}%
                  }%
                }%
              }%
            }%
          }%
        }%
        \xintifForLast{\\}{&}%
      }%
    }%
  \end{NiceTabular}
  \restorecomparemode%
  }%
}%

\NewDocumentCommand\BuildKakuroCreation{mmm}{%
  % #1 taille (forcément carré)
  % #2 template  
  \mplibforcehmode%
  \mplibnumbersystem{double}%
  \begin{mplibcode}
    randomseed:=\useKV[Kakuro]{Graine};
    ValeurMax=\useKV[Kakuro]{ValeurMax};
    ValeurMin=\useKV[Kakuro]{ValeurMin};
    Taille:=\useKV[Kakuro]{Taille};
    %
    u:=\mpdim{\useKV[Kakuro]{Largeur}};
    % 
    % "Permuter"
    % 
    vardef Permute(expr nbd,nba)(expr nbe)(text t)=
      % nbd : nombre départ
      % nba : nombre arrivée
      % nbe : nombre d'éléments à garder
      % t: liste des éléments déjà utilisés.
      nbelimine:=0;
      for p_=t:
        if (p_>nbd-1) and (p_<nba+1):
          retour:=0;
          for k=1 upto nbelimine:
            if Me[k]=p_:
              retour:=retour+1;
            fi;
          endfor;
          if retour=0:  
            nbelimine:=nbelimine+1;
            Me[nbelimine]:=p_;
          fi;
        fi;
      endfor;
      % Liste avec les valeurs éventuellement éliminées   
      numeric Md[];
      for k=1 upto (nba-nbd+1):
        Md[k]=nbd-1+k;
      endfor;
      if nbelimine=0:
        Total:=nba-nbd+1;
      else:
        tt:=1;
        k:=0;
        forever: exitif tt=(nba-nbd+2);
          retour:=0;
          for l=1 upto nbelimine:
            if Me[l]=Md[tt]:
              retour:=retour+1;
            fi;
          endfor;
          if retour=0:
            k:=k+1;
            Total:=k;
            Md[k]:=Md[tt];
          fi;
          tt:=tt+1;  
        endfor;
      fi;
      % On choisit les valeurs.
      k:=0;
      forever: exitif k=nbe;
        alpha:=ceiling(uniformdeviate(Total-k));
        N:=Md[alpha];
        Md[alpha]:=Md[nba-nbd-k+1-nbelimine];
        Md[nba-nbd-k+1-nbelimine]:=N;
        k:=k+1;
        Mr[k]:=N;
      endfor;
    enddef;
    % Affichage Kakuro
    boolean Solution,Allume[][],Leger,Vide,Produit;
    color CoulSol;
    Solution=\useKV[Kakuro]{Solution};
    if Solution:
      CoulSol=\useKV[Kakuro]{CouleurSolution};
    fi;
    Leger=\useKV[Kakuro]{Leger};
    Vide=\useKV[Kakuro]{Vide};
    Produit=\useKV[Kakuro]{Produit};
    pair A[][];
    path Case;
    Case=unitsquare scaled u;
    largeura:=#1-1;
    % Tracé du terrain de jeu
    for k=0 upto largeura:
      for l=0 upto largeura:
        A[k][l]=u*(l,-k);
        if (k=0) or (l=0) or (k=largeura) or (l=largeura):
          if (k=largeura) or (l=largeura):
            if Leger=false:
              fill Case shifted (A[k][l]-center Case);
              trace Case shifted (A[k][l]-center Case);
            fi;
          else:
            fill Case shifted (A[k][l]-center Case) if Leger: withcolor Orange fi;
            if Leger=false:trace Case shifted (A[k][l]-center Case); fi;
          fi;
          Allume[k][l]=false;
        else:
          Allume[k][l]=true;
          trace Case shifted (A[k][l]-center Case);
        fi;
      endfor;
    endfor;
    for p_=#2:
      fill Case shifted (A[1+p_ div Taille][1+p_ mod Taille]-center Case) if Leger:withcolor 0.7white fi;% withcolor Orange
      Allume[1+p_ div Taille][1+p_ mod Taille]:=false;
    endfor;
    if Vide=false:  
%    % Choix des nombres
    for k=1 upto largeura-1:
      for l=1 upto largeura-1:
        if Allume[k][l]:
          % Déterminer le nombre de cases supérieures à ne pas utiliser
          nbs:=1;
          forever: exitif Allume[k-nbs][l]=false;
            nbs:=nbs+1;
          endfor;
          % Détermine le nombre de cases antérieures à ne pas utiliser
          nbi:=1;
          forever: exitif Allume[k][l-nbi]=false;
            nbi:=nbi+1;
          endfor;
          % Détermination des nombres
          if (nbs+nbi-2)=0:%Si aucune valeurs n'est à "déduire"
            Permute(ValeurMin,ValeurMax)(1)(0);
            Num[k][l]=Mr[1];
          else:
            nbelim:=0;
            if nbs-1>0:
              for s=1 upto nbs-1:
                nbelim:=nbelim+1;
                Mk[nbelim]:=Num[k-s][l];
              endfor;
            fi;
            if nbi-1>0:
              for s=1 upto nbi-1:
                nbelim:=nbelim+1;
                Mk[nbelim]:=Num[k][l-s];
              endfor;
            fi;
            Permute(ValeurMin,ValeurMax)(1)(0 for s=1 upto nbelim:,Mk[s] endfor);
            Num[k][l]=Mr[1];
          fi;
        fi;
      endfor;
      endfor;
    fi;
    % Affichage des indices
    for k=0 upto largeura-1:
      for l=0 upto largeura-1:
        if Leger:
          if Allume[k][l]=false:
            fill (Case shifted (A[k][l]-center Case)) withcolor white;
            trace (Case shifted (A[k][l]-center Case)) withcolor white;
            if (Allume[k+1][l]):
              fill (polygone(point(0) of Case,point(3) of Case,point(1) of Case) shifted (A[k][l]-center Case)) withcolor 0.7white;
              trace polygone(point(0) of Case,point(3) of Case,point(1) of Case) shifted (A[k][l]-center Case);
              if Vide=false:
              nbcases:=1;
              if Produit:
                Somme:=1;
                forever: exitif Allume[k+nbcases][l]=false;
                  Somme:=Somme*Num[k+nbcases][l];
                  nbcases:=nbcases+1;
                endfor;
              else:
                Somme:=0;
                forever: exitif Allume[k+nbcases][l]=false;
                  Somme:=Somme+Num[k+nbcases][l];
                  nbcases:=nbcases+1;
                endfor;
              fi;
              label.urt(TEX("\bfseries\num{"&decimal(Somme)&"}"),(point(0) of Case) shifted(A[k][l]-center Case));
              fi;
            fi;
            if (Allume[k][l+1]):
            fill (polygone(point(2) of Case,point(3) of Case,point(1) of Case) shifted (A[k][l]-center Case)) withcolor 0.7white;
            trace polygone(point(2) of Case,point(3) of Case,point(1) of Case) shifted (A[k][l]-center Case);
            if Vide=false:
              nbcases:=1;
              if Produit:
                Somme:=1;
                forever: exitif Allume[k][l+nbcases]=false;
                  Somme:=Somme*Num[k][l+nbcases];
                  nbcases:=nbcases+1;
                endfor;
              else:
                Somme:=0;
                forever: exitif Allume[k][l+nbcases]=false;
                  Somme:=Somme+Num[k][l+nbcases];
                  nbcases:=nbcases+1;
                endfor;
              fi;
              label.llft(TEX("\bfseries\num{"&decimal(Somme)&"}"),(point(2) of Case) shifted(A[k][l]-center Case));
              fi;
            fi;
            fi;
        else:
          if Allume[k][l]=false:
            if (Allume[k+1][l]) or (Allume[k][l+1]):
              trace ((point(1) of Case)--(point(3) of Case)) shifted (A[k][l]-center Case) withpen pencircle scaled 1.1 withcolor white;
            fi;
            if Vide=false:  
            if (Allume[k+1][l]):
              nbcases:=1;
              if Produit:
                Somme:=1;
                forever: exitif Allume[k+nbcases][l]=false;
                  Somme:=Somme*Num[k+nbcases][l];
                  nbcases:=nbcases+1;
                endfor;
              else:
                Somme:=0;
                forever: exitif Allume[k+nbcases][l]=false;
                  Somme:=Somme+Num[k+nbcases][l];
                  nbcases:=nbcases+1;
                endfor;
              fi;
              drawoptions( withcolor white);
              label.urt(TEX("\bfseries\num{"&decimal(Somme)&"}"),(point(0) of Case) shifted(A[k][l]-center Case));
              drawoptions();
            fi;
            if (Allume[k][l+1]):
              nbcases:=1;
              if Produit:
                Somme:=1;
                forever: exitif Allume[k][l+nbcases]=false;
                  Somme:=Somme*Num[k][l+nbcases];
                  nbcases:=nbcases+1;
                endfor;
              else:
                Somme:=0;
                forever: exitif Allume[k][l+nbcases]=false;
                  Somme:=Somme+Num[k][l+nbcases];
                  nbcases:=nbcases+1;
                endfor;
              fi;
              drawoptions( withcolor white);
              label.llft(TEX("\bfseries\num{"&decimal(Somme)&"}"),(point(2) of Case) shifted(A[k][l]-center Case));
              drawoptions();
              fi;
              fi;
          fi;
        fi;
      endfor;
    endfor;
    % Terrain à retracer si Leger
    if Leger:
      for k=0 upto largeura:
        for l=0 upto largeura:
          if Allume[k][l]:
            trace Case shifted (A[k][l]-center Case);
          fi;
        endfor;
      endfor;
    fi;
    % Indices :
    \ifemptyKV[Kakuro]{ListeNombres}{}{%
      for k=0 upto largeura:
        for l=0 upto largeura:
          if Allume[k][l]:
            retour:=0;
            for p_=#3:
              if p_=Num[k][l]:
                label(TEX(decimal(Num[k][l])),A[k][l]);
              fi;
            endfor;  
          fi;
        endfor;
      endfor;
    }%
    % Solution ?
    if Solution:
      drawoptions(withcolor CoulSol);
      for k=1 upto largeura-1:
        for l=1 upto largeura-1:
          if Allume[k][l]:
            label(TEX("\num{"&decimal(Num[k][l])&"}"),A[k][l]);
          fi;
        endfor;
      endfor;
    fi;
  \end{mplibcode}%
  \mplibnumbersystem{scaled}%
}%