BattleBlock Theatre 퍼즐 해결


13

BattleBlock Theater 게임 에는 때때로 일반화 된 버전의 Lights Out 퍼즐이 포함되어 있습니다 . 3 개의 인접한 블록이 있으며 각 블록은 막대를 포함하여 1과 4 사이의 레벨을 나타냅니다. 예 :

|
||||
||

블록을 터치하면 해당 블록과 인접 블록이 레벨을 증가시킵니다 (4에서 1로 다시 감김). 세 블록 모두 동일한 레벨을 표시하면 퍼즐이 해결됩니다 (어떤 레벨은 중요하지 않음). 블록을 터치하는 순서는 중요하지 않으므로 각 블록을 얼마나 자주 터치하는지에 대한 솔루션을 나타냅니다. 위 입력에 대한 최적의 솔루션은 다음과 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 아니, 당신은하지 않습니다.
Martin Ender

정확히 하나의 솔루션을 인쇄해야합니까 아니면 모든 솔루션을 인쇄 할 수 있습니까?
Jakube

@Jakube 정확히 하나주세요. 나는 모든 / 최적의 솔루션에 대한 보너스를 추가해야했지만, 초기에는 충분히 생각하지 않았으므로 하나의 솔루션입니다.
Martin Ender

답변:


10

파이썬 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

이것은 Martin과 문제를 논의하면서 내가 작성한 프로그램의 골프 버전입니다.

입력은 STDIN을 통한 목록입니다. 출력은 솔루션이있는 경우 마지막으로 찾은 솔루션을 나타내는 목록이고없는 경우 0입니다. 예를 들면 다음과 같습니다.

>>>
[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

의무적 인 항구. 3 바이트 절약을위한 @Jakube에게 감사합니다.

입력 방법은 위와 동일 합니다 . 온라인으로 시도하십시오 .


설명 (논리로 가득하고 길다!)

먼저 두 가지 기본 관찰 :

  • 관찰 1 : 블록을 터치하는 순서는 중요하지 않습니다.

  • 관찰 2 : 블록을 4 번 터치하면 한 번만 터치하는 것과 같습니다.

다시 말해, 솔루션이 있다면 터치 횟수가 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의 블록 수에 따라 세 가지 경우가 있습니다. 각 블록에 대한 설명은 동일합니다.-여러 블록에 대해 한 번만 터치하면 모든 블록 레벨이 증가 하는 블록 의 하위 세트가 있습니다. 정확히 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

여기에는 0 mod 31 mod 3에 대한 4 가지 솔루션이 있으며 일반적으로에 대한 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합니까? 이 두 패턴을 다시 살펴 보겠습니다.

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])

두 번째로 :

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두 번째부터 시작하는 모든 세 번째 요소의 합을 취하고 첫 번째부터 시작하는 모든 세 번째 요소의 합과 비교되는 임의의 숫자로 일반화됩니다 .

이제 마지막으로, x[0]모든 가능성을 시도하는 대신 무엇이 필요한지 알아낼 수 있습니까? 결국 목표 수준 C을 0으로 제한했기 때문에 or의 경우 (as )에 x[0]솔루션을 제공하는 것은 하나뿐입니다 .0 mod 31 mod 34 solutions / 4 final levels = 1 solution for a specific final level

대답은 ... 그렇습니다! 우리는 이것을 위해 할 수 있습니다 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, 우리는 모든 블록을 다루는 두 개의 부분 집합이 있기 때문에 실제로 어떤 것을 선택할 수 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

영리한. 슬라이싱 대신 마지막 요소 를 표시 하는 경우 J대신 H2자를 사용하여 1 개의 문자를 저장할 수 있습니다 PJ. <J_1. V4J]NVQaJ%_+s>J_2@QN4)I!eJPJB
Jakube

@Jakube 아 감사합니다. pop을 읽을 때 목록에서 제거하는 동안 마지막 요소를 반환하는 Python pop과 같다고 생각했습니다. 이제는 그렇지 않습니다.
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]동일 하다는 것을 깨달았습니다. 따라서 솔루션을 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

