Determinar se existe um movimento em um jogo Bejeweled / match 3

20

fundo

Nos jogos Bejeweled e similares, o jogador deve trocar quaisquer duas gemas adjacentes (sem diagonais) em uma grade de gemas 8x8 para combinar três da mesma cor seguidas. As gemas podem ser combinadas horizontalmente ou verticalmente. A jogabilidade continua até que não exista nenhum movimento que possa ser feito, resultando em três seguidas, altura em que o jogo termina.

Tarefa

O objetivo é escrever um programa que determine se um jogo de Bejeweled ainda não terminou. Em outras palavras, ele deve verificar se há uma possível movimentação que faça pelo menos três seguidas. Pode haver mais de três gemas seguidas e ainda é uma jogada válida.

Entrada

Seu programa deve aceitar via entrada padrão uma representação 8x8 de uma grade Bejeweled. Cada uma das sete cores de gemas será representada por um dígito de 1 a 7. Cada linha conterá uma linha e serão inseridas 8 linhas, cada uma consistindo em 8 dígitos. Veja os exemplos. Você pode assumir que a entrada sempre seguirá esse formato e nunca conterá três em uma linha.

Saída

O programa deve então enviar (para saída padrão) yesou nodependendo da existência de pelo menos um movimento válido que resultaria em três ou mais gemas seguidas. Seu programa não deve produzir nada além de uma única instância de um yesou outro no.

Regras

Seu programa não deve usar arquivos ou recursos externos, argumentos de linha de comando ou exigir um determinado nome de arquivo. O programa com o menor número de bytes em seu código-fonte vence.

Exemplos

Entrada:

12314131
13224145
54762673
61716653
61341144
23453774
27645426
75575656

Saída: yes

Entrada:

35261546
76421754
15743271
62135642
35617653
64565476
54427254
15635465

Saída: no

Veja a resposta do MT0 abaixo para casos de teste adicionais.

bdr9
fonte
São apenas linhas ou colunas também.
TheDoctor
Colunas @TheDoctor também. Quando uso a frase "três em linha", quero dizer que elas devem estar alinhadas na direção horizontal ou vertical.
Bdr9 #
@ bdr9 você pode querer editar que em
John Dvorak
@JanDvorak Done.
Bdr9
Também convém editar se 4+ em uma linha for permitido.
236 Justin Justin

Respostas:

12

Solução original: JavaScript - 261 255 228 227 179 153 caracteres

/(\d)(\1(\d|.{6}|.{9})|(\d|.{6}|.{9})\1|.{7}\1(.|.{9})|(.|.{9})\1.{7}|(.{7,9}|.{17})\1.{8}|.{8}\1(.{7,9}|.{17}))\1/.test(s.replace(/\n/g,'A'))?'yes':'no'

Supondo que a sequência a ser testada esteja na variável s(para torná-la uma função f, inclua f=s=>no início do código ou, caso contrário, obtenha a entrada de um prompt e substitua spor prompt()).

As saídas são para o console.

3 rd Solução: JavaScript (ECMAScript 6) - 178 Characters

p=x=>parseInt(x,36);for(t="2313ab1b8a2a78188h9haj9j8iaiir9r",i=v=0;s[i];i++)for(j=0;t[j];v|=s[i]==s[i+a]&s[i]==s[i+b]&i%9<8&(b>3|(i+b-a)%9<8))a=p(t[j++]),b=p(t[j++]);v?'yes':'no'

Tomei a 2 nd solução, abaixo, (que usa expressões regulares para verificar se há caracteres em determinadas configurações) e reformulado para apenas verificar a string de caracteres idênticos nas mesmas configurações sem o uso de expressões regulares.

A cadeia de base-36 "2313ab1b8a2a78188h9haj9j8iaiir9r"dá pares de deslocamentos para verificar - ou seja, os pares de 23resultados na verificação de se i th carácter é idêntico ao do (i + 2) th personagem e a (i + 3) th personagem (o equivalente da expressão regular (.).\1\1- com algumas verificações adicionais para garantir que o caractere não idêntico não seja uma nova linha).

2 nd solução: JavaScript (ECMAScript 6) - 204 caracteres

