Solucionador de quebra-cabeças binário

10

Introdução

Regras do quebra-cabeça:

O quebra-cabeça Binário (também conhecido como Takuzu ou Subiku) é muito simples de entender e possui apenas algumas regras:
Como o nome do jogo é binário, é bastante óbvio, mas você só pode preencher zeros e uns.

  1. Não mais que dois do mesmo dígito podem ser vertical ou horizontalmente adjacentes um ao outro
  2. Cada linha e cada coluna deve conter uma quantidade igual de zeros e uns (isso significa implicitamente que todo jogo binário sempre terá dimensões iguais).
  3. Pode não haver linhas duplicadas nem colunas duplicadas (com exatamente a mesma ordem de zeros e uns).

Você pode jogar o jogo em www.binarypuzzle.com, se quiser.

Táticas:

De acordo com a regra 1, sempre podemos preencher um dígito se:
- Já existem dois do mesmo dígito vertical ou horizontalmente adjacentes um ao outro; nesse caso, podemos preencher o dígito oposto em ambos os lados. Ou seja, .11...0110...
- Existem dois dígitos iguais na vertical ou na horizontal, com apenas um espaço entre eles. Ou seja, .1.1...101..

De acordo com a regra 1, quando restam três lacunas e não podemos ter três adjacentes do mesmo dígito, podemos preencher uma das lacunas. Ou seja, .0.1.010.1.0(Ainda temos que preencher dois, e não podemos ter três adjacentes no meio, portanto a primeira lacuna deve ser a 1.)

De acordo com a regra 2, sempre podemos preencher as lacunas restantes em uma linha ou coluna se metade delas já estiver preenchida com o dígito oposto. Ou seja, .1.011010011

De acordo com a regra 3, sempre podemos preencher os dígitos opostos se restarem apenas dois para resolver em uma linha igualmente ordenada. Ou seja, 101100 & 1..100101100 & 110100

De acordo com a regra 3, às vezes podemos preencher uma lacuna quando restam três lacunas em uma linha igualmente ordenada. Ou seja, 010011 & .1.01.010011 & .1.010(Aqui não podemos preencher um 1no final, porque isso significa que precisamos preencher zeros nas outras duas lacunas, tornando as duas linhas iguais em ordem.)

Exemplo:

Começamos com a seguinte grade 6x6 com alguns e zeros preenchidos (e os pontos são lacunas que ainda precisamos preencher):

.1....
.10.0.
1.11..
.1....
...1.0
......

Devido às regras 1 e 2, podemos preencher estes dígitos:

.1.01.
.1010.
101100
010011
.0.1.0
.010..

Devido à regra 1, podemos preencher um 1 na linha 5, coluna 1:

.1.01.
.1010.
101100
010011
10.1.0
.010..

De acordo com a regra 3, podemos preencher um 0 na linha 1, coluna 6 (ao observar a linha 4):

.1.010
.1010.
101100
010011
10.1.0
.010..

Agora podemos continuar preenchendo lacunas com dígitos devido às regras 1 e 2:

.1.010
010101
101100
010011
10.1.0
.010.1

Agora podemos concluir a linha 5 devido à regra 3 (ao olhar para a linha 3):

.1.010
010101
101100
010011
100110
.010.1

E então podemos terminar o quebra-cabeça devido às regras 1 e 2:

011010
010101
101100
010011
100110
101001

Desafio:

O desafio é simples: dada a grade inicial, produza o quebra-cabeça resolvido.

NOTA: Você não precisa implementar as regras acima. É claro que você pode, e isso deve lhe dar dicas sobre como implementar esse desafio, mas forçar brutalmente a solução com as regras em mente é ótimo.
A solução é sua, mas o desafio é produzir o quebra-cabeça resolvido.

Regras do desafio:

  • O formato de entrada e saída da grade é flexível, mas indique o que você usa. (Ou seja, matriz de bytes 2D; String com novas linhas; etc.)
  • Isso acima também se aplica aos caracteres usados. No exemplo que eu usei 01., mas se você quiser, pode usar ABx. Por favor, indique qual formato de entrada / saída e caracteres que você usou.
  • Você pode assumir que apenas os seguintes tamanhos de grade serão usados 6x6:; 8x8; 10x10; 12x12; 14x14; 16x16.

Regras gerais:

  • Isso é , então a resposta mais curta em bytes vence.
    Não permita que idiomas com código de golfe o desencorajem a postar respostas com idiomas que não sejam codegolf. Tente encontrar uma resposta o mais curta possível para 'qualquer' linguagem de programação.
  • As regras padrão se aplicam à sua resposta, para que você possa usar STDIN / STDOUT, funções / método com os parâmetros adequados, programas completos. Sua chamada.
  • As brechas padrão são proibidas.
  • Se possível, adicione um link com um teste para o seu código.
  • Além disso, adicione uma explicação, se necessário.

