Esta placa Tic-Tac-Toe é válida?

48

Desafio

Dada uma placa de jogo da velha em qualquer formato, determine se é válida ou não. Se um tabuleiro puder ser o resultado de um jogo da velha, então é válido. Por exemplo, este quadro é válido:

XOX
OXO
XOX
Pelo contrário, este fórum é inválido:

XXX
XXO
OOO

Entrada

  • Um tabuleiro completo (9/9) de jogo da velha (o resultado, não o jogo).

Regras

  • O formato de entrada deve ser capaz de representar todas as 512 placas de entrada possíveis. Ele deve ser especificado, juntamente com as instruções para criá-lo, se estiver obscuro / claro. Você deve indicar as marcas do quadro individualmente.
  • Deve haver duas saídas possíveis, uma para validade e outra para invalidez.
  • Você pode assumir que o tabuleiro não possui pontos vazios.

Casos de teste

Válido:

XOX
OXO
XOX

XOX
XOX
OXO

XOO
OOX
OXX

OXO
XOX
OXO

Inválido:

XXX
XXX
XXX

OOO
OOO
OOO

XXX
OOO
XXX

OOO
OOX
XXX

XXO
OXO
OOX

Uma ajudinha?

Uma diretoria é considerada válida (para esse desafio) se e somente se as duas condições a seguir forem válidas:

  • Existem 5 X e 4 O ou 4 X e 5 O. Por exemplo,
    XXX
    OXO
    XXX
    é considerado inválido, porque existem 7 Xs e 2 Os.
  • Apenas o jogador com 5 pontos venceu ou nenhum deles venceu. Por exemplo,
    XXX
    OOO
    OOX
    é considerado inválido, pois a linha de Os ou a linha de Xs serão formadas primeiro. Os dois jogadores não podem ter sua vez simultaneamente.

O atual vencedor é ...

... resposta da geléia de ais523 , com surpreendentes 26 bytes!

Erik, o Outgolfer
fonte
2
Talvez também adicione um exemplo de caso de teste O O O X O X X O X, para mostrar que o mesmo jogador pode ter uma linha horizontal e vertical.
SMLS
2
Você deve indicar as marcas do quadro individualmente. Não tenho certeza de entender essa parte. Você poderia fornecer um contra-exemplo?
Arnauld 12/12
3
@ Tim X tem 4 marcas.
Martin Ender
2
@Sparr "Apenas o jogador com 5 pontos venceu ou nenhum deles venceu."
Martin Ender
2
@Kevin (resposta ao 1º comentário) Porque nenhum painel 9/9 será completado se o segundo jogador (jogador com 4 notas) vencer.
Erik the Outgolfer

Respostas:

11

Geléia , 26 bytes

ṢŒrṪ4=$$Ðfx3ðœ-µẆm€6R¤;/µL

Experimente online!

O formato de entrada é um pouco incomum; é uma string que representa o quadro, mas com as novas linhas do Windows (retorno de carro seguido de nova linha). Por exemplo XXO\r\nOXO\r\nOOX,. (Na verdade, qualquer string de preenchimento de dois caracteres entre as linhas funciona, mas as novas linhas do Windows são muito mais defensáveis ​​do que as outras opções.)

A ideia básica é que procuremos caracteres que apareçam 4 vezes na entrada, mas não tenham três ocorrências igualmente espaçadas na string original. Com dois ou mais caracteres de preenchimento entre as linhas de uma grade 3 × 3, todas as linhas horizontais, verticais e diagonais são espaçadas igualmente, mas nenhuma outra linha espaçada uniformemente pode ter três elementos.

Explicação:

A ðe µs são separadores de cadeia , os quais dividem o programa em múltiplas partes que são cada um independente. Substituí-os por espaços abaixo, para tornar as coisas um pouco mais claras.