p=x=>parseInt(x,18);g=a=>a?a>1?"(.|\\n){"+a+"}":".":"";f=(x,a,b)=>RegExp("(.)"+g(a)+"\\1"+g(b)+"\\1").test(x);for(t="10907160789879h8",i=v=0;t[i];v|=f(s,x,y)||f(s,y,x))x=p(t[i++]),y=p(t[i++]);v?'yes':'no'

Constrói várias expressões regulares (veja abaixo para obter mais detalhes) usando pares de valores retirados da string Base-18 10907160789879h8e realiza ORtodos os testes. Para reduzi-lo ainda mais, observe que as expressões regulares vêm em pares, onde um é o "reverso" do outro (ignorando as Expressões regulares de 3 em linha horizontal e verticalmente, pois o OP afirma que nunca estarão presentes - se você deseja adicionar esses testes novamente no anexo 0088à string Base-18).

Explicação

Comece com 16 expressões regulares, cobrindo todas as configurações possíveis de movimentos válidos:

REs=[
    /(\d)\1\1/,                 // 3-in-a-row horizontally
    /(\d).\1\1/,                // 3-in-a-row horizontally after left-most shifts right
    /(\d)\1.\1/,                // 3-in-a-row horizontally after right-most shifts left
    /(\d)(?:.|\n){9}\1\1/,  // 3-in-a-row horizontally after left-most shifts down
    /(\d)(?:.|\n){7}\1.\1/, // 3-in-a-row horizontally after middle shifts down
    /(\d)(?:.|\n){6}\1\1/,  // 3-in-a-row horizontally after right-most shifts down
    /(\d)\1(?:.|\n){6}\1/,  // 3-in-a-row horizontally after left-most shifts up
    /(\d).\1(?:.|\n){7}\1/, // 3-in-a-row horizontally after middle shifts up
    /(\d)\1(?:.|\n){9}\1/,  // 3-in-a-row horizontally after right-most shifts up
    /(\d)(?:.|\n){7,9}\1(?:.|\n){8}\1/, // 3-in-a-row vertically (with optional top shifting left or right)
    /(\d)(?:.|\n){7}\1(?:.|\n){9}\1/,   // 3-in-a-row vertically after middle shifts right
    /(\d)(?:.|\n){9}\1(?:.|\n){7}\1/,   // 3-in-a-row vertically after middle shifts left
    /(\d)(?:.|\n){8}\1(?:.|\n){7}\1/,   // 3-in-a-row vertically after bottom shifts right
    /(\d)(?:.|\n){8}\1(?:.|\n){9}\1/,   // 3-in-a-row vertically after bottom shifts left
    /(\d)(?:.|\n){17}\1(?:.|\n){8}\1/,  // 3-in-a-row vertically after top shifts down
    /(\d)(?:.|\n){8}\1(?:.|\n){17}\1/,  // 3-in-a-row vertically after bottom shifts up
];

( Nota: os regexs para-3-em-linha horizontal (0 ° ) e verticalmente (parte do 9 th ) são irrelevantes como os estados que OP entradas correspondentes estes nunca estarão presentes. )

Testar cada um deles em relação à entrada determinará se uma movimentação válida dessa configuração pode ser encontrada.

No entanto, as expressões regulares podem ser combinadas para fornecer 6:

/(\d)(?:.|(?:.|\n){9}|(?:.|\n){6})?\1\1/            // Tests 0,1,3,5
/(\d)\1(?:.|(?:.|\n){9}|(?:.|\n){6})?\1/            // Tests 0,2,6,8
/(\d)(?:.|\n){7}\1(?:.|(?:.|\n){9})\1/              // Tests 4,10
/(\d)(?:.|(?:.|\n){9})\1(?:.|\n){7}\1/              // Tests 7,11
/(\d)(?:(?:.|\n){7,9}|(?:.|\n){17})\1(?:.|\n){8}\1/ // Tests 9,14
/(\d)(?:.|\n){8}\1(?:(?:.|\n){7,9}|(?:.|\n){17})\1/ // Tests 9a,12,13,15

Eles podem ser combinados em uma única expressão regular:

