レーザーの宛先に一致するミラー構成を見つける


13

更新されたスコアリングこのチャレンジは予想以上に難しいので、スコアリングを調整しました。単一のミラー入力を解決できるプログラムが有効な答えです。より洗練されたプログラムは、スコアにボーナスを与えます。

PPCGには、ミラーボックス内のレーザーパスを見つけるためのパズルがいくつかあります。このパズルでは、いくつかのレーザーの宛先に一致するミラーのボックスを作成する必要があります。

レーザーが出入りするボックスと仕様が表示されます。プログラムは、仕様を満たすために正確にN個の両面ミラーをボックスに配置する必要があります。ミラーは45度の角度にする必要がありますが、前方に傾斜することも後方に傾斜することもできます。

入力

プログラムは、STDIN、コマンドライン引数、または次の形式のファイルを介してボックスグリッドを受け入れる必要があります。

+--G--+     +abcde+
G     |     f/////d
|    /|     a//   c
+-----+     f     |
            +-b-e-+

文字ペア([a-zA-Z]を使用可能)は、最大52個のレーザーの入力/出力を示します。箱の中にはN個の/ミラーがあります。ボックスの寸法は3 <= W、H <= 200です。ボックスは+|-文字で構成されています。ボックスには、ゼロを含む任意の数のミラーがあります。

出力

/文字が移動および/または\文字に変更される可能性があることを除いて、出力は入力と一致する必要があります。プログラムは、正しいミラーボックス文字列をSTDOUTまたはファイルに送信する必要があります。末尾の改行はオプションです。ミラーの配置が入力仕様を満たすことができない場合、output Impossible\n。可能な解決策の例:

+--G--+     +abcde+
G  /  |     f \ \ d
|     |     a/ \  c
+-----+     f / //|
            +-b-e-+

テスト例

入力:

+abcdefghijklmnopqrstuvwxyA-+
|///////////////            |
|///////////////            |
|                           |
+-Abcdefghijklmnopqrstuvwxya+

出力例:

+abcdefghijklmnopqrstuvwxyA-+
|\                         \|
|/                        / |
|\\\\\\\\\\\\\\\\\\\\\\\\\\ |
+-Abcdefghijklmnopqrstuvwxya+

スコアリング(更新)

これはボーナス付きのコードゴルフです。プログラムで解決できるミラーの数(N)を回答で指定する必要があります。スコアは、バイト単位のプログラムの長さをNで割ったものです。これにより、人々は簡単なプログラムで入ることができますが、より多くの野心プログラマーにボーナスが与えられます。

標準の抜け穴は許可されません。


3
ゴルフに関係なく、これは難しい問題のように聞こえます。
orlp

2
ヒント:ブルートフォースはオプションではありません。大きい例では、1秒間に10,000個のオプションで3つの宇宙の年齢が必要です。
-Sanchises

@sanchisesどのミラーも反転できるので、もっと時間がかかると思うので* 2^30、そこにもコンポーネントが必要だと思います
-VisualMelon

追加のヒント:パズルのプロパティを活用して、検索スペースを整理する必要があります。部分的な解の組み合わせを使用したり、完全な解に近い部分的な解からの山登りを使用することもできます。より簡単なソリューションで答えることができるようになったため、1つまたは2つのミラーパズルを解決するプログラムも歓迎します。
ロジックナイト

回答:


2

C#-897 862バイト

ミラーを配置できない深刻なバグを発見しました。うまくいけばうまくいきます!また、軽いゴルフもしましたが、whileループをそこに残せませんでした...

完全なプログラム。STDINから入力を受け取り、STDOUTに出力します。

これは非常に楽しかったです。7x 5の問題に対処し(ミラーの1つを削除すると不可能になる)、30 x 5を解決するのに約1時間かかりました。

