Correction du sujet CCINP 2024, jeu de l'awalé

le sujet CCINP 2024

Q1

Alice ne peut pas jouer les cases 0, 1 et 3 qui sont vides

Alice peut jouer la case 2, 4 et 5. Nous analyserons les possibles récoltes dans les questions suivantes.

Q2

  • Si Alice joue la case 2, il n’y a pas de rĂ©colte car on n’atteint pas les cases de Bob.

Gain : O.

  • Si Alice joue la case 4, elle sème jusqu'a la case 9 après un tour complet, les cases 9,8 et 7 qui contiennent 2 ou 3 graines sont rĂ©coltĂ©es.

Gain : 8

  • Si Alice joue la case 5, elle sème jusqu'Ă  la case 10 qui ne contient qu'une graine et il n'y a donc pas de rĂ©colte.

Gain : 0

Q3

Premier cas :

  • Si Alice joue la case 5, elle sème jusqu'a la case 10. Cela entrainerait une rĂ©colte de toutes les cases de 6 Ă  10, ce qui affamerait Bob.

La récolte est donc complètement annulée.

Deuxième cas :

  • Si Alice joue la case 5, elle sème jusqu'a la case 10. Cela permet la rĂ©colte des cases de 6 Ă  10, car il reste ue graine dans la case 11.

Gain : 10

Q4

La parité jeu['n'] permet de savoir quel joueur doit jouer. Au début de la partie jeu['n'] vaut 0 et c'est au joueur 1 de jouer. Ainsi le joueur 1 joue quand jeu['n'] est pair.

L'opérateur % (modulo) donne le reste de la division euclidienne, ce qui indique la parité avec 2.

Python
def tour_joueur1(jeu):
    return jeu['n'] % 2 == 0 # Renvoie True quand  jeu['n'] est pair

Q5

Pour pour permuter les valeurs dans un tableau nous pouvons utiliser la syntaxe Python a, b = b , a qui permet d'échanger les valeurs de deux variables.

Python
1
2
3
4
def tourner_plateau(jeu):
    plateau = jeu['plateau']
    for i in range(6): # Parcours des 6 premiers indice de 0 Ă  5 
        plateau[i], plateau[i + 6] = plateau[i +6], plateau[i] # échange avec la case symétrique

Il est aussi possible de permuter plateau[:6], plateau[6:] = plateau[6:], plateau[:6] sans utiliser de boucle.

Q6

Une case ne peut contenir plus que toutes les graines du jeu : 48.

Est-ce un majorant ou un maximum ?

Nous remarquons que \( 2^5 = 32 < 48 < 2^6 = 64\).

Les nombres de 0 Ă  47 se codent donc sur 6 bits.

Q7

Pour chaque valeur du dictionnaire jeu nous utilisons la fonction copy().

Python
1
2
3
4
5
6
7
8
def copie(jeu):
    jeu_copie = {}
    jeu_copie['joueur1'] = jeu['joueur1']
    jeu_copie['joueur2'] = jeu['joueur2']
    jeu_copie['score'] = jeu['score'].copy()
    jeu_copie['n'] = jeu['n'] # ici c'est un entier donc l'affectation suffit
    jeu_copie['plateau'] = jeu['plateau'].copy()
    return jeu_copie

Q8

Nous utilisons une quantité graines qui est décrémentée de un à chaque fois que l'on distribue une graine dans une csae qui n'est pas celle jouée.

Python
def deplacer_graines(plateau, case):
    graines = plateau[case]
    plateau[case] = 0 # pour vider la case
    ind = case + 1 # pour tourner autour du plateau
    while graine > 0: # Il reste une ou des graines Ă  distribuer
        if ind % 12 != case: # case différente de celle jouée, le %12 permet de revenir à 0 quand la valeur est un multiple de 12
            plateau[ind % 12] += 1
            graines -= 1
        ind += 1 # pour passer Ă  la case suivante
    return (ind - 1) % 12

Q9

La question de la famine est ici éludée, donc la vérification porte sur le coté de la case et son nombres de graines qui doit être 2 ou 3 pour pouvoir être ramassée.

