Ігри "Тік-так-носок"


15

Створіть детерміновану програму для гри n d tic-tac-toe з іншими учасниками.

Ваша програма повинна працювати, коли n(ширина) та d(розмірне число) знаходяться в цих діапазонах:

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

n = 3; d = 2(3 2, тобто 3 на 3):

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

n = 3; d = 3(3 3, тобто 3 на 3 на 3):

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

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

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

n = 6; d = 2(6 2, тобто 6 на 6):

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

І так далі.

Вхід:

Вхід буде STDIN. Перший рядок введення буде двома цифрами nі dу формі n,d.

Після цього буде лінія, що складається з координат, що вказують зроблені рухи. Координати будуть перераховані в наступному вигляді: 1,1;2,2;3,3. Лівий верхній кут є початком (0,0 для 2D). У загальному випадку цей список буде схожий на те, 1,2,...,1,4;4,0,...,6,0;...коли перше число позначає ліворуч-право-ность, друге вниз-третє, третє через 3-й вимір тощо. Зауважте, що перша координата - це Xперший поворот, другий це Oперший поворот, ....

Якщо це буде перший крок, то введено число, а потім 1 порожній рядок.

Для послідовності введення завжди закінчується новим рядком. Зразок введення (\ n - новий рядок):

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

Для першого кроку:

10,10\n\n

де \nсимвол нового рядка.

Вихід:

Виведіть хід, який ви хочете зробити, у тому ж форматі, що і введення (список, розділений комами). Недійсний хід (тобто вже зроблений) призведе до втрати для гри.

Примітка: Ви можете використовувати генератор випадкових чисел до тих пір, поки ви почнете його з таким значенням, що кожний запуск буде ідентичним за однакових умов. Іншими словами, програма повинна бути детермінованою.

Примітка. Дозволені лише дійсні ходи.

Виграючі ігри (Якщо ви зіграли достатньо багатовимірного тик-нога, це те саме.)

Для того щоб мати виграш, один гравець повинен мати всі сусідні квадрати вздовж лінії. Тобто той гравець повинен мати nходи по лінії, щоб бути переможцем.

Суміжні:

  • кожна плитка - крапка; наприклад (0,0,0,0,0) - точка вd=5
  • сусідні плитки - це плитки, вони є обома точками на одному одиничному d-кубі. Іншими словами, відстань Чебишева між плитками дорівнює 1.
  • іншими словами, якщо точка pприлягає до точки q, то кожна координата p, яка відповідає відповідній координаті, qвідрізняється від неї не більш ніж одиницею. Крім того, принаймні, пара координат відрізняється рівно на одиницю.

Рядки:

  • Лінії визначаються векторами і плиткою. Рядок - це кожна плитка, потрапила в рівняння:p0 + t<some vector with the same number of coordinates as p0>

Умови моделювання та перемоги:

  • Вкажіть свою відповідь, чи вона готова до оцінювання. Тобто чітко вкажіть, чи зроблена ваша відповідь чи ні.

  • Якщо ваша відповідь буде позначена як виконана, вона не буде оцінена як мінімум через 24 години після останнього редагування коду.

  • Програми повинні працювати в автономному режимі. Якщо програма виявляється як обман, вона автоматично отримає бал -1і більше не буде набрана. (Як би хто-небудь закінчився з їх обманом?)

  • Якщо ваша програма видає недійсний вихід, вона негайно зараховується як втрата для гри

  • Якщо програма не дає результату через 1 хвилину, це негайно зараховується як втрата для гри. При необхідності оптимізуйте для швидкості. Мені не потрібно чекати годину, щоб перевірити програму з іншої.

  • Кожна програма буде запускатися проти інших програм двічі для кожної nв діапазоні [3,6]і кожної dв діапазоні [2,5], один раз як Xі один раз O. Це один тур.

  • За кожну гру виграє програма, вона отримує +3свій рахунок. Якщо програма зрівняла (1 виграш і 1 програш в одному раунді або зв'язали обидві гри), то вона отримує +1. Якщо програма програла, вона отримує +0(тобто немає змін).

  • Перемагає програма з найвищим балом. Якщо має бути нічия, тоді виграє програма з найменшою кількістю програних ігор (із зв'язаних учасників).

Примітка. Залежно від кількості відповідей, мені може знадобитися допомога в проведенні тестів.

Удачі! І нехай симуляції запускаються колись на вашу користь!


Виклик виграш-шашки. <---- Ця задача полягає у створенні програм, щоб перевірити, чи виграна певна гра. Це дуже пов'язане з цим викликом.
Джастін

Автономний тег , який ви тільки що винайшли нічого не додає до класифікації тегів. Я думаю, ви повинні її зняти.
Говард

@Howard Гаразд. Я помітив, що в багатьох питаннях було таке обмеження, тому я вважав, що тег буде відповідним.
Джастін

4
Дивна гра. Єдиний виграшний хід - це не грати.
german_guy

чи (w, x, y, z) допустимий вихідний формат?
Олександр-Бретт

Відповіді:


2

Пітон 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))])))

Це просто випадковий ай. Він випадковим чином вибирає переміщення з тих кроків, які ще доступні.


2

Python 2.7

Це подання незавершеного виробництва, яке я надаю, щоб дати скелет / натхнення іншим відповідачам. Він працює, перераховуючи всі можливі виграшні рядки, а потім застосовуючи певну евристичну оцінку цього рядка відповідно до того, наскільки він цінний. Наразі евристика порожня (надпотаємні розробки). Я також додав обробку помилок виграшу та зіткнення.

Примітки щодо проблеми

Скільки виграшних ліній? Розглянемо одновимірний випадок; є 2 вершини, 1 край і 1 рядок. У двох вимірах у нас є 4 вершини, з'єднані двома лініями, і 4 ребра, з'єднані 2 * n лініями. У 3 вимірах у нас є 8 вершин, з'єднаних 4 лініями, 12 ребер, з’єднаних 6 * n лініями, і 6 граней, з'єднаних 3*n^2лініями.

Загалом, назвемо вершину 0-гранею, ребром - 1-фасетою тощо. Далі N(i)позначимо кількість i-граней, dкількість розмірів та nдовжину сторони. Тоді кількість виграшних рядків - це 0.5*sum(N(i)*n^i,i=0..d-1).

За вікіпедією, N(i)=2^(d-i)*d!/(i!*(n-1)!)тому остаточна формула:

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

який вольфрам | альфа дуже не любить. Це стає дуже великим досить швидко, тому я не сподіваюся, що моя програма матиме керований час виконання для d> 8.

Деякі результати (вибачте за форматування:

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

На даний момент введення потрібно вводити як: tictactoe.py <ret> n,d <ret> move;move <ret>- відзначте кілька рядків і немає остаточного ;.

Вихід виглядає (x_1,x_2,x_3...), наприклад:

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

Правка: для вводу / виводу додана логіка. Я вважаю, це зараз готово відзначити

Зауважте, що цей коментар спочатку був заповнювачем місця, який я видалив і відмінив.


1

Пітон 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)))

Більшість коду точно такі ж, як і випадковий ІІ Quincunx . Замість випадкового вибору ходу він вибирає перший доступний лексикографічний хід (тобто (0,0, ... 0), потім (0,0, ... 1), потім (0,0, ... 2) тощо).

Це досить сміттєва стратегія, але вона майже завжди б'є грати навмання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.