import pickle
import random

class Trenovac:
    def __init__(self, pocet_riadky = 3, pocet_stlpce = 3, pocet_vyhra = 3):
        self.pocet_riadky = pocet_riadky  # pocet riadkov hracieho planu
        self.pocet_stlpce = pocet_stlpce  # pocet stlpcov hracieho planu
        self.pocet_vyhra = pocet_vyhra   # pocet vedla seba na vyhru
        self.siet = {}  # mapuje situacie v hre na mozne tahy (postupne eliminuje tahy, ktore nevedu k vitazstvu)

    def uloz_siet(self, subor):
        with open(subor, 'wb') as f:
            pickle.dump(self.pocet_riadky, f)
            pickle.dump(self.pocet_stlpce, f)
            pickle.dump(self.pocet_vyhra, f)
            pickle.dump(self.siet, f)

    def nacitaj_siet(self, subor):
        try:
            with open(subor, 'rb') as f:
                self.pocet_riadky = pickle.load(f)
                self.pocet_stlpce = pickle.load(f)
                self.pocet_vyhra = pickle.load(f)
                self.siet = pickle.load(f)
        except FileNotFoundError:
            print('subor siete neexistuje')

    def trenuj(self):
        # realizacia jednej hry a pripadna uprava siete
        # tah siete X, tah hraca O, volne .
        # zaciname v situacii, ze vsetky policka su volne
        self.situacia = [['.'] * self.pocet_stlpce for _ in range(self.pocet_riadky)]
        self.je_siet_na_rade = random.choice((True, False))
        # zoznam tahov [situacia, tah], ak siet prehra, spatne odstrani prehravajuce tahy
        self.realizovane_tahy = []
        while self.existuje_tah() and self.kto_vyhral() == 'nikto':
            self.tlac_situaciu()
            if self.je_siet_na_rade:
                if not self.je_situacia_v_sieti():  # tento stav hry este siet nepozna
                    self.pridaj_situaciu()
                try:
                    tah = random.choice(self.siet[self.vytvor_kluc()])
                except IndexError:  # neexistuje tah siete, lebo vsetky vedli k prehre, tak ich siet odstranila
                    self.uprav_siet()
                    print('vzdavam sa')
                    return
                # do zoznamu tahov siete pridame aktualny tah
                self.realizovane_tahy.append([self.vytvor_kluc(), tah])
                self.situacia[tah[0]][tah[1]] = 'X'
            else:
                tah = input('zadaj tah "r s": ').split(' ')
                tah[0] = int(tah[0])
                tah[1] = int(tah[1])
                # test na chybny tah
                if not(0 <= tah[0] < self.pocet_riadky and 0 <= tah[1] < self.pocet_stlpce):
                    continue
                if self.situacia[tah[0]][tah[1]] != '.':
                    continue
                self.situacia[tah[0]][tah[1]] = 'O'
            self.je_siet_na_rade = not self.je_siet_na_rade
        print('----------------------------')
        self.tlac_situaciu()
        if self.kto_vyhral() == 'siet':
            print('vyhrala siet')
        elif self.kto_vyhral() == 'hrac':
            self.uprav_siet()
            print('vyhral hrac')
        else:
            print('remiza')

    def existuje_tah(self):
        for riadok in self.situacia:
            for hodnota in riadok:
                if hodnota == '.':
                    return True
        return False

    def kto_vyhral(self):
        def vrat_riadok_str(idx_r):
            '''vrati riadok idx_r ako str'''
            return ''.join(self.situacia[idx_r])

        def vrat_stlpec_str(idx_s):
            '''vrati stlpec idx_s ako str'''
            s = ''
            for idx_r in range(self.pocet_riadky):
                s = s + self.situacia[idx_r][idx_s]
            return s

        def vrat_uhlopriecku_pd_str(idx_u_pd):
            '''vratu pravo-dolnu uhlopriecku idx_u_pd ako str
            uhlopriecky sa cisluju od lava-dolna do prava-horna
            uhlopriecky sa cisluju od -self.pocet_riadky + 1 do self.pocet_stlpce - 1)'''
            r, s = (-idx_u_pd, 0) if idx_u_pd < 0 else (0, idx_u_pd)
            uhlopriecka = ''
            while r < self.pocet_riadky and s < self.pocet_stlpce:
                uhlopriecka = uhlopriecka + self.situacia[r][s]
                r += 1
                s += 1
            return uhlopriecka

        def vrat_uhlopriecku_ph_str(idx_u_ph):  # (-self.r .. self.s)
            '''vratu pravo-hornu uhlopriecku idx_u_ph ako str
            uhlopriecky sa cisluju od lava-horna do prava-dolna
            uhlopriecky sa cisluju od -self.pocet_riadky + 1 do self.pocet_stlpce - 1)'''
            r, s = (self.pocet_riadky + idx_u_ph - 1, 0) if idx_u_ph < 0 else (self.pocet_riadky - 1, idx_u_ph)
            v = ''
            while r >= 0 and s < self.pocet_stlpce:
                v = v + self.situacia[r][s]
                r -= 1
                s += 1
            return v

        siet_vyhra = 'X' * self.pocet_vyhra
        hrac_vyhra = 'O' * self.pocet_vyhra
        for idx_r in range(self.pocet_riadky):
            riadok = vrat_riadok_str(idx_r)
            if siet_vyhra in riadok:
                return 'siet'
            elif hrac_vyhra in riadok:
                return 'hrac'

        for idx_s in range(self.pocet_stlpce):
            stlpec = vrat_stlpec_str(idx_s)
            if siet_vyhra in stlpec:
                return 'siet'
            elif hrac_vyhra in stlpec:
                return 'hrac'

        for idx_u_pd in range(-self.pocet_riadky + 1, self.pocet_stlpce - 1):
            u_pd = vrat_uhlopriecku_pd_str(idx_u_pd)
            if siet_vyhra in u_pd:
                return 'siet'
            elif hrac_vyhra in u_pd:
                return 'hrac'

        for idx_u_ph in range(-self.pocet_riadky + 1, self.pocet_stlpce - 1):
            u_ph = vrat_uhlopriecku_ph_str(idx_u_ph)
            if siet_vyhra in u_ph:
                return 'siet'
            elif hrac_vyhra in u_ph:
                return 'hrac'
        return 'nikto'

    def tlac_situaciu(self):
        for riadok in self.situacia:
            for hodnota in riadok:
                print(f'{hodnota} ', end='')
            print()
        print()

    def vytvor_kluc(self):
        return tuple(tuple(riadok) for riadok in self.situacia)

    def je_situacia_v_sieti(self):
        kluc = self.vytvor_kluc()
        return kluc in self.siet

    def pridaj_situaciu(self):
        kluc = self.vytvor_kluc()
        tahy = []
        for idx_riadok in range(self.pocet_riadky):
            for idx_stlpec in range(self.pocet_stlpce):
                if self.situacia[idx_riadok][idx_stlpec] == '.':
                    tahy.append([idx_riadok, idx_stlpec])
        self.siet[kluc] = tahy

    def uprav_siet(self):
        while self.realizovane_tahy:
            tah = self.realizovane_tahy.pop()  # vyberieme posledny tah veduci k prehre
            self.siet[tah[0]].remove(tah[1])  # zo siete tento tah odstranime, aby sa nedal v buducnosti zopakovat
            if self.siet[tah[0]]:  # ak pre tuto situaciu ostal este nejaky mozny tah, koncime upravu siete
                break              # v opacnom pripade odstranime aj predchadzajuci tah, lebo jednoznacne viedie k prehre

#################### TRENOVANIE ########################################
tictactoe = Trenovac()
tictactoe.nacitaj_siet('seminar.bin')

while True:
    tictactoe.trenuj()
    print('***********************************')
    if input('Dalsia hra? [a/n]: ') != 'a':
        break
tictactoe.uloz_siet('seminar.bin')


