Os jogos Tic-tac-dedo do pé

15

Criar um programa determinista para jogar n d tic-tac-toe com os outros concorrentes.

Seu programa deve funcionar quando n(largura) e d(número da dimensão) estiverem nestes intervalos:

n∈[3,∞)∩ℕ  ie a natural number greater than 2
d∈[2,∞)∩ℕ  ie a natural number greater than 1

n = 3; d = 2(3 2 ou seja, 3 por 3):

[][][]
[][][]
[][][]

n = 3; d = 3(3 3 ou seja, 3 por 3 por 3):

[][][]
[][][]
[][][]

[][][]
[][][]
[][][]

[][][]
[][][]
[][][]

n = 6; d = 2(6 2 ou seja, 6 por 6):

[][][][][][]
[][][][][][]
[][][][][][]
[][][][][][]
[][][][][][]
[][][][][][]

E assim por diante.

Entrada:

A entrada será para STDIN. A primeira linha de entrada será dois números ne dno formato n,d.

Depois disso, haverá uma linha composta por coordenadas, especificando os movimentos que foram feitos. Coordenadas serão listados no formulário: 1,1;2,2;3,3. O canto superior esquerdo é a origem (0,0 para 2D). No caso geral, essa lista será como 1,2,...,1,4;4,0,...,6,0;...onde o primeiro número representa esquerda-direita, a segunda cima-baixo, a terceira até a terceira dimensão, etc. Observe que a primeira coordenada é Xa primeira volta, a segunda é Oa primeira vez, ....

Se este for o primeiro movimento, a entrada será um número seguido por 1 linha em branco.

Por consistência, a entrada sempre terminará com uma nova linha. Entrada de amostra (\ n é nova linha):

10,10\n0,0,0,0,0,0,0,0,0,0;0,2,3,4,5,6,7,8,9,0;0,1,2,3,4,5,6,7,8,9\n

Para o primeiro movimento:

10,10\n\n

onde \nestá o caractere de nova linha.

Resultado:

Envie a movimentação que você deseja fazer no mesmo formato que a entrada (uma lista separada por vírgula). Uma jogada inválida (isto é, uma que já foi realizada) resultará em uma perda para o jogo.

Nota: Você pode usar um gerador de números aleatórios, desde que o propague com um valor tal que cada execução seja idêntica, nas mesmas condições. Em outras palavras, o programa deve ser determinístico.

Nota: somente movimentos válidos são permitidos.

Jogos Vencedores (Se você já jogou o suficiente jogo da velha multidimensional, é o mesmo.)

Para que haja uma vitória, um jogador deve ter todos os quadrados adjacentes ao longo de uma linha. Ou seja, esse jogador deve ter nmovimentos em uma linha para ser um vencedor.

Adjacente:

  • cada peça é um ponto; por exemplo (0,0,0,0,0) é um ponto emd=5
  • peças adjacentes são peças tais que são os dois pontos na mesma unidade d-cubo. Em outras palavras, a distância Chebyshev entre os blocos é 1.
  • em outras palavras, se um ponto pé adjacente a um ponto q, então todas as coordenadas em ps coordenadas correspondentes qdiferem dele em não mais que um. Além disso, pelo menos no par de coordenadas difere em exatamente um.

Linhas:

  • As linhas são definidas por vetores e um bloco. Uma linha é cada bloco atingido pela equação:p0 + t<some vector with the same number of coordinates as p0>

Simulação e condições vencedoras:

  • Indique na sua resposta se está pronto para a classificação. Ou seja, indique claramente se sua resposta foi feita ou não.

  • Se sua resposta estiver marcada como concluída, ela não será classificada até pelo menos 24 horas após a última edição do código.

  • Os programas devem funcionar offline. Se um programa for trapaceado, ele receberá automaticamente uma pontuação de -1e não será mais pontuado. (Como alguém terminaria trapaceando com seus programas?)

  • Se o seu programa produzir saída inválida, será imediatamente contada como uma perda para o jogo

  • Se você programar uma falha na produção após 1 minuto, será imediatamente contabilizado como uma perda para o jogo. Se necessário, otimize a velocidade. Não quero esperar uma hora para testar um programa fora de outro.

  • Cada programa será executado contra os outros programas duas vezes para cada um nno intervalo [3,6]e cada um dno intervalo[2,5] , uma vez Xe uma vez como O. Esta é uma rodada.

  • Para cada jogo que um programa vence, ele atinge +3sua pontuação. Se o programa empatou (1 vitória e 1 derrota em uma única rodada ou empata nos dois jogos), então+1 . Se o programa for perdido, será exibido +0(ou seja, nenhuma alteração).

  • O programa com a pontuação mais alta vence. Se houver empate, o programa com o menor número de jogos perdidos (fora dos competidores empatados) vence.

