BattleBlock Theaterパズルを解く


13

ゲームBattleBlock Theaterには、Lights Outの一般化バージョンであるパズルが含まれることがあります。隣接する3つのブロックがあります。各ブロックは、バーを含む1〜4のレベルを示します。例:

|
||||
||

ブロックにタッチすると、そのブロックと隣接するブロックのレベルが増加します(4から1に戻ります)。パズルは、3つのブロックすべてが同じレベルを示すときに解決されます(どのレベルでもかまいません)。ブロックをタッチする順序は重要ではないため、各ブロックがタッチされる頻度によってソリューションを示します。上記の入力の最適なソリューションは次のとおりです201

|    --> || --> |||     |||
||||     |      ||      |||
||       ||     ||  --> |||

ゲームは非常に簡単に任意の数のブロックを一般化しますが、一部の数では、すべての構成が解決可能ではありません。

チャレンジ

ブロックレベルのシーケンスが与えられたら、パズルを解くために各ブロックをタッチする必要がある頻度を返します。例えば、上記の例はとして与えられ、結果として142生じる可能性があり201ます。解決策がない場合は、選択した一貫した出力を返します。これは、すべての潜在的な解決策、-1または空の文字列などと区別できます。

関数またはプログラムを記述し、STDIN、コマンドライン引数または関数引数を介して入力を任意の便利なリスト形式または文字列形式で取得し、同様に戻り値を介して出力するか、STDOUTに出力します。

コードは、妥当なマシンで1分以内にすべてのテストケースに対して正しい結果を返す必要があります。(これは完全に厳密な制限ではないため、ソリューションに1分10秒かかる場合は問題ありませんが、3分かかる場合はそうではありません。優れたアルゴリズムは数秒で簡単に解決できます。)

これはコードゴルフなので、最短の回答(バイト単位)が勝ちです。

ソリューションは一意ではないため、異なる結果が得られる場合があります。

Input                          Output

1                              0
11                             00
12                             No solution
142                            201
434                            101
222                            000
4113                           0230
32444                          No solution
23432                          10301
421232                         212301
3442223221221422412334         0330130000130202221111
22231244334432131322442        No solution
111111111111111111111222       000000000000000000000030
111111111111111111111234       100100100100100100100133
412224131444114441432434       113013201011001101012133

私の知る限り、ブロック数が0 mod 3または1 mod 3である入力ごとに正確に4つのソリューションがあり、2 mod 3である0または16のソリューションがあります。


最適なソリューションを出力する必要がありますか?
xnor

@xnorいいえ、そうではありません。
マーティンエンダー14

ソリューションを1つだけ印刷する必要がありますか、それともすべてを印刷することはできますか?
寂部14

@ジャクベ1つだけください。すべて/最適なソリューションにボーナスを追加する必要がありましたが、私はそれを十分に早く考えていなかったので、任意の(1つの)ソリューションです。
マーティンエンダー14

回答:


10

Python 2、115バイト

n=input()
for F in range(4):
 t=[F];b=0;exec"x=(-n[b]-sum(t[-2:]))%4;t+=x,;b+=1;"*len(n)
 if x<1:print t[:-1];break

これは、マーティンと問題を話し合っている間に書いたプログラムのゴルフ版です。

入力は、STDINを介したリストです。出力は、解決策がある場合は最後に見つかった解決策を表すリストであり、ない場合はゼロです。例えば:

>>>
[1, 4, 2]
[2, 1, 1]
>>>
[1, 2]
0
>>>
map(int,"3442223221221422412334")
[2, 3, 3, 2, 1, 3, 2, 0, 0, 2, 1, 3, 2, 2, 0, 0, 2, 2, 3, 1, 1, 3]

Pyth、32 29バイト

V4J]NVQaJ%_+s>J_2@QN4)I!eJPJB

必須ポート。@ Jakube、3バイトの節約に感謝します。

入力方法は上記と同じですオンライン試してください


説明(長くて完全なロジック!)

まず、2つの基本的な観察結果:

  • 観察1:ブロックに触れる順序は関係ありません

  • 観察2:ブロックに4回触れると、1回触れることに相当する

言い換えると、解決策があれば、タッチの数が0から3の間である解決策があります。

モジュロ4は非常に優れているので、ブロックでも同様に行います。この説明の残りの部分では、ブロックレベル0はブロックレベル4と同等です。