Casos de teste:

Os pontos são adicionados apenas para facilitar a leitura, fique à vontade para usar espaços ou qualquer outra coisa que você preferir para lacunas. O formato de saída e é flexível.

Input:
1..0..
..00.1
.00..1
......
00.1..
.1..00

Output:
101010
010011
100101
011010
001101
110100

Input:
.1....
.10.0.
1.11..
.1....
...1.0
......

Output:
011010
010101
101100
010011
100110
101001

Input:
.......1..
.00..0..1.
.0..1..0.0
..1...1...
1.1......1
.......1..
.0..1...0.
....11...0
.0.0..1..0
0...0...1.

Output:
0110010101
1001100110
1001101010
0110011001
1010100101
0101010110
1001101001
0110110100
1010011010
0101001011
Kevin Cruijssen
fonte

Respostas:

4

Braquilog , 34 bytes

{ℕ<2}ᵐ²&≜{d?ọᵐctᵐ=&{ḅlᵐ⌉<3}ᵐ}&\↰₂&

Experimente online!

Como é muito lento, o caso de teste no TIO é 4x4. Atualmente, estou executando o caso de teste 6x6 no meu computador para ver quanto tempo leva.

Isso leva uma lista de listas como entrada. Os valores desconhecidos devem ser indicados com variáveis, ou seja, com todas as letras maiúsculas (e todas devem ser diferentes, caso contrário, você indicaria que algumas células devem ter o mesmo valor)

Explicação

Nós restringimos os valores a serem inseridos {0,1}, depois tentamos instanciações das variáveis ​​até que se respeite todas as três regras. É por isso que isso é tão lento (porque tentará todos eles até encontrar um; e, nesse caso, o Brachylog não é implementado suficientemente bem para que restrições possam ser impostas antes de tentar uma possível matriz).

                                 &  Output = Input
{   }ᵐ²                             Map two levels on the Input (i.e. each cell):
 ℕ<2                                  The cell is either 0 or 1
       &≜                           Assign values to all cells
         {                  }       Define predicate 2:
          d?                          The Input with no duplicates is still the Input
                                        (i.e. all rows are different)
           ?ọᵐctᵐ=                    All occurences of 1s and 0s for each rows are equal
                  &{      }ᵐ          Map on rows:
                    ḅlᵐ                 Get the lengths of runs of equal values
                       ⌉<3              The largest one is strictly less than 3
                             &\↰₂   Apply predicate 2 on the transpose of the Input
                                      (i.e. do the same checks but on columns)
Fatalizar
fonte
Por curiosidade, como Brachylog indica variáveis ​​além do alfabeto maiúsculo? Então, digamos que sua solução funcione mais rapidamente, não será possível preencher todos os espaços vazios em uma grade 14x14 com Athrough Y(com o Zparâmetro output). Será que ela continue com AA, AB, etc?
Kevin Cruijssen 5/05
2
@KevinCruijssen Qualquer identificador com todas as letras maiúsculas é uma variável, então sim AAé uma variável e KEVINCRUIJSSENtambém é uma variável.
Fatalize
3
Como eu suspeitava, um desafio feito para Brachylog: D
Jonathan Allan
3

JavaScript (ES6), 274 270 bytes

Recebe a entrada como uma matriz 2D, onde as células vazias são marcadas com 2. Imprime todas as soluções possíveis no console.

f=(a,x=0,y=0,w=a.length,p,R=a[y])=>(M=z=>!a.some((r,y)=>/(0|1),\1,\1/.exec(s=r.map((v,x)=>(v=z?v:a[x][y],b-=v&1,c-=!v,m|=v&2,v),b=c=w/2))||b*c<0|o[b*c||s]&(o[s]=1),o={}))(m=0)&M(1)&&(m?R&&[0,1].map(n=>(p=R[x])==n|p>1&&(R[x]=n,f(a,z=(x+1)%w,y+!z),R[x]=p)):console.log(a))

Como funciona

A primeira parte do código usa a M()função para verificar a validade da placa atual, horizontal e verticalmente.

M = z =>
  !a.some((r, y) =>
    /(0|1),\1,\1/.exec(
      s = r.map((v, x) =>
        (
          v = z ? v : a[x][y],
          b -= v & 1,
          c -= !v,
          m |= v & 2,
          v
        ),
        b = c = w / 2
      )
    ) ||
    b * c < 0 |
    o[b * c || s] &
    (o[s] = 1),
    o = {}
  )