Nota: Dependendo do número de respostas, posso precisar de ajuda para executar os testes.

Boa sorte! E que as simulações sejam sempre a seu favor!

Justin
fonte
Desafio do verificador de vitória. <---- Esse desafio é criar programas para verificar se um determinado jogo foi vencido. Está muito relacionado a esse desafio.
Justin
A tag independente que você acabou de inventar não adiciona nada à classificação da tag. Eu acho que você deveria removê-lo.
Howard
@ Howard Okay. Percebi que muitas perguntas tinham essa restrição, então pensei que uma tag seria apropriada.
23616 Justin Just
4
Um jogo estranho. O único movimento vencedor é não jogar.
german_guy
é (w, x, y, z) um formato de saída válido?
alexander-Brett

Respostas:

2

Python 3

import random as rand
import re

def generateMoves(width, dim):
    l = [0] * dim
    while existsNotX(l, width - 1):
        yield l[:]
        for i in range(dim):
            if l[i] < width - 1:
                l[i] += 1
                break
            else:
                l[i] = 0
    yield l

def existsNotX(l, x):
    for i in l:
        if i != x:
            return True
    return False

input_s = input()
dims, moves = None, None
#this is to allow input as a single paste, instead of ENTER inputting.
try:
    dims, moves = input_s.splitlines()
except ValueError:
    dims = input_s
    moves = input()

rand.seed(moves + dims)

dims = eval(dims) #change into tuple

moves = moves.split(';')
if len(moves[0]):
    moves = [eval(m) for m in moves] #change into tuples

output =[x for x in generateMoves(dims[0], dims[1]) if x not in moves]
print(re.sub('[^\\d,]', '', str(output[rand.randint(0, len(output))])))

Isso é simplesmente um ai aleatório. Ele seleciona aleatoriamente um movimento dentre os movimentos que ainda estão disponíveis.

Justin
fonte
2

Python 2.7

Esta é uma submissão de trabalho em andamento que estou fornecendo para fornecer um esqueleto / inspiração para outros respondentes. Ele funciona listando todas as linhas vencedoras possíveis e, em seguida, aplicando alguma heurística para pontuar essa linha de acordo com a sua importância. Atualmente, a heurística está em branco (trabalhos super-secretos). Também adicionei o tratamento de erros de vitória e choque.

Notas sobre o problema

Quantas linhas vencedoras existem? Considere o caso unidimensional; existem 2 vértices, 1 aresta e 1 linha. Em 2 dimensões, temos 4 vértices unidos por 2 linhas e 4 arestas unidas por 2 * n linhas. Em 3 dimensões, temos 8 vértices unidos por 4 linhas, 12 arestas unidas por 6 * n linhas e 6 faces unidas por3*n^2 linhas.

Em geral, vamos chamar um vértice de faceta 0, uma aresta de 1 faceta, etc. Em seguida, vamos N(i)denotar o número de facetas i, do número de dimensões e no comprimento lateral. Então o número de linhas vencedoras é 0.5*sum(N(i)*n^i,i=0..d-1).

Por wikipedia, N(i)=2^(d-i)*d!/(i!*(n-1)!)a fórmula final é:

sum(2^(d-i-1) n^i d! / (i! * (n-i)!),i=0..d-1)

qual wolfram | alpha não gosta muito. Isso fica muito grande rapidamente, então não espero que meu programa tenha tempo de execução gerenciável para d> 8.

