# tic_tac_toe.py
import math
import random
import sys
from typing import List, Optional, Tuple

# Plansza jako lista 9 pól: 'X', 'O' lub ' ' (puste)
Board = List[str]

WIN_COMBINATIONS = [
    (0,1,2), (3,4,5), (6,7,8),  # wiersze
    (0,3,6), (1,4,7), (2,5,8),  # kolumny
    (0,4,8), (2,4,6)            # przekątne
]

def new_board() -> Board:
    return [' '] * 9

def print_board(b: Board) -> None:
    def cell(i):
        return b[i] if b[i] != ' ' else str(i+1)
    print()
    print(f" {cell(0)} | {cell(1)} | {cell(2)} ")
    print("---+---+---")
    print(f" {cell(3)} | {cell(4)} | {cell(5)} ")
    print("---+---+---")
    print(f" {cell(6)} | {cell(7)} | {cell(8)} ")
    print()

def check_winner(b: Board) -> Optional[str]:
    for a, c, d in WIN_COMBINATIONS:
        if b[a] != ' ' and b[a] == b[c] == b[d]:
            return b[a]
    return None

def is_full(b: Board) -> bool:
    return all(cell != ' ' for cell in b)

def available_moves(b: Board) -> List[int]:
    return [i for i, cell in enumerate(b) if cell == ' ']

# Minimax dla AI (X lub O — przekazujemy symbol AI i symbol przeciwnika)
def minimax(b: Board, depth: int, is_maximizing: bool, ai_sym: str, human_sym: str) -> Tuple[int, Optional[int]]:
    winner = check_winner(b)
    if winner == ai_sym:
        return (10 - depth, None)
    elif winner == human_sym:
        return (depth - 10, None)
    elif is_full(b):
        return (0, None)
    
    best_move = None
    if is_maximizing:
        best_score = -math.inf
        for move in available_moves(b):
            b[move] = ai_sym
            score, _ = minimax(b, depth + 1, False, ai_sym, human_sym)
            b[move] = ' '
            if score > best_score:
                best_score = score
                best_move = move
        return (best_score, best_move)
    else:
        best_score = math.inf
        for move in available_moves(b):
            b[move] = human_sym
            score, _ = minimax(b, depth + 1, True, ai_sym, human_sym)
            b[move] = ' '
            if score < best_score:
                best_score = score
                best_move = move
        return (best_score, best_move)

def ai_move(b: Board, ai_sym: str, human_sym: str) -> int:
    # Jeśli na pierwszym ruchu, wygodnie losować róg/środek dla urozmaicenia
    if b.count(' ') == 9:
        choice = random.choice([0, 2, 4, 6, 8])  # prefer róg lub środek
        return choice
    _, move = minimax(b, 0, True, ai_sym, human_sym)
    if move is None:
        # zabezpieczenie - wybierz losowo
        return random.choice(available_moves(b))
    return move

def player_move(b: Board, sym: str) -> None:
    moves = available_moves(b)
    while True:
        try:
            raw = input(f"Ruch gracza ({sym}). Wybierz pole (1-9): ").strip()
            if raw.lower() in ('q','quit','exit'):
                print("Koniec gry. Do widzenia!")
                sys.exit(0)
            pos = int(raw) - 1
            if pos in moves:
                b[pos] = sym
                return
            else:
                print("Nieprawidłowy ruch — pole zajęte lub poza zakresem.")
        except ValueError:
            print("Wpisz numer od 1 do 9 lub 'q' aby wyjść.")

def play_two_players():
    b = new_board()
    current = 'X'
    while True:
        print_board(b)
        player_move(b, current)
        winner = check_winner(b)
        if winner:
            print_board(b)
            print(f"Gracz {winner} wygrywa! 🎉")
            break
        if is_full(b):
            print_board(b)
            print("Remis!")
            break
        current = 'O' if current == 'X' else 'X'

def play_vs_ai():
    # pozwól wybrać gracza X lub O
    while True:
        first = input("Czy chcesz być X i zaczynać? (t/n): ").strip().lower()
        if first in ('t','tak','y','yes'):
            human_sym = 'X'
            ai_sym = 'O'
            human_turn = True
            break
        if first in ('n','nie','no'):
            human_sym = 'O'
            ai_sym = 'X'
            human_turn = False
            break
        print("Wpisz 't' (tak) lub 'n' (nie).")
    
    b = new_board()
    while True:
        print_board(b)
        if human_turn:
            player_move(b, human_sym)
        else:
            move = ai_move(b, ai_sym, human_sym)
            b[move] = ai_sym
            print(f"Komputer ({ai_sym}) wykonał ruch na polu {move+1}.")
        
        winner = check_winner(b)
        if winner:
            print_board(b)
            if winner == human_sym:
                print("Gratulacje — wygrałeś! 🏆")
            else:
                print("Komputer wygrał. Spróbuj ponownie!")
            break
        if is_full(b):
            print_board(b)
            print("Remis! Nikt nie wygrał.")
            break
        human_turn = not human_turn

def main():
    print("=== KÓŁKO I KRZYŻYK ===")
    print("1) Gra 2 graczy (lokalnie)")
    print("2) Gra z komputerem (AI)")
    print("q) Wyjście")
    while True:
        choice = input("Wybierz tryb gry (1/2/q): ").strip().lower()
        if choice == '1':
            play_two_players()
            break
        elif choice == '2':
            play_vs_ai()
            break
        elif choice in ('q','quit','exit'):
            print("Do zobaczenia!")
            return
        else:
            print("Nieprawidłowy wybór. Wpisz 1, 2 lub q.")
    print("Dziękuję za grę!")

if __name__ == "__main__":
    main()
