################################################################################
#                          TP 1 d'informatique - MPSI                          #
#               Rappels sur la programmation Python - 15/09/2026               #
#                                  Correction                                  #
################################################################################

###########################################
# Exercice 1.  Fonction mystère
###########################################

def f():
    x = 0
    for i in range(2, 9):
        if i % 3 == 0:
            x += i
        else:
            x -= i
    return x

# Voici les valeurs de x a la fin de chaque tour de boucle:
# i = 2 -> x = -2
# i = 3 -> x = 1
# i = 4 -> x = -3
# i = 5 -> x = -8
# i = 6 -> x = -2
# i = 7 -> x = -9
# i = 8 -> x = -17
# Donc f() vaut -17

### Tests:

# print(f())

###########################################
# Exercice 2. Calcul de la moyenne
###########################################

################
### Question 1
################

def moy(n1, n2, n3):
    """moy(n1: int, n2: int, n3: int) -> float"""
    return (n1 + n2 + n3)/3

### Tests:

# print(moy(5, 6, 7))
# print(moy(1, 1, 1))
# print(moy(-4, 6, 8))

################
### Question 2
################

def moy_ponderee(n1, n2, n3, w1, w2, w3):
    """
    n1, n2, n3, w1, w2, w3: int
    Returns: float
    """
    assert w1 + w2 + w3 != 0
    return (w1*n1 + w2*n2 + w3*n3)/(w1 + w2 + w3)

### Tests:

# print(moy_ponderee(5, 6, 7, 1, 1, 1))
# print(moy_ponderee(5, 3, 7, 6, -7, 2))
# print(moy_ponderee(1, 2, 3, 4, 5, 6))
# print(moy_ponderee(5, 6, 7, 1, 1, -2))

################
### Question 3
################

def moy_geom_ponderee(n1, n2, n3, w1, w2, w3):
    """
    n1, n2, n3, w1, w2, w3: int
    Returns: float
    """
    assert w1 + w2 + w3 != 0
    return (n1**w1 * n2**w2 * n3**w3)**(1/(w1 + w2 + w3))

### Tests:

# print(moy_geom_ponderee(5, 6, 7, 1, 1, 1))
# print(moy_geom_ponderee(5, 3, 7, 6, -7, 2))
# print(moy_geom_ponderee(1, 2, 3, 4, 5, 6))
# print(moy_geom_ponderee(5, 6, 7, 1, 1, -2))

###########################################
# Exercice 3. Coefficients binomiaux
###########################################

################
### Question 1
################

def fact(n):
    """fact(n: int) -> int"""
    assert n >= 0
    res = 1
    for i in range(1, n+1):
        res = res * i
    return res

### Tests:

# print(fact(0))
# print(fact(1))
# print(fact(2))
# print(fact(5))
# print(fact(12))
# print(fact(-1))

################
### Question 2
################

def binom(n,k):
    """binom(n: int, k: int) -> int"""
    assert n >= 0 and k >= 0
    if n < k:
        return 0
    else:
        return fact(n)//(fact(k)*fact(n-k))

### Tests:

# print(binom(7, 0))
# print(binom(7, 7))
# print(binom(8, 4))
# print(binom(5, 10))
# print(binom(-1, 5))
# print(binom(5, -2))

###########################################
# Exercice 4.  Conversion de durées
###########################################

################
### Question 1
################

def hms_to_s(h, m, s):
    """hms_to_s(h: int, m: int, s: int) -> int"""
    return s + m*60 + h*3600

### Tests:

# print(hms_to_s(0, 0, 50))
# print(hms_to_s(0, 1, 0))
# print(hms_to_s(1, 0, 0))
# print(hms_to_s(2, 1, 50))
# print(hms_to_s(22, 100, 50))

################
### Question 2
################

# print("On a les egalites 1+1 =", 1+1, "et 2*3 =", 2*3)

# # Cette commande affiche successivement dans la console:
# # - "On a les egalites 1+1 ="
# # - Puis "2", c'est a dire le resultat de 1+1
# # - Puis "et 2*3 ="
# # - Puis "6", c'est a dire le resultat de 2*3

