决定井字游戏的赢家

2024-04-20 04:38:15 发布

您现在位置:Python中文网/ 问答频道 /正文

我想用python为一个3x3的tic-tac-toe板编写一个过程,名为winner(board),它采用一个表示tic-tac-toe板的二维列表,并确定是否有赢家(如果有赢家,是谁)或者游戏是否以平局结束。棋盘使用'-'代表一个空缺位置,'x'代表玩家1,'o'代表玩家2。在

我还希望使用过程select_row(array, i)select_col(array,i),它们接受二维数组(用嵌套列表表示)和行或列的索引,并返回一个表示与该索引关联的行或列的列表,以及select_main_diag(array)select_counter_diag(array)过程,它们接受一个二维正方形数组并返回一个包含主对角线或反对角线元素的列表。在

回报如下:

winner([['-','-','-'], ['-','-','-'], ['-','-','-']])
False

winner([['x','x','x'], ['-','-','-'], ['-','-','-']])
'x'

winner([['o', '-','-'], ['-', 'o','-'], ['-','-','o']])
'o'

winner([['-','-','x'], ['-','-','x'], ['-','-', 'x']])
'x'

winner([['x','o','x'], ['x','o','o'], ['o','x','x']])
"Draw"

winner([['x','o','x'], ['x','o','o'], ['o','-','x']])
False

我希望在守则中包括和使用的程序如下:

^{pr2}$

我对使用python完全陌生,对它也不太熟悉,所以关于这个问题的任何建议——包括如何使用它以及如何将它放入代码中——都会很有帮助。在


Tags: false列表过程玩家代表数组ticarray
2条回答

这就是我如何确定赢家:

  1. 对于每一行、每列和每一对角线:这三个空格都一样吗?如果是这样,而且他们不是空的,那就是赢家。在
  2. 否则,电路板是否已满(即没有空位)?如果是这样,那就是平局。在
  3. 否则,就没有赢家,比赛继续。在

在下面的实现中,程序试图最大限度地提高获胜的机会。一个更强的版本会尽量减少失败的机会。另一种策略是学习什么样的行动会导致胜利,什么样的行动会导致失败。至于胜利者,grade_grid(state.grid)给出了答案。在

import tkinter
from itertools import product
from math import trunc
from functools import lru_cache, wraps
from random import choice
from sys import modules

################################################################################

class Cross(tkinter.Frame):

    @classmethod
    def main(cls):
        tkinter.NoDefaultRoot()
        root = tkinter.Tk()
        root.title('Demo')
        root.resizable(False, False)
        widget = cls(root)
        widget.grid()
        root.mainloop()

    def __init__(self, master=None, cnf={}, **kw):
        super().__init__(master, cnf, **kw)
        self.__ai = CrossAI()
        space = ' ' * 11
        self.__buttons = {}
        for r, c in product(range(3), range(3)):
            b = tkinter.Button(self, text=space, command=self.__bind(r, c))
            b.grid(row=r, column=c, padx=10, pady=10)
            self.__buttons.setdefault(r, {})[c] = b

    def __bind(self, row, column):
        return lambda: self.click(row, column)

    def click(self, row, column):
        r, c = self.__ai.move(row, column)
        if r != -1 != c:
            self.__buttons[row][column]['text'] = '    X    '
            if r != -2 != c:
                self.__buttons[r][c]['text'] = '    O    '

################################################################################

def enum(names):
    "Create a simple enumeration having similarities to C."
    return type('enum', (), dict(map(reversed, enumerate(
        names.replace(',', ' ').split())), __slots__=()))()

################################################################################

class Static(type):

    def __new__(cls, name, bases, members):
        for name, member in members.items():
            if callable(member):
                members[name] = cls.__wrap(member)
            elif isinstance(member, property):
                members[name] = property(cls.__wrap(member.fget),
                                         cls.__wrap(member.fset),
                                         cls.__wrap(member.fdel),
                                         member.__doc__)
            elif isinstance(member, (classmethod, staticmethod)):
                members[name] = type(member)(cls.__wrap(member.__func__))
        return super().__new__(cls, name, bases, members)

    @classmethod
    def __wrap(cls, function):
        if function:
            annotations = function.__annotations__
            co_varnames = function.__code__.co_varnames
            if not annotations:
                return function
            @wraps(function)
            def wrapper(*args):
                for arg, name in zip(args, co_varnames):
                    cls.__raise(arg, annotations[name])
                value = function(*args)
                cls.__raise(value, annotations['return'])
                return value
            return wrapper

    @staticmethod
    def __raise(item, klass):
        if klass is None:
            klass = type(None)
        elif isinstance(klass, str):
            klass = vars(modules[item.__module__])[klass]
        if not isinstance(item, klass):
            raise TypeError('{} must be of type {}'.format(item, klass))

################################################################################

class CrossAI(metaclass=Static):

    STATE = enum('changing, victory, defeat, error, draw')

    def __init__(self: 'CrossAI') -> None:
        self.__db = State(((0, 0, 0), (0, 0, 0), (0, 0, 0)), 1)

    def move(self: 'CrossAI', row: int, column: int) -> tuple:
        if not self.__db.moves:
            return -1, -1
        self.__make_move(row, column)
        return self.__best_move()

    def __make_move(self: 'CrossAI', row: int, column: int) -> None:
        copy = tuple(map(list, self.__db.grid))
        copy[row][column] = 1
        self.__db = State(tuple(map(tuple, copy)), -1)

    def __best_move(self: 'CrossAI') -> tuple:
        if not self.__db.moves:
            return -2, -2
        score = min(move.grade for move in self.__db.moves)
        moves = tuple(move for move in self.__db.moves if move.grade == score)
        final = choice(moves)
        for r, c in product(range(3), range(3)):
            if self.__db.grid[r][c] != final.grid[r][c]:
                self.__db = State(final.grid, 1)
                return r, c

################################################################################

class State(tuple):

    @lru_cache(None)
    def __new__(cls, grid, next_move):
        return super().__new__(cls, (grid, make_moves(grid, next_move)))

    @property
    def grid(self):
        return self[0]

    @property
    def moves(self):
        return self[1]

    @property
    def grade(self):
        return grade(*self)

################################################################################

@lru_cache(None)
def make_moves(grid, next_move):
    moves = []
    for r, c in available_moves(grid):
        copy = tuple(map(list, grid))
        copy[r][c] = next_move
        moves.append(State(tuple(map(tuple, copy)), -next_move))
    return frozenset(moves)

@lru_cache(None)
def available_moves(grid):
    return () if grade_grid(grid) else \
        tuple((r, c) for r, c in product(range(3), range(3)) if not grid[r][c])

@lru_cache(None)
def grade(grid, moves):
    return grade_grid(grid) + grade_moves(moves)

@lru_cache(None)
def grade_grid(grid):
    for triplet in combinations(grid):
        grade = trunc(sum(triplet) / 3)
        if grade:
            return grade
    return 0

@lru_cache(None)
def combinations(grid):
    combos = list(grid)
    for c in range(3):
        combos.append(tuple(grid[r][c] for r in range(3)))
    combos.append(tuple(grid[i][i] for i in range(3)))
    combos.append(tuple(grid[i][2 - i] for i in range(3)))
    return combos

@lru_cache(None)
def grade_moves(moves):
    return sum(grade(*move) for move in moves)

################################################################################

if __name__ == '__main__':
    Cross.main()

相关问题 更多 >