우리가 어떤을 사용 따라서 경우 Y0Y1, 우리가 계산할 수 있습니다 Y2, Y3그리고 Y4다음과 같은 방법으로한다.

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

마지막 exept 모든 수준은 우리가 방정식을 사용하지 않았기 때문에 (동일보다 = Q4 + Y3 + Y4. 마지막 하나는 경우 확인하려면 다른 수준으로 동일하면, 우리는 간단하게 확인할 수 있습니다 (Q3 - Q4 + Y2) mod 4 == 0. 공지 사항, 왼쪽의 값이 될 것이라고 Y5솔루션의 6 번째 부분을 계산하면 0인지 간단히 확인할 수 있습니다.

내 접근 방식에서는 가능한 모든 시작 ( [0,0], to [3,3])을 반복하고 length (input) -1 더 많은 항목을 계산하고 0으로 끝나는 모든 솔루션을 필터링합니다.

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

이 솔루션 목록에 빈 목록을 추가하여 하나 이상의 항목을 포함시킵니다.

 +....]Y

첫 번째 해결책을 취하고 h마지막 요소를 팝하여 p인쇄하십시오.

 Ph

블록이 하나 뿐인 경우에도 작동합니다. 내 접근 방식에서 시작 위치 [0,0]을 가져 와서 확장하지 않습니다. 마지막 항목은 0이므로 솔루션 [0]을 인쇄합니다.

두 번째 특별한 경우 (2 블록)는 결국 그렇게 특별하지 않습니다. 확실하지 않은 이유는 무엇입니까?


나는 아무것도 인쇄하지 않는 유일한 해결책이라면 아무것도 인쇄하지 않아도된다고 생각합니다. 그래도 @ MartinBüttner를 확인해야 할 수도 있습니다.
Sp3000

?**lQ]0qhQeQ<lQ3h+f!%-+ePQ@T_3eQ4mu+G]%+&H@G_3-@QH@QhH4UttQd^UT2]Y66 바이트입니다. 성능이 약간 떨어졌지만 여전히 <1s에서 가장 큰 테스트 사례를 실행합니다. 당신이 골프의 일부에 대한 설명을 원한다면 저에게 핑; 이 댓글에 충분한 공간이 없습니다) 희망 당신은 Pyth 사용 즐기고있다 : D를
FryAmTheEggman

+<list><int>와 동일한 효과가 +<list>]<int>있으므로 첫 번째를 제거 할 수 있습니다 ]. 또한 매우 좋은 해결책입니다.
isaacg

@isaacg도 마찬가지 ~입니까? 내가 시도했을 때처럼 보이지 않았다
Sp3000

@ SP3000 그냥 교체 ~a- ~<list>]<int>에 해당합니다 a<list><int>. ~되는 +=동안, a이다.append()
isaacg

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 * ''

좋아, 이거 재미 있었어. 다음 은 예제 중 하나에서 설명하는 것처럼 {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 인덱싱되기 때문에 모든 것보다 하나 더 큽니다).

3033233103233301320210
0330130000130202221111

각 숫자가 올바른 숫자와 하나 떨어져 있음을 알았 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...11112또는 11...1113뿐만 아니라! 다행스럽게도 의미가 없지만 작동하는 마법의 공식을 반복적으로 적용하면 이러한 것들을 분류 할 수 있습니다.

그래서 거기 있습니다. 이해하기 시작하지만 점점 더 추악한 해킹으로 분해되는 프로그램. 코드 골프 솔루션의 경우 매우 일반적이라고 생각합니다. :)


1
나는 당신의 코드에서 마지막 주석을 좋아합니다 :). 로 변경 exit if (r+=1)>5하여 2 개의 문자를 저장할 수 있습니다 (r+=1)>5&&exit. 또한 (code)while cond구문이보다 짧습니다 while cond \n code \n end.
Cristian Lupascu

2

파이썬 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.