Ruby, Rev B 121 bytes
Submissão é a função anônima, menos o f=
. Mostrado no programa de teste para ilustrar o uso.
f=->n{["~mK)\7","}uYwQO"][l=n%2].bytes{|t|9.times{|i|(m=n|1<<i)==n||8.times{|j|m/2*257>>j&255==126-t&&t+j%2!=119&&l=m}}}
l}
puts g=f[gets.to_i]
puts
[7,6,5,
8,0,4,
1,2,3].each{|i|print g>>i&1; puts if i/3==1}
2 bytes salvos tornando o quadrado do centro o bit menos significativo em vez do bit mais significativo (remova por em /2
vez de %256
). Economias remanescentes por uma reorganização da tabela de movimentos aceitáveis. Organizar como quadrado central livre / ocupado, em vez de pelo número total de X, permite um teste mais simples. Além disso, agora existem apenas duas cadeias na matriz, portanto a %w{string1 string2}
sintaxe é abandonada em favor da ["string1","string2"]
sintaxe. Isso permite que um caractere não imprimível \7
seja incluído, o que, por sua vez, permite que uma codificação mais simples seja usada: em 126-t
vez de (36-t)%120
.
Ruby, Rev A 143 bytes
->n{l=r=("%b"%n).sum%8
%w{$ %5 - I+Wy Q S#}[r].bytes{|t|9.times{|i|(m=n|1<<i)==n||8.times{|j|m%256*257>>j&255==(t-36)%120&&t+j%2!=43&&l=m}}}
l}
Esta é uma função anônima. O formato de entrada / saída foi deixado aberto, por isso procurei um número binário de 9 bits. o bit de 512 representa o centro, com os bits restantes em espiral em torno dele (o bit de 1 é considerado um canto).
Existem muito mais entradas possíveis do que saídas aceitáveis; portanto, o algoritmo é tentar todas as jogadas e encontrar uma que se encaixe em um padrão de saída aceitável. Os padrões de saída aceitáveis para cada número de X são codificados.
As informações sobre o quadrado central são removidas e os 8 bits restantes são multiplicados por 257 para duplicá-los. Esse padrão é então rotacionado para além dos padrões aceitáveis pela mudança de direitos.
O loop não é encerrado quando um padrão é encontrado; portanto, o padrão retornado será o ÚLTIMO padrão aceitável encontrado. Por esse motivo, os padrões preferenciais (onde há uma preferência) aparecem mais adiante na lista.
Dada a estratégia 'Cavaleiros se movem', é de pouca importância se um padrão é girado 45 graus ou não. A versão não-gasta segue a estratégia de movimento dos cavaleiros e, portanto, não precisa diferenciar os quadrados dos cantos e dos quadrados das arestas: três de uma vez devem ser evitados de qualquer maneira.
No entanto, descobri que essa nem sempre é a melhor estratégia, pois existe o seguinte truque. Se o seu oponente for o primeiro e tomar o centro, ele deve vencer. Mas em seu segundo movimento, ele comete o erro de permitir que você faça um quadrado de 2x2, você deve aceitá-lo, pois isso permite que você o force a fazer três seguidas. Isso é implementado na versão golfed. É necessário um pouco de código extra nesse caso para distinguir entre três Xs em um canto (forçar o oponente a perder) e 3 Xs em uma borda (suicídio imediato).
Ungolfed in program program
A versão ungolfed segue a lógica expressa na pergunta.
Na versão golfed a tabela é ligeiramente modificado para [[0],[1,17],[9],[37,7,51,85],[45],[47,119]]
implementar o comportamento ligeiramente diferente para o caso r=3
. Em seguida, é compactado para ASCII imprimível (exigindo decodificação (t-36)%120
). É necessário um pouco de lógica adicional para diferenciar entre três Xs em um canto e três Xs ao longo de uma borda no caso da entrada da tabela 7:&&t+j%2!=43
f=->n{l=r=("%b"%n).sum%8 #convert input to text, take character checksum to count 1's(ASCII 49.)
#0 is ASCII 48, so %8 removes unwanted checksum bloat of 48 per char.
#l must be initialised here for scoping reasons.
[[0],[1,17],[9],[11,13,37,51,85],[45],[47,119]][r].each{|t| #according to r, find the list of acceptable perimeter bitmaps, and search for a solution.
9.times{|i|(m=n|1<<i)==n|| #OR 1<<i with input. if result == n, existing X overwritten, no good.
#ELSE new X is in vacant square, good. So..
8.times{|j|m%256*257>>j&255==t&&l=m}} #%256 to strip off middle square. *257 to duplicate bitmap.
#rightshift, see if pattern matches t. If so, write to l
}
l} #return l (the last acceptable solution found) as the answer.
#call function and pretty print output (not part of submission)
puts g=f[gets.to_i]
puts
[6,7,0,
5,8,1,
4,3,2].each{|i|print g>>i&1; puts if i<3}
Saída do programa de teste
É o que acontece quando o computador se reproduz.
C: \ Usuários \ steve> ruby tictac.rb
0 0
256
000
010
000
C: \ Usuários \ steve> ruby tictac.rb
256
384
010
010
000
C: \ Usuários \ steve> ruby tictac.rb
384
400
010
010
100
C: \ Usuários \ steve> ruby tictac.rb
400
404
010
010
101
C: \ Usuários \ steve> ruby tictac.rb
404
436
010
110
101
C: \ Usuários \ steve> ruby tictac.rb
436
444
010
110
111
ANÁLISE DO JOGO QUE JOGA PRIMEIRO
Isso é realmente muito simples e linear.
Ao jogar primeiro, o quadrado do meio será sempre o primeiro quadrado ocupado.
r = 0
... binary representation 0
.X.
...
r = 2
X.. binary representation 1001=9
.XX
...
r = 4
X.. binary representation 101101=45
.XX
XX.
Existe apenas uma maneira (até simetria) de ter cinco Xs, incluindo o quadrado do meio no tabuleiro, sem que o jogo termine. Há um X no quadrado do meio, um em cada diagonal (a 90 graus um do outro) e um em cada linha central horizontal / vertical (a 90 graus um do outro). Como uma borda inteira não pode ser ocupada, a única acima é a única arranjo possível. Outro jogador deve perder na próxima jogada.
ANÁLISE DO JOGO EM SEGUNDO JOGO
O jogo é bem diferente dependendo do outro jogador escolher o quadrado do meio.
r = 1
praça do meio ocupada
.X. X.. binary representation 1
.X. .X.
... ...
praça do meio livre
X.. .X. binary representation 10001=17
... ...
..X .X.
r = 3
Quadrado médio ocupado, se outro jogador jogar ao lado do seu último X. Jogar a jogada de um cavaleiro, como abaixo, é suportado na versão não-gasta
XX. .XX binary representation 1011=11
.X. XX. or mirror image 1101=13
X.. ...
No entanto, a descrição acima NÃO é a melhor jogada e não é suportada na versão golfed. A melhor jogada é a seguinte, forçando uma vitória no próximo turno:
XX. binary representation 111=7. XXX
XX. Only to be used where j is odd. .X.
... Even j would look like image to right. ...
Quadrado médio ocupado, se outro jogador jogar a 90 ou 135 graus até o seu último X (toque o cavaleiro se afastar).
X.X .X. binary representation 100101=37
.X. .XX
.X. X..
Praça do meio livre
X.X .X. XX. binary representations:
... X.X ... 1010101=85 (first two)
X.X .X. .XX and 110011=51 (last one)
r = 5
praça do meio ocupada. Pelas razões expostas acima em r = 4, há quatro movimentos possíveis, todos perdendo. apenas um é suportado: 101111 = 47.
praça do meio livre. Existe apenas uma placa possível até simetria, como segue. Outro jogador deve perder na próxima jogada, portanto não há necessidade de apoiar r> 5.
XX. binary representation 1110111=119
X.X
.XX