Circuits Domino

36

Tableau de bord

Voici les scores bruts (c.-à-d. Le nombre de dominos) pour la soumission de VisualMelon. Je transformerai ces résultats en les scores normalisés décrits ci-dessous, lorsque davantage de réponses entreront. La solution existante peut maintenant résoudre tous les circuits de la référence:

 Author       Circuit:   1   2   3   4    5    6   7    8   9  10  11  12   13  14   15   16   17   18  19   20   21  22   23   24    25   26   27   28    29    30    31    32   33   34    35    36     37      38   39
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
VisualMelon             39  45  75  61  307  337  56  106  76  62  64  62  182  64  141  277  115  141  92  164  223  78  148  371  1482  232  107  782  4789  5035  1314  3213  200  172  1303  3732  97596  156889  857
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Legend:
  I - invalid circuit
  B - circuit too big
  W - circuit computes wrong function
  T - exceeded time limit

Le défi

Il est possible de construire des portes logiques simples à partir de dominos. Par conséquent, en combinant ces éléments ou autrement, des fonctions binaires arbitraires peuvent être calculées avec des dominos.

Mais bien sûr, tous ceux qui ont joué avec des dominos (à l'exception de Robin Paul Weijers) ont connu la déception lorsqu'ils en ont manqué. Par conséquent, nous voulons utiliser nos dominos aussi efficacement que possible afin de pouvoir effectuer des calculs vraiment intéressants avec le matériel dont nous disposons.

Notez que vous ne pouvez pas produire de sortie non nulle à partir d'une entrée zéro en tant que telle. Nous aurons donc besoin d'ajouter une "ligne d'alimentation" qui correspond à votre configuration et sur laquelle vous pouvez extraire 1à tout moment.

Ta tâche

Étant donné une fonction booléenne avec des Mentrées et des Nsorties ( f: {0,1}^M --> {0,1}^Npour les inclinés mathématiquement), créez un circuit domino avec le moins de dominos possible pour calculer cette fonction. Vous allez utiliser les symboles |, -, /, \pour représenter dominos dans diverses orientations.

Contribution

Vous aurez une entrée via des arguments en ligne de commande:

[command for your solver] M N f

Met Nsont des entiers positifs et fest la table de vérité séparée par des virgules dans l'ordre canonique. C'est-à-dire, fcontiendra des 2^Mvaleurs de longueur N. Par exemple, si M = N = 2et le premier bit de la sortie était la fonction AND alors que le deuxième bit était la fonction OR, flirait

00,01,01,11

Sortie

Écrivez à STDOUT une grille ASCII représentant la configuration du domino. Votre configuration doit s’inscrire dans le cadre suivant

/////.../////
 ????...????
I????...????O
I????...????O
.............
.............
I????...????O
I????...????O
I????...????O
  • La rangée supérieure est entièrement constituée de /, et le domino le plus à gauche est sûr d’être renversé au début - c’est votre ligne électrique.
  • La colonne la plus à gauche est constituée de vos entrées. Chacun Ipeut être un espace ou un |, de sorte qu'il existe exactement M |s.
  • La colonne la plus à droite est constituée de vos sorties. Chacun Opeut être un espace ou un |, de sorte qu'il y a exactement N |s.
  • Notez qu'il y a au moins un blanc avant le premier |en entrée ou en sortie.
  • Les .indiquent que la grille peut être arbitrairement grande.
  • Vous pouvez remplir ?comme vous le souhaitez.

Notez que l'entrée du bas est la plus rapide, tandis que l'entrée du haut correspond 0à la première moitié des sorties et 1à la seconde moitié.

Règles

Les dominos se propagent comme spécifié dans Golfing for Domino Day . En bref, si nous représentons les directions en baisse sous forme de lettres

Q W E
A   D
Z X C

alors ce sont toutes des combinaisons uniques qui peuvent se propager (ainsi que leurs rotations et leurs réflexions):

D|   ->    DD          D\   ->    DE          D/   ->    DC

C|   ->    CD          C/   ->    CC

C    ->    C           C    ->    C           C    ->    C
 |          D           -          X           /          C

Toutes les règles ci-dessus sont appliquées simultanément à chaque pas de temps. Si deux de ces règles sont en conflit (c'est-à-dire qu'une tuile est poussée simultanément dans deux directions valides opposées), la tuile affectée ne tombera pas et sera effectivement verrouillée pour le reste de la simulation.

Restrictions

  • Met Nne dépassera jamais 6.
  • Votre solveur doit produire un circuit dans un délai de N * 2 M secondes .
  • Votre solveur ne doit pas utiliser plus de 1 Go de mémoire . C'est une limite souple, car je vais surveiller cela manuellement et tuer votre processus s'il dépasse cette limite de manière significative / continue.
  • Aucun circuit n'est autorisé à contenir plus de 8 000 000 cellules ou 1 000 000 de dominos .
  • Votre soumission doit être déterministe . Vous êtes autorisé à utiliser des générateurs de nombres pseudo-aléatoires, mais ils doivent utiliser une graine codée en dur (que vous êtes libre d'optimiser autant que vous le souhaitez).

Notation

Pour chaque circuit, prenons Dle nombre total de dominos dans votre circuit et Ble nombre le plus bas de dominos avec lesquels ce circuit a été résolu (par vous ou tout autre participant). Ensuite, votre score pour ce circuit est donné par 10,000 * B / Darrondi. Si vous ne parvenez pas à résoudre le circuit, votre score est égal à 0. Votre score global correspond à la somme d'un ensemble de tests de référence. Les circuits qui n'ont pas encore été résolus par quiconque ne seront pas inclus dans le score total.

Chaque participant peut ajouter un cas de test à la référence (et toutes les autres soumissions seront réévaluées, y compris le nouveau cas de test).

Le fichier de référence se trouve sur GitHub .

Exemples

Voici quelques exemples résolus de manière non optimale.

Constante 1

1 1
1,1

///////
   /
|   |||

Nombre de domino: 12

Porte OU

2 1
0,1,1,1

///////////

|||||/
      |||||
|||||\

Nombre de domino: 28

ET porte

2 1
0,0,0,1

///////////////////

       \-/
       - -
|||||/|\ /|||/
      /      -
       -    \-
      \-   \ -
|||||\ /  \  /
        |\    |||||

Nombre de domino: 62

Échanger des voies

2 2
00,10,01,11

////////////

||||/  \||||
     /\
     \/
||||\  /||||

Nombre de domino: 36

Notes complémentaires

Les règles de propagation sont telles que les voies en diagonale peuvent se croiser en utilisant la forme d’un losange (voir dernier exemple), même si l’une tombe avant l’autre (contrairement aux vrais dominos).

Comme point de départ, vous pouvez utiliser les portes logiques (non minimisées) de cet élément et essayer de combiner le moins possible. Pour un moyen simple (non optimal) de créer des fonctions booléennes arbitraires à partir des portes AND, OR et NOT, examinez les formes normales conjonctives et disjonctives .

Il existe un vérificateur dans ce référentiel GitHub pour tester votre code, qui sera également utilisé pour noter toutes les soumissions. Cela génère les scores bruts (comptes de dominos) et les enregistre dans un fichier qui sera traité par un marqueur distinct (également dans ce référentiel) pour obtenir les scores finaux.

La documentation générale se trouve dans les deux fichiers Ruby, mais il controller.rbfaut deux commutateurs de ligne de commande avant le fichier de référence:

  • -v vous donne plus de sortie, y compris les circuits réels produits par votre solveur.
  • -cvous permet de sélectionner un sous-ensemble du point de référence que vous souhaitez tester. Fournissez les circuits souhaités sous forme de liste d’indices à base 1 séparés par des virgules. Vous pouvez également utiliser les gammes Ruby pour pouvoir faire quelque chose comme -c 1..5,10,15..20.

S'il vous plaît inclure dans votre réponse:

  • Votre code
  • Une commande pour (compiler et) exécuter votre code. Je vous demanderai où trouver les compilateurs / interprètes nécessaires si je ne les ai pas.
  • Une table de vérité supplémentaire avec un nom, à ajouter comme cas de test à la référence. (Ceci est facultatif, mais fortement encouragé.)

Je vais tester toutes les soumissions sur Windows 8.

Martin Ender
la source
Tous poussés en même temps?
l4m2
@ l4m2 Oui, les entrées de la colonne la plus à gauche sont basculées simultanément.
Martin Ender

Réponses:

33

C # - Solution massive, lente et inefficace

Confession: a écrit cette solution il y a quelque temps alors que la question était encore dans le bac à sable, mais ce n'est pas très bon: vous pouvez faire mieux!

Edit: a remplacé la résolution ennuyeuse par une méthode moins ennuyeuse, plus flexible et généralement meilleure

Vous exécutez le programme en compilant avec csc dominoPrinter.cs, puis en transmettant des arguments à l'exécutable, par exemple (le vérificateur principal 4 bits):

dominoPrinter.exe 4 1 0,0,1,1,0,1,0,1,0,0,0,1,0,1,1,1

Explication:

"Imprimante Domino" est un programme en 3 étapes:

Étape 1 : Le "résolveur" génère un arbre d'expression d'opérations "ifnot" et "ou" avec les entrées indiquées, et un "1" à partir de la ligne à haute tension.

  • S'il y a moins de 4 entrées, le programme propose une solution du plus petit nombre possible d'opérations.

  • S'il y a 4 entrées ou plus, le programme brute chaque bloc de sortie de 8 bits, puis combine les résultats pour donner le résultat souhaité. Les bits brutés sont flexibles: plus les bits sont brutes, plus la solution est petite, mais plus le temps d'exécution est long.

Le "résolveur" est ce qui prend tout le temps (ou du moins ce qu’il avait l'habitude de faire), et c'est aussi l'essentiel du code. Je crois qu’il existe une solution bien documentée, rapide, pas très gourmande en mémoire, et probablement optimale à ce problème, mais quel serait le plaisir de le rechercher?

L'arbre d'expression (brute) du vérificateur principal 4 bits est

((2 or 1) ifnot (((0 ifnot 1) or ((1 ifnot 0) or (0 ifnot 2))) ifnot 3))

où les nombres sont les index des entrées.

Etape 2 : "l'organisateur" prend l'arbre d'expression en tant qu'entrée et assemble une présentation "squelette", qui décrit précisément une présentation en domino constituée d'un ensemble de cellules se chevauchant 4x5. Vous trouverez ci-dessous le squelette du vérificateur principal 4 bits bruté (vous devez modifier la bruteBasevariable entière de la ligne 473 en 4 (ou plus) pour obtenir ce résultat).

18 9
I ___ _ _______  O
 v _ X X ____  uu 
I X X X u    UU/  
 v X X v ___///   
I X X \ u   //    
 v X \ v __//     
I_X \ \_u  /      
   \ \ ___/       
    \_U 

Cette sortie est composée de deux parties, l’évaluateur à droite, créé à partir de l’arbre d’expressions de l’étape 1, et le "tableau" à gauche, qui permute et divise les entrées afin qu’elles arrivent dans la bons endroits pour "l'évaluateur" à gérer.

Il existe une marge considérable pour compacter la mise en page à ce stade, mais le programme effectue actuellement très peu ce travail. Le code pour cette étape est horrible, mais assez simple en dessous (voir la méthode "orifnot"). La sortie est transmise à l'étape 3.

Etape 3 : "L'imprimante" prélève la sortie de "l'organisateur" et imprime les "cellules" 4x5 correspondantes se chevauchant avec la ligne d'alimentation. Vous trouverez ci-dessous une animation du vérificateur d’amorce de 4 bits brute vérifiant si 5 est premier.

Apparemment, 5 est premier

Code le manque d'indentation est d'éviter de dépasser la limite de caractères SE 30k qu'il serait autrement :

using System;
using System.Collections.Generic;

namespace dominoPrinter
{
 class Program
 {
  static string bstring(bool[] barr)
  {
   string str = "";
   foreach (bool b in barr)
    str += b?1:0;
   return str;
  }

  public static void Main(string[] args)
  {

   int inputCount;
   val[] vals = resolveVals(args[0], args[1], args[2], out inputCount);

   System.IO.StringWriter sw = new System.IO.StringWriter();
   orifnot(inputCount, vals, sw);
   System.IO.StringReader sr = new System.IO.StringReader(sw.ToString());

   printDominoes(sr, Console.Out, args.Length > 3 && args[3] == "quite");
  }

  public abstract class val
  {
   public int size;
   public bool[] rs;
   public abstract string strness();
  }

  public class baseVal : val
  {
   public bool b;
   public int id;

   public baseVal(int idN)
   {
    id = idN;
    size = 1;
   }

   public override string strness()
   {
    return id.ToString();
   }
  }

  public abstract class biopVal : val
  {
   public val a, b;

   public biopVal(val aN, val bN)
   {
    a = aN;
    b = bN;
    size = a.size + b.size;
   }

   public bool buildCheckApply(nodev ntree)
   {
    nodev cur = ntree;
    rs = new bool[a.rs.Length];
    bool notOK = true;
    for (int i = 0; i < rs.Length; i++)
    {
     bool r = rs[i] = go(a.rs[i], b.rs[i]);
     if (notOK)
     {
      if (r)
      {
       if (cur.a == null)
        notOK = false;
       else
       {
        cur = cur.a;
        if (cur == nodev.full)
         return false;
       }
      }
      else
      {
       if (cur.b == null)
        notOK = false;
       else
       {
        cur = cur.b;
        if (cur == nodev.full)
         return false;
       }
      }
     }
    }

    ntree.apply(this, 0);
    return true;
   }

   public abstract bool go(bool a, bool b);
  }

  public class ifnotVal : biopVal
  {
   public override bool go(bool a, bool b)
   {
     return a ? false : b; // b IF NOT a, else FALSE
   }

   public ifnotVal(val aN, val bN) : base(aN, bN)
   {
   }

   public override string strness()
   {
    return "(" + b.strness() + " ifnot " + a.strness() + ")";
   }
  }

  public class orval : biopVal
  {
   public override bool go(bool a, bool b)
   {
    return a || b; // a OR b
   }

   public orval(val aN, val bN) : base(aN, bN)
   {
   }

   public override string strness()
   {
    return "(" + b.strness() + " or " + a.strness() + ")";
   }
  }

  static bool boolCompare(bool[] a, bool b)
  {
   for (int i = 0; i < a.Length; i++)
   {
    if (a[i] != b)
    {
     return false;
    }
   }
   return true;
  }

  static bool boolFlat(bool[] a)
  {
   bool p = a[0];
   for (int i = 1; i < a.Length; i++)
   {
    if (a[i] != p)
     return false;
   }
   return true;
  }

  static bool boolCompare(bool[] a, bool[] b)
  {
   if (a.Length != b.Length)
    return false; // let's do this proeprly
   for (int i = 0; i < a.Length; i++)
   {
    if (a[i] != b[i])
    {
     return false;
    }
   }
   return true;
  }

  // solver

  // these is something VERY WRONG with the naming in this code
  public class nodev
  {
   public static nodev full = new nodev();

   public nodev a, b;

   public nodev()
   {
    a = null;
    b = null;
   }

   public bool contains(bool[] rs)
   {
    nodev cur = this;
    if (cur == full)
     return true;

    for (int i = 0; i < rs.Length; i++)
    {
     if (rs[i])
     {
      if (cur.a == null)
       return false;
      cur = cur.a;
     }
     else
     {
      if (cur.b == null)
       return false;
      cur = cur.b;
     }

     if (cur == full)
      return true;
    }
    return true;
   }

   public bool contains(val v)
   {
    nodev cur = this;
    if (cur == full)
     return true;

    for (int i = 0; i < v.rs.Length; i++)
    {
     if (v.rs[i])
     {
      if (cur.a == null)
       return false;
      cur = cur.a;
     }
     else
     {
      if (cur.b == null)
       return false;
      cur = cur.b;
     }

     if (cur == full)
      return true;
    }
    return true;
   }

   // returns whether it's full or not
   public bool apply(val v, int idx)
   {
    if (v.rs[idx])
    {
     if (a == null)
     {
      if (idx == v.rs.Length - 1)
      { // end of the line, fellas
       a = full;
       if (b == full)
        return true;
       return false;
      }
      else
      {
       a = new nodev();
      }
     }
     if (a.apply(v, idx + 1))
      a = full;
     if (a == full && b == full)
      return true;
    }
    else
    {
     if (b == null)
     {
      if (idx == v.rs.Length - 1)
      { // end of the line, fellas
       b = full;
       if (a == full)
        return true;
       return false;
      }
      else
      {
       b = new nodev();
      }
     }
     if (b.apply(v, idx + 1))
      b = full;
     if (a == full && b == full)
      return true;
    }
    return false;
   }
  }

  public static void sortOutIVals(baseVal[] ivals, int rc)
  {
   for (int i = 0; i < ivals.Length; i++)
   {
    ivals[i].rs = new bool[rc];
    ivals[i].b = false;
   }

   int eri = 0;

   goto next;
  again:
   for (int i = ivals.Length - 1; i >= 0; i--)
   {
    if (ivals[i].b == false)
    {
     ivals[i].b = true;
     goto next;
    }
    ivals[i].b = false;
   }

   return;
  next:
   for (int i = ivals.Length - 1; i >= 0; i--)
   {
    ivals[i].rs[eri] = ivals[i].b;
   }

   eri++;
   goto again;
  }

  public static val[] resolve(int inputCount, int c, bool[][] erss, out baseVal[] inputs)
  {
   val[] res = new val[erss.Length];

   List<List<val>> bvals = new List<List<val>>();
   nodev ntree = new nodev();

   List<val> nvals = new List<val>();

   baseVal tval = new baseVal(-1);
   baseVal fval = new baseVal(-2);
   baseVal[] ivals = new baseVal[inputCount];
   inputs = new baseVal[inputCount + 2];

   for (int i = 0; i < inputCount; i++)
   {
    ivals[i] = new baseVal(i); // value will change anyway
    inputs[i] = ivals[i];
   }
   inputs[inputCount] = fval;
   inputs[inputCount + 1] = tval;

   sortOutIVals(ivals, c);

   for (int i = 0; i < inputCount; i++)
   {
    nvals.Add(ivals[i]);
   }

   tval.rs = new bool[c];
   fval.rs = new bool[c];
   for (int i = 0; i < c; i++)
   {
    tval.rs[i] = true;
    fval.rs[i] = false;
   }

   nvals.Add(tval);
   nvals.Add(fval); // ifnot and or do nothing with falses

   bvals.Add(new List<val>());

   foreach (val v in nvals)
   {
    ntree.apply(v, 0);
    if (!boolFlat(v.rs))
     bvals[0].Add(v); // I trust these are distinct..
   }

   Func<biopVal, bool> checkValb = (v) =>
   {
    if (!v.buildCheckApply(ntree))
    {
     return false;
    }
    bvals[v.size-1].Add(v);
    return true;
   };

   Action<biopVal, List<val>> checkVal = (v, li) =>
   {
    if (checkValb(v))
     li.Add(v);
   };

   int maxSize = 1;

  again:
   for (int i = 0; i < erss.Length; i++)
   {
    bool[] ers = erss[i];
    if (res[i] == null && ntree.contains(ers))
    {
     // there is a reason this is separate... I'm sure there is....
     foreach (val rv in nvals)
     {
      if (boolCompare(rv.rs, ers))
      {
       res[i] = rv;
       break;
      }
     }
    }
   }

   for (int i = 0; i < erss.Length; i++)
   {
    if (res[i] == null)
     goto notoveryet;
   }
   return res;

  notoveryet:

   maxSize++;
   bvals.Add(new List<val>()); // bvals[maxSize-1] always exists

   nvals.Clear();
   long cc = 0;

   List<val> sbvals = bvals[maxSize - 2];
   // NOTs have a habit of working out, get it checked first
   for (int i = sbvals.Count - 1; i >= 0; i--)
   { // also known as nvals, but let's ignore that
    val arv = sbvals[i];
    checkVal(new ifnotVal(arv, tval), nvals);
    cc += 1;
   }

   for (int s = 1; s < maxSize; s++)
   {
    List<val> abvals = bvals[s - 1];
    int t = maxSize - s;
    if (t < s)
     break;
    List<val> bbvals = bvals[t - 1];

    for (int i = abvals.Count - 1; i >= 0; i--)
    {
     val arv = abvals[i];

     int jt = t == s ? i : bbvals.Count - 1;
     for (int j = jt; j >= 0; j--)
     {
      val brv = bbvals[j];

      checkVal(new ifnotVal(brv, arv), nvals);
      checkVal(new ifnotVal(arv, brv), nvals);
      checkVal(new orval(brv, arv), nvals); // don't technically need ors, but they are good fun
      cc += 3;
     }
    }
   }

   int bc = 0;
   foreach (List<val> bv in bvals)
    bc += bv.Count;
   goto again;
  }

  public static val[] resolveVals(string mStr, string nStr, string erStr, out int inputCount)
  {
   int ic = int.Parse(mStr);
   int oc = int.Parse(nStr);
   inputCount = ic;
   int bruteBase = 3;
   if (inputCount <= bruteBase)
    return resolveVals(ic, oc, erStr);
   else
    return resolveValFours(bruteBase, ic, oc, erStr);
  }

  public static val joinVals(val low, val high, baseVal inp, baseVal tval, baseVal fval)
  {
   val lowCut = low == fval ? (val)fval : low == tval ? (val)new ifnotVal(inp, tval) : (val)new ifnotVal(inp, low);

   val highCut = high == fval ? (val)fval : high == tval ? (val)inp : (val)new ifnotVal(new ifnotVal(inp, tval), high);

   if (highCut == fval)
    return lowCut;
   if (lowCut == fval)
    return highCut;
   return new orval(highCut, lowCut);
  }

  public static val resolveValFour(int n, int m, int inputCount, bool[] ers)
  {
   // solves fours
   int fc = ers.Length / m;
   bool[][] fours = new bool[fc][];

   for (int i = 0; i < fc; i++)
   {
    fours[i] = new bool[m];
    for (int j = 0; j < m; j++)
    {
     fours[i][j] = ers[i*m+j];
    }
   }

   baseVal[] inputs;
   val[] fres = resolve(n, m, fours, out inputs);
   baseVal tval = inputs[inputs.Length - 1];
   baseVal fval = inputs[inputs.Length - 2];

   for (int i = 0; i < n; i++)
   {
    inputs[i].id += inputCount - n;
   }

   // assemble
   for (int i = 0, c = 1; c < fc; c *= 2, i++)
   {
    for (int j = 0; j + c < fc; j += c * 2)
    {
     fres[j] = joinVals(fres[j], fres[j+c], new baseVal((inputCount - n - 1) - i), tval, fval);
    }
   }

   return fres[0];
  }

  public static val[] resolveValFours(int n, int inputCount, int outputCount, string erStr)
  {
   int m = 1;
   for (int i = 0; i < n; i++)
    m *= 2;

   val[] res = new val[outputCount];

   string[] data = erStr.Split(',');
   for (int i = 0; i < outputCount; i++)
   {
    bool[] ers = new bool[data.Length];
    for (int j = 0; j < data.Length; j++)
     ers[j] = data[j][i] == '1';
    res[i] = resolveValFour(n, m, inputCount, ers);
   }

   return res;
  }

  public static val[] resolveVals(int inputCount, int outputCount, string erStr)
  {
   val[] res;

   string[] data = erStr.Split(',');
   bool[][] erss = new bool[outputCount][];
   for (int i = 0; i < outputCount; i++)
   {
    bool[] ers = new bool[data.Length];
    for (int j = 0; j < data.Length; j++)
     ers[j] = data[j][i] == '1';
    erss[i] = ers;
   }

   baseVal[] inputs; // no need
   res = resolve(inputCount, data.Length, erss, out inputs);

   return res;
  }

  // organiser
  public class vnode
  {
   private static vnode[] emptyVC = new vnode[0];
   public static vnode oneVN = new vnode('1');
   public static vnode noVN = new vnode(' ');
   public static vnode flatVN = new vnode('_');
   public static vnode moveUpVN = new vnode('/');
   public static vnode moveDownVN = new vnode('\\');
   public static vnode inputVN = new vnode('I');
   public static vnode outputVN = new vnode('O');
   public static vnode swapVN = new vnode('X');
   public static vnode splitDownVN = new vnode('v');

   public int size;
   public vnode[] children;
   public char c;
   public int id = -3;

   public vnode(char cN)
   {
    c = cN;
    children = emptyVC;
    size = 1;
   }

   public vnode(val v)
   {
    biopVal bv = v as biopVal;

    if (bv != null)
    {
     children = new vnode[2];
     children[0] = new vnode(bv.a);
     children[1] = new vnode(bv.b);
     size = children[0].size + children[1].size;

     if (bv is orval)
      c = 'U';
     if (bv is ifnotVal)
      c = 'u';
    }
    else
    {
     children = emptyVC;
     size = 1;
     c = 'I';
     id = ((baseVal)v).id;
    }
   }
  }

  public class nonArray<T>
  {
   public int w = 0, h = 0;
   Dictionary<int, Dictionary<int, T>> map;

   public nonArray()
   {
    map = new Dictionary<int, Dictionary<int, T>>();
   }

   public T this[int x, int y]
   {
    get
    {
     Dictionary<int, T> yd;
     if (map.TryGetValue(x, out yd))
     {
      T v;
      if (yd.TryGetValue(y, out v))
      {
       return v;
      }
     }
     return default(T);
    }
    set
    {
     if (x >= w)
      w = x + 1;
     if (y >= h)
      h = y + 1;
     Dictionary<int, T> yd;
     if (map.TryGetValue(x, out yd))
     {
      yd[y] = value;
     }
     else
     {
      map[x] = new Dictionary<int, T>();
      map[x][y] = value;
     }
    }
   }
  }

  public static int fillOutMap(nonArray<vnode> map, vnode rn, int y, int x)
  {
   if (rn.children.Length == 0)
   {
    map[y,x] = rn;
    return 1;
   }
   else
   {
    map[y+1,x] = rn;
    for (int i = 0; i < rn.children.Length; i++)
    {

     if (i == 0)
     {
      fillOutMap(map, rn.children[i], y, x + 1);
     }

     if (i == 1)
     {
      int ex = x + rn.children[0].size;
      for (int j = 1; j < ex - x; j++)
       map[y - j + 1,ex - j] = vnode.moveUpVN;
      fillOutMap(map, rn.children[i], y, ex);
     }

     y += rn.children[i].size;
    }
   }

   return rn.size;
  }

  public static void orifnot(int inputCount, val[] vals, System.IO.TextWriter writer)
  {
   // step one - build weird tree like thing of death
   nonArray<vnode> map = new nonArray<vnode>();

   int curY = 0;
   foreach (val v in vals)
   {
    vnode vnt = new vnode(v);
    map[curY, 0] = vnode.outputVN;
    curY += fillOutMap(map, vnt, curY, 1);
   }

   // step two - build the thing to get the values to where they need to be
   // find Is
   List<int> tis = new List<int>();
   for (int y = 0; y < map.w; y++)
   {
    for (int x = map.h - 1; x >= 0; x--)
    {
     vnode vn = map[y,x];
     if (vn != null && vn.c == 'I')
     {
      tis.Add(vn.id);
      if (vn.id > -2)
      {
       for (;x < map.h; x++)
       {
        map[y,x] = vnode.flatVN;
       }
      }
      goto next;
     }
    }
    tis.Add(-2);
   next:
    continue;
   }

   // I do not like this piece of code, it can be replaced further down for the better if you get round to thinking about it
   // add unused Is
   for (int z = 0; z < inputCount; z++)
   {
    if (!tis.Contains(z))
    {
     int midx = tis.IndexOf(-2);
     if (midx != -1)
     {
      tis[midx] = z;
      map[midx,map.h-1] = vnode.noVN;
     }
     else
     {
      tis.Add(z);
      map[map.w,map.h-1] = vnode.noVN;
     }
    }
   }

   int curX = map.h;

  MORE:
   for (int y = 0; y < map.w; y++)
   {
    if (y == map.w - 1)
    {
     if (tis[y] == -2)
      map[y,curX] = vnode.noVN;
     else
      map[y,curX] = vnode.flatVN;
    }
    else
    {
     int prev = tis[y];
     int cur = tis[y + 1];

     if (cur != -2 && (prev == -2 || cur < prev))
     { // swap 'em
      map[y,curX] = vnode.noVN;
      if (prev == -2)
       map[y+1,curX] = vnode.moveDownVN;
      else
       map[y+1,curX] = vnode.swapVN;
      int temp = tis[y];
      tis[y] = tis[y + 1];
      tis[y + 1] = temp;
      y++; // skip
     }
     else
     {
      if (/*thatThingThat'sAThing*/ prev == cur && cur != -2)
      {
       map[y,curX] = vnode.noVN;
       map[y+1,curX] = vnode.splitDownVN;
       int temp = tis[y];
       tis[y+1] = -2;
       y++; // skip
      }
      else
      {
       if (prev == -2)
        map[y,curX] = vnode.noVN;
       else
        map[y,curX] = vnode.flatVN;
      }
     }
    }
   }

   // check if sorted
   for (int y = 0; y < map.w - 1; y++)
   {
    int prev = tis[y];
    int cur = tis[y + 1];

    if (cur != -2 && (prev == -2 || cur < prev))
     goto NOTSORTED;
   }

   goto WHATNOW;

  NOTSORTED:
   curX++;
   goto MORE;

  WHATNOW:

   tis.Add(-2); // this is to avoid boud checking y+2
   // so... it's sorted now, so add the splits
  morePlease:
   curX++;
   for (int y = 0; y < map.w; y++)
   {
    if (y == map.w - 1)
    {
     if (tis[y] == -2)
      map[y,curX] = vnode.noVN;
     else
      map[y,curX] = vnode.flatVN;
    }
    else
    {
     int prev = tis[y];
     int cur = tis[y + 1];
     int next = tis[y + 2];

     if (cur != -2 && prev == cur && cur != next)
     { // split
      map[y,curX] = vnode.noVN;
      map[y+1,curX] = vnode.splitDownVN;
      tis[y + 1] = -2;
      y++; // skip
     }
     else
     {
      if (prev == -2)
       map[y,curX] = vnode.noVN;
      else
       map[y,curX] = vnode.flatVN;
     }
    }
   }

   // check if collapsed
   for (int y = 0; y < map.w - 1; y++)
   {
    int prev = tis[y];
    int cur = tis[y + 1];

    if (cur != -2 && prev == cur)
     goto morePlease;
   }

   // ok... now we put in the Is and 1
   curX++;
   map[0, curX] = vnode.oneVN;
   int eyeCount = 0;
   int ly = 0;
   for (int y = 0; y < map.w; y++)
   {
    if (tis[y] > -1)
    {
     map[y, curX] = vnode.inputVN;
     eyeCount++;
     ly = y;
    }
   }

   // step three - clean up if we can
   // push back _  esq things to  _
   //           _/               /
   // this /shouldn't/ be necessary if I compact the vals properlu
   for (int y = 0; y < map.w - 1; y++)
   {
    for (int x = 1; x < map.h; x++)
    {
     if (map[y, x] != null && map[y+1, x] != null && map[y+1, x-1] != null)
     {
      char uc = map[y+1, x-1].c;
      if (map[y, x].c == '_' && map[y+1, x].c == '_'
          && (uc == 'U' || uc == 'u'))
      {
       map[y, x] = vnode.noVN;
       map[y, x-1] = vnode.flatVN;
       map[y+1, x] = map[y+1, x-1];
       map[y+1, x-1] = vnode.noVN;
      }
     }
    }
   }

   // step four - write out map
   writer.WriteLine(map.h + " " + map.w);

   for (int y = 0; y < map.w; y++)
   {
    for (int x = map.h - 1; x >= 0; x--)
    {
     vnode vn = map[y,x];
     if (vn != null)
      writer.Write(vn.c);
     else
      writer.Write(' ');
    }
    writer.WriteLine();
   }
  }

  // printer
  static string up1 = @"      /     /     /     /";
  static string input = @"                    |||||";
  static string output = @"                    |    ";
  static string flat = @"            |/  \  /|\   ";
  static string splitDown = @"|//   / /\  |\/    /     ";
  static string splitUp = @"         \  |/\ \ \/|\\  ";
  static string moveDown = @"|//     /     /    /     ";
  static string moveUp = @"         \    \   \ |\\  ";
  static string swap = @"|/  |  /\   /\   \/ |\  |";
  static string orDown = @"|/    /     |/  \  /|\   ";
  static string orUp = @"|/    /  \  |\  \   |\   ";
  static string ifnotDown = @"|/     /     -   \/ |\  |";
  static string ifnotUp = @"|/  |  /\    -   \  |\   ";

  public static void printDominoes(System.IO.TextReader reader, System.IO.TextWriter writer, bool moreverbosemaybe)
  {
   string line;
   string[] data;

   line = reader.ReadLine();
   data = line.Split(' ');
   int w = int.Parse(data[0]);
   int h = int.Parse(data[1]);

   int ox = 0;
   int oy = 0;
   int cx = 5;
   int cy = 5;

   char[,] T = new char[ox + w * cx, oy + h * (cy - 1) + 1];

   Action<int, int, string> setBlock = (int x, int y, string str) =>
   {
    for (int i = 0; i < cx; i++)
    {
     for (int j = 0; j < cy; j++)
     {
      char c = str[i + j * cx];
      if (c != ' ')
       T[ox + x * cx + i, oy + y * (cy - 1) + j] = c;
     }
    }
   };

   // read and write
   for (int j = 0; j < h; j++)
   {
    line = reader.ReadLine();
    for (int i = 0; i < w; i++)
    {
     if (line[i] != ' ')
     {
      switch (line[i])
      {
       case '1':
        setBlock(i, j, up1);
        break;
       case '_':
        setBlock(i, j, flat);
        break;
       case '^':
        setBlock(i, j, splitUp);
        break;
       case 'v':
        setBlock(i, j, splitDown);
        break;
       case '/':
        setBlock(i, j, moveUp);
        break;
       case '\\':
        setBlock(i, j, moveDown);
        break;
       case 'X':
        setBlock(i, j, swap);
        break;
       case 'U':
        setBlock(i, j, orUp);
        break;
       case 'D':
        setBlock(i, j, orDown);
        break;
       case 'u':
        setBlock(i, j, ifnotUp);
        break;
       case 'd':
        setBlock(i, j, ifnotDown);
        break;
       case 'I':
        setBlock(i, j, input);
        break;
       case 'O':
        setBlock(i, j, output);
        break;
      }
     }
    }
   }

   // end
   for (int i = 0; i < T.GetLength(0); i++)
   {
    T[i, 0] = '/';
   }

   // writeout
   w = T.GetLength(0) - cx + 1;
   h = T.GetLength(1);
   if (moreverbosemaybe)
    writer.Write(w + " " + h + " ");
   for (int j = 0; j < T.GetLength(1); j++)
   {
    for (int i = 0; i < T.GetLength(0) - cx + 1; i++)
    {
     char c = T[i, j];
     writer.Write(c == 0 ? ' ' : c);
    }
    if (!moreverbosemaybe)
     writer.WriteLine();
   }
  }
 }
}

Un cas de test supplémentaire:

4 1 0,0,0,1,0,0,1,1,0,0,0,1,1,1,1,1

Ceci vérifie si deux bits adjacents (non enveloppants) sont des 1 (par exemple, vrai pour 0110, mais faux pour 0101 et 1001)

VisualMelon
la source
2
C'est beau. Nous avons maintenant besoin d’un résolveur de méta-dominos prenant la table de vérité Iet dont les sorties spécifient une nouvelle mise en page de domino
correct
Je suis confus quant à la façon dont cette table de vérité représente un vérificateur principal de quatre bits. Ne dit-il pas que 14 et 15 sont premiers?
Quintopia
@quintopia ayant regardé de nouveau ... vous semblez avoir raison, et C'EST de ma faute, celle que Martin utilise est correcte, mais je ne reconstruis pas ce primechecker maintenant!
VisualMelon