使用tkinter Draw功能的Python Tic-Tac-Toe游戏

时间:2018-12-16 16:35:26

标签: python tkinter tic-tac-toe

我正在用minimax算法编写井字游戏。当玩家失败时,我已经添加了topWindow,但是我找不到使程序显示Draw消息的方法。在这种情况下我该怎么办?

from tkinter import *
from tkinter.font import Font
from copy import deepcopy

class Board:
    def __init__(self, other=None):
        self.player = 'X'
        self.opponent = 'O'
        self.empty = ''
        self.size = 3
        self.fields = {}
        for y in range(self.size):
            for x in range(self.size):
                self.fields[x, y] = self.empty
        # copy constructor
        if other:
            self.__dict__ = deepcopy(other.__dict__)

    def move(self, x, y):
        board = Board(self)
        board.fields[x, y] = board.player
        (board.player, board.opponent) = (board.opponent, board.player)
        return board

    def __minimax(self, player):
        if self.won():
            if player:
                return (-1, None)
            else:
                return (+1, None)
        elif self.tied():
            return (0, None)
        elif player:
            best = (-2, None)
            for x, y in self.fields:
                if self.fields[x, y] == self.empty:
                    value = self.move(x, y).__minimax(not player)[0]
                    if value > best[0]:
                        best = (value, (x, y))
            return best
        else:
            best = (+2, None)
            for x, y in self.fields:
                if self.fields[x, y] == self.empty:
                    value = self.move(x, y).__minimax(not player)[0]
                    if value < best[0]:
                        best = (value, (x, y))
            return best

    def best(self):
        return self.__minimax(True)[1]

    def tied(self):
        for (x, y) in self.fields:
            if self.fields[x, y] == self.empty:
                return False
        return True

    def won(self):
        # horizontal
        for y in range(self.size):
            winning = []
            for x in range(self.size):
                if self.fields[x, y] == self.opponent:
                    winning.append((x, y))
            if len(winning) == self.size:
                return winning
        # vertical
        for x in range(self.size):
            winning = []
            for y in range(self.size):
                if self.fields[x, y] == self.opponent:
                    winning.append((x, y))
            if len(winning) == self.size:
                return winning
        # diagonal
        winning = []
        for y in range(self.size):
            x = y
            if self.fields[x, y] == self.opponent:
                winning.append((x, y))
        if len(winning) == self.size:
            return winning
        # other diagonal
        winning = []
        for y in range(self.size):
            x = self.size - 1 - y
            if self.fields[x, y] == self.opponent:
                winning.append((x, y))
        if len(winning) == self.size:
            return winning
        # default
        return None

    def __str__(self):
        string = ''
        for y in range(self.size):
            for x in range(self.size):
                string += self.fields[x, y]
            string += "\n"
        return string

class GUI:
    def __init__(self):
        self.app = Tk()
        self.app.title('Tic-Tac-Toe')
        self.app.resizable(width=False, height=False)
        self.board = Board()
        self.font = Font(family="Helvetica", size=32)
        self.buttons = {}
        for x, y in self.board.fields:
            handler = lambda x=x, y=y: self.move(x, y)
            button = Button(self.app, command=handler, font=self.font, width=2, height=1)
            button.grid(row=y, column=x)
            self.buttons[x, y] = button
        handler = lambda: self.reset()
        button = Button(self.app, text='Restart', command=handler)
        button.grid(row=self.board.size + 1, column=0, columnspan=self.board.size, sticky="WE")
        self.update()

    def reset(self):
        self.board = Board()
        self.update()
        self.top.destroy()

    def move(self, x, y):
        self.app.config(cursor="watch")
        self.app.update()
        self.board = self.board.move(x, y)
        self.update()
        move = self.board.best()
        if move:
            self.board = self.board.move(*move)
            self.update()
        self.app.config(cursor="")

    def topMessage(self):
        """Congratulation message pops up"""
        self.top = Toplevel()
        self.top.title("Congratulations!")
        self.top.minsize(300, 150)
        # top.geometry("200x150")
        self.topText = Label(self.top, text="O is the winner!", font=("Helvetica", 20), pady=20)
        self.topButton = Button(self.top, text="Restart", width=10, height=2, command=lambda: self.reset())
        self.topText.pack(fill=X)
        self.topButton.pack()

    def update(self):
        for (x, y) in self.board.fields:
            text = self.board.fields[x, y]
            self.buttons[x, y]['text'] = text
            self.buttons[x, y]['disabledforeground'] = 'black'
            if text == self.board.empty:
                self.buttons[x, y]['state'] = 'normal'
            else:
                self.buttons[x, y]['state'] = 'disabled'
        winning = self.board.won()
        if winning:
            for x, y in winning:
                self.buttons[x, y]['disabledforeground'] = 'red'
            for x, y in self.buttons:
                self.buttons[x, y]['state'] = 'disabled'
            self.topMessage()
        for (x, y) in self.board.fields:
            self.buttons[x, y].update()

    def mainloop(self):
        self.app.mainloop()