ṢŒrṪ4=$$Ðfx3 œ- Ẇm€6R¤;/ L
Ṣ                           sorted version of the input
 Œr                         run-length-encode it
        Ðf                  keep only elements where
   Ṫ                        delete the last element, and it was
    4=                      equal to 4
      $$                    parse Ṫ4= as a group
          x3                repeat each element three times

                Ẇ           all sublists of the input
                 m€         take every nth element of each (€) sublist
                   6R       for each n in 1..6
                     ¤      parse 6R as a group
                      ;/    flatten one level (m€ creates a nested structure)

             œ-             multiset difference
                         L  length of that difference

Em outras palavras, encontramos a lista de caracteres que aparecem exatamente quatro vezes na entrada e fazemos uma lista que consiste em três cópias de cada um deles; encontramos a lista de todas as subsequências espaçadas igualmente na string original; e se subtrairmos o segundo do primeiro, queremos que o resultado tenha o comprimento 1 (ou seja, um jogador jogou quatro vezes, mas não venceu). Note que, como estamos em uma grade 3 × 3 e cada quadrado está cheio, é impossível que os dois jogadores joguem quatro vezes. No Jelly, 1 é verdade, 0 é falsey, portanto, não precisamos fazer nada de especial para converter a lista resultante em um booleano. ( µLPorém, é necessário, porque, caso contrário, ambos “XXX”e “OOO”seriam possíveis valores reais de saída, e a pergunta exige que todas as placas válidas produzam a mesma saída.)

Greg Martin
fonte
3
Isso é totalmente legível.
Pabrams
21

JavaScript (ES6), 88 87 bytes

s=>(a=[...s]).sort()[5]-a[3]&[7,56,73,84,146,273,292,448].every(j=>j&i,i=`0b`+s^~-a[4])

Toma entrada como uma cadeia de 9 0e 1caracteres e retorna 1para válido, 0para inválido. Classificamos os caracteres em ordem. Se os três caracteres do meio agora são os mesmos, o tabuleiro é inválido, pois há muitos de uma peça. Caso contrário, convertemos a placa original em binária, invertendo os bits se houver mais 0s que 1s. Nesse ponto, o quadro é válido se 0não tiver uma linha de três; portanto, simplesmente testamos todas as oito linhas por meio de uma matriz de máscaras de bits. Editar: salvou 1 byte graças a @ETHproductions.

Neil
fonte
@ETHproductions Ah, é claro, o resultado será apenas 0 ou 1 de qualquer maneira.
Neil
14

Python 3, 131 127 125 100 96 bytes

Para uma abordagem algorítmica diferente (e que será realmente adequada para essas linguagens de golfe de vários bytes com compactação embutida), em vez de calcular se a placa é válida, vamos ter um número de 512 bits em que cada bit representa se deve ou não uma placa específica é válida ou não e passa um valor binário que representa a placa. Além disso, devido à simetria, a segunda metade da tabela pode ser eliminada, juntamente com um monte de zeros:

def z(b):return int('agqozfx67wwye6rxr508ch2i8qicekpreqkap0725pk',36)<<24&1<<b+(b>255)*(511-b-b)

O valor do teste:

X X X
O X O
X X X

É representado como o valor binário 0b111010111e a função retorna um valor diferente de zero se a placa for válida.

Ken YN
fonte
Removida a variável intermediária por 4 bytes a menos #
Ken YN
Dois bytes a menos, pois a&(1<<b)não precisam de colchetes.
Ken YN
Derrubei 25 bytes usando simetria e mais um abreviando os 24 bits zero mais baixos - deve haver uma maneira mais eficiente de fazer isso if b>255:b=511-b!
quer
Encontrei uma maneira de jogar golfe if.
quer
11

Lote, 140 bytes

@set/aXXX=OOO=O=0
@for %%l in (%* %1%2%3 %1%4%7 %1%5%9 %2%5%8 %3%5%7 %3%6%9 %4%5%6 %7%8%9)do @set/a%%l+=1
@cmd/cset/a!XXX*!(O-=5)+!OOO*!~O

