CJam, 33 32 20 19 17 байт
Пересмотренная версия с массовой поддержкой @Sp3000 и @MartinBüttner:
<textarea id=O cols="20" rows="8">AAAAAABBBBBBBBB
AABBBBBBBBBBBBB
AAAAAAAAAABBBBB
AABBBBBBBBBBBBB
AAAAAAAAAAAAAAB</textarea>
<button id=P>Test</button>
<p id=Q>Result: </p>
Попробуйте онлайн
Взносы
- @Sp3000 предложил критическое упрощение моего исходного алгоритма.
- @MartinBüttner применил свои безумные навыки игры в гольф к пересмотренному подходу, что почти наверняка привело к более короткому коду, чем я мог бы придумать, даже после рассмотрения упрощения.
Алгоритм и доказательство
Ниже объясняются критерии раздвижения головоломки по горизонтали. Вертикальный регистр можно определить, просмотрев столбцы вместо строк или транспонировав матрицу символов и снова просмотрев строки.
Я буду использовать термин «растяжение» для обозначения максимальной последовательности одинаковых букв. Например, следующие строки имеют 1, 2 и 3 растяжения соответственно:
a=x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))
P.onclick=_=>Q.innerHTML='Result: '+a(O.value)
Я также буду использовать термин «связанный» для обозначения ряда/головоломки, которая не может разъединиться.
Ключевое наблюдение состоит в том, что головоломка может развалиться тогда и только тогда, когда все строки имеют не более двух отрезков.. Или наоборот, это блокируется тогда и только тогда, когда есть строка, содержащая более двух отрезков.
Следующее, возможно, не квалифицируется как строгое математическое доказательство, но я считаю, что оно дает убедительное объяснение, почему это должно быть так.
Легко увидеть, что головоломка переплетена, если в ней есть ряды, состоящие более чем из двух отрезков. Глядя на ряд с 3 растяжками:
0
ясно, что это не дает головоломке развалиться, потому что 1
stretch is locked between the x=>(t=s=>!/AB+A|BA+B/.test(s),z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),t(x)|t(z))
тянется. Это означает, что ряды взаимосвязаны, что, в свою очередь, делает всю головоломку взаимосвязанной.
Противоположное направление доказательства не столь очевидно. Нам нужно показать, что не существует взаимосвязанных головоломок, в которых все строки имеют только 1 или 2 участка. Начну с пары наблюдений:
- Ряды только с одним растяжением не способствуют сцеплению головоломки, поскольку они могут скользить в любом направлении без каких-либо столкновений.
- Если все строки с двумя растяжениями имеют одинаковый порядок
x=>!(y=/AB+A|BA+B/).test(x)|(z=[],x.split`
`.map(b=>b.split``.map((c,i)=>z[i]+=c)),!y.test(z))
and ⊂∘⍉,⊂ The matrix and its transpose.
{...}¨ For each of them:
2≠/⍵ On each row, replace each adjacent pair with 1 if they differ, with 0 otherwise
2>+/ Take the sum on each row and check that it's less than 2
∧/ AND over all rows
∨/ OR over the resulting two values
, загадка явно не взаимосвязана. В этом случае все 1
cells are left of all f ← (∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂
f 4 3 ⍴ 'AAAAABBBAAAB'
ячеек или наоборот, и при раздвижении двух частей не возникает столкновений.
Единственный сложный случай — это головоломки, в которых есть строки с двумя участками разного порядка. Я собираюсь показать, что такие головоломки не существует по заданным характеристикам. Чтобы продемонстрировать это, давайте посмотрим на частичную головоломку, имеющую такую конфигурацию, где AAAA
ABBB
AAAB
are wildcards:
0
Теперь в спецификации говорится, что оба 1
and (∨/{∧/2>+/2≠/⍵}¨)⊂∘⍉,⊂
во всех действительных головоломках ячейки просто соединены между собой. Чтобы сделать {a,a,b,a,a,a,b,b,b,b,b,b,b,b}
cells connected in the partial puzzle above, we have two options:
Мы обходим один из участков {a,a,b,a,a}
, for example:
{a,a,b,b,b}
Для этого мы неизбежно расширяем одну из строк, чтобы у нее было 3 отрезка, так что это никогда не даст нам корректную головоломку, в которой все строки имеют не более 2 отрезков.
Соединяем их по прямой дорожке:
g=Max[Length/@Split/@#]<3&;g[c=Characters@StringSplit@#]||g@Thread@c&
<pre id=O></pre>
cells are now simply connected, and there are still no rows with more than 2 stretches. However, the f=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]==10))
// 4 bytes more- for any symbol, not just 1 and 0 (for instance A and B):
g=s=>[w=~s.search`
`,-w,-1,1].some(o=>![...s].some((x,p)=>x+s[p+o]=='AB'))
//TEST
console.log=x=>O.innerHTML+=x+'\n'
testOk = [
'111\n100\n111',
'10',
'0\n1',
'01\n01',
'000\n111',
'00001\n01111',
'0110\n0110\n0000',
'000000111111111\n001111111111111\n000000000011111\n001111111111111\n000000000000001',
'000000000000\n010101010101\n111111111111',
'10000011\n11000111\n11101111\n11101111\n11101111\n11111111\n11111111',
'000\n100\n000'
]
testKo = [
'1111\n1001\n1011',
'1111\n1001\n0011',
'1111111\n1111101\n0000001\n1111111',
'1000\n1010\n1110\n0010\n0000',
'0000000\n0111110\n0000010\n1111110',
'10000011\n11000111\n11101111\n11101111\n11100111\n11111111\n11111111',
'000\n010\n110\n010\n000'
]
console.log('Expecting true')
testOk.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
console.log('Expecting false')
testKo.forEach(t=>console.log(t+'\n'+f(t)+'\n'))
ячейки также должны быть просто соединены. Прямой путь теперь заблокирован подключенным a=> !/* check string horizontally */ || !/* check string vertically by transposing it and
running the same horizontal check */
a=> !a.match(R=/AB+A|BA+B/) || !/* ... */
// check for lines containing something like BAAAAAB or ABBBBBBBA
// this is the only way something can get blocked horizontally
// eg AAAAAAA
// AAABAAA <<< note the B in the middle of As here
// AAABBBB <<< blocked from being pulled out horizontally
// AAAAAAA
a=> /* ... */ ||!( a = a.split('\n').map(b=> b.split('')) ) // split a into 2D array
[0].map((_,c)=>a.map(d=>d[c])) // transpose it
.some(e=>e.join``.match(R)) // run the check again using `some` to go line by line
// which is shorter than .join().match() outside
a=> !/* returns null if no horizontal obstacles and an array if there are */
|| !/* same thing */
// negate both to cast to a boolean (false if obstacles, true if not)
// an input can only be unslidable if both directions are blocked
// so (no obstacles vertically? || no obstacles horizontally?) gives the answer
cells, and the only way to connect the <textarea id='S'>BAAAAABB
BBAAABBB
BBBABBBB
BBBABBBB
BBBABBBB
BBBBBBBB
BBBBBBBB</textarea>
<p id='P'></p>
ячеек заключается в циклическом обходе одного из участков slidey=
// code
a=>!a.match(R=/AB+A|BA+B/)||!(a=a.split`
`.map(b=>b.split``))[0].map((_,c)=>a.map(d=>d[c])).some(e=>e.join``.match(R))
// IO
var S =document.getElementById('S');
S.onkeyup = _=> document.getElementById('P').innerText = slidey(S.value);
document.getElementById('P').innerText = slidey(S.value);
cells. This leads back to case 1, where we can't do that without creating rows of 3 stretches.
Для подсчета растяжек реализация использует оператор CJam RLE.
Объяснение кода
find
||answer||
Улитки, 14
T
Если головоломку можно раздвинуть, она печатает область ввода. В противном случае он печатает 0.
Для более крупных примеров это немного медленно, так как для этого требуется факториал времени в области сетки.
a
||answer||
JavaScript (ES6), 108 107 98 91 82 байта
g
Живая демонстрация. Протестировано в Firefox. Принимает входные данные в виде строки, разделенной символом новой строки.
Правки:
- Сохранено 1 байт путем изменения
A
to a literal newline.
- Сэкономлено 9 байт за счет выполнения теста RegExp непосредственно на многострочной строке вместо преобразования в массив.
- Устранено еще 9 байтов за счет использования функции определения массива для разделения строки, перемещение ! в
B
function and calling RegExp directly on array instead of using /AB+A|BA+B/
.
- Продолжил арифметическую последовательность, сохранив еще 9 байт. Выполнил модуль индекса вместо разделения массива на символы новой строки перед транспонированием.
Как это работает
Предыдущая версия:
g
- Примите вводные данные
"undefinedAAABBA"
and split it by newlines into an array of strings.
- Транспонировать
T
and store it in T
. Использовать i
to iterate over each element of i
, разделите строку на массив символов и используйте map
again to append the a
-й символ в строке map
th line of T
. Поскольку каждый элемент a
is uninitialized, it will end up looking something like a
, но это не будет иметь значения.
- Создайте функцию тестирования на основе RegExp.
a=>(T=[],a.split`
`.map(s=>s.split``.map((c,i)=>T[i]+=c)),!T.find(g=s=>/AB+A|BA+B/.test(s)))|!g(a)
that matches the pattern find
. Если оно совпадает, фигуры фиксируются в заданном направлении, потому что тогда существует набор g
s sandwiched between two or more \n
с или наоборот.
- Используйте функцию тестирования
a=>!(T=[],R=/AB+A|BA+B/).test([...a].map((c,i)=>T[i%-~a.search`
`]+=c))|!R.test(a)
to test all elements of the block ,, the program will print the number of starting cells matching this pattern
o ,, pick a cardinal direction
(
t ,, teleport to any cell on the grid
\B+ ,, match "B" 1 or more times, moving in the direction set by 'o'.
,, when a cell is matched, it gets slimed and can't be matched again.
~ ,, match an out-of-bounds cell
)+ ,, do parenthesized instructions 1 or more times
!( ,, the following must not match:
t\B ,, teleport to some cell and match 'B'
и его транспонировать o(t\B+~)+!(t\B
for a match using qN/ Get input and split at newlines.
_z Make a transposed copy.
] Wrap the original and transposed puzzle in an array so that we can
loop over the two.
{ Start of loop over original and transposed puzzle.
:e` Apply RLE to all rows.
z, Transpose the matrix with the RLE rows, and take the element count of the
result. Or in other words, take the column count. This will be the length
of the longest row after RLE.
3< Check the length for less than 3.
}/ End of loop over original and transposed puzzle.
| Or the results of the two.
. Если оба совпадают, то части блокируются в обоих направлениях, поэтому выведите ложное значение, в противном случае — истинное.