################
### Question 3
################

def print_duree(s):
    """print_duree(s: int) -> NoneType"""
    if s >= 0:
        h = s//3600
        s1 = s % 3600
        m = s1//60
        s2 = s1 % 60
        print(h, "h,", m, "min et", s2, "s")

### Tests:

# print_duree(10)
# print_duree(60)
# print_duree(3600)
# print_duree(33333)
# print_duree(86400)
# print_duree(-5)

################
### Question 4.a
################

# Voir les valeurs de h, s1, m et s2 dans le programme de la question 3.

###########################################
# Exercice 5. Nombre de diviseurs d'un entier
###########################################

################
### Question 1
################

# On écrit d'abord une fonction nb_div qui renvoie le nombre de
# diviseurs d'un entier
def nb_div(n):
    """nb_div(n: int) -> int"""
    cpt = 0
    for d in range(1, n+1):
        if n%d == 0:
            cpt += 1
    return cpt

def get_D():
    """get_D() -> int"""
    D = 0
    for n in range(1, 15001):
        Dn = nb_div(n)
        if Dn > D:
            D = Dn
    return D

### Tests:

# print(get_D())

################
### Question 2
################

def print_max_div():
    """print_max_div() -> NoneType"""
    for i in range(1, 15001):
        if nb_div(i) == 72:
            print(i)

### Tests:

# print_max_div()

###########################################
# Exercice 6. Morpion
###########################################

# Remarque: le programme serait plus élégant si on utilisait des
# listes (qu'on verra dans le prochain cours) à la place des 9
# variables grilleij.

import tkinter as tk
from tkinter import messagebox
import math

# On utilise des variables globales pour éviter d'avoir trop
# d'arguments dans les fonctions. Voici les variables globales que
# nous utiliserons:
# * L = hauteur et largeur de la grille (qui est carrée).
# * C = hauteur et largeur de chaque case de la grille.
# * T = hauteur et largeur des croix et des cercles.
# * coup = 0,1,2,3 ... = le numéro du coup qui vient d'être joué.
# * grille00, grille01, grille02
#   grille10, grille11, grille12
#   grille20, grille21, grille22
#   = 9 variables telles que grilleij vaut 0 si aucun joueur n'a joué
#   la case (i,j), 1 si le joueur 1 a joué cette case et 2 si le
#   joueur 2 a joué cette case.
# * can = le canevas utilisé pour tracer le jeu.
# **********************
# La variable L peut être modifiée ci-dessous. Toutes les autres
# variables globales sont définies dans la fonction "main".

L = 500

def open_window(width, height, title):
    """open_window(width: int, height: int, title: str) -> canevas"""
    win = tk.Tk()
    win.protocol("WM_DELETE_WINDOW", win.destroy)
    win.title(title)
    can = tk.Canvas(win, width=width, height=height,
                    highlightthickness=0, bd = 0)
    can.pack()
    can.config(bg = "white")
    return can

# (x,y) sont les coordonnées du pixel qui a été cliqué.
# (i,j) sont les coordonnées de la case correspondante (0 <= i,j <= 2).
def trouver_case(x,y):
    """trouver_case(x: int,y: int) -> (int, int)"""
    i = math.floor(x/C)
    j = math.floor(y/C)
    return i,j

def tracer_grille():
    """tracer_grille() -> NoneType"""
    can.config(bg = "white")
    can.create_line(C, 0, C, L, width = 2)
    can.create_line(2*C, 0, 2*C, L, width = 2)
    can.create_line(0, C, L, C, width = 2)
    can.create_line(0, 2*C, L, 2*C, width = 2)

# Trace une croix centrée sur le point (x,y).
# En pratique (x,y) seront les coordonnées du centre d'une case.
def tracer_croix(x,y):
    """tracer_croix(x: int, y: int) -> NoneType"""
    can.create_line(x-T/2, y-T/2, x+T/2, y+T/2, width = 5)
    can.create_line(x+T/2, y-T/2, x-T/2, y+T/2, width = 5)