/(\d)(?:.|(?:.|\n){9}|(?:.|\n){6})?\1\1|(\d)\2(?:.|(?:.|\n){9}|(?:.|\n){6})?\2|(\d)(?:.|\n){7}\3(?:.|(?:.|\n){9})\3|(\d)(?:.|(?:.|\n){9})\4(?:.|\n){7}\4|(\d)(?:(?:.|\n){7,9}|(?:.|\n){17})\5(?:.|\n){8}\5|(\d)(?:.|\n){8}\6(?:(?:.|\n){7,9}|(?:.|\n){17})\6/

O que só precisa ser testado com relação à entrada.

Casos de teste

Alguns casos de teste que outras pessoas podem achar úteis (não estão de acordo com o formato de entrada de uso apenas dos dígitos 1 a 7, mas são facilmente corrigidos e são apenas uma grade 8x4 - pois esse é o mínimo necessário para um teste de todas as entradas válidas )

No formato de um mapa da sequência de entrada com a qual das 16 expressões regulares acima corresponde.

Tests={
    "12345678\n34567812\n56781234\n78123456": -1, // No Match
    "12345678\n34969912\n56781234\n78123456": 1,    // 3-in-a-row horizontally after left-most shifts right 
    "12345678\n34567812\n59989234\n78123456": 2,    // 3-in-a-row horizontally after right-most shifts left
    "12345978\n34567899\n56781234\n78123456": 3,    // 3-in-a-row horizontally after left-most shifts down
    "12345978\n34569892\n56781234\n78123456": 4,    // 3-in-a-row horizontally after middle shifts down
    "12345678\n34967812\n99781234\n78123456": 5,    // 3-in-a-row horizontally after right-most shifts down
    "12399678\n34967812\n56781234\n78123456": 6,    // 3-in-a-row horizontally after left-most shifts up
    "12345678\n34597912\n56789234\n78123456": 7,    // 3-in-a-row horizontally after middle shifts up
    "12345998\n34567819\n56781234\n78123456": 8,    // 3-in-a-row horizontally after right-most shifts up
    "12945678\n34597812\n56791234\n78123456": 9,    // 3-in-a-row vertically after top shifts right
    "12349678\n34597812\n56791234\n78123456": 9,    // 3-in-a-row vertically after top shifts left
    "12345978\n34569812\n56781934\n78123456": 10,   // 3-in-a-row vertically after middle shifts right
    "92345678\n39567812\n96781234\n78123456": 11,   // 3-in-a-row vertically after middle shifts left
    "12945678\n34967812\n59781234\n78123456": 12,   // 3-in-a-row vertically after bottom shifts right
    "12349678\n34569812\n56781934\n78123456": 13,   // 3-in-a-row vertically after bottom shifts left
    "12395678\n34567812\n56791234\n78193456": 14,   // 3-in-a-row vertically after top shifts down
    "12345698\n34567892\n56781234\n78123496": 15,   // 3-in-a-row vertically after bottom shifts up
    "12345678\n34567899\n96781234\n78123456": -1,   // No match - Matches (.)\1.\1 but not 3 in a row
    "12345679\n99567812\n56781234\n78123456": -1,   // No match - Matches (.).\1\1 but not 3 in a row
};

Editar 1

Substituir \ds por .- salva 6 caracteres.

Editar 2

Substitua (?:.|\n)por [\s\S]grupos extra-captura e removidos e referências anteriores atualizadas (conforme sugerido pelo m-buettner ) e adicionadas na saída yes / no.

Editar 3

  • Adicionada a solução ECMAScript 6 para criar as Expressões regulares individuais a partir de uma string Base-18.
  • Removidos os testes para 3 em linha horizontalmente (conforme sugerido por m-buettner ).

Editar 4

Adicionada outra solução (mais curta) e mais dois casos de testes não correspondentes.

Editar 5

  • Solução original reduzida, substituindo novas linhas por um caractere não numérico (conforme sugerido por VadimR ).

Editar 6

  • Solução original reduzida, combinando bits da expressão regular (conforme sugerido por VadimR ).