Python
1
2
3
4
5
def case_ramassable(plateau, case):
    cond_cote = case >= 6
    cond_graine = 2 <= plateau[case] <= 3

    return cond_cote and cond_graine # Les deux conditions doivent être vérifiées, d'où le and

Q10

Il est important de répondre à la question qui demande d'écrire une fonction récursive.

Il faut donc une condition d'arrêt, ici le fait que la case ne soit pas ramassable, ce qui arrête la récolte.

Attention une erreur était présente dans mon corrigé, j'avais oublié de mettre la case ramassée à 0. C'est corrigé.

Python
def ramasser_graines(plateau, case):
    if not case_ramassables(plateau, case): # teste si la case n'est pas ramassable, condition d'arrĂŞt
        return 0 # la récolte est nulle

    # sinon
    # vide la case et
    # retourne la somme du nombre de graines de la case
    # et du nombre de la récolte de la case d'avant (case - 1),

    nb_graines = plateau[case]
    plateau[case] = 0
    return nb_graine + ramasser_graines(plateau, (case - 1) % 12)

Q11

Nous voulons tester la famine sans modifier le plateau nous devons simuler la distribution et la récolte et vérifier qu'il reste des graines à l'adversaire.

Pour cela, nous pouvons utiliser une copie du plateau.

Python
1
2
3
4
5
6
7
8
def test_famine(plateau,case):
    plateau_copie = plateau.copy()
    case_fin = deplacer_graines(plateau_copie,case) # distribution sur le plateau copié
    ramasser_graines(plateau_copie,case_fin) # ramassage sur le plateau copié
    for i in range(6,12):
        if plateau_copie[i] != 0: # une case non vide a été trouvée
            return True
    return False # toute les cases ont vides

Q12

Nous regroupons les tests des questions 9 et 11, ainsi que la case n'est pas vide.

Python
1
2
3
4
5
6
7
    def test_case(plateau,case):
    cafe_fin = ramasser_graines(plateau, case)
        condition1 = plateau[case] >0
    condition2 = case_ramassable(plateau, case_fin)
    condition3 = test_famine(plateau,case)
    test = condition1 and condition2 and condition3
    return test

Q13

Après avoir une copie du plateau, pour ne pas modifier le dictionnaire jeu.

Pour toutes les cases de 0 à 5 inclus, nous vérifions les 3 conditions avec la fonction test_case.

Si le test renvoie True l'indice de la case est ajoutée à la liste des cases possibles.

Il est possible de créer cette liste avec une boucle for, ou une liste par compréhension.

Python
1
2
3
    def cases_possibles(jeu):
        plateau = jeu['plateau'][:] # pour créer une copie
        return [ i for i in range(6) if test_case(plateau, plateau[i]) ] 

Q14

Il faut implémenter la vérification des conditions écrites dans l'énnoncé.

Python
1
2
3
4
5
6
7
8
def tour_suivant(jeu):
    score = jeu['score']
    cond1 =  score[0] < 25 and  score[1] < 25 # aucun des joueurs n'a plus de 24 graines
    cond2 = jeu['n'] < 100
    cond3 = (score[0] + score[1]) <= 45 # il reste au moins 3 graines sur le plateau
    cond4 = len(cases_possibles(jeu)) > 0 # au moins une case est jouable

    return cond1 and cond2 and cond3 and cond4 

Q15

  • Instruction 1:

case_fin = deplacer_graines(plateau, case)

  • Instruction 2:

la variable case_fin de l'instruction 1 est nécessaire ppur ramasser à partir de cette case.

graines_gagnees sera utilisé dans les lignes suivantes.

graines_gagnees = ramasser_graines(plateau, case_fin)

  • Condition 1:

tour_joueur1(jeu)

  • Instruction 3:

jeu['n'] += 1

Q16

Il faut penser Ă  ramasser les graines du plateau avant de compter les points.

Ce ramassage se fera en fonction du tour.

En effet les graines du coté du plateau de 0 à 5 seront affectées au score du joueur à qui c'est le tour de jouer.

