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.