井字游戏


15

创建一个确定性的程序来播放N d井字棋与其他选手。

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 x 3):

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

n = 3; d = 3 (3 3即3 x 3 x 3):

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

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

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

n = 6; d = 2 (6 2即6 x 6):

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

等等。

输入:

输入将输入到STDIN。输入的第一行将是两个数字,n并且d在形式n,d

在这之后是一条由坐标组成的线,用于指定已完成的移动。坐标将以以下形式列出:1,1;2,2;3,3。左上角是原点(2D为0,0)。在一般情况下,此列表类似于1,2,...,1,4;4,0,...,6,0;...第一个数字代表左右方向,第二个上下方向,第3到第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,则ps中相应坐标中的每个坐标q相差不超过一个。此外,至少在一个坐标对上相差一个。

行数:

  • 线条由矢量和图块定义。一条线是方程式命中的每个图块:p0 + t<some vector with the same number of coordinates as p0>

模拟和获胜条件:

  • 在您的答案中说明是否准备好评分。也就是说,明确指出您的答案是否完成。

  • 如果您的答案标记为已完成,则必须等到对代码的最后一次编辑后至少24小时才能对其评分。

  • 程序必须脱机工作。如果发现某个程序在作弊,它将自动获得的分数-1,并且不会进一步评分。(任何人都会以他们的程序作弊而告终?)

  • 如果您的程序产生无效输出,则立即计为游戏损失

  • 如果您的程序在1分钟后未能产生输出,则立即计为游戏损失。如有必要,优化速度。我不想等待一个小时来测试另一个程序。

  • 每个程序都会对其他项目各2次运行n范围[3,6]和每个d范围内[2,5],一次是X,一次作为O。这是一轮。

  • 对于每个游戏,一个程序获胜,都会+3达到其得分。如果程序并列(两场比赛在单局或平局中获胜1负),则得到+1。如果程序丢失,那么它将得到+0(即不变)。

  • 得分最高的程序将获胜。如果出现平局,则输掉最少游戏(在并列选手中)的程序获胜。

注意:根据答案的数量,我可能需要运行测试的帮助。

祝好运!并希望模拟能够永远对您有利!


胜利者挑战。<----这个挑战是创建程序来检查是否赢得了某个游戏。这与这一挑战非常相关。
贾斯汀2014年

您刚刚发明的自包含标签不会在标签分类中添加任何内容。我认为您应该删除它。
2014年

@霍华德好吧。我注意到很多问题都有这个限制,所以我认为使用标签是合适的。
贾斯汀2014年

4
一个奇怪的游戏。唯一的制胜法宝是不参加比赛。
german_guy 2014年

(w,x,y,z)是有效的输出格式吗?
alexander-brett 2014年

Answers:


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

这只是一个随机的人工智能。它从仍然可用的移动中随机选择一个移动。


2

Python 2.7

这是一个进行中的提交,我将提供给其他答复者一个框架/启发。它的工作原理是列出每条可能的获胜线,然后应用启发式方法根据该线的价值对其进行评分。当前,启发式方法是空白的(超级秘密工作)。我还添加了赢和冲突错误处理。

关于问题的说明

有几条中奖线?考虑一维情况;有2个顶点,1条边和1条线。在2维中,我们有2条直线连接的4个顶点和2 * n条直线连接的4个边。在3维中,我们有4条直线连接的8个顶点,6 * n条直线连接的12个边和6条直线连接的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)

Wolfram | alpha不太喜欢。这很快就变得非常大,因此我不希望我的程序在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

输入输出

目前,输入内容需要输入: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() 

编辑:对于I / O,添加了逻辑。我相信现在已经准备好纪念

请注意,此评论最初是一个占位符,我已将其删除并取消删除。


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

大多数代码与Quincunx的随机AI完全相同。与其随机选择一个动作,不如按字典顺序选择第一个可用的动作(即(0,0,... 0),然后是(0,0,... 1),然后是(0,0,... 2)等)。

这是一个非常垃圾的策略,但几乎总是比随机播放更好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.