ここでa[k]、ブロックの現在のレベルでkあり、ソリューションでx[k]ブロックkに触れる回数であることを示しましょう。またn、ブロックの総数をみましょう。@Jakubeが指摘したように、解決策は次の条件を満たす必要があります。

  a[0]   + x[0] + x[1]
= a[1]   + x[0] + x[1] + x[2]
= a[2]          + x[1] + x[2] + x[3]
= a[3]                 + x[2] + x[3] + x[4]
...
= a[n-1]                                     ...  + x[n-2] + x[n-1] + x[n]
= a[n]                                       ...           + x[n-1] + x[n]
= C

ここCで、すべてのブロックが最終レベルであり、0から3までの間(レベル4をレベル0として扱っていることを思い出してください)であり、上記の式はすべて4を法とする合同です。

ここからがおもしろい部分です。

  • 観察3:ソリューションが存在する場合、最終ブロックレベルのソリューションが存在します0 <= C <= 3

ブロックの数に基づいて、3例は、それらのそれぞれの説明が同じである3を法があります-任意の数のブロックのために、あなたが一度にそれらのそれぞれに触れた場合、増大し、ブロックのサブセットが存在するすべてのことによって、ブロックレベルは正確に1。

0 mod 3 (touch every third block starting from the second):
    .X. / .X. / .X.

1 mod 3 (touch every third block starting from the first):
    X. / .X. / .X. / .X

2 mod 3 (touch every third block starting from either the first or second):
    X. / .X. / .X. / .X.
    .X. / .X. / .X. / .X

これは、とに4つのソリューションが0 mod 3あり1 mod 3、通常はに16のソリューションがある理由を説明してい2 mod 3ます。既に解決策がある場合は、上記のようにブロックをタッチすると、別の解決策が得られ、最終的にはより高いブロックレベルになります(ラップアラウンド)。

これはどういう意味ですか?必要な最終ブロックレベルCを選択できます!を選んC = 0でみましょう、これはバイトを節約するからです。

方程式は次のようになります。

0 = a[0] + x[0] + x[1]
0 = a[1] + x[0] + x[1] + x[2]
0 = a[2] + x[1] + x[2] + x[3]
0 = a[3] + x[2] + x[3] + x[4]
...
0 = a[n-1] + x[n-2] + x[n-1] + x[n]
0 = a[n] + x[n-1] + x[n]

そして再配置:

x[1] = -a[0] - x[0]
x[2] = -a[1] - x[0] - x[1]
x[3] = -a[2] - x[1] - x[2]
x[4] = -a[3] - x[2] - x[3]
...
x[n] = a[n-1] - x[n-2] - x[n-1]
x[n] = a[n] - x[n-1]

ですから、もしあればx[0]、最後を除くすべての方程式を使用して、他のすべてを見つけることができますx[k]。最後の方程式は、確認する必要がある追加の条件です。

これにより、アルゴリズムが得られます。

  • すべての値を試してください x[0]
  • 上記の式を使用して、他のすべてを解決します x[k]
  • 最後の条件が満たされているかどうかを確認します。その場合、ソリューションを保存します。

これで上記のソリューションが得られます。

では、なぜ解決策が得られないの2 mod 3ですか?これらの2つのパターンをもう一度見てみましょう。

X. / .X. / .X. / .X.
.X. / .X. / .X. / .X

次に、これらの位置での方程式、つまり最初の方程式を考えます。

0 = a[0] + x[0] + x[1]
0 = a[3] + x[2] + x[3] + x[4]
0 = a[6] + x[5] + x[6] + x[7]
0 = a[9] + x[8] + x[9] + x[10]

それらを追加します。

0 = (a[0] + a[3] + a[6] + a[9]) + (x[0] + x[1] + ... + x[9] + x[10])

2番目の場合:

0 = a[1] + x[0] + x[1] + x[2]
0 = a[4] + x[3] + x[4] + x[5]
0 = a[7] + x[6] + x[7] + x[8]
0 = a[10] + x[9] + x[10]

それらを再度追加します。

0 = (a[1] + a[4] + a[7] + a[10]) + (x[0] + x[1] + ... + x[9] + x[10])