Recebe entrada como nove argumentos e saídas de linha de comando separados 1para válidos e 0inválidos. Funciona rastreando o número de vezes que vê uma Olinha ortogonal de OOOou XXX. Convenientemente, o Lote nos permite executar aritmética inteira indiretamente, portanto, não estamos incrementando, %%lmas algumas variáveis ​​(embora apenas nos interessemos pelas três variáveis ​​mencionadas). Precisamos, então, testar se ou Xnão ganhou e há cinco Osegundos ou que Onão venceu e há quatro Osegundos.

Neil
fonte
10

Mathematica, 82 75 bytes

Agradecemos a Martin Ender por economizar 7 bytes!

t=Total;3<(b=#~t~2)<6&&{t[c=If[b>4,1-#,#]],t/@c,Tr@c,Tr@Reverse@c}~FreeQ~3&

Função sem nome, tendo uma lista aninhada 3x3 de 1s e 0s como entrada e saída Trueou False.

Utiliza alguma flexibilidade prática da Totalfunção (aqui tindicada para ): dado um exemplo de matriz e = { {1,2,3} , {4,5,6} , {7,8,9} }, o comando t[e]soma os três vetores (aqui produzindo {12,15,18}); o comando t/@esoma cada sub-lista individualmente (aqui produzindo {6,15,24}); e o comando e~t~2soma todos os nove elementos (aqui rendendo 45).

Então, primeiro testamos, com 3<(b=#~t~2)<6, se o número total de 1s é 4 ou 5; se não, saímos com False. Nesse caso, usamos c=If[b>4,1-#,#]para forçar a existência de quatro 1s, não cinco. Em seguida, calculamos as somas da coluna t[c], as somas da linha t/@c, a soma da diagonal principal Tr@ce a soma da diagonal oposta Tr@Reverse~ce usamos ~FreeQ~3para verificar se as 3falhas não aparecem em nenhum nível nessas somas calculadas.

Nota lateral divertida: ao contrário da maioria das aparências neste site, aqui Trnão é usado para somar uma lista unidimensional, mas na verdade é usado como projetado - para calcular o traço de uma matriz bidimensional!

Greg Martin
fonte
6

Pitão - 36 bytes

Incluo diagas e uso dois ternários.

JsM+sCBQm@VdU3_BQ?q5KssQ*FJ?qK4!}3JZ

Suíte de teste

Maltysen
fonte
5

JavaScript (ES6), 101 bytes

Recebe a entrada como uma máscara binária de 9 bits em que X = 1e O = 0(MSB = célula superior esquerda, LSB = célula inferior direita).

n=>[7,56,73,84,146,273,292,448,o=x=0].map((m,i)=>(c-=n>>i&1,m&n^m?m&n||o++:m&&x++),c=4)&&!(c|x&&~c|o)

Casos de teste

Arnauld
fonte
Eu sabia que tinha que haver uma solução bit a bit (um tanto) simples. Bom trabalho
ETHproductions
5

Python 2, 158 132 109 92 91 123 bytes

def v(b):f=sum(b,());w={x[0]for x in b+zip(*b)+[f[::4],f[-3:1:-2]]if len(set(x))==1};return sum(map(`b`.count,w))==len(w)*5

Input é uma lista / tupla de linhas, cada uma com três tuplas de strings, por exemplo:
[('X', 'O', 'X'), ('O', 'X', 'O'), ('X', 'O', 'X')]

Economizou alguns bytes ignorando diagonais pela resposta de @ Maltysen, que também reduziu a expressão a seguir.

Obrigado @vaultah por salvar 17 18 bytes.

A verificação das diagonais acaba sendo necessária, o que removeu muitas das economias acima.

Experimente aqui.

Explicação

def v(b):
  f=sum(b,())
  w={x[0]for x in b+zip(*b)+[f[::4],f[-3:1:-2]]if len(set(x))==1}
  return sum(map(`b`.count,w))==len(w)*5

fé a entrada achatada para fatiar.
wcontém os personagens com seqüências vencedoras.
Conte as ocorrências de cada personagem vencedor, que será 0 se westiver vazio ou 5 se len(w)for 1. A soma 10 quando ambos tiverem uma sequência vencedora é impossível. O vencedor com 5 implica que o perdedor tem 4. Você não pode ter> 5 sem uma sequência vencedora.

Jake Cobb
fonte
lambda b:len({x[0]for x in b+zip(*b)if len(set(x))==1})<2and set(map(b .count,'XO'))=={4,5}salva alguns bytes.
vaultah
e acabei de notar que ...and{4,5}==set(map(b .count,'XO'))salva mais um byte.
vaultah
Acho que isso considera incorretamente o último exemplo "Inválido" da pergunta como válido, porque não garante que o vencedor seja o jogador com 5 pontos.
SMLS
@smls Você está certo. A verificação dessa condição custa muitos bytes, talvez ela possa ser jogada ainda mais.
Jake Cobb
5

R, 88 82 bytes

x=scan();`if`(sum(x)%in%4:5,all(apply(combn(which(x==(sum(x)<5)),3),2,sum)!=15),F)

Todas as combinações de três números inteiros de 1 a 9 que somam 15 são as linhas / colunas / diagonais do quadrado mostrado abaixo.

2 7 6
9 5 1
4 3 8

A função recebe a entrada como um vetor de booleanos, T para "X", F para "O", que é a representação achatada da placa. MAS, estes são reordenados para que seu índice seja o mesmo que o número no quadrado, na ordem (2,7,6,9,5,1,4,3,8). Essa ordem pode ser alcançada aplainando a placa da maneira normal e depois cortando em c (6,1,8,7,5,3,2,9,4). Então, é isso

X O X
O X O
X O X

é representado como:

c(T, F, T, F, T, F, T, F, T)[c(6,1,8,7,5,3,2,9,4)]

qual é:

c(F, T, F, T, T, T, F, T, F)

A função primeiro determina se existe um jogador com exatamente quatro marcas. Nesse caso, a função usa o fato das coisas que somam até 15 para determinar se esse jogador tem três em linha (o tabuleiro é inválido se esse jogador tiver).

Se você quisesse usar uma placa achatada convencionalmente como entrada, o código teria a seguinte aparência:

f=function(x)ifelse(sum(x)%in%4:5,all(apply(combn(c(2,7,6,9,5,1,4,3,8)[which(x==(sum(x)<5))],3),2,sum)!=15),F)

Eu sou novo nisso, conselhos seriam apreciados.

ixodesbeta
fonte
11
Salve 2 bytes se você usar if(): f=function(x)if (sum(x)%in%4:5,all(apply(combn(which(x==(sum(x)<5)),3),2,sum)!=15),F). Não é extensivamente testado. Os backticks arruinam o código, mas é backtick if backtick(.
Jonathan Carroll
11
Melhor ainda; x=scan();if (sum(x)%in%4:5,all(apply(combn(which(x==(sum(x)<5)),3),2,sum)!=15),F)e entrada como 1e 0. 82 bytes.
Jonathan Carroll
3

JavaScript (ES6), 145 139 131 127 bytes

s=>!(q="XO"[s.split`O`.length-5])|![...s].some((c,i)=>c==q&!/(.)(\1|..(\1|.(\1|.\1.).)..)\1/.test(s.slice(0,i)+0+s.slice(i+1)))

Entrada como uma cadeia de caracteres separada por espaço, como "XOX OXO XOX". Saídas 1para uma placa inválida, 0para uma válida. Obviamente, essa não é a melhor técnica, pelo menos não com JavaScript ...

Isso basicamente verifica se os seguintes itens se mantêm:

  • Existem exatamente 4 ou 5 Os, E
  • existe pelo menos uma das partes de 5 instâncias que cria um jogo indeciso quando removido.

O regex é verificar se um jogo foi decidido. Ele corresponde a uma placa se houver execuções de três caracteres com 0 (linha), 2 (diagonal para baixo à direita), 3 (coluna) ou 4 caracteres (diagonal para a esquerda) que separam cada par.

Snippet de teste

ETHproductions
fonte
2

Ruby, 104 99 91 bytes

->x{[7,56,448,292,146,73,84,273].none?{|y|b=x.to_i 2;((a=x.count'1')==4?b:a==5?~b:7)&y==y}}

Formato de entrada: sequência binária de 9 símbolos (0s e 1s) representando a placa, por exemplo, o primeiro caso de teste 101010101. Primeiro converta-o para um número binário, verifique se o número de pop-ups é 4 ou 5, se é 5, inverta o número para que sempre tenhamos 4. Verifique se três deles estão alinhados (mascaramento com horizontal, vertical, diagonal).

TL; DR : Retorna false se um jogador com 4 pontos conquistou, caso contrário, true.

Obrigado Jordan pelos comentários,

Não consigo reproduzir a sequência UTF-8 que salvaria outro byte.

GB
fonte
Você pode substituir .select{...}[0]por .find{...}.
Jordan
E você pode economizar mais um byte, substituindo a matriz de números por "8ǀĤITđ".unpack("U*")(caso algo se perca na conversão, a sequência é o resultado da chamada pack("U*")na matriz original; são 12 bytes).
Jordan
você poderia usar em any?vez de none?inverter a saída e salvar um byte inteiro?
Alexis Andersen
Eu tentei com um? em vez de nenhum? mas então eu preciso de um! para inverter a saída.
GB
1

Perl 6 , 103 99 bytes

{my \c=%(.flat.Bag.invert)<5>;?all c,|(.[0]===c if [eq] $_ for |.flat[<0 4 8>,<2 4 6>],|$_,|.&zip)}

Um lambda que aceita uma lista de listas como (('X','O','X'), ('O','X','O'), ('X','O','X'))e retorna um Bool.

Funciona assim:

  1. Verifique qual marca aparece exatamente 5 vezes e guarde-a c. (Se nenhuma marca aparecer exatamente 5 vezes, isso conterá um valor falso)
  2. Itere todas as diagonais, linhas e colunas e filtre as "vencedoras" (ou seja, aquelas em que as três letras são iguais) .
  3. Verifique se cé verdade e cada linha vencedora é do tipo c.
smls
fonte
1

PHP, 125 bytes

for($p=$n=$argv[1];$p;$p/=2)$i+=$p&1;foreach([7,56,448,73,146,292,273,84]as$m)$n&$m^$m?$n&$m||$o++:$x++;echo!$x|!$o&&2>$i^=4;

Tive a mesma ideia que Arnauld : a placa é válida se houver 4 ou 5 bits configurados e Xou Oou ninguém tiver uma raia (mas não as duas).

Para gerar entrada do campo, substitua Xpor 1e Ocom 0, junte linhas e converte binário em decimal, forneça como argumento da linha de comando.

impressões 1válidas; saída vazia por inválido. Corra com -r.

demolir

// count set bits
for($p=$n=$argv[1];$p;$p/=2)$i+=$p&1;
    /* ($p/=2 takes longer than $p>>=1, but eventually
       $p will come close enough to 0 for the loop to finish */
// count streaks for X and O
foreach([7,56,448,73,146,292,273,84]as$m)
    $n&$m^$m            // ($n masked with streak)!=streak <=> no streak for X
        ?$n&$m||$o++    // true: O has a streak if ($n masked with streak) is empty
        :$x++;          // false: X has a streak
echo!$x|!$o&&2>$i^=4;   // valid if not both have a streak
                        // AND $i is 4 or 5 (toggle 4 -> result 0 or 1)
Titus
fonte
1

Rápido, 178 bytes

func t(i:String)->Bool{let r=i.characters.filter({$0=="X"}).count;let g=i.characters.split(separator:"\n").map(String.init).contains;return(r==5||r==4)&&(!g("XXX") && !g("OOO"))}
Caleb Kleveter
fonte
0

ES6 (Javacript) 130, 138, 117 bytes

EDITAR% S:

  • Desconto de 21 bytes graças aos excelentes conselhos do @Neil!
  • A versão inicial era propensa a um bug, que agora deveria ser corrigido ao custo de +8 bytes. (Obrigado @ETHproductions por apontar)

Uma abordagem extremamente direta. Provavelmente pode ser jogado um pouco mais longe.

Aceita entrada como 9 argumentos separados, 1es e 0es

  • 1 é para X
  • 0 é para O

Argumentos: 1-3 - primeira linha, 4-6 - segunda linha, 7-9 - terceira linha.

Golfe

(a,b,c,d,e,f,g,h,j)=>![a+b+c,d+e+f,g+h+j,a+d+g,b+e+h,c+f+j,a+e+j,g+e+c,7].some(x=>x=="7777307777"[a+b+c+d+e+f+g+h+j])

"Cama de teste" interativa

var a=b=c=d=e=f=g=h=j=0;

T=(a,b,c,d,e,f,g,h,j)=>![a+b+c,d+e+f,g+h+j,a+d+g,b+e+h,c+f+j,a+e+j,g+e+c,7].some(x=>x=="7777307777"[a+b+c+d+e+f+g+h+j]);

function test() {
  if(T(a,b,c,d,e,f,g,h,j)) {
     grid.style.backgroundColor='green';
     msg.innerHTML="GOOD"
  } else {
     grid.style.backgroundColor='red';
     msg.innerHTML="BAD"
  }
}
<table id=grid style="background: red">
<thead>
  <tr>
     <td id=msg align="center" colspan="3">BAD</td>
    </tr>
  </thead>
  <tr>
      <td><input type="checkbox" onchange="a=this.checked*1;test();" id="ca"/></td>
      <td><input type="checkbox" onchange="b=this.checked*1;test();" id="cb"/></td>
      <td><input type="checkbox" onchange="c=this.checked*1;test();" id="cc"/></td>
    </tr>
    <tr>
      <td><input type="checkbox" onchange="d=this.checked*1;test();" id="cd"/></td>
      <td><input type="checkbox" onchange="e=this.checked*1;test();" id="ce"/></td>
      <td><input type="checkbox" onchange="f=this.checked*1;test();" id="cf"/></td>
    </tr>
    <tr>
      <td><input type="checkbox" onchange="g=this.checked*1;test();" id="cg"/></td>
      <td><input type="checkbox" onchange="h=this.checked*1;test();" id="ch"/></td>
      <td><input type="checkbox" onchange="j=this.checked*1;test();" id="cj"/></td>
    </tr>
 </table>

zepelim
fonte
Posso estar errado, mas parece que isso só verifica se há um vencedor. Um quadro válido não pode ter vencedor; por exemplo, [1,0,1,1,0,1,0,1,0]( XOX XOX OXO).
ETHproductions 12/12
Sim, eu perdi a negação enquanto jogava golfe. É suposto verificar que um lado oposto não é o vencedor. Deve ser corrigido agora. Valeu !
Zeppelin
(Comecei a comentar antes da edição mais recente) Você pode a) escrever em a+b+c+d+e+f+g+H+ivez de F.reduce((r,c)=>r+=c*1)(nesse momento não precisa F) b) escrever .includes(C)(e continuar com Co valor do inline )?
Neil
@ Neil, isso provavelmente vai funcionar, vou tentar amanhã. Valeu !
Zeppelin
É OOO XXX OXOum fracasso?
Ismael Miguel