Ele mapeia uma linha ou coluna completa para a sequência s . Este é realmente um array coagido a uma string, assim parece "1,2,2,0,2,2".

Usa:

  • A expressão regular /(0|1),\1,\1/para detectar 3 ou mais dígitos idênticos consecutivos.
  • O contadores b e c para manter o controle do número de uns e zeros . Ambos os contadores são inicializados com w / 2 e decrementados cada vez que um ou um zero (respectivamente) é encontrado. Isso leva a:
    • b = c = 0 b * c = 0 → a linha está completa e correcta (como muitos zeros como aqueles )
    • b> 0 E c> 0 b * c> 0 → a linha não está completa, mas está correta até o momento (não temos mais que w / 2 zeros ou mais que w / 2 uns )
    • b <0 OU c <0 b * c <0 → a linha é inválida
  • O sinalizador m (para 'ausente') que é diferente de zero se houver pelo menos um dois restantes no tabuleiro.
  • O objeto o de manter o controle de todos os padrões de linha encontradas até agora.

Se o fórum for inválido, paramos imediatamente. Se o quadro for válido e completo, imprimi-lo no console. Caso contrário, a segunda parte das tentativas de código para substituir cada 2 quer com um de zero ou um um com chamadas recursivas:

[0, 1].map(n =>
  (p = a[y][x]) == n |
  p > 1 && (
    a[y][x] = n,
    f(a, z = (x + 1) % w, y + !z),
    a[y][x] = p
  )
)

Demo

Arnauld
fonte
Obrigado por adicionar a explicação. E eu gosto de como você imprime todas as saídas possíveis, em vez de apenas uma!
Kevin Cruijssen
11
@KevinCruijssen Isso provavelmente está longe de ser o ideal, mas foi divertido de escrever. Bom desafio!
Arnauld
1

Geléia , 53 51 bytes

ṡ€3ḄFf0,7L
SḤnLṀȯÇ
⁻QȯÇ
Fṣ©2L’0,1ṗż@€®F€s€LÇÐḟZÇ$Ðḟ

Recebe uma lista de listas representando a grade, contendo 0, 1e 2(os espaços). Retorna uma lista de listas de listas, cada lista de listas está no mesmo formato (embora sem 2s) e representa uma possível solução para a entrada.

Experimente online! (isso não executará nenhum dos casos de teste da pergunta devido a limitações de memória - todas as 2grades do nSpaces são criadas como uma lista de listas de números inteiros - mas coloquei um caso relativamente robusto com uma única solução). O rodapé separa e formata as grades.

Método de força bruta pura - implementa as regras e as verifica para cada grade que pode ser formada substituindo qualquer um dos 2s por 1s ou 0s.

ṡ€3ḄFf0,7L - Link 1, # of runs of 3 1s or 3 0s by row: list of lists
ṡ€3        - all contiguous slices of length 3 for €ach list
   Ḅ       - convert all results from binary
    F      - flatten into one list
     f     - filter keep values in:
      0,7  -   0 paired with 7: [0,7]
         L - length

SḤnLṀȯÇ - Link 2, unequal counts of 1s and 0s by column ...or link 1: list of lists
S       - sum (vectorises, hence "by column", counts 1s since only 1s or 0s appear)
 Ḥ      - double
   L    - length (number of rows - OK since square)
  n     - not equal? (vectorises)
    Ṁ   - maximum (1 if any not equal)
     ȯÇ - ... or last link (1) as a monad

⁻QȯÇ - Link 3, rows are unique ...or link 2: list of lists
 Q   - unique
⁻    - not equal?
  ȯÇ - ... or last link (2) as a monad

Fṣ©2L’0,1ṗż@€®F€s€LÇÐḟZÇ$Ðḟ - Main link: list of lists
F                           - flatten
 ṣ©2                        - split at 2s and copy the result to the register
    L                       - length (# of such slices)
     ’                      - decrement (# of 2s)
      0,1                   - 0 paired with 1
         ṗ                  - Cartesian power (all binary lists of length # of 2s)
             ®              - recall value from register (the flat version split at 2s)
          ż@€               - zip (reversed @rguments) for €ach (1s & 0s where 2s were)
              F€            - flatten €ach
                s€L         - split €ach into chunks of length length(input) (into rows)
                    Ðḟ      - filter discard if:
                   Ç        -   call last link(3) as a monad
                         Ðḟ - filter discard if:
                        $   -   last two links as a monad:
                      Z     -     transpose
                       Ç    -     call last link(3) as a monad
Jonathan Allan
fonte