도미노 회로


36

스코어 보드

다음은 VisualMelon 제출에 대한 원시 점수 (예 : 도미노 수)입니다. 더 많은 답변이 나오면 아래에 설명 된 정규화 된 점수로 바꾸겠습니다. 기존 솔루션은 이제 벤치 마크의 모든 회로를 해결할 수 있습니다.

 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

도전

이다 가능한 도미노에서 간단한 논리 게이트를 구축 할 수 있습니다. 따라서, 이들 또는 다른 방식을 조합하여 임의의 이진 함수를 도미노로 계산할 수 있습니다.

물론, 도미노 (Robin Paul Weijers 제외)를 사용한 모든 사람들은 도미노가 부족할 때 실망을 경험했습니다. 따라서 우리는 가능한 한 효율적으로 도미노를 사용하기를 원하므로 우리는 우리가 가진 재료로 정말 흥미로운 계산을 할 수 있습니다.

제로 입력 자체에서 0이 아닌 출력을 생성 할 수 없으므로 설정에 따라 "전원 라인"을 추가해야 1하며 언제든 s를 가져올 수 있습니다 .

당신의 작업

M입력 및 N출력을 가진 부울 함수 ( f: {0,1}^M --> {0,1}^N수학적으로 기울어 진 경우)가 주어지면 해당 함수를 계산할 수있는 도미노가 가능한 적은 도미노 회로를 생성하십시오. 당신이 기호를 사용할 것 |, -, /, \다양한 방향에서 도미노를 나타냅니다.

입력

명령 행 인수를 통해 입력이 제공됩니다.

[command for your solver] M N f

여기서 MN양의 정수이며, f정규 순서 콤마 분리 진리표이다. 즉, length 값을 f포함 2^M합니다 N. 예를 들어 만약 M = N = 2상기 2 비트는 OR 함수 동안 출력의 첫 번째 비트가 AND 함수이고, f판독 할

00,01,01,11

산출

STDOUT에 도미노 설정을 나타내는 ASCII 그리드를 작성하십시오. 설정은 다음 프레임 워크에 맞아야합니다.

/////.../////
 ????...????
I????...????O
I????...????O
.............
.............
I????...????O
I????...????O
I????...????O
  • 맨 윗줄은 전적으로로 구성되어 있으며 맨 /왼쪽 도미노는 처음에 넘어 질 수 있습니다. 이것은 전원 선입니다.
  • 가장 왼쪽 열은 입력으로 구성됩니다. 각각 I은 공백 또는 a |일 수 있으므로 정확히 M |s가 있습니다.
  • 가장 오른쪽 열은 출력으로 구성됩니다. 각각 O은 공백 또는 a |일 수 있으므로 정확히 N |s가 있습니다.
  • |입력 또는 출력에서 첫 번째 앞에 공백이 하나 이상 있습니다.
  • .그리드 임의로 클 수 있음을 나타냅니다.
  • ?원하는 방식으로 채울 수 있습니다 .

맨 아래 입력은 진리표를 따르는 동안 가장 빠르게 변하는 반면, 상단 입력은 0출력의 전반부와 후반부에 대한 것 1입니다.

규칙

도미노는 Golfing for Domino Day에 지정된대로 전파됩니다 . 요컨대 떨어지는 방향을 글자로 나타내면

Q W E
A   D
Z X C

다음은 전파 할 수있는 독특한 조합입니다 (회전 및 반사).

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

C|   ->    CD          C/   ->    CC

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

위의 모든 규칙은 각 시간 단계에서 동시에 적용됩니다. 이러한 규칙 중 두 규칙이 충돌하는 경우 (즉, 타일이 동시에 두 개의 유효한 반대 방향으로 밀림 ) 영향을받는 타일이 떨어지지 않고 나머지 시뮬레이션을 위해 효과적으로 고정됩니다.