MT0
fonte
1
Ótima solução! Eu não teria pensado que o regex poderia funcionar. Por favor, inclua a ?'yes':'no'contagem de caracteres em sua justiça, porque ela está nos requisitos e todos os outros a estão usando.
bdr9
Obrigado pelos casos de teste adicionais, adicionei um link à sua resposta para que outras pessoas possam vê-los.
Bdr9
Uau. 1 para regex
DankMemes
H-mm, nenhum modificador em JS para .corresponder a qualquer caractere, incluindo nova linha? Com o Perl, o regexp combinado é uma mera string de 129 bytes (que, sendo preguiçoso, compilei com o Regexp :: Assemble ), portanto, todo o programa Perl tem cerca de 150 bytes.
User2846289
1
@VadimR Obrigado, mas você pode ir até substituindo ainda mais .{8}|.{9}com .{8,9}e .{7}|.{8}com.{7,8}
MT0
3

Python 383

Apenas uma única linha * de Python!

a=[list(l)for l in raw_input().split('\n')];z=any;e=enumerate;c=lambda b:z(all(p==b[y+v][x+u]for(u,v)in o)for y,r in e(b[:-2])for x,p in e(r[:-2])for o in [[(0,1),(0,2)],[(1,0),(2,0)]]);print z(c([[q if(i,j)==(m,n)else a[m][n]if(i,j)==(y+1,x+1)else p for j,p in e(r)]for i,r in e(a)])for y,t in e(a[1:-1])for x,q in e(t[1:-1])for n,m in((x+u,y+v)for u,v in[(1,0),(1,2),(0,1),(2,1)]))

* Bem, com ponto-e-vírgula, mas isso ainda não é trivial em python (os linux one-liners são divertidos! )

KSab
fonte
3
Upvoted para compreensões incompreensíveis :)
alexander-Brett
2

Node.js - Solução ingênua - 905 bytes

Bem, ainda não tenho respostas, então postarei uma solução realmente ingênua no Node.js

Ele realiza todos os movimentos possíveis e, em seguida, testa o painel resultante para ver se há três seguidos.

Golfed (com o compilador de fechamento do google) (algumas coisas hacky lá dentro, como! 0 e! 1; nem tenho certeza do que ele fez com minha troca XOR)

Array.prototype.a=function(){for(var f=[],d=0;d<this.length;d++)f[d]=this[d].a?this[d].a():this[d];return f};for(var a=[],b=0;8>b;b++)a[b]=[];for(b=2;b<process.argv.length;b++)for(var c=process.argv[b].split(""),e=0;e<c.length;e++)a[b-2][e]=parseInt(c[e],10);function h(){for(var d=l,f=0;f<d.length-2;f++)for(var g=0;g<d[f].length-2;g++){var k=d[f][g];if(k==d[f+1][g]&&k==d[f+2][g]||k==d[f][g+1]&&k==d[f][g+2])return!0}return!1}function m(){console.log("yes");process.exit()}for(b=0;b<a.length;b++)for(e=0;e<a[b].length;e++){var l=a.a();0!=b&&(l[b-1][e]^=l[b][e],l[b][e]^=l[b-1][e],l[b-1][e]^=l[b][e],h()&&m(),l=a.a());b!=a.length-1&&(l[b+1][e]^=l[b][e],l[b][e]^=l[b+1][e],l[b+1][e]^=l[b][e],h()&&m(),l=a.a());0!=e&&(l[b][e-1]^=l[b][e],l[b][e]^=l[b][e-1],l[b][e-1]^=l[b][e],h()&&m(),l=a.a());e!=a[b].length-1&&(l[b][e+1]^=l[b][e],l[b][e]^=l[b][e+1],l[b][e+1]^=l[b][e],h()&&m(),l=a.a())}console.log("no");

Observe que escrevi tudo isso no meu celular e não tenho tempo para testá-lo ou algo assim. Comente se houver algum erro, eu mesmo verificarei mais tarde.

A versão legível humana pré-golfe

