# tictactoe_clean.py
import math
HUMAN = "X"
AI = "O"
def display_board(board):
print()
for i in range(3):
row = board[3*i:3*i+3]
print(" " + " | ".join(cell if cell != " " else str(3*i + j + 1) for j,cell in enumerate(row)))
if i < 2:
print("---+---+---")
print()
def check_winner(board):
wins = [
(0,1,2),(3,4,5),(6,7,8),
(0,3,6),(1,4,7),(2,5,8),
(0,4,8),(2,4,6)
]
for a,b,c in wins:
if board[a] == board[b] == board[c] and board[a] != " ":
return board[a]
if " " not in board:
return "Tie"
return None
def available_moves(board):
return [i for i,cell in enumerate(board) if cell == " "]
def minimax(board, player):
winner = check_winner(board)
if winner == AI:
return (1, None)
elif winner == HUMAN:
return (-1, None)
elif winner == "Tie":
return (0, None)
if player == AI:
best_score = -math.inf
best_move = None
for move in available_moves(board):
board[move] = AI
score, _ = minimax(board, HUMAN)
board[move] = " "
if score > best_score:
best_score = score
best_move = move
return (best_score, best_move)
else:
best_score = math.inf
best_move = None
for move in available_moves(board):
board[move] = HUMAN
score, _ = minimax(board, AI)
board[move] = " "
if score < best_score:
best_score = score
best_move = move
return (best_score, best_move)
def human_move(board):
while True:
try:
s = input("Ton coup (1-9) : ").strip()
choice = int(s)
idx = choice - 1
if idx not in range(9):
print("Choix invalide. Entre un nombre entre 1 et 9.")
elif board[idx] != " ":
print("Case déjà prise, choisis une autre case.")
else:
board[idx] = HUMAN
return
except ValueError:
print("Entrée non valide. Entre un nombre entier entre 1 et 9.")
except (EOFError, KeyboardInterrupt):
print("\nEntrée interrompue. Fin du jeu.")
raise SystemExit
def ai_move(board):
_, move = minimax(board, AI)
if move is None:
moves = available_moves(board)
move = moves[0] if moves else None
if move is not None:
board[move] = AI
print("L'IA joue en case {}.".format(move+1))
def play():
board = [" "] * 9
print("Tic-Tac-Toe : tu es 'X', l'IA est 'O'.")
first = ""
try:
while first.lower() not in ("j", "i"):
first = input("Qui commence ? (J pour toi / I pour l'IA) : ").strip() or "J"
except (EOFError, KeyboardInterrupt):
print("\nEntrée interrompue. Fin du jeu.")
return
human_turn = (first.lower() == "j")
display_board(board)
while True:
if human_turn:
human_move(board)
else:
ai_move(board)
display_board(board)
winner = check_winner(board)
if winner:
if winner == HUMAN:
print("Bravo — tu as gagné !")
elif winner == AI:
print("Dommage, l'IA a gagné.")
else:
print("Égalité !")
break
human_turn = not human_turn
if __name__ == "__main__":
play()
IyB0aWN0YWN0b2VfY2xlYW4ucHkKaW1wb3J0IG1hdGgKCkhVTUFOID0gIlgiCkFJID0gIk8iCgpkZWYgZGlzcGxheV9ib2FyZChib2FyZCk6CiAgICBwcmludCgpCiAgICBmb3IgaSBpbiByYW5nZSgzKToKICAgICAgICByb3cgPSBib2FyZFszKmk6MyppKzNdCiAgICAgICAgcHJpbnQoIiAiICsgIiB8ICIuam9pbihjZWxsIGlmIGNlbGwgIT0gIiAiIGVsc2Ugc3RyKDMqaSArIGogKyAxKSBmb3IgaixjZWxsIGluIGVudW1lcmF0ZShyb3cpKSkKICAgICAgICBpZiBpIDwgMjoKICAgICAgICAgICAgcHJpbnQoIi0tLSstLS0rLS0tIikKICAgIHByaW50KCkKCmRlZiBjaGVja193aW5uZXIoYm9hcmQpOgogICAgd2lucyA9IFsKICAgICAgICAoMCwxLDIpLCgzLDQsNSksKDYsNyw4KSwKICAgICAgICAoMCwzLDYpLCgxLDQsNyksKDIsNSw4KSwKICAgICAgICAoMCw0LDgpLCgyLDQsNikKICAgIF0KICAgIGZvciBhLGIsYyBpbiB3aW5zOgogICAgICAgIGlmIGJvYXJkW2FdID09IGJvYXJkW2JdID09IGJvYXJkW2NdIGFuZCBib2FyZFthXSAhPSAiICI6CiAgICAgICAgICAgIHJldHVybiBib2FyZFthXQogICAgaWYgIiAiIG5vdCBpbiBib2FyZDoKICAgICAgICByZXR1cm4gIlRpZSIKICAgIHJldHVybiBOb25lCgpkZWYgYXZhaWxhYmxlX21vdmVzKGJvYXJkKToKICAgIHJldHVybiBbaSBmb3IgaSxjZWxsIGluIGVudW1lcmF0ZShib2FyZCkgaWYgY2VsbCA9PSAiICJdCgpkZWYgbWluaW1heChib2FyZCwgcGxheWVyKToKICAgIHdpbm5lciA9IGNoZWNrX3dpbm5lcihib2FyZCkKICAgIGlmIHdpbm5lciA9PSBBSToKICAgICAgICByZXR1cm4gKDEsIE5vbmUpCiAgICBlbGlmIHdpbm5lciA9PSBIVU1BTjoKICAgICAgICByZXR1cm4gKC0xLCBOb25lKQogICAgZWxpZiB3aW5uZXIgPT0gIlRpZSI6CiAgICAgICAgcmV0dXJuICgwLCBOb25lKQoKICAgIGlmIHBsYXllciA9PSBBSToKICAgICAgICBiZXN0X3Njb3JlID0gLW1hdGguaW5mCiAgICAgICAgYmVzdF9tb3ZlID0gTm9uZQogICAgICAgIGZvciBtb3ZlIGluIGF2YWlsYWJsZV9tb3Zlcyhib2FyZCk6CiAgICAgICAgICAgIGJvYXJkW21vdmVdID0gQUkKICAgICAgICAgICAgc2NvcmUsIF8gPSBtaW5pbWF4KGJvYXJkLCBIVU1BTikKICAgICAgICAgICAgYm9hcmRbbW92ZV0gPSAiICIKICAgICAgICAgICAgaWYgc2NvcmUgPiBiZXN0X3Njb3JlOgogICAgICAgICAgICAgICAgYmVzdF9zY29yZSA9IHNjb3JlCiAgICAgICAgICAgICAgICBiZXN0X21vdmUgPSBtb3ZlCiAgICAgICAgcmV0dXJuIChiZXN0X3Njb3JlLCBiZXN0X21vdmUpCiAgICBlbHNlOgogICAgICAgIGJlc3Rfc2NvcmUgPSBtYXRoLmluZgogICAgICAgIGJlc3RfbW92ZSA9IE5vbmUKICAgICAgICBmb3IgbW92ZSBpbiBhdmFpbGFibGVfbW92ZXMoYm9hcmQpOgogICAgICAgICAgICBib2FyZFttb3ZlXSA9IEhVTUFOCiAgICAgICAgICAgIHNjb3JlLCBfID0gbWluaW1heChib2FyZCwgQUkpCiAgICAgICAgICAgIGJvYXJkW21vdmVdID0gIiAiCiAgICAgICAgICAgIGlmIHNjb3JlIDwgYmVzdF9zY29yZToKICAgICAgICAgICAgICAgIGJlc3Rfc2NvcmUgPSBzY29yZQogICAgICAgICAgICAgICAgYmVzdF9tb3ZlID0gbW92ZQogICAgICAgIHJldHVybiAoYmVzdF9zY29yZSwgYmVzdF9tb3ZlKQoKZGVmIGh1bWFuX21vdmUoYm9hcmQpOgogICAgd2hpbGUgVHJ1ZToKICAgICAgICB0cnk6CiAgICAgICAgICAgIHMgPSBpbnB1dCgiVG9uIGNvdXAgKDEtOSkgOiAiKS5zdHJpcCgpCiAgICAgICAgICAgIGNob2ljZSA9IGludChzKQogICAgICAgICAgICBpZHggPSBjaG9pY2UgLSAxCiAgICAgICAgICAgIGlmIGlkeCBub3QgaW4gcmFuZ2UoOSk6CiAgICAgICAgICAgICAgICBwcmludCgiQ2hvaXggaW52YWxpZGUuIEVudHJlIHVuIG5vbWJyZSBlbnRyZSAxIGV0IDkuIikKICAgICAgICAgICAgZWxpZiBib2FyZFtpZHhdICE9ICIgIjoKICAgICAgICAgICAgICAgIHByaW50KCJDYXNlIGTDqWrDoCBwcmlzZSwgY2hvaXNpcyB1bmUgYXV0cmUgY2FzZS4iKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgYm9hcmRbaWR4XSA9IEhVTUFOCiAgICAgICAgICAgICAgICByZXR1cm4KICAgICAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICAgICAgcHJpbnQoIkVudHLDqWUgbm9uIHZhbGlkZS4gRW50cmUgdW4gbm9tYnJlIGVudGllciBlbnRyZSAxIGV0IDkuIikKICAgICAgICBleGNlcHQgKEVPRkVycm9yLCBLZXlib2FyZEludGVycnVwdCk6CiAgICAgICAgICAgIHByaW50KCJcbkVudHLDqWUgaW50ZXJyb21wdWUuIEZpbiBkdSBqZXUuIikKICAgICAgICAgICAgcmFpc2UgU3lzdGVtRXhpdAoKZGVmIGFpX21vdmUoYm9hcmQpOgogICAgXywgbW92ZSA9IG1pbmltYXgoYm9hcmQsIEFJKQogICAgaWYgbW92ZSBpcyBOb25lOgogICAgICAgIG1vdmVzID0gYXZhaWxhYmxlX21vdmVzKGJvYXJkKQogICAgICAgIG1vdmUgPSBtb3Zlc1swXSBpZiBtb3ZlcyBlbHNlIE5vbmUKICAgIGlmIG1vdmUgaXMgbm90IE5vbmU6CiAgICAgICAgYm9hcmRbbW92ZV0gPSBBSQogICAgICAgIHByaW50KCJMJ0lBIGpvdWUgZW4gY2FzZSB7fS4iLmZvcm1hdChtb3ZlKzEpKQoKZGVmIHBsYXkoKToKICAgIGJvYXJkID0gWyIgIl0gKiA5CiAgICBwcmludCgiVGljLVRhYy1Ub2UgOiB0dSBlcyAnWCcsIGwnSUEgZXN0ICdPJy4iKQogICAgZmlyc3QgPSAiIgogICAgdHJ5OgogICAgICAgIHdoaWxlIGZpcnN0Lmxvd2VyKCkgbm90IGluICgiaiIsICJpIik6CiAgICAgICAgICAgIGZpcnN0ID0gaW5wdXQoIlF1aSBjb21tZW5jZSA/IChKIHBvdXIgdG9pIC8gSSBwb3VyIGwnSUEpIDogIikuc3RyaXAoKSBvciAiSiIKICAgIGV4Y2VwdCAoRU9GRXJyb3IsIEtleWJvYXJkSW50ZXJydXB0KToKICAgICAgICBwcmludCgiXG5FbnRyw6llIGludGVycm9tcHVlLiBGaW4gZHUgamV1LiIpCiAgICAgICAgcmV0dXJuCgogICAgaHVtYW5fdHVybiA9IChmaXJzdC5sb3dlcigpID09ICJqIikKICAgIGRpc3BsYXlfYm9hcmQoYm9hcmQpCgogICAgd2hpbGUgVHJ1ZToKICAgICAgICBpZiBodW1hbl90dXJuOgogICAgICAgICAgICBodW1hbl9tb3ZlKGJvYXJkKQogICAgICAgIGVsc2U6CiAgICAgICAgICAgIGFpX21vdmUoYm9hcmQpCgogICAgICAgIGRpc3BsYXlfYm9hcmQoYm9hcmQpCiAgICAgICAgd2lubmVyID0gY2hlY2tfd2lubmVyKGJvYXJkKQogICAgICAgIGlmIHdpbm5lcjoKICAgICAgICAgICAgaWYgd2lubmVyID09IEhVTUFOOgogICAgICAgICAgICAgICAgcHJpbnQoIkJyYXZvIOKAlCB0dSBhcyBnYWduw6kgISIpCiAgICAgICAgICAgIGVsaWYgd2lubmVyID09IEFJOgogICAgICAgICAgICAgICAgcHJpbnQoIkRvbW1hZ2UsIGwnSUEgYSBnYWduw6kuIikKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIHByaW50KCLDiWdhbGl0w6kgISIpCiAgICAgICAgICAgIGJyZWFrCgogICAgICAgIGh1bWFuX3R1cm4gPSBub3QgaHVtYW5fdHVybgoKaWYgX19uYW1lX18gPT0gIl9fbWFpbl9fIjoKICAgIHBsYXkoKQo=