Книга знаний

Инф. технологии

Крестики-нолики 3x3 (программа на Python)

Программа игры в крестики-нолики, состоящая из трех основных классов:
<br>- PlayButton (рисует фигуры)
<br>- MainWindow (создает игровое поле с кнопками, выполняет некоторые системные задачи, рисует
"линию победы", обрабатывает ход человека и делает ход от лица компьютера)
<br>- Game (содержит массив field, представляющий игровое поле, возвращает или устанавливает фигуру,
проверяет на победу)
Автор статьи: Волшебник
Последняя редакция №1 от 03.10.23
URL: http://kb.mista.ru/article.php?id=983

Ключевые слова: Крестики-нолики, игра, Python, задача



from PyQt6.QtCore import QPoint, QSize, Qt
from PyQt6.QtGui import QFont, QPainter, QPen
from PyQt6.QtWidgets import QApplication, QGridLayout, QMainWindow, QLabel, QPushButton,
QVBoxLayout, QWidget
from enum import Enum
import random
import time

#
====================================================================================================
=============
class Figure(Enum):
   X = 1
   O = 2

class GameState(Enum):
   PLAYING = 1
   HUMAN_WINNER = 2
   COMPUTER_WINNER = 3
   DRAW = 4

class VictoryType(Enum):
   COLUMN = 1
   ROW = 2
   DIAGONAL_From_LeftTop = 3   # \
   DIAGONAL_From_RightTop = 4  # /

#
====================================================================================================
=============
class PlayButton(QPushButton):
   def __init__(self, y, x):
       super().__init__()

       self.y = y
       self.x = x

       self.setFixedSize(QSize(50,50))

   def drawFigure(self, fig):
       if fig == Figure.X:
           self.setText("×")
           self.setFont(QFont("Arial", 40))

       elif fig == Figure.O:
           self.setText("◯")
           self.setFont(QFont("Arial", 25))
       else:
           self.setText("")