// set it up
Array.prototype.clone = function() {
    var arr = [];
    for( var i = 0; i < this.length; i++ ) {
        if( this[i].clone ) {
             arr[i] = this[i].clone();
        } else {
             arr[i] = this[i];
        }
    }
};
var board=[];
for(var i=0;i<8;i++)board[i]=[];
for(var i=2;i<process.argv.length;i++){
    var row=process.argv[i].split("");
    for(var j=0;j<row.length;j++)board[i-2][j]=parseInt(row[j], 10);
}
// function to test
function testBoard(arr){
    for(var i=0;i<arr.length-2;i++){
        for(var j=0;j<arr[i].length-2;j++){
            var val=arr[i][j];
            if(val==arr[i+1][j] && val==arr[i+2][j])return true;
            if(val==arr[i][j+1] && val==arr[i][j+2])return true;
        }
    }
    return false;
}
// functions to exit
function yay(){console.log("yes");process.exit();}
function nay(){console.log("no");}
// super slow naive solution time
for(var i=0;i<board.length;i++){
    for(var j=0;j<board[i].length;j++){
        var newboard=board.clone();
        if(i!=0){
            newboard[i-1][j]=newboard[i-1][j]^newboard[i][j];// whoa, it's a
            newboard[i][j]=newboard[i-1][j]^newboard[i][j];  // cool algorithm
            newboard[i-1][j]=newboard[i-1][j]^newboard[i][j];// at least this 
                                                             // isn't all naive
            if(testBoard(newboard))yay();
            newboard=board.clone();
        }
        if(i!=board.length-1){
            newboard[i+1][j]=newboard[i+1][j]^newboard[i][j];
            newboard[i][j]=newboard[i+1][j]^newboard[i][j];
            newboard[i+1][j]=newboard[i+1][j]^newboard[i][j];
            if(testBoard(newboard))yay();
            newboard=board.clone();
        }
        if(j!=0){
            newboard[i][j-1]=newboard[i][j-1]^newboard[i][j];
            newboard[i][j]=newboard[i][j-1]^newboard[i][j];
            newboard[i][j-1]=newboard[i][j-1]^newboard[i][j];
            if(testBoard(newboard))yay();
            newboard=board.clone();
        }
        if(j!=board[i].length-1){
            newboard[i][j+1]=newboard[i][j+1]^newboard[i][j];
            newboard[i][j]=newboard[i][j+1]^newboard[i][j];
            newboard[i][j+1]=newboard[i][j+1]^newboard[i][j];
            if(testBoard(newboard))yay();
            newboard=board.clone();
        }
    }
}
nay();
DankMemes
fonte
Hah, na verdade, eu perdi o primeiro post por 10 minutos. Eu meio que gosto disso ...
DankMemes
Ah, exatamente o mesmo método que eu usei (código ingênuo, mas pequeno!). +1 por ser muito mais descritivo do que eu
KSab
Gostaria de saber se há um algoritmo mais eficiente ...
DankMemes
2

Perl, 114 96 95 93 92 87 86 85 bytes

Inclui + para -a0p

Execute com a entrada em STDIN:

bejeweled.pl
12314131
13224145
54762673
61716653
61341144
23453774
27645426
75575656
^D

bejeweled.pl:

#!/usr/bin/perl -a0p
$i/s%.%chop$F[$i++&7]%eg>3|/(.)((.|\H{6}|\H{9})\1|\H{7}\1.)\1/||redo;$_=$1?yes:n.o

Isso combina uma solução regex horizontal de direção única com rotações

Explicação:

Nesta solução, girarei repetidamente e faça os 4 testes a seguir:

/(.).\1\1/,      // 3-in-a-row horizontally after left-most shifts right
/(.)\C{9}\1\1/,  // 3-in-a-row horizontally after left-most shifts down
/(.)\C{7}\1.\1/, // 3-in-a-row horizontally after middle shifts down
/(.)\C{6}\1\1/,  // 3-in-a-row horizontally after right-most shifts down

Onde \Cestá "qualquer caractere" (ao contrário .disso, inclui nova linha). Exceto que isso \Cfoi preterido e leva a avisos, então eu uso \H(espaço não horizontal) em vez disso, o que é bom o suficiente para capturar todos os dígitos e nova linha.

Após 4 rotações, todos os 16 testes serão necessários.