using Q=System.Console;class P{static int w,L;static string S(char[]M,int t,int r,int i,int d,int[]B){var s="";if(r<0)return s;M=(char[])M.Clone();B=(int[])B.Clone();B[i]=1;for(i+=d;M[t]<48|t==i;i=t+(d=t<w?w:t>L-w?-w:t%w<1?1:-1))if(++t>=L){for(i=0;++i<L&r>0;)if(B[i]<1&M[i]<33){M[i]='.';r--;}return r<1?new string(M):s;}int c=M[i];if(c>32)s=c>47|c<46?s=c==M[t]?S(M,t,r,t,0,B):s:S(M,t,r,i,c<47?w/d:-w/d,B);else if((s=S(M,t,r,i,d,B))==""&B[i]<1){M[i]='.';s=S(M,t,r-1,i,w/d,B);if(s==""){M[i]='/';s=S(M,t,r-1,i,-w/d,B);}}return s;}static void Main(){string a,A="",R=A;for(;(a=Q.ReadLine())!=null;A+=a)L+=(w=a.Length);var G=A.ToCharArray();int r=0,i=L;for(;i>0;G[i]=G[i]=='|'?',':G[i])if(G[--i]==47|G[i]==92){r++;G[i]=' ';}a=S(G,0,r,1,w,new int[L]);if(a=="")R="Impossible\n";else for(;i<L;i+=w)R+=a.Substring(i,w)+"\n";Q.Write(R.Replace(".","\\").Replace(",","|"));}}

7 x 5の例:

+abcde+
f/////d
a//   c
f     |
+-b-e-+

+abcde+
f   \ d
a/  //c
f/ \ /|
+-b-e-+

不可能なバージョン:

+abcde+
f ////d
a//   c
f     |
+-b-e-+

Impossible

異なるもの(プログラムは元のミラーレイアウトを見ません):

+a----+
|//// |
|/////|
|/////|
+----a+

+a----+
| /\\\|
|\\\\\|
|\\/\\|
+----a+

30 x 5のソリューション:

+abcdefghijklmnopqrstuvwxyA-+
| \\\\\\\\\\\\\\\\\\\\\\\\ \|
| /                       //|
|\                         \|
+-Abcdefghijklmnopqrstuvwxya+

各レーザー光源を順番に見て、有効なルートを構築し(可能な場合)、次のレーザー光源に移動します。これは非常に単純な深さ優先の検索であり、どのレーザー光源(ターゲット)を見ているか、配置するミラーの数、現在のセルが「いる」場所、移動する方向、および各セルを知る必要があります既にアクセスされている(そのため、既にアクセスされている場所にミラーを配置しないように)。最後の3つは、現在のターゲットのパスを組み立てるために使用され、ターゲットが変更されるとリセット時に使用されます。すべてのレーザーがリンクされると、先に進み、空のままにする必要のないギャップを埋めます(アクセスしたすべての場所を知る必要がある別の理由)。

ルートを構築するときは、ミラーを挿入するよりも「前に進む」ことを優先します。そうする場合は、「\」ミラーを優先します。これは、上の「異なる」例で最もよく見られ、最上位の「a」は、解決策を見つけることができる場合は「\」、それ以外の場合は「/」を入力し続けます(当然、最初のセルをスキップすると解決策が見つからない場合は、バックトラックし、代わりにミラーを配置してみてください)。

using Q=System.Console;

class P
{
    static int w,L;