#
====================================================================================================
=============
class MainWindow(QMainWindow):
     
   def __init__(I):
       super().__init__()

       I.setWindowTitle("Tic-tac-toe")
       
       I.mainLayout = QVBoxLayout()

       I.initElements();

       container = QWidget()
       container.setLayout(I.mainLayout)
       I.setCentralWidget(container)

       I.startNewGame()

   # ---------------------------------------
   def initElements(I):
       
       # кнопка New game
       I.btNewGame = QPushButton("New game")
       I.btNewGame.clicked.connect(I.startNewGame)
       I.mainLayout.addWidget(I.btNewGame)

       # игровое поле с кнопками
       I.initGamePanel()

       # строка состояния
       I.statusString = QLabel()
       I.mainLayout.addWidget(I.statusString)
       I.updateStatusString("")

   # ---------------------------------------
   def initGamePanel(I):
       I.CellButtons = {(y,x): PlayButton(y,x) for y in [1, 2, 3] for x in [1, 2, 3]}            
       layoutField = QGridLayout()
       for coord in I.CellButtons:
           button = I.CellButtons[coord]
           button.clicked.connect(lambda checked, bt = button: I.HumanMove(bt))
           layoutField.addWidget(button,button.y,button.x)

       I.mainLayout.addLayout(layoutField)

   # ---------------------------------------
   def updateGamePanel(I):

       for y in [1, 2, 3]:
            for x in [1, 2, 3]:
                cell = (y,x)
                figure = I.game.field[cell]
                button = I.CellButtons[cell]
                button.drawFigure(figure)
                button.setEnabled(figure is None)
       I.repaint()

   # ---------------------------------------
   def updateStatusString(I, text):
       I.statusString.setText(text)
       I.repaint()

   # ---------------------------------------
   def startNewGame(I):
       I.game = Game()
       I.updateGamePanel()
       I.updateStatusString("")

   # ---------------------------------------
   def AfterMove(I):
       I.updateGamePanel()

       X_wins, vType, n = I.game.checkVictory(Figure.X)

       if X_wins:
           I.updateStatusString("You won!")
           I.repaint()
           # victory line will be painted in paintEvent
           return GameState.HUMAN_WINNER

       (O_wins, vType, n) = I.game.checkVictory(Figure.O)
       if O_wins:
           I.updateStatusString("Computer won!")
           I.repaint()
           # victory line will be painted in paintEvent
           return GameState.COMPUTER_WINNER

       if len(I.game.getFreeCells()) == 0:
           I.updateStatusString("Draw!")
           return GameState.DRAW

       return GameState.PLAYING

   # ---------------------------------------
   def drawVictoryLine(I, vType, n):

       lineWidth = 10
       lineColor = Qt.GlobalColor.red
       pen = QPen(lineColor, lineWidth, Qt.PenStyle.SolidLine)

       qp = QPainter()
       qp.begin(I)
       qp.setPen(pen)

       if vType == VictoryType.COLUMN:
           bt1 = I.CellButtons[(1, n)]
           bt2 = I.CellButtons[(3, n)]
           x = bt1.pos().x() + bt1.width() // 2
           beginPoint = QPoint(x, bt1.pos().y())
           endPoint = QPoint(x, bt2.pos().y()+bt2.height())

       elif vType == VictoryType.ROW:
           bt1 = I.CellButtons[(n, 1)]
           bt2 = I.CellButtons[(n, 3)]
           y = bt1.pos().y()+bt1.width()//2
           beginPoint = QPoint(bt1.pos().x(), y)
           endPoint = QPoint(bt2.pos().x() + bt2.width(), y)

       elif vType == VictoryType.DIAGONAL_From_LeftTop: # \

           bt1 = I.CellButtons[(1, 1)]
           bt2 = I.CellButtons[(3, 3)]

           beginPoint = QPoint(bt1.pos().x(), bt1.pos().y())
           endPoint = QPoint(bt2.pos().x() + bt2.width(), bt2.pos().y()+bt2.height())

       elif vType == VictoryType.DIAGONAL_From_RightTop: # /

           bt1 = I.CellButtons[(1, 3)]
           bt2 = I.CellButtons[(3, 1)]

           beginPoint = QPoint(bt1.pos().x() + bt1.width(), bt1.pos().y())
           endPoint = QPoint(bt2.pos().x(), bt2.pos().y()+bt2.height())
       else:
           raise NotImplementedError

       qp.drawLine(beginPoint, endPoint)
       qp.end()

   # ---------------------------------------
   def paintEvent(I, event):

       for fig in list(Figure):
           victory, VictoryType, n = I.game.checkVictory(fig)
           if victory:
               I.drawVictoryLine(VictoryType, n)
               break

   # ---------------------------------------
   def ComputerMove(I):
       selectedCell = I.game.getRandomFreeCell()
       I.game.setFigure(selectedCell, Figure.O)
       I.AfterMove()

   # ---------------------------------------
   def HumanMove(I, button):
       cell = (button.y, button.x)

       if I.game.getFigure(cell) is not None: return

       I.game.setFigure(cell, Figure.X)

       if I.AfterMove() == GameState.PLAYING:
           time.sleep(0.25) # imitation of thinking
           I.ComputerMove()
       
#
====================================================================================================
=============
class Game:

   def __init__(self):
       self.field = {(y,x):None for y in [1, 2, 3] for x in [1, 2, 3]}

   def getFreeCells(self):
       return [cell for cell in self.field if self.getFigure(cell)==None]

   def getRandomFreeCell(self):
       freeCells = self.getFreeCells()
       return random.choice(freeCells)

   def checkVictory(self, figure) -> tuple:
       F = self.field

       for y in [1, 2, 3]:
           if F[(y, 1)] == F[(y, 2)] == F[(y, 3)] == figure: return (True,
VictoryType.ROW, y)

       for x in [1, 2, 3]:
           if F[(1, x)] == F[(2, x)] == F[(3, x)] == figure: return (True,
VictoryType.COLUMN, x)
       
       if F[(1, 1)] == F[(2, 2)] == F[(3, 3)] == figure: return (True,
VictoryType.DIAGONAL_From_LeftTop, None) # \

       if F[(1, 3)] == F[(2, 2)] == F[(3, 1)] == figure: return (True,
VictoryType.DIAGONAL_From_RightTop, None) # /

       return (False, None, None)

   def getFigure(self, cell):
       return self.field[cell]

   def setFigure(self, cell, figure):
       self.field[cell] = figure

#
====================================================================================================
=============
app = QApplication([])

window = MainWindow()
window.show()

app.exec()

Описание | Рубрикатор | Поиск | ТелепатБот | Захваченные статьи | Установки | Форум
© Станислав Митичкин (Волшебник), 2005-2025 | Mista.ru

Яндекс.Метрика