-p                            Read lines from STDIN, print $_ at the end
-0                            No line ending => slurp ALL of STDIN
-a                            Split $_ into @F. Since there are no spaces
                              on the rows this means each element of @F is
                              1 row

    s%.%chop$F[$i++&7]%eg     Replace each row by the removed last column
                              This is therefore a left rotation. Very short
                              but at the cost of using @F. To make sure that
                              @F gets refilled from $_ each time I won't be
                              able to use while, until, eval or do$0 for the
                              loops but have to use redo. That costs a few
                              bytes but less than having to do my own split
$i/                      >3   The previous regex replacement always
                              returns 64 and each time through the loop $i is
                              increased by 64. So if this division reaches
                              4 all rotations have been done

/(.)((.|\H{6}|\H{9})\1|\H{7}\1.)\1/ This is the 4 regexes mentioned above
  ||redo                      Stop the loop if the regex matches or we
                              rotated 4 times
$_=$1?yes:n.o                If the regex matched $1 will be one of the
                              color digits (which cannot be 0) and this will
                              assign "yes" to $_. If the regex didn't match
                              in 4 times $1 will get its value from the last
                              succesful regex in scope which will be the one
                              from the rotation, but that one doesn't have
                              any () so $1 will be unset. So in case there
                              is no move $_ will be set to "no" (which needs
                              to be constructed because "no" is a keyword)
Ton Hospel
fonte
1

Python3, 314B

import itertools as T,copy
r=[]
K=range(8)
J=[list(input())for w in K]
P=T.product
f=lambda A:["yes"for b in[A[m][n:]for m,n in P(K,K[:6])]if b[0]==b[1]==b[2]]
for i,j,x in P(K,K,[0,1]):
 t=j+1-x
 if i+x<8and t<8:B=copy.deepcopy(J);B[i][j],B[i+x][t]=B[i+x][t],B[i][j];r+=f(B)+f(list(zip(*B)))
r+=["no"]
print(r[0])

Altere 8, 5 na linha 6 e 8s na linha 9 para manipular tamanhos de entrada arbitrariamente grandes; também não se importa com o valor de cada valor, para que você possa alimentá-lo:

absdefgh
sdkljahs
lsdfjasd
fjdhsdas
dkjhfasd
sdfhaskd
sdkfhkas
weriuwqe

e ele retornará yes.

Anotações

import itertools as T,copy 
            # itertools.product is going to save us lots of for loops
r=[]        # result
K=range(8)  # we can use range(8) everywhere, so this saves more than the usual R=range
J=[list(input())for w in K] 
            # input handling: keep everything as a length-1 string to avoid map(int,input())
P=T.product
f=lambda A:["yes"for b in[A[m][n:]for m,n in P(K,K[:6])]if b[0]==b[1]==b[2]] 
            # check the condition horiontally only. K[:6] is the same as range(5)
            # A[m][n:n+3] would be neater, but not actually needed
for i,j,x in P(K,K,[0,1]): 
            # <3 itertools.product! 3 for-loops without it.
            # NB we're only going right and downwards
 t=j+1-x
 if i+x<8and t<8: 
            # don't want out-of-bounds errors at the edges
  B=copy.deepcopy(J) 
            # preserve the reference array
  B[i][j],B[i+x][t]=B[i+x][t],B[i][j] 
            # do the switch
  r+=f(B)+f(list(zip(*B))) 
            # do the test. you could end up with lots of 'yes's in r.
            # zip(*B) takes the transpose, so that f checks the columns too
r+=["no"]   # happens to ensure that r is nonempty
print(r[0]) # only prints no if r was empty before the last line
alexander-brett
fonte
1

GNU sed 255 + 2 = 257B

Eu pensei que isso não seria tão bom quanto o python, mas agora é: - / Estive sem acesso à Internet hoje, então me ocupei em resolver isso no sed :). Precisa ser chamado com a bandeira -r, ou seja, sed -rf command.sed < inputadicionei 2 à minha pontuação.

:a
$!N
s/\n/ /g
ta
:b
/^((\w)(\w\2\2|\2\w\2|\w\2\w* \w\2|\2\w* \w\w\2|\w* (\2\w* \w* \2|\w* \2\w* \2|\w\2\2|\w\2\w* \2|\2\w* \w\2|\w\2\w* \w\2))|\w((\w)(\w* \6\w\6|\6\w* \6|\w* (\6\w \w\6|\w\6\w* \6|\6\w* \6))|\w(\w)\w* \9\9))/c\yes
s/\w(\w*)/\1/g
tb
c\no