その場合(a[1] + a[4] + a[7] + a[10])(a[0] + a[3] + a[6] + a[9])同じではありません、我々は解決策を持っていません。しかし、それらが等しい場合、16のソリューションが得られます。これはそのn = 11場合でしたが、もちろんこれは任意の数に一般化されます2 mod 3-2番目から始まる3番目ごとの要素の合計を取り、最初から3番目ごとの要素の合計と比較します。

最後に、x[0]すべての可能性を試す代わりに、何をすべきかを理解することは可能ですか?結局、ターゲットレベルCを0に制限しているためx[0]0 mod 3or 1 mod 3ケース(として4 solutions / 4 final levels = 1 solution for a specific final level)でソリューションを提供するのは1つだけです。

答えは...はい!これを行うことができます0 mod 3

 .X..X
.X..X.

次のように変換されます:

0 = a[2] + x[1] + x[2] + x[3]   -> 0 = (a[2] + a[5]) + (x[1] + ... + x[5])
0 = a[5] + x[4] + x[5]          /


0 = a[1] + x[0] + x[1] + x[2]   -> 0 = (a[1] + a[4]) + (x[0] + x[1] + ... + x[5])
0 = a[4] + x[3] + x[4] + x[5]   /

減算は以下を提供します:

x[1] = (a[2] + a[5]) - (a[1] + a[4])

同様に、1 mod 3このパターンを実行できます。

 .X..X.
X..X..X

与えるもの:

x[0] = (a[2] + a[5]) - (a[0] + a[3] + a[6])

これらはもちろん、インデックスを3ずつ増やすことで一般化します。

ために2 mod 3、我々はすべてのブロックをカバーする2つのサブセットを持っているので、我々は実際にいずれかを選ぶことができますx[0]。実際、これはx[0], x[1], x[3], x[4], x[6], x[7], ...(基本的に2 mod 3、いずれかのサブセットでカバーされていないため、と一致しないインデックス)に当てはまります。

したがって、x[0]すべての可能性を試す代わりに、選択する方法があります...

...しかし、悪いニュースはこれがバイト(124バイト)を節約しないことです:

def f(n):s=[];L=len(n);B=sum(n[~-L%3::3])-sum(n[-~L%3::3]);x=A=0;exec"s+=B%4,;A,B=B,-n[x]-A-B;x+=1;"*L*(L%3<2or B<1);print s

賢い。スライスする代わりに最後の要素をポップする場合JHと2文字の代わりに1文字を保存できますPJ<J_1V4J]NVQaJ%_+s>J_2@QN4)I!eJPJB
寂部14

@ジャクベああありがとう。ポップを読んだとき、リストから削除するときに最後の要素を返すPythonポップのようなものだと思いました。今ではそうではないことがわかります。
Sp3000

4

Pyth、72 76 73 66 39 38文字

Ph+f!eTmu+G%+&H@G_3-@QH@QhH4UtQd^UT2]Y

編集4:実現し、計算のことQ[N]-Q[N+1]+solution[-3]とはQ[-2]-Q[-1]+solution[-3]IDENTです。したがって、ソリューションを1だけ過剰計算し、最後のエントリが0であるソリューションをフィルタリングします。次に、最後のエントリをポップします。幸いなことに、このアプローチでは特別なケースに特別な処置は必要ありません。-27文字

編集3: FryAmTheEggmanからいくつかのゴルフトリックを適用する:-7キャラクター

編集2:フィルター、リデュース、マッピングを使用:-3文字

編集1:私の最初のバージョンでは、解決策がなければ、何も印刷しませんでした。私はそれが許可されていないと思うので、+ 4文字です。

整数のリストを入力として期待し[1,4,2]、有効なソリューションが[2,0,1]あればそれを出力し、そうでなければ空のリストを出力します[]

説明:

ましょうQ5つのレベルYのリストとソリューションのリスト。次の方程式が成り立つ必要があります。

  Q0 + Y0 + Y1 
= Q1 + Y0 + Y1 + Y2
= Q2      + Y1 + Y2 + Y3
= Q3           + Y2 + Y3 + Y4
= Q4                + Y3 + Y4

したがって、Y0and を使用するとY1、次の方法Y2Y3Y4を計算できます。

Y2 = (Q0 - Q1     ) mod 4
Y3 = (Q1 - Q2 + Y0) mod 4
Y4 = (Q2 - Q3 + Y1) mod 4