제한 사항

  • MN6을 초과하지 않습니다.
  • 솔버는 N * 2 M 내에 회로를 생성해야합니다 .
  • 솔버는 1GB 이상의 메모리를 사용해서는 안됩니다 . 이 제한을 수동으로 모니터링하고 프로세스가이 제한을 크게 / 지속적으로 초과하면 프로세스가 종료되므로 소프트 한계입니다.
  • 어떤 회로도 8,000,000 개 이상의 또는 1,000,000 개의 도미노 를 포함 할 수 없습니다 .
  • 제출은 결정 론적 이어야합니다 . 의사 난수 생성기를 사용할 수 있지만 하드 코딩 된 시드 (원하는만큼 자유롭게 최적화 할 수있는)를 사용해야합니다.

채점

각 회로에 대해 회로의 DB도미노 수와이 회로로 해결 한 최소 도미노 수 (사용자 또는 다른 참가자가)로 하자 . 그런 다음이 회로에 대한 점수는 10,000 * B / D반올림하여 제공됩니다 . 회로를 풀지 못하면 점수는 0입니다. 전체 점수는 벤치 마크 세트의 테스트 사례에 대한 합계입니다. 아직 아무도 해결하지 않은 회로는 총점에 포함되지 않습니다.

각 참가자는 하나의 테스트 사례를 벤치 마크에 추가 할 수 있습니다 (그리고 새로운 테스트 사례를 포함하여 다른 모든 제출물은 재평가됩니다).

벤치 마크 파일 은 GitHub에서 찾을 수 있습니다 .

다음은 최적화되지 않은 일부 예입니다.

상수 1

1 1
1,1

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

도미노 수 : 12

OR 게이트

2 1
0,1,1,1

///////////

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

도미노 수 : 28

AND 게이트

2 1
0,0,0,1

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

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

도미노 수 : 62

차선 교체

2 2
00,10,01,11

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

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

도미노 수 : 36

추가 사항

전파 규칙은 실제 레인 과 달리 대각선 레인 이 다이아몬드 모양을 사용하여 교차 할 수 있도록하는 것입니다 (마지막 예 참조).

시작점 으로이 요지 에서 (최소화되지 않은) 논리 게이트를 사용 하고 가능한 적은 수의 게이트를 결합 해보십시오. AND, OR 및 NOT 게이트에서 임의의 부울 함수를 작성하는 간단한 (최적의 방법이 아닌) 방법은 결속결측 정규형을 살펴보십시오 .

이 GitHub 리포지토리 에 코드를 테스트 할 수 있는 검증 도구가 있으며 , 이는 모든 제출물의 점수를 매기는데도 사용됩니다. 이는 원시 점수 (domino counts)를 출력하고 파일에 저장하여 별도의 득점자가 처리 할 수 ​​있도록 (저장소에도 있음) 최종 점수를 얻습니다.

일반 문서는 두 개의 Ruby 파일에서 찾을 수 있지만 controller.rb벤치 마크 파일 앞에 두 개의 명령 행 스위치가 있습니다.

  • -v 솔버에서 생성 된 실제 회로를 포함하여 더 많은 출력을 제공합니다.
  • -c테스트하려는 벤치 마크의 하위 세트를 선택할 수 있습니다. 원하는 회로를 쉼표로 구분 된 1 기반 인덱스 목록으로 제공하십시오. Ruby 범위를 사용할 수도 있으므로 다음과 같은 작업을 수행 할 수 -c 1..5,10,15..20있습니다.

답변에 포함하십시오 :

  • 귀하의 코드
  • 코드를 컴파일하고 실행하는 명령입니다. 필요한 컴파일러 / 인터프리터가없는 경우 어디서 구할 수 있는지 묻습니다.
  • 벤치 마크에 테스트 케이스로 추가 될 이름을 가진 추가 진리표. (선택 사항이지만 강력히 권장됩니다.)

Windows 8에서 모든 제출물을 테스트하겠습니다.


모두 동시에 밀어?
l4m2

@ l4m2 그렇습니다. 가장 왼쪽 열의 입력이 동시에 토핑됩니다.
마틴 엔더

답변:


33

C #-거대하고 느리고 비효율적 인 솔루션

