Construa uma IA perfeita para o jogo 15


8

No jogo 15, dois jogadores se revezam na seleção de números de 1 a 9 (sem escolher nenhum número que um dos jogadores já tenha selecionado). Um jogador vence se tiver três números que somam 15. Se todos os números foram selecionados e nenhuma combinação dos dois somar 15, então o jogo está empatado.

Sua tarefa é criar uma função que leva o estado de um jogo de 15 (representado na forma que você desejar) e retorna o número a ser movido a seguir, que atuará como uma IA para jogar com outro jogador. Você pode assumir que a posição é legal (nenhum jogador tem mais de um número a mais que o outro jogador e nenhum jogador já tem três números que somam 15).

A IA deve ser perfeita - isto é, se receber uma posição vencedora, deve forçar uma vitória e se receber uma posição não perdida (uma posição em que seu oponente não tem uma estratégia vencedora), não deve permitir sua vitória. oponente para lhe dar uma posição perdida (o que é possível, pois 15 é um jogo resolvido).

O código mais curto vence.

(observação: aceitarei a resposta mais curta atualmente e a alterarei se aparecer uma resposta mais curta.)


Entendo corretamente que podemos perder se entrarmos em uma posição vencível em que nossa IA não poderia ter entrado?
quer

Sim, está correto. Se a IA for apresentada com um estado de jogo que não pode vencer ou empatar (por exemplo, uma armadilha dupla), ela poderá perder. No entanto, a IA nunca deve permitir que o jogo entre no dito estado perdedor de um tabuleiro vazio.
Joe Z.

assumo corretamente que a IA é sempre a primeira a jogar?
quer

1
hmm ... parece que estamos autorizados a empatar, mesmo que possamos forçar uma vitória. Isso é verdade?
precisa

2
Hah. Acabei de aprender sobre esse jogo, e a primeira coisa que cheguei em casa foi publicá-lo aqui. Infelizmente, eu fui derrotado por isso.
Boothby

Respostas:


6

GolfScript ( 129 86 81 85 75 caracteres)

{:C[{.`{1$-\{+15\-}+%}:T+/}/10,1>C~+-:^{.{^T&}+C/,2<*T}/5^{1&}$][]*^&0=}:N;

Formato de entrada esperado: [[int int ...][int int ...]]onde a primeira lista é meus números e a segunda lista são os números do meu oponente. Para teste interativo, adicione ~Nno final do script e forneça uma string nesse formato: por exemplo

$ golfscript.rb 15.gs <<<"[[5][2 8]]"
9
$ golfscript.rb 15.gs <<<"[[2][5 8]]"
6

Heurísticas:

  1. Se eu puder vencer este turno, faça
  2. Se o oponente vencer no próximo turno, bloqueie
  3. Se eu conseguir forçar o oponente a um quadrado que os impeça de criar um garfo, faça-o
  4. 5 é o único número que pode contribuir para ganhar de quatro maneiras, então pegue-o, se disponível.
  5. Favor igualar as probabilidades

Estrutura de teste:

{:Set;[1 5 9 1 6 8 2 4 9 2 5 8 2 6 7 3 4 8 3 5 7 4 5 6]3/{.Set&=},!!}:isWin;
{
    # Mine His
    .isWin{
        "Lost "@`@`++puts
    }{
        # If there are available moves, it's my move.
        # If I won before my move, I've still won after it.
        1$1$+,9<!!{
            # my move
            1$1$[\\]N 2$\+
            # Mine His Mine'
            .isWin!{
                # his move
                10,1>2$2$+-{
                    2$\+1$\
                    # Mine His Mine' Mine' His'
                    fullTest
                    # Mine His Mine'
                }/
            }*;
        }*;;
    }if
}:fullTest;
# I move first
'[][]fullTest'puts [][]fullTest
# He moves first
'[][1]fullTest'puts [][1]fullTest
'[][2]fullTest'puts [][2]fullTest
'[][5]fullTest'puts [][5]fullTest

Você pode executar esta entrada para mim: [5][3](deve retornar 4 ou 8).
Joe Z.

9- mas parece que você mudou as regras.
Peter Taylor

Além disso, por que apenas 4ou 8?
Peter Taylor

Oh espera, não importa, eu estava errado. Qualquer coisa, exceto, 7deve funcionar, por isso 9é aceitável.
Joe Z.

Além disso, não pretendia mudar as regras; é que eu as expressei errado da primeira vez.
Joe Z.

4

Ruby, 330 315 341 caracteres

def m i
$_=i.join
l='159258357456168249267348'.scan /(.)(.)(.)/
return 1 if /^5(28|46|64|82)$/
return 4 if /^[258]{3}$/
return 2 if /^[456]{3}$/
i.each{|i|l.map{|l|return l if(l-=i).size==1&&!/[#{l=l[0]}]/}}
.map{|i|l.map{|m|l.map{|l|return (l&m)[0] if(z=l-i|m-i).size==3&&l!=m&&!/[#{z.join}]/}}}
"524681379".chars{|z|return z if !/#{z}/}
end

Por enquanto, reteremos os detalhes, mas digamos que ele se baseia no algoritmo ideal para um problema semelhante que também foi resolvido e cujo algoritmo ideal funcionou tão bem aqui. Foram feitas suposições - isso escolherá jogadas ruins em situações que não podem ser produzidas por esse algoritmo jogando contra outro jogador, apenas por dois jogadores um contra o outro.

Entrada: uma matriz de duas matrizes de cadeias de caracteres de um dígito. Cada matriz representa os valores obtidos por um jogador - o primeiro é o AI, o segundo é o oponente.

Saída: um número ou uma sequência de um dígito. Eles são semanticamente equivalentes. A normalização para strings custaria 8 caracteres.

Mais três caracteres podem ser salvos se assumirmos a ordem dos números dados pelo chamador - altere a expressão regular em L5 para /^285$/ou /^258$/dependendo da ordem produzida no jogo (opponent)5-(ai)2-(opponent)8.


Parece que você tem uma economia fácil nas últimas três linhas, basta ir 5para o início do seu pedido de preferência.
Peter Taylor #

@ ah, sim, obrigado. Eu tive outro passo que removi (estojo especial para o topo) e esqueci de mesclar os degraus ao redor. Vou editar quando chegar em casa (a menos que você seja voluntário antes).
John Dvorak

1

GolfScript ( 90 85 84 caracteres)

{.~.,3/*..2%.@^++3*3/{~++15=},,*10,1>2$~+-@`{([2$+]+v[0=~)\]}+%[1,]or$0=+}:v{1=}+:N;

Isso adota uma abordagem completamente diferente, mas é potencialmente suscetível a otimizações para superar a heurística. Aqui fazemos uma análise completa da árvore do jogo, incrivelmente devagar. (Não, quero dizer. Demora várias horas para executar o teste completo, principalmente por causa do `{...}+que adiciona o estado atual ao loop do próximo movimento). Observe que a parte difícil é identificar um estado vencedor (um terço do código, atualmente).

Existem alguns hacks feios nas seções não recursivas. Em particular, quando a posição é identificada como perdida, tomamos nossas posições como [value move]matriz, confiando no movimento como sendo irrelevante e o valor sendo diferente de zero.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.