最後のレベルを除くすべてのレベルが等しい(等式を使用しなかったため= Q4 + Y3 + Y4。この最後のレベルも他のレベルと等しいかどうかを確認するために、単純にチェックでき(Q3 - Q4 + Y2) mod 4 == 0ます。Y5。ソリューションの6番目の部分を計算する場合、それがゼロかどうかを簡単に確認できます。

私のアプローチでは[0,0]、考えられるすべての開始(、〜[3,3])を繰り返し処理し、length(input)-1個の追加エントリを計算し、ゼロで終わるすべてのソリューションをフィルタリングします。

mu+G%+&H@G_3-@QH@QhH4UtQd^UT2   generates all possible solutions

基本的には次のとおりです。

G = start value           //one of "^UT2", [0,0], [0,1], ..., [9,9]
                          //up to [3,3] would be enough but cost 1 char more
for H in range(len(Q)-1): //"UtQ"
   G+=[(H and G[-3])+(Q(H)-Q(H+1))%4] //"+G%+&H@G_3-@QH@QhH4"
   //H and G[-3] is 0, when H is empty, else G[-3]

次に、有効なものについてこれらの可能なソリューションをフィルタリングします:

f!eT //only use solutions, which end in 0

このソリューションのリストに空のリストを追加して、少なくとも1つのアイテムが含まれるようにします

 +....]Y

そして最初の解決策を取り、h最後の要素pをポップして印刷します

 Ph

ブロックが1つしかない場合、これも機能することに注意してください。私のアプローチでは、開始位置[0,0]を取得し、それを拡張しません。最後のエントリは0なので、ソリューション[0]を出力します。

2番目の特別なケース(2ブロック)は、結局それほど特別ではありません。なぜか、私が以前に物事を過度に複雑にした理由。


何も印刷しないのが唯一のケースである場合、何も印刷しないのは解決策がないと思います。けれども確認するには、Get @MartinBüttnerする必要があるかもしれません
SP3000

?**lQ]0qhQeQ<lQ3h+f!%-+ePQ@T_3eQ4mu+G]%+&H@G_3-@QH@QhH4UttQd^UT2]Y66バイトです。パフォーマンスは少し低下しましたが、それでも1秒未満で最大のテストケースを実行します。いくつかのゴルフの説明が必要な場合は私に連絡してください。このコメントには十分なスペースがありません;)Pythを使用して楽しんでいるといいのですが:D
FryAmTheEggman 14

+<list><int>と同じ効果が+<list>]<int>あるので、最初のを削除できます]。また、非常に素晴らしい解決策。
isaacg 14

@isaacgも同じ~ですか?私がしようとしたときのように見えるしませんでした
SP3000

SP3000ただ、@置き換える~a- ~<list>]<int>に相当しますa<list><int>~is +=、while ais.append()
isaacg 14

3

ルビー、320 313文字

m=gets.chop.chars.map{|x|x.to_i-1}
a=m.map{0}
t=->n{m[n]+=1
m[n-1]+=1if n>0
m[n+1]+=1if n<m.size-1
m.map!{|x|x%4}
a[n]=(a[n]+1)%4}
t[0]until m[0]==1
(2...m.size).map{|n|t[n]until m[n-1]==1}
r=0
while m.uniq.size>1&&m[-1]!=1
(0...m.size).each_with_index{|n,i|([1,3,0][i%3]).times{t[n]}}
(r+=1)>5&&exit
end
$><<a*''

間違いなくもっとゴルフができます。解決できないパズルについては何も出力しません。

ゴルフされていないバージョン:

#!/usr/bin/ruby

nums = gets.chomp.chars.map {|x| x.to_i-1 }
touches = nums.map {0}

# our goal: make all the numbers 1
# utility function
touch = ->n {
    nums[n] += 1
    nums[n-1] += 1 if n > 0
    nums[n+1] += 1 if n < (nums.length-1)
    nums.map! {|x| x % 4 }
    touches[n] = (touches[n] + 1) % 4
}

# first, start with the very first number
touch[0] until nums[0] == 1

# then, go from index 2 to the end to make the previous index right
(2...nums.length).each {|n|
    touch[n] until nums[n-1] == 1
}

iters = 0
if nums.uniq.length != 1
    # I have no idea why this works
    while nums[-1] != 1
        (0...nums.length).each_with_index {|n, i|
            ([1, 3, 0][i % 3]).times { touch[n] }
        }
        if (iters += 1) > 5
            puts -1
            exit
        end
    end
