I giochi Tic-tac-toe


15

Creare un programma deterministico per giocare n d tic-tac-toe con gli altri concorrenti.

Il programma dovrebbe funzionare quando n(larghezza) e d(numero dimensione) rientrano in questi intervalli:

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

n = 3; d = 2(3 2 ovvero 3 per 3):

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

n = 3; d = 3(3 3 vale a dire 3 per 3 per 3):

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

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

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

n = 6; d = 2(6 2 ovvero 6 per 6):

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

E così via.

Ingresso:

L'ingresso sarà su STDIN. La prima riga di input sarà composta da due numeri ne dnel modulo n,d.

Dopo questo sarà una linea composta da coordinate che specificano le mosse che sono state fatte. Le coordinate saranno elencati nella forma: 1,1;2,2;3,3. L'angolo in alto a sinistra è l'origine (0,0 per 2D). Nel caso generale, questo elenco sarà simile a quello in 1,2,...,1,4;4,0,...,6,0;...cui il primo numero rappresenta la destra-sinistra, la seconda su-giù, la terza attraverso la terza dimensione, ecc. Si noti che la prima coordinata è Xla prima svolta, la seconda è Oil primo turno, ....

Se questa è la prima mossa, l'input sarà un numero seguito da 1 riga vuota.

Per coerenza, l'input termina sempre con una nuova riga. Input di esempio (\ n è newline):

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

Per la prima mossa:

10,10\n\n

dov'è \nil personaggio newline.

Produzione:

Stampa la mossa che desideri effettuare nello stesso formato dell'input (un elenco separato da virgole). Una mossa non valida (cioè una che è già stata presa) comporterà una perdita per il gioco.

Nota: è possibile utilizzare un generatore di numeri casuali, purché lo si semini con un valore tale che ogni serie sia identica, date le stesse condizioni. In altre parole, il programma deve essere deterministico.

Nota: sono consentite solo mosse valide.

Giochi vincenti (Se hai giocato abbastanza tic tac toe multidimensionale, questo è lo stesso.)

Affinché ci sia una vittoria, un giocatore deve avere tutti i quadrati adiacenti lungo una linea. Cioè, quel giocatore deve avere delle nmosse su una linea per essere un vincitore.

Adiacente:

  • ogni tessera è un punto; per esempio (0,0,0,0,0) è un punto ind=5
  • le tessere adiacenti sono tessere tali da essere entrambi punti sulla stessa unità d-cubo. In altre parole, la distanza di Chebyshev tra le tessere è 1.
  • in altre parole, se un punto pè adiacente a un punto q, allora ogni coordinata in pcoordinata corrispondente qdifferisce da essa per non più di una. Inoltre, almeno sulla coppia di coordinate differisce esattamente di uno.

Linee:

  • Le linee sono definite da vettori e una tessera. Una linea è ogni piastrella colpita dall'equazione:p0 + t<some vector with the same number of coordinates as p0>

Simulazione e condizioni vincenti:

  • Indica nella tua risposta se è pronto per la valutazione. Cioè, indica chiaramente se la tua risposta è stata o meno eseguita.

  • Se la tua risposta è contrassegnata come completata, non verrà valutata fino ad almeno 24 ore dopo l'ultima modifica del codice.

  • I programmi devono funzionare offline. Se si scopre che un programma sta imbrogliando, riceverà automaticamente un punteggio di -1e non verrà segnato ulteriormente. (Come si finirebbe per imbrogliare i propri programmi?)

  • Se il tuo programma produce un output non valido, viene immediatamente conteggiato come una perdita per il gioco

  • Se il programma non riesce a produrre output dopo 1 minuto, viene immediatamente conteggiato come una perdita per il gioco. Se necessario, ottimizzare per la velocità. Non voglio aspettare un'ora per testare un programma fuori da un altro.

  • Ogni programma verrà eseguito due volte rispetto agli altri programmi per ciascuno nnell'intervallo [3,6]e ciascuno dnell'intervallo [2,5], una volta come Xe una volta come O. Questo è un round.

  • Per ogni partita che un programma vince, arriva +3al suo punteggio. Se il programma ha pareggiato (1 vittoria e 1 sconfitta in un singolo round o pareggi per entrambe le partite), allora ottiene +1. Se il programma viene perso, viene visualizzato +0(ovvero nessuna modifica).

  • Vince il programma con il punteggio più alto. In caso di pareggio, vince il programma con il minor numero di partite perse (fuori dai concorrenti in parità).

Nota: a seconda del numero di risposte, potrei aver bisogno di aiuto per eseguire i test.

In bocca al lupo! E che le simulazioni siano sempre a tuo favore!


Sfida win-checker. <---- Questa sfida è quella di creare programmi per verificare se un determinato gioco viene vinto. È molto legato a questa sfida.
Giustino

Il tag autonomo che hai appena inventato non aggiunge nulla alla classificazione del tag. Penso che dovresti rimuoverlo.
Howard

@Howard Okay. Ho notato che molte domande avevano questa limitazione, quindi ho pensato che un tag sarebbe stato appropriato.
Giustino

4
Uno strano gioco. La sola mossa vincente è non giocare.
german_guy,

(w, x, y, z) è un formato di output valido?
alexander-brett,

Risposte:


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

Questo è semplicemente un I casuale. Seleziona casualmente uno spostamento dalle mosse ancora disponibili.


2

Python 2.7

Questa è una proposta in corso di elaborazione che sto fornendo per dare uno scheletro / ispirazione ad altri rispondenti. Funziona elencando ogni possibile linea vincente, quindi applicando un po 'di euristica per segnare quella linea in base a quanto è prezioso. Attualmente, l'euristica è vuota (meccanismi super segreti). Ho anche aggiunto la gestione degli errori win e clash.

Note sul problema

Quante linee vincenti ci sono? Considera il caso monodimensionale; ci sono 2 vertici, 1 bordo e 1 linea. In 2 dimensioni, abbiamo 4 vertici uniti da 2 linee e 4 bordi uniti da 2 * n linee. In 3 dimensioni, abbiamo 8 vertici uniti da 4 linee, 12 bordi uniti da 6 * n linee e 6 facce unite da 3*n^2linee.

In generale, chiamiamo un vertice una faccia 0, un bordo una faccia 1, ecc. Quindi N(i)denotiamo il numero di facce i, dil numero di dimensioni e nla lunghezza del lato. Quindi il numero di linee vincenti è 0.5*sum(N(i)*n^i,i=0..d-1).

Per Wikipedia, N(i)=2^(d-i)*d!/(i!*(n-1)!)quindi la formula finale è:

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

quale wolfram | alpha non piace molto. Questo diventa molto grande abbastanza rapidamente, quindi non mi aspetto che il mio programma abbia un runtime gestibile per d> 8.

Alcuni risultati (mi dispiace per la formattazione:

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

Al momento, l'input deve essere inserito come: tictactoe.py <ret> n,d <ret> move;move <ret>- annotare le righe multiple e non il finale ;.

L'output è simile (x_1,x_2,x_3...), ad esempio:

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

Modifica: per I / O, logica aggiunta. Credo che questo sia ora pronto per essere segnato

Nota che questo commento inizialmente era un segnaposto che ho eliminato e cancellato.


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

Gran parte del codice è esattamente lo stesso dell'IA casuale di Quincunx . Invece di selezionare casualmente una mossa, seleziona la prima mossa disponibile dal punto di vista lessicografico (ovvero (0,0, ... 0), quindi (0,0, ... 1), quindi (0,0, ... 2) , eccetera.).

Questa è una strategia piuttosto spazzatura, ma batte quasi sempre a casaccio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.