我正在寻找在python中编写一个名为winner(board)
的3x3 tic-tac-toe板的程序,它采用代表Tic-Tac-Toe板的二维列表并确定是否有赢家(如果有胜利者,那是谁)或者游戏以平局结束。董事会使用'-'
表示未结头寸,玩家1为'x'
,玩家2为'o'
。
我还希望使用接受二维数组(表示为嵌套列表)和行或列索引的过程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
我希望在代码中包含和使用的程序如下:
def select_row(lst, i):
return lst[i]
def select_col(lst, i):
return [lst[idx][i] for idx in range(len(lst))]
def select_main_diag(array):
return [array[i][i] for i in range(len(array))]
def select_counter_diag(array):
return [array[len(array)-i-1][i] for i in range(len(array))]
我对使用python完全不熟悉并且对它非常不熟悉,所以关于这个问题的任何建议 - 无论是如何处理它以及如何将它放入代码中 - 都会非常有用。
答案 0 :(得分:1)
这就是我对获胜者决心的看法:
答案 1 :(得分:0)
在以下实施中,该计划试图最大化获胜的机会。更强大的版本会尽量减少失败的机会。另一种策略是学习什么动作导致胜利以及什么动作导致失败。至于获胜者,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()