end

puts touches * ''

さて、これは楽しかったです。以下に、基本的なアルゴリズムを示します。例の1つで示されている{n}ように、の上にn個の「タッチ」を表しnます。

we want each number to be a 1
first make the first number a 1
3442223221221422412334
2}
1242223221221422412334
 {3} now keep "touch"ing until the number to the left is a 1
1131223221221422412334
  {2}
1113423221221422412334
   {2}
1111243221221422412334
... (repeat this procedure)
1111111111111111111110

ここで少し困惑しました。どうすれば111...1110同じ数字のシリーズに変換できますか?そこで、私は自分のソリューションと正しいソリューションを比較しました(注:「タッチ」カウントは、入力が1インデックスで、出力が0インデックスであるため、すべてが本来の数より1大きくなります)。

3033233103233301320210
0330130000130202221111

各番号が正しい番号から1つ離れていることに気づいたmod 4ので+-s、s、sでマークしました=

3033233103233301320210 original program output
+-=+-=+-=+-=+-=+-=+-=+ amount to modify by (+1, -1, or 0 (=))
4334534404534602621511 result (the correct answer)

0330130000130202221111 (the original solution, digits equal to result mod 4)

私は時々 、最終的な結果だったことに気づいたまでそれは、しばらくの間、働いていた111...1111211...1113にも!幸いなことに、意味をなさないがうまく機能する魔法の公式を繰り返し適用すると、これらも整理されます。

だから、あなたはそれを持っています。意味のあることから始まりますが、それが進むにつれてますますいハックに分解されるプログラム。コードゴルフソリューションとしては非常に典型的だと思います。:)


1
あなたのコードの最後のコメントが大好きです:)。に変更exit if (r+=1)>5すると、2文字を保存できます(r+=1)>5&&exit。また、(code)while cond構文はより短いですwhile cond \n code \n end
クリスチャンルパスク14

2

Python 2、294,289,285,281 273バイト

n=input();l=len(n);s=[0]*l
for i in range(2,l):
 a=(n[i-2]-n[i-1])%4;s[i]+=a;n[i-1]+=a;n[i]+=a
 if i+1<l:n[i+1]+=a
 n=[a%4for a in n]
if l%3>1 and n!=[n[0]]*l:print"x"
else:
 for i in range(l%3,l-1,3):s[i]+=(n[l-1]-n[l-2])%4
 m=min(s);s=[(a-m)%4 for a in s];print s

デモ

これはさらにゴルフができると確信しています。

テストケースの結果は次のとおりです。

[1]
-> [0]

[1,1]
-> [0, 0]

[1,2]
-> x

[1,4,2]
-> [2, 0, 1]

[4,3,4]
-> [1, 0, 1]

[2,2,2]
-> [0, 0, 0]

[4,1,1,3]
-> [0, 2, 3, 0]

[3,2,4,4,4]
-> x

[2,3,4,3,2]
-> [0, 0, 3, 3, 1]

[4,2,1,2,3,2]
-> [2, 0, 2, 3, 3, 1]

[3,4,4,2,2,2,3,2,2,1,2,2,1,4,2,2,4,1,2,3,3,4]
-> [0, 3, 3, 0, 1, 3, 0, 0, 0, 0, 1, 3, 0, 2, 0, 2, 2, 2, 1, 1, 1, 1]

[2,2,2,3,1,2,4,4,3,3,4,4,3,2,1,3,1,3,2,2,4,4,2]
-> x

[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2]
-> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0]

[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,4]
-> [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 3, 3]

[4,1,2,2,2,4,1,3,1,4,4,4,1,1,4,4,4,1,4,3,2,4,3,4]
-> [1, 0, 3, 0, 0, 3, 2, 3, 1, 0, 0, 1, 0, 3, 1, 1, 3, 1, 0, 0, 2, 1, 2, 3]

アルゴリズムは最初に、最後のブロックを除くすべてのブロックの値が同じであることを確認します(最初の2つを除くすべてのブロックの「タッチカウント」を繰り返して追加します)。次に、ブロック数で許可されている場合((num_of_blocks - 1) % 3 != 1)、戻って、残りのブロックの値が最後のブロックと一致することを確認します。x解決策がない場合に印刷します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.