    // M is cur grid
    // t is target edge thing (0->L)
    // r is mirrors remaining
    // i is pos
    // d is dir
    static string S(char[]M,int t,int r,int i,int d,int[]B)
    {
        var s="";

        if(r<0) // no mirrors left
            return s;

        // clone everything
        M=(char[])M.Clone();
        B=(int[])B.Clone();

        B[i]=1; // can't write to this

        for(i+=d; // move i
            M[t]<48|t==i; // only if target is something sensible (increment if i==t)
            i=t+(d=t<w?w:t>L-w?-w:t%w<1?1:-1)) // reflect, should be fine for w=3
            if(++t>=L) // run off the end
            {
                for(i=0;++i<L&r>0;) // don't need I any more (count through everything)
                    if(B[i]<1&M[i]<33) // not been here & it's open space
                    {
                        M[i]='.'; // doesn't matter
                        r--;
                    }
                return r<1?new string(M):s; // none remaining ? victory : defeat
            }

        int c=M[i];
        if(c>32) // not boring
            s=c>47|c<46? // hit edge
                s=c==M[t]? // hit the correct thing
                    S(M,t,r,t,0,B): // i+0=t, tells it to increment t
                    s
            :S(M,t,r,i,c<47?w/d:-w/d,B); // mirror
        else // boring
            if((s=S(M,t,r,i,d,B))==""&B[i]<1) // fwd
            {
                M[i]='.'; // use . instead of \
                s=S(M,t,r-1,i,w/d,B); // \
                if(s=="")
                {
                    M[i]='/';
                    s=S(M,t,r-1,i,-w/d,B); // /
                }
            }

        return s;
    }

    static void Main()
    {
        string a,A="",R=A; // R is free
        for(;(a=Q.ReadLine())!=null;A+=a) // read input
            L+=(w=a.Length); // note width, accumulate length

        var G=A.ToCharArray();

        int r=0,i=L; // count mirrors (I refuse to make these static)
        for(;i>0; // end on i=0
            G[i]=G[i]=='|'?',':G[i]) // replace | with ,
            if(G[--i]==47|G[i]==92) // remove and count mirrors
            {
                r++;
                G[i]=' '; // storing G[i] doesn't seem to save anything
            }

        // search
        a=S(G,0,r,1,w,new int[L]);

        if(a=="") // defeat
            R="Impossible\n";
        else // victory
            for(;i<L;i+=w) // for each line
                R+=a.Substring(i,w)+"\n";

        Q.Write(R.Replace(".","\\").Replace(",","|")); // swap back | and \
    }
}

いい解決策。新採点システムによると、あなたはスコアを少なくとも7分の917 = 131
ロジック騎士

2

Python、671 654バイト

解決策ではなく、試みです。以下をお読みください。

import random as R
def V(F):
 for S,_x,_y in (F[0],0,1),(F[-1],0,-1),([L[0] for L in F],1,0),([L[-1] for L in F],-1,0):
  for i,C in enumerate(S):
   if not C in '+-|':
    x=_x;y=_y
    if not x: X=i;Y=y
    elif not y: Y=i;X=x
    while F[Y][X] != C:
     if F[Y][X]=='\\':x,y=y,x
     if F[Y][X]=='/':a=x+y;x,y=x-a,y-a
     X+=x;Y+=y
     try:
      if F[Y][X] in '+-|':return False
     except:
      return False
 return True
F=open(input()).read().split('\n')
while 1:
 _=[F[0]]+['\n'.join([L[0]+''.join([R.choice(' \\/')for i in range(len(F[0])-2)])+L[-1] for L in F[1:-1]])]+[F[-1]]
 if V(_):
  for l in _: print l
  break

私は解決策に満足していないので、これを最大限にゴルフしませんでした。 V副業で見つけたF各キャラクターのフィールドを歩いて、特定のソリューションを検証しCます。ソリューションはランダムに生成されます。いですが、entry1で機能しますが、他のエントリにはかなり時間がかかります。ランダムに解決策を試行するため、これは特定の問題の実際の解決策とは見なしません。しかし、それは他の人を助けるかもしれません。

実行: echo "entry1.txt" | python script.py


1
新しいスコアリングシステムでは、これは有効なソリューションですが、除数ボーナスを獲得しません(2つ以上のミラーで問題を解決できる場合を除く)。無効な構成をより早く削除することでこれを最適化できると思います(例:エッジに文字がある各列または行には、少なくとも1つのミラーが必要です-一致する文字が互いに反対でない場合)。
ロジックナイト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.