고백 : 질문이 여전히 샌드 박스에 있었을 때 얼마 전에이 솔루션을 작성했지만 좋지 는 않습니다. 더 잘할 수 있습니다!

편집 : 보링 해결을 덜 지루하고 유연하며 일반적으로 더 나은 방법으로 대체

csc dominoPrinter.cs예를 들어 (4 비트 소수 검사기)와 같이 인수를 컴파일 하고 실행 파일에 전달 하여 프로그램을 실행합니다 .

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

설명:

"Domino Printer"는 3 단계 프로그램입니다.

1 단계 : "솔버"는 주어진 입력으로 "ifnot"및 "or"이진 연산과 전력선에서 "1"의 식 트리를 생성합니다. 입력 수에 따라 2 가지 방법이 있습니다.

  • 입력이 4 개 미만인 경우 프로그램은 가장 적은 수의 작업을 해결합니다.

  • 4 개 이상의 입력이있는 경우 프로그램은 각 8 비트 출력 덩어리를 무너 뜨린 다음 결과를 결합하여 원하는 출력을 제공합니다. 융통성이있는 비트는 유연합니다. 비트가 많을수록 솔루션은 작지만 런타임은 길어집니다.

"솔버"는 모든 시간이 걸리거나 (또는 ​​적어도 익숙해졌으며) 대부분의 코드입니다. 나는 잘 문서화되고 빠르며 메모리가 부족하지 않으며이 문제에 대한 최적의 해결책이 있다고 생각 하지만 재미는 어디에서 찾을 수 있습니까?

4 비트 프라임 검사기의 잔인한 식 트리는 다음과 같습니다.

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

여기서 숫자는 입력의 색인입니다.

2 단계 : "오거나이저"는 표현 트리를 입력으로 사용하고 "스켈레톤"레이아웃을 구성합니다. 여기에는 4x5 겹치는 셀 세트로 만든 도미노 레이아웃이 정확하게 설명되어 있습니다. 다음은 4 비트 소수 프라임 검사기의 골격입니다 ( bruteBase이 결과를 얻으려면 473 행 의 정수 변수를 4 이상 으로 변경해야합니다 ).

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 

이 출력은 1 단계의 식 트리에서 생성 된 오른쪽의 "평가자"와 왼쪽의 "교환 판"으로 구성되어 입력을 교환하고 분할하여 "평가자"가 처리 할 적절한 장소.

이 시점에서 레이아웃을 압축하는 데 상당한 범위가 있지만 프로그램은 현재 그러한 작업을 거의 수행하지 않습니다. 이 단계의 코드는 끔찍하지만 그 아래는 매우 간단합니다 ( "orifnot"방법 참조). 출력은 3 단계로 전달됩니다.

3 단계 : "프린터"는 "오거나이저"에서 출력을 가져 와서 해당 4x5 겹치는 "셀"을 전력선과 함께 인쇄합니다. 아래는 5가 소수인지 검사하는 4 비트 소수 검사기의 애니메이션입니다.

분명히 5는 소수입니다

들여 쓰기가 부족한 코드 는 SE 30k 문자 제한을 초과하지 않는 것입니다 .

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();
   }
  }
 }
}

추가 테스트 사례 :

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

이는 두 개의 인접한 (래핑되지 않은) 비트가 모두 1인지 여부를 확인합니다 (예 : 0110의 경우 true, 0101 및 1001의 경우 false)


2
이것은 아름답다. 이제 우리는 진리표를 가져 I오고 그 출력이 새로운 도미노 레이아웃을 지정 하는 메타 도미노 솔버가 필요합니다
wrongu

그 진리표가 4 비트 프라임 검사기를 어떻게 나타내는 지 혼란 스럽습니다. 14와 15가 소수라고 말하지 않습니까?
quintopia

@quintopia는 다시 보았습니다 ... 당신은 옳은 것처럼 보이며 IT는 내 잘못입니다 .Martin이 사용하는 것은 맞지만 지금은 그 프라임 체커를 재구성하지 않습니다!
VisualMelon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.