def tracer_cercle(x,y):
    """tracer_cercle(x: int, y: int) -> NoneType"""
    can.create_oval(x-T/2, y-T/2, x+T/2, y+T/2, width = 5)

# Renvoie le joueur qui joue le coup numéro 'c':
def get_joueur(c):
    """get_joueur(c: int) -> int"""
    return ((c-1) % 2) + 1

# Indique si le joueur a gagné la partie.
def gagne(joueur):
    """gagne(joueur: int) -> bool"""
    return (
        # Lignes
        grille00 == grille01 == grille02 == joueur or
        grille10 == grille11 == grille12 == joueur or
        grille20 == grille21 == grille22 == joueur or
        # Colonnes
        grille00 == grille10 == grille20 == joueur or
        grille01 == grille11 == grille21 == joueur or
        grille02 == grille12 == grille22 == joueur or
        # Diagonales
        grille00 == grille11 == grille22 == joueur or
        grille02 == grille11 == grille20 == joueur)

# Cette fonction indique si on peut jouer sur la case (i,j).
def est_libre(i,j):
    return \
        ((i,j) == (0,0) and grille00 == 0) or\
        ((i,j) == (0,1) and grille01 == 0) or\
        ((i,j) == (0,2) and grille02 == 0) or\
        ((i,j) == (1,0) and grille10 == 0) or\
        ((i,j) == (1,1) and grille11 == 0) or\
        ((i,j) == (1,2) and grille12 == 0) or\
        ((i,j) == (2,0) and grille20 == 0) or\
        ((i,j) == (2,1) and grille21 == 0) or\
        ((i,j) == (2,2) and grille22 == 0)

# Cette fonction joue un coup en case (i,j)
def jouer(i,j,joueur):
    assert est_libre(i,j)
    global grille00, grille01, grille02
    global grille10, grille11, grille12
    global grille20, grille21, grille22
    if (i,j) == (0,0):
        grille00 = joueur
    elif (i,j) == (0,1):
        grille01 = joueur
    elif (i,j) == (0,2):
        grille02 = joueur
    elif (i,j) == (1,0):
        grille10 = joueur
    elif (i,j) == (1,1):
        grille11 = joueur
    elif (i,j) == (1,2):
        grille12 = joueur
    elif (i,j) == (2,0):
        grille20 = joueur
    elif (i,j) == (2,1):
        grille21 = joueur
    elif (i,j) == (2,2):
        grille22 = joueur
    x = i*C + C/2
    y = j*C + C/2
    if joueur == 1:
        tracer_croix(x, y)
    else:
        tracer_cercle(x, y)

def on_click(event):
    """on_click(event: evenement) -> NoneType"""
    global coup  # Pour pouvoir modifier cette variable globale.
    i,j = trouver_case(event.x, event.y)
    coup_licite = est_libre(i,j)
    if not coup_licite:
        tk.messagebox.showinfo("Coup impossible", "Cette case a déjà été jouée")
        return
    coup += 1
    joueur = get_joueur(coup)
    jouer(i,j,joueur)
    if gagne(joueur):
        tk.messagebox.showinfo("Partie terminée", "Le joueur " + str(joueur) + " a gagné !")
        can.master.destroy()
        return
    elif coup == 9:
        tk.messagebox.showinfo("Partie terminée", "Égalité !")
        can.master.destroy()
        return

def main():
    """main() -> NoneType"""
    global C, T, coup, can
    global grille00, grille01, grille02
    global grille10, grille11, grille12
    global grille20, grille21, grille22
    C = L/3
    T = 2*C/3
    coup = 0
    grille00 = grille01 = grille02 = 0
    grille10 = grille11 = grille12 = 0
    grille20 = grille21 = grille22 = 0
    can = open_window(L, L, "Morpion")
    ###
    tracer_grille()
    can.bind('<Button-1>', on_click)
    can.mainloop()

### Tests:

# main()