Python
def gagnant(jeu):
    plateau = jeu['plateau']
    score = jeu['score']
    if tour_joueur1(jeu):
        actuel = 0
        autre = 1
    else:
        actuel = 1
        autre = 0

    score[actuel] += sum(plateau[i] for i in range(6)) # ajout des graines des cases de 0 Ă  5 au joueur actuel
    score[autre] += sum(plateau[i] for i in range(6,12)) # ajout des graines des cases de 6 Ă  11 a l'autre joueur


    if  score[0]  >  score[1] :
        return jeu['joueur1']
    elif score[0] < score[1] :
        return jeu['joueur2']
    else
        return ’égalité’

Q17

Python
def gain(jeu,case):
    copie_jeu = copie(jeu) # pour ne pas modifier jeu, passé en argument
    derniere_case = deplacer_graines(copie_jeu["plateau"],case)
    graines_gagnees = ramasser_graines(copie_jeu["plateau"],derniere_case)
    if tour_joueur1(copie_jeu):
        copie_jeu[’score’][0] = copie_jeu[’score’][0] + graines_gagnees
    else:
        copie_jeu[’score’][1] = copie_jeu[’score’][1] + graines_gagnees

    copie_jeu["n"] += 1 # inrémentation du tour
    tourner_plateau(copie_jeu)
    return graines_gagnees,copie_jeu

Q18

Q19

  • La condition 1 vĂ©rifie si le jeu peut continuer

condition 1: not tour_suivant(jeu)

  • La condition 2 sert Ă  limiter la profondeur de recherche:

condition 2: profondeur == profondeur_max

  • L'instruction 1 utlise la fonction gain qui renvoie un couple que sera dĂ©paquetĂ©e dans l'instruction 1.

Nous remarquons que le gain est appelé g dans les lignes qui suivent

instruction 1: g, copie_jeu = gain(jeu,case)

  • L'instruction 2 sert Ă  calculer p, valeur estimĂ©e de VJ par NegaAwale.

instruction 2: p = NegaAwale(copie_jeu, profondeur_max, profondeur + 1)

remqrque le return de la fin de fonction est mal indenté, en effet pour être dans le dernier else il doit être identé au même niveau que le for.

Q20

Nous remarquons d'après le code de la fonction précédente que les les éléments de max_vals sont au format [case, p] cela nous permet de différencier la valeur à retourner.

Python
def max_vals(vals_jeu, profondeur):

    # recherche des maximums indice et valeur
    case_maxi = vals_jeu[0][0]
    maxi=vals_jeu[0][1]
    for i in range(1 , len(vals_jeu)):
        if  vals_jeu[i][1] > maxi :
            case_maxi = vals_jeu[i][0]
            maxi=vals_jeu[i][1]

    if profondeur:
        return maxi # si la profondeur n'est pas 0 on retourne la valeur du jeu max
    return case_maxi # sinon on retourne la case Ă  jouer

Q21

La ligne où le numéro de case à jouer est demandée avec ìnput sera remplacée par un le choix de la fonction NegaAwale

Python
defa wale_jcj_IA(nom_joueur1, nom_joueur2):
    jeu=initialisation(nom_joueur1, nom_joueur2)
    jeu_continue=True

    while jeu_continue:
        affiche(jeu["plateau"])
        case_choisie=NegaAwale(jeu,6,0) # Ligne modifiée
        jeu_continue=tour_jeu(jeu,case_choisie)

    return gagnant(jeu)

SQL⚓︎

Q22

SQL
SELECT id_joueur FROM Joueur WHERE niveau > 1900

Q23

SQL
1
2
3
4
SELECT COUNT(*) * 100.0 / (SELECT COUNT(*) FROM Partie WHERE jeu LIKE "a%")
AS Pourcentage 
FROM Partie 
WHERE resultat = 1 AND jeu LIKE "a%"

Q24

SQL
SELECT nom, prenom FROM Joueur ORDER BY niveau DESC LIMIT 3

Q25

SQL
1
2
3
4
5
6
7
SELECT nom, prenom, COUNT(resultat) AS nb_victoires
FROM Partie
JOIN Joueur ON id_Joueur = id_joueur1
WHERE resultat = 1
GROUP BY id_Joueur1
HAVING nb_victoires >= 2
ORDER BY nb_victoires