from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from random import randint, choice

class Toe(QWidget):

    def __init__ (self):

        self.turn = 0
        self.first_cross = ''
        self.grid = [0,1,2,3,4,5,6,7,8]
        self.status = ['e','e','e','e','e','e','e','e','e']
        self.lines = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[6,4,2]]
        self.finish = 0
        self.buttons = []
        QWidget. __init__ (self)
        self.resize(360, 435)
        self.setMaximumSize(360,435)
        self.setMinimumSize(360,435)
        self.one = QPushButton(self)
        self.one.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.one, 0))
        self.one.setGeometry(QRect(40,70,70,60))
        self.one.setText('...')
        self.buttons.append(self.one)
        self.two = QPushButton(self)
        self.two.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.two, 1))
        self.two.setGeometry(QRect(140,70,70,60))
        self.two.setText('...')
        self.buttons.append(self.two)
        self.three = QPushButton(self)
        self.three.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.three, 2))
        self.three.setGeometry(QRect(240,70,70,60))
        self.three.setText('...')
        self.buttons.append(self.three)
        self.four = QPushButton(self)
        self.four.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.four, 3))
        self.four.setGeometry(QRect(40,155,70,60))
        self.four.setText('...')
        self.buttons.append(self.four)
        self.five = QPushButton(self)
        self.five.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.five, 4))
        self.five.setGeometry(QRect(140,155,70,60))
        self.five.setText('...')
        self.buttons.append(self.five)
        self.six = QPushButton(self)
        self.six.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.six, 5))
        self.six.setGeometry(QRect(240,155,70,60))
        self.six.setText('...')
        self.buttons.append(self.six)
        self.seven = QPushButton(self)
        self.seven.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.seven, 6))
        self.seven.setGeometry(QRect(40,240,70,60))
        self.seven.setText('...')
        self.buttons.append(self.seven)
        self.eight = QPushButton(self)
        self.eight.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.eight, 7))
        self.eight.setGeometry(QRect(140,240,70,60))
        self.eight.setText('...')
        self.buttons.append(self.eight)
        self.nine = QPushButton(self)
        self.nine.clicked.connect(lambda event= '<Button-1>' : self.set_it(self.nine, 8))
        self.nine.setGeometry(QRect(240,240,70,60))
        self.nine.setText('...')
        self.buttons.append(self.nine)
        self.result = QLabel('', self)
        self.result.setGeometry(QRect(125,360,111,18))
        self.crossornull = QCheckBox(self)
        self.crossornull.setGeometry(QRect(335, 25, 16, 16))
        self.startgame = QPushButton(self)
        self.startgame.setGeometry(QRect(175,20,80,30))
        self.startgame.setText("Старт!")
        self.startgame.clicked.connect(self.start)
        self.fornull = QLabel("За 'нолик':", self)
        self.fornull.setGeometry(QRect(270,25, 55,12))
        self.setWindowTitle("Tic-tac-toe")
        self.mylevel = QComboBox(self)
        self.mylevel.addItems(['Easy', 'Normal', 'Dead end'])
        self.mylevel.setGeometry(QRect(88,20,70,30))
        self.leveltext = QLabel('Сложность:', self)
        self.leveltext.setGeometry(10,30,58,15)
        
        self.show()

    def set_it(self, button, n):
        if self.mylevel.currentText() == 'Easy':
            if not self.crossornull.isChecked():
                button.setText('X')
                button.setDisabled(True)
                self.grid.remove(n)
                self.status[n] = 'X'
                if not self.check():
                    self.easy_ai('O')
            else:
                button.setText('O')
                button.setDisabled(True)
                self.grid.remove(n)
                self.status[n] = 'O'
                if not self.check():
                    self.easy_ai('X')
        elif self.mylevel.currentText() == 'Normal':
            if not self.crossornull.isChecked():
                button.setText('X')
                button.setDisabled(True)
                self.grid.remove(n)
                self.status[n] = 'X'
                if not self.check():
                    self.normal_ai('O')
            else:
                button.setText('O')
                button.setDisabled(True)
                self.grid.remove(n)
                self.status[n] = 'O'
                if not self.check():
                    self.normal_ai('X')
        elif self.mylevel.currentText() == 'Dead end':
            if not self.crossornull.isChecked():
                button.setText('X')
                button.setDisabled(True)
                self.grid.remove(n)
                self.status[n] = 'X'
                if not self.check():
                    self.deadend_ai('O')
            else:
                button.setText('O')
                button.setDisabled(True)
                self.grid.remove(n)
                self.status[n] = 'O'
                if not self.check():
                    self.deadend_ai('X')
            
    def start(self):
        self.turn = 0
        self.finish = 0
        self.grid = [0,1,2,3,4,5,6,7,8]
        self.status = ['e','e','e','e','e','e','e','e','e']
        for elem in self.buttons:
            elem.setText('...')
            elem.setDisabled(False)
        self.result.setText('')
        if self.crossornull.isChecked():
            if self.mylevel.currentText() == 'Easy':
                self.easy_ai('X')
            elif self.mylevel.currentText() == 'Normal':
                self.normal_ai('X')
            elif self.mylevel.currentText() == 'Dead end':
                self.deadend_ai('X')
                
    def easy_ai (self, sign, choose=''):
        if len(self.grid) > 0:
            if choose == '':
                choose = choice(self.grid)
            self.grid.remove(choose)
            if choose == 0:
                self.one.setText(sign)
                self.one.setDisabled(True)
            elif choose == 1:
                self.two.setText(sign)
                self.two.setDisabled(True)
            elif choose == 2:
                self.three.setText(sign)
                self.three.setDisabled(True)
            elif choose == 3:
                self.four.setText(sign)
                self.four.setDisabled(True)
            elif choose == 4:
                self.five.setText(sign)
                self.five.setDisabled(True)
            elif choose == 5:
                self.six.setText(sign)
                self.six.setDisabled(True)
            elif choose == 6:
                self.seven.setText(sign)
                self.seven.setDisabled(True)
            elif choose == 7:
                self.eight.setText(sign)
                self.eight.setDisabled(True)
            else:
                self.nine.setText(sign)
                self.nine.setDisabled(True)
            self.status[choose] = sign
            self.check()

    def normal_ai(self, sign, hlp=0):
        test = 0
        p = 0 #~!!!!
        if sign == 'O':
            enemy = 'X'
        else: enemy = 'O'
        if len(self.grid) >= 8:
            self.easy_ai(sign)
        else:
            for elem in self.lines:
                for i in elem:
                    if self.status[i] == sign:
                        test+=1
                    if test > 1 and any(self.status[k] == 'e' for k in elem):
                        self.easy_ai(sign, int(list(filter(lambda j : self.status[j] == 'e', elem))[0]))
                        p+=1
                        break
                if test > 1 and p > 0:
                    break
                test = 0

            if p == 0:
                test = 0
                for elem in self.lines:
                    for i in elem:
                        if self.status[i] == enemy:
                            test+=1
                        if test > 1 and any(self.status[k] == 'e' for k in elem):
                            self.easy_ai(sign, int(list(filter(lambda j : self.status[j] == 'e', elem))[0]))
                            p+=1
                            break
                    if test > 1 and p > 0:
                        break
                    test = 0
            if test <= 1 and p == 0:
                if hlp == 0:
                    self.easy_ai(sign)
                else:
                    self.deadend_ai(sign, iam=0)

    def deadend_ai(self, sign, iam=1):
        arr = [0,2,6,8]
        lin = [1,5,7,3]
        if sign == 'X':
            if self.turn == 0:
                self.easy_ai(sign, 4)
                self.turn+=1
            elif self.turn == 1:
                self.turn+=1
                if self.status[0] == 'O':
                    self.easy_ai(sign, 8)
                elif self.status[1] == 'O':
                    self.easy_ai(sign, 6)
                elif self.status[2] == 'O':
                    self.easy_ai(sign, 6)
                elif self.status[3] == 'O':
                    self.easy_ai(sign, 2)
                elif self.status[5] == 'O':
                    self.easy_ai(sign, 6)
                elif self.status[6] == 'O':
                    self.easy_ai(sign, 2)
                elif self.status[7] == 'O':
                    self.easy_ai(sign, 0)
                else:
                    self.easy_ai(sign, 0)
            elif self.turn > 1:
                self.normal_ai(sign)
        elif sign == 'O':
            if self.turn == 0:
                self.turn+=1
                if self.status[4] == 'X':
                    n = choice(arr)
                    self.easy_ai(sign, n)
                    self.first_cross = 'c'
                elif self.status[4] != 'X' and (self.status[0] == 'X' or self.status[2] == 'X' or self.status[6] == 'X' or  self.status[8] == 'X'):
                    self.easy_ai(sign, 4)
                    self.first_cross = 'co'
                else:
                    self.first_cross = 'line'
                    self.easy_ai(sign, 4)
                                  
            elif self.turn > 0 and self.first_cross == 'c':
                if iam==1:
                    self.normal_ai(sign, hlp=1)
                else:
                    try:
                        n = choice([i for i in arr if i in self.grid])
                        self.easy_ai(sign, n)
                    except:
                        self.easy_ai(sign)
            elif self.turn > 0 and self.first_cross ==  'co':
                if self.turn == 1:
                    if iam == 1:
                        self.normal_ai(sign, hlp=1)
                    else:
                        self.turn+=1
                        try:
                            if self.status[0] == 'X':
                                self.easy_ai(sign, 8)
                            elif self.status[2] == 'X':
                                self.easy_ai(sign, 6)
                            elif self.status[6] == 'X':
                                self.easy_ai(sign, 2)
                            elif self.status[8] == 'X':
                                self.easy_ai(sign, 0)
                        except:
                            n = choice([i for i in lin if i in self.grid])
                            self.easy_ai(sign, n)
                else:
                    self.normal_ai(sign)
                            
            elif self.turn > 0 and self.first_cross ==  'line':
                if self.turn == 1:
                    if iam == 1:
                        self.normal_ai(sign, hlp=1)
                        
                    else:
                        if self.status[0] == 'X' or self.status[2] == 'X' or self.status[6] == 'X' or self.status[8] == 'X':
                            print('!222@')
                            try:
                                if self.status[0] == 'X':
                                    self.easy_ai(sign, 8)
                                elif self.status[2] == 'X':
                                    self.easy_ai(sign, 6)
                                elif self.status[6] == 'X':
                                    self.easy_ai(sign, 2)
                                elif self.status[8] == 'X':
                                    self.easy_ai(sign, 0)
                                self.turn+=1
                            except:
                                self.normal_ai(sign)
                        elif (self.status[1] == 'X' and self.status[7] == 'X') or (self.status[3] == 'X' and self.status[5] == 'X'):
                            n = choice(arr)
                            self.easy_ai(sign, n)
                            self.turn+=1
                        elif ((self.status[3] == 'X' and self.status[1] == 'X') or (self.status[1] == 'X' and self.status[5] == 'X') or
                              (self.status[5] == 'X' and self.status[7] == 'X') or (self.status[7] == 'X' and self.status[3] == 'X')):
                            if (self.status[3] == 'X' and self.status[1] == 'X'):
                                self.easy_ai(sign, 0)
                            elif (self.status[1] == 'X' and self.status[5] == 'X'):
                                self.easy_ai(sign, 2)
                            elif (self.status[5] == 'X' and self.status[7] == 'X'):
                                self.easy_ai(sign, 8)
                            elif (self.status[7] == 'X' and self.status[3] == 'X'):
                                self.easy_ai(sign, 6)
                            self.turn+=1                                                                                           
                elif self.turn > 1:
                    self.normal_ai(sign)
                        
    def check(self):    
        for elem in self.lines:
            if all(self.status[i] == 'X' for i in elem) and not self.crossornull.isChecked():
                self.result.setText("<html><head/><body><p><span style=\" font-size:12pt; color:green;\">Вы выиграли!</span></p></body></html>")
                self.finish += 1
            if all(self.status[i] == 'X' for i in elem) and self.crossornull.isChecked():
                self.result.setText("<html><head/><body><p><span style=\" font-size:12pt; color:red;\">Вы проиграли.</span></p></body></html>")
                self.finish+=1
            if all(self.status[i] == 'O' for i in elem) and  self.crossornull.isChecked():
                self.result.setText("<html><head/><body><p><span style=\" font-size:12pt; color:green;\">Вы выиграли!</span></p></body></html>")
                self.finish+=1
            if all(self.status[i] == 'O' for i in elem) and  not self.crossornull.isChecked():
                self.result.setText("<html><head/><body><p><span style=\" font-size:12pt; color:red;\">Вы проиграли.</span></p></body></html>")
                self.finish+=1
        if len(self.grid) < 1 and self.finish == 0:
            self.result.setText("<html><head/><body><p><span style=\" font-size:12pt; color:grey;\"><center>Ничья.</center></span></p></body></html>")
        if self.finish > 0:
            return True
        
if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    window = Toe()
    sys.exit(app.exec_())