if __name__ == '__main__':
    GUI().mainloop()

............................................... ................................................... ................................................... ................................................... ................................................... ................................................... .......................

1 个答案:

答案 0 :(得分:0)

我对boolean不熟悉。但是根据您的代码,当export class Product implements IProduct { [key: string]: number | boolean; b: number; c: boolean; } 中的变量export class Product implements IProduct { b: number; c: boolean; a: string /* Gives erorr, since string is not allowed index type */ } import 'package:flutter/material.dart'; import 'package:flutter/physics.dart'; import 'dart:math'; const BOX_COLOR = Colors.cyan; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: "Spring Box", theme: ThemeData( primaryColor: Colors.red, ), home: HomePage(), ); } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Container( child: Padding( child: PhysicsBox(), padding: EdgeInsets.only( left: 20.0, right: 20.0, top: 20.0, bottom: 20.0, ), ), )); } } class PhysicsBox extends StatefulWidget { @override BoxState createState() => BoxState(); } class BoxState extends State<PhysicsBox> with TickerProviderStateMixin { AnimationController controller; AnimationController controller2; Animation<double> animation; SpringSimulation simulation; double _position; @override void initState() { super.initState(); simulation = SpringSimulation( SpringDescription( mass: 1.0, stiffness: 100.0, damping: 5.0, ), 400.0, 208.0, -4000.0, ); controller2 = AnimationController(vsync: this,duration: Duration(milliseconds: 70)); animation = Tween(begin: 200.0, end: 400.0).animate(controller2) ..addListener((){ if(controller2.status == AnimationStatus.completed){controller.reset();} setState(() { _position = animation.value; }); }); controller = AnimationController(vsync: this,duration: Duration(milliseconds: 700))..forward() ..addListener(() { if(controller.status == AnimationStatus.completed){controller2.reset();} setState(() { _position = simulation.x(controller.value); }); print('${simulation.x(controller.value)}'); }); } @override Widget build(BuildContext context) { return Container( color: Colors.yellow, width:500.0, height:500.0, child: Stack( fit: StackFit.expand, children: <Widget>[ Positioned( top: _position, child: GestureDetector( onTap: (){ if (controller.status == AnimationStatus.completed) { controller2.forward();//controller.reset(); }else{ controller.forward();} }, child: Container( width: 100.0, height: 100.0, color: Colors.red, ), ), ) ], ) ); } } (由minimax algorithm产生)时,游戏结束。但是,游戏结束后,您的代码不会返回游戏结果。

建议修改move以返回游戏结果:

None

还要修改self.board.best()以适应上述更改,并在游戏结束时显示GUI.move()Board.best()消息:

def best(self):
    return self.__minimax(True) # return the game result as well, not just the move

最后修改GUI.move()以接受另一个参数,即游戏结果:

tie

请注意,lose中存在一个问题:在游戏中单击def move(self, x, y): self.app.config(cursor="watch") self.app.update() self.board = self.board.move(x, y) self.update() result, move = self.board.best() # get the result of game as well if move: self.board = self.board.move(*move) self.update() else: # tie or lose game self.topMessage('tie' if result == 0 else 'lose') self.app.config(cursor="") 按钮时,变量topMessage()尚未定义,并且引发异常。因此,只有在定义def topMessage(self, result='win'): """Congratulation message pops up""" self.top = Toplevel() self.top.title("Congratulations!") self.top.minsize(300, 150) # top.geometry("200x150") if result == 'win': message = "O is the winner!" elif result == 'lose': message = "X is the winner!" else: message = "No winner!" self.topText = Label(self.top, text=message, font=("Helvetica", 20), pady=20) self.topButton = Button(self.top, text="Restart", width=10, height=2, command=lambda: self.reset()) self.topText.pack(fill=X) self.topButton.pack() 时,才能销毁它:

GUI.reset()