Alguns resultados (desculpe pela formatação:

d\n 0   1    2      3      4       5        6        7         8         9
0   1   1    1      1      1       1        1        1         1         1
1   2   4    6      8      10      12       14       16        18        20
2   4   11   26     47     74      107      146      191       242       299
3   8   40   120    272    520     888      1400     2080      2952      4040
4   16  117  492    1437   3372    6837     12492    21117     33612     50997
5   32  364  2016   7448   21280   51012    107744   206896    368928    620060
6   64  1093 8128   37969  131776  372709   908608   1979713   3951424   7352101
7   128 3280 32640  192032 807040  2687088  7548800  18640960  41611392  85656080
8   256 9834 130809 966714 4907769 19200234 62070009 173533434 432891129 985263594

I / O

No momento, a entrada precisa ser inserida como: tictactoe.py <ret> n,d <ret> move;move <ret>- observe as várias linhas e nenhuma final; .

A saída parece (x_1,x_2,x_3...), por exemplo:

tictactoe.py <ret> 6,5 <ret> <ret> => 0, 0, 0, 0, 0

tictactoe.py <ret> 6,5 <ret> 0,0,0,0,0;0,0,0,0,5 <ret> => 0, 0, 0, 5, 0

# Notes on terminology:
#
# - A hypercube is the region [0,n]^d
# - An i-facet is an i-dimensional facet of a hypercube,
#   which is to say, a 0-facet is a vertex, a 1-facet an
#   edge, a 2-facet a face, and so on.
# - Any tuple {0,n}^i is a vertex of an i-hypercube
#   which is why I've used vertex to describe such
#   tuples
# - A winning line is a set of n coordinates which joins
#   two opposite i-facets
# - i-facets are opposite if they differ in every co-
#   ordinate which defines them
#
# Test Data:
#  


import numpy
import itertools

def removeDuplicates(seq):
    noDupes = []
    [noDupes.append(i) for i in seq if not noDupes.count(i)]
    return noDupes 


def listPairedVertices (i,n):
    """
    listPairedVertices returns a list L of elements of {0,n}^i which has the
    property that for every l in L, there does not exist l' such that
    l+l' = {n}^i.
    """

    vertices = numpy.array([[b*(n-1)  for b in a] for a in [
        list(map(int,list(numpy.binary_repr(x,i)))) for x in range(2**i)
    ]])
    result = []
    while len(vertices)>1:
        for j in range(len(vertices)):
            if numpy.all(vertices[j] + vertices[0] == [n-1]*i):
                result.append(vertices[0])
                vertices=numpy.delete(vertices,[0,j],axis=0)
                break
    return result


def listSequences (d,l):
    """
    listSequences returns the subset of {0,1}^d having precisely n 1s.
    """
    return numpy.array([
        r for r in itertools.product([0,1],repeat=d) if sum(r)==l
    ])


def listPaddedConstants (s,n):
    """
    listPaddedConstants takes a sequence in {0,1}^d and returns a number in
    {0..n}^sum(s) padded by s
    """
    result = numpy.zeros([n**sum(s),len(s)],dtype=numpy.int)
    for i,x in enumerate([list(z) for z in 
        itertools.product(range(n),repeat=sum(s))]):
        for j in range(len(s)):
            if s[j]: result[i][j] = x.pop()
    return result


def listWinningVectorsForDimension(d,i,n):
    """
    List the winning lines joining opposite i-facets of the hypercube.

    An i-facet is defined by taking a vertex v and a sequence s, then forming 
    a co-ordinate C by padding v with zeroes in the positions indicated by s.
    If we consider s = s_0.e_0 + s_1+e_1... where the e_j are the canonical
    basis for R^d, then the formula of the i-facet is 
        C+x_0.s_0.e_0+x_1.s_1.e_1... 
    for all vectors x = (x_0,x_1...) in R^n

    We know that winning lines only start at integral positions, and that the
    value of a will only be needed when s_j is nonempty, so the start point S
    of a winning line is in fact determined by:
     + vertex v in {0,n}^(d-i), padded by s
     + a in R^i, padded by the complement of s, s'

    Having performed the following operations, the co-ordinates of the winning
    lines are abs(S-k*s') for k in [0..n-1]
    """
    vertices = listPairedVertices(d-i,n)
    sequences = listSequences(d,i)
    result = []
    for s in sequences:
        for v in vertices:
            C = [0]*d
            j = 0
            for index in range(d):
                if s[index]: C[index] = 0
                else: 
                    C[index] = v[j]
                    j+=1
            result += [
                [numpy.absolute(S-k*(numpy.absolute(s-1))) for k in range(n)] 
                    for S in [C+a for a in listPaddedConstants(s,n)]
            ]
    return result


def AllWinningLines (d,n):
    """
    has the structure [[x_1,x_2,x_3],[y_1,y_2,y_3]] where each l_k is a
    length-d co-ordinate
    """
    result = []
    for i in range(d):
        result += listWinningVectorsForDimension(d,i,n)
    return result


def movesAlreadyMade ():
    """
    Returns a list of co-ordinates of moves already made read from STDIN
    """
    parameters = raw_input()
    moves = raw_input()
    parameters = list(map(int,parameters.split(',')))
    moves = [map(int,a.split(',')) for a in moves.split(';')] \
        if moves != '' else []
    return {'n':parameters[0], 'd':parameters[1], 'moves':moves}

def scoreLine (moves, line, scores, n):
    """
    Gives each line a score based on whatever logic I choose
    """
    myMoves          = moves[0::2]
    theirMoves       = moves[1::2]
    if len(moves)%2: myMoves, theirMoves = theirMoves, myMoves

    lineHasMyMove    = 0
    lineHasTheirMove = 0
    score            = 0

    for coord in line:
        if coord.tolist() in myMoves: 
            lineHasMyMove += 1
            if coord.tolist() in theirMoves: raise Exception('Move clash')
        elif coord.tolist() in theirMoves: lineHasTheirMove += 1

    if lineHasMyMove == len(line):
        raise Exception('I have won')
    elif lineHasTheirMove == len(line):
        raise Exception('They have won')
    elif lineHasMyMove and lineHasTheirMove: 
        pass
    elif lineHasTheirMove == len(line)-1: 
        score = n**lineHasTheirMove
    else: 
        score = n**lineHasMyMove

    for coord in line:
        if coord.tolist() not in moves: 
            scores[tuple(coord)]+=score

def main():
    """
    Throw it all together
    """
    data      = movesAlreadyMade()
    dimension = data['d']
    length    = data['n']
    lines     = AllWinningLines(dimension, length)
    scores    = numpy.zeros([length]*dimension, dtype=numpy.int)

    try: [scoreLine(data['moves'], line, scores, length) for line in lines]
    except Exception as E:
            print 'ERROR: ' + E.args[0]
            return
    print ','.join(map(
        str,numpy.unravel_index(numpy.argmax(scores),scores.shape)
        ))


if __name__ == "__main__": main() 

Editar: para E / S, lógica adicionada. Eu acredito que isso agora está pronto para marcar

Observe que este comentário foi inicialmente um espaço reservado que eu excluí e cancelei.

alexander-brett
fonte
1

Python 2

import re
import itertools

input_s = raw_input()
dims, moves = None, None
#this is to allow input as a single paste, instead of ENTER inputting.
try:
    dims, moves = input_s.splitlines()
except ValueError:
    dims = input_s
    moves = raw_input()

dims = eval(dims) #change into tuple

moves = moves.split(';')
if len(moves[0]):
    moves = [eval(m) for m in moves] #change into tuples

allSpaces = [x for x in itertools.product(range(dims[0]), repeat=dims[1])]
move = None
for space in allSpaces:
    if space not in moves:
        move = space
        break
print(re.sub('[^\\d,]', '', str(move)))

A maior parte do código é exatamente a mesma da IA ​​aleatória do Quincunx . Em vez de selecionar aleatoriamente um movimento, ele escolhe o primeiro movimento disponível lexicograficamente (ou seja, (0,0, ... 0), depois (0,0, ... 1) e depois (0,0, ... 2) etc.).

Esta é uma estratégia bastante inútil, mas quase sempre é melhor do que jogar aleatoriamente.

tttppp
fonte