Como funciona:

  1. Leia a grade em uma única linha de caracteres separados por espaço
  2. Use o regex motherload para descobrir se há uma correspondência na primeira coluna * - se sim, troque a linha inteira por 'yes' (finalizando o programa)
  3. Retire o primeiro caractere de cada coluna e vá para 2 se fizermos
  4. Se não o fizermos (a linha está vazia), substitua a linha inteira por 'no'
alexander-brett
fonte
1

Ruby, 201 bytes

Fiquei desapontado por não encontrar soluções para esse grande desafio que não usem força regex ou força bruta (embora sejam ótimas), então escrevi uma. É necessário entrada no STDIN.

O principal algoritmo aritmético bit a bit é derivado desta resposta fantástica no Game Development Stack Exchange da @leander.

s=$<.read
$><<(?1..?9).any?{|n|a=[0]*19
s.scan(n){i=$`.size
a[i/9+1]+=2**(i%9)
a[i%9+10]+=2**(i/9)}
a.each_cons(3).any?{|x,y,z|q=y&y<<1
l=q<<1
q>>=2
y&(l<<1|q>>1)|(q|l|(y&y<<2)>>1)&(x|z)>0}}?"yes":"no"

Ruby lambda, 181 bytes

Aqui está como um lambda que pega uma string e retorna trueou false:

->s{(?1..?9).any?{|n|a=[0]*19
s.scan(n){i=$`.size
a[i/9+1]+=2**(i%9)
a[i%9+10]+=2**(i/9)}
a.each_cons(3).any?{|x,y,z|q=y&y<<1
l=q<<1
q>>=2
y&(l<<1|q>>1)|(q|l|(y&y<<2)>>1)&(x|z)>0}}}

Veja em repl.it: https://repl.it/ColJ/2

Ungolfed & explicação

->s{
  (?1..?9).any? {|n|
    a = [0] * 19

    s.scan(n) {
      i = $`.size
      a[i/9+1] += 2**(i%9)
      a[i%9+10] += 2**(i/9)
    }

    a.each_cons(3).any? {|x,y,z|
      q = y & y << 1
      l = q << 1
      q >>= 2
      y & (l << 1 | q >> 1) |
        (q | l | (y & y << 2) >> 1) &
        (x | z) > 0
    }
  }
}

O código itera sobre os dígitos "1" a "9." Cada iteração possui duas etapas distintas:

O primeiro passo é a transformação da placa, que você pode ver no s.scan(n)bloco no código não-destruído. Ele transforma a placa em uma matriz de 8 números inteiros, um para cada linha, tratando os dígitos correspondentes como 1s e todos os outros como 0s em uma sequência binária. Por exemplo, pegue a linha 12231123. Na primeira iteração, isso se tornará a sequência binária 10001100(todos os 1s se tornam - er, permanecem - 1s e todos os outros dígitos se tornam 0s), que é o número decimal 140. Na segunda iteração, a mesma linha se torna 01100010(todos os 2s se tornam 2s e todos os outros dígitos se tornam 0s) ou decimal 98.

Ele executa simultaneamente uma segunda transformação, que é a mesma que a primeira, mas com a placa girada 90 graus. Isso nos permite usar a mesma lógica para fazer correspondências horizontais das verticais. Por uma questão de simplicidade, concatena as duas placas em uma única longa e com zero no começo, meio (para separar as duas placas) e final para preenchimento.

O segundo passo é procurar possíveis correspondências, que você pode ver no each_cons(3).any?bloco. As linhas transformadas (que agora são números inteiros de 8 bits) são registradas em grupos (sobrepostos) de três linhas ( x , y , z ) usando aritmética bit a bit. Cada grupo é verificado para ver se uma correspondência pode ser feita na linha y , deslocando uma peça na linha y ou deslocando uma peça para y de x ou z . Como não existe uma "linha" zero antes e depois das linhas originais e rotativas das placas, não precisamos verificar se estamos na primeira ou na última linha de uma placa.

Se nenhuma correspondência for encontrada, ela continuará para a próxima iteração.

Jordânia
fonte