Los juegos de tres en raya

15

Crea un programa determinista para jugar n d tic-tac-toe con los otros concursantes.

Su programa debería funcionar cuando n(ancho) y d(número de dimensión) están en estos rangos:

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

n = 3; d = 2(3 2 es decir, 3 por 3):

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

n = 3; d = 3(3 3 es decir, 3 por 3 por 3):

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

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

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

n = 6; d = 2(6 2 es decir, 6 por 6):

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

Y así.

Entrada:

La entrada será a STDIN. La primera línea de entrada será dos números, ny den la forma n,d.

Después de esto habrá una línea que consiste en coordenadas que especifican los movimientos que se han realizado. Coordenadas se mostrarán en el formulario: 1,1;2,2;3,3. La esquina superior izquierda es el origen (0,0 para 2D). En el caso general, esta lista será como 1,2,...,1,4;4,0,...,6,0;...donde el primer número representa izquierda-derecha-ness, el segundo arriba-abajo-ness, el tercero a la 3ra dimensión, etc. Tenga en cuenta que la primera coordenada es Xel primer giro, el segundo es Oel primer turno ...

Si este es el primer movimiento, la entrada será un número seguido de 1 línea en blanco.

Por coherencia, la entrada siempre terminará con una nueva línea. Entrada de muestra (\ n es nueva línea):

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 el primer movimiento:

10,10\n\n

¿Dónde \nestá el personaje de nueva línea?

Salida:

Imprima el movimiento que desea realizar en el mismo formato que la entrada (una lista separada por comas). Un movimiento no válido (es decir, uno que ya se ha realizado) dará como resultado una pérdida para el juego.

Nota: Puede usar un generador de números aleatorios, siempre que lo siembre con un valor tal que cada ejecución sea idéntica en las mismas condiciones. En otras palabras, el programa debe ser determinista.

Nota: solo se permiten movimientos válidos.

Juegos ganadores (si ha jugado suficientes tic tac toe multidimensionales, esto es lo mismo).

Para que haya una victoria, un jugador debe tener todos los cuadrados adyacentes a lo largo de una línea. Es decir, ese jugador debe tener nmovimientos en una línea para ser un ganador.

Adyacente:

  • cada azulejo es un punto; por ejemplo (0,0,0,0,0) es un punto end=5
  • las fichas adyacentes son fichas, de modo que ambos son puntos en la misma unidad d-cubo. En otras palabras, la distancia de Chebyshev entre las fichas es 1.
  • en otras palabras, si un punto pes adyacente a un punto q, entonces cada coordenada en ps coordenada correspondiente qdifiere de ella en no más de uno. Además, al menos en el par de coordenadas difiere exactamente en uno.

Líneas:

  • Las líneas están definidas por vectores y un mosaico. Una línea es cada mosaico golpeado por la ecuación:p0 + t<some vector with the same number of coordinates as p0>

Simulación y condiciones ganadoras:

  • Indique en su respuesta si está listo para calificar. Es decir, indique claramente si su respuesta está hecha o no.

  • Si su respuesta está marcada como terminada, no se calificará hasta al menos 24 horas después de la última edición del código.

  • Los programas deben funcionar sin conexión. Si se descubre que un programa está haciendo trampa, recibirá automáticamente una puntuación de -1, y no se puntuará más. (¿Cómo terminaría alguien con sus programas haciendo trampa?)

  • Si su programa produce resultados no válidos, se cuenta inmediatamente como una pérdida para el juego.

  • Si su programa no produce resultados después de 1 minuto, se cuenta inmediatamente como una pérdida para el juego. Si es necesario, optimice la velocidad. No quiero tener que esperar una hora para probar un programa de otro.

  • Cada programa se ejecutará contra los otros programas dos veces para cada uno nen el rango [3,6]y cada uno den el rango [2,5], una vez como Xy una vez como O. Esta es una ronda.

  • Por cada juego que gana un programa, llega +3a su puntaje. Si el programa empató (1 victoria y 1 derrota en una sola ronda o empates para ambos juegos), entonces se pone +1. Si el programa se pierde, entonces se obtiene +0(es decir, sin cambios).

  • El programa con la puntuación más alta gana. En caso de empate, gana el programa con el menor número de juegos perdidos (fuera de los concursantes empatados).

Nota: Dependiendo de la cantidad de respuestas, es posible que necesite ayuda para ejecutar las pruebas.

¡Buena suerte! ¡Y que las simulaciones corran siempre a tu favor!

Justin
fuente
Desafío de check-win. <---- Este desafío es crear programas para verificar si se gana un determinado juego. Está muy relacionado con este desafío.
Justin
La etiqueta autónoma que acaba de inventar no agrega nada a la clasificación de etiquetas. Creo que deberías eliminarlo.
Howard
@Howard Okay. Noté que muchas preguntas tenían esa restricción, así que pensé que una etiqueta sería apropiada.
Justin
44
Un juego extraño El único movimiento ganador es no jugar.
german_guy
¿Es (w, x, y, z) un formato de salida válido?
alexander-brett

Respuestas:

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))])))

Esto es simplemente un ai aleatorio. Selecciona aleatoriamente un movimiento fuera de los movimientos que todavía están disponibles.

Justin
fuente
2

Python 2.7

Esta es una presentación de trabajo en progreso que estoy proporcionando para dar un esqueleto / inspiración a otros respondedores. Funciona enumerando todas las líneas ganadoras posibles, luego aplicando un poco de heurística para calificar esa línea de acuerdo con lo valiosa que sea. Actualmente, la heurística está en blanco (trabajos súper secretos). También he agregado el manejo de errores de victoria y choque.

Notas sobre el problema

¿Cuántas líneas ganadoras hay? Considere el caso unidimensional; Hay 2 vértices, 1 borde y 1 línea. En 2 dimensiones, tenemos 4 vértices unidos por 2 líneas y 4 bordes unidos por 2 * n líneas. En 3 dimensiones, tenemos 8 vértices unidos por 4 líneas, 12 bordes unidos por 6 * n líneas y 6 caras unidas por 3*n^2líneas.

En general, llamemos a un vértice una faceta 0, una arista una faceta, etc. Luego N(i)denotemos el número de facetas i, del número de dimensiones y nla longitud del lado. Entonces el número de líneas ganadoras es 0.5*sum(N(i)*n^i,i=0..d-1).

Según wikipedia, N(i)=2^(d-i)*d!/(i!*(n-1)!)la fórmula final es:

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

que wolfram | alpha no le gusta mucho. Esto se hace muy grande rápidamente, así que no espero que mi programa tenga un tiempo de ejecución manejable para d> 8.

Algunos resultados (perdón por el formato:

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

Por el momento, la entrada debe ingresarse como: tictactoe.py <ret> n,d <ret> move;move <ret>- observe las múltiples líneas y no la final ;.

El resultado se ve (x_1,x_2,x_3...), por ejemplo:

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 agregada. Creo que esto ya está listo para marcar

Tenga en cuenta que este comentario fue inicialmente un marcador de posición que eliminé y eliminé.

alexander-brett
fuente
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)))

La mayor parte del código es exactamente el mismo que la IA aleatoria de Quincunx . En lugar de seleccionar un movimiento al azar, elige el primer movimiento disponible lexicográficamente (es decir (0,0, ... 0), luego (0,0, ... 1), luego (0,0, ... 2) , etc.)

Esta es una estrategia bastante basura, pero casi siempre es mejor que jugar al azar.

tttppp
fuente