{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# pierre-feuille-ciseaux\n", "## Un tour de python3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nous allons tenter d'écrire pas à pas un programme qui reproduit le jeu \"pierre feuilles ciseaux\" ou chifoumi.\n", "Ce sera également l'occasion de se familiariser avec les notebooks.\n", "\n", "Le jeu comporte deux joueurs : \"Heckel\" et \"Jeckel\". Le programme leur attribue un coup de façon aléatoire et indique qui est le gagnant.\n", "Nous commencerons en décomposant le problème en trois parties : les coups, les règles, le résultat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Première version : un peu nulle" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Les coups\n", "\n", "Dans une partie, Heckel et Jeckel ont chacun un coup. Nous allons utiliser des variables pour conserver en mémoire le coup joué par chacun des joueurs. Nous ferons varier les coups par la suite." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "feuille\n" ] } ], "source": [ "heckel = \"feuille\"\n", "jeckel = \"pierre\"\n", "print(heckel) # En python3 print est une fonction, parenthèses obligatoires" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous pouvez éxecuter la cellule ci-dessus, elle sera interprétée par Python (clic sur bouton \"play\" ou shift+return). Vous pouvez également modifier les valeurs possibles ou les noms de variable et éxecuter à nouveau.\n", "\n", "Il n'y a pas de norme pour les noms de variable. Python donne des conseils dans la PEP 8 (Python Enhancement Proposal) : [Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/#naming-conventions). Ici nous écrirons les noms de variables en minuscule avec des underscores au besoin." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Les règles\n", "\n", "Ensuite nous devons implémenter les règles du jeu pour déterminer le vainqueur. Dans un premier temps nous allons le faire naïvement à l'aide de disjonctions imbriquées 'si pierre et ciseaux alors pierre gagne, sinon etc...'. Pour cela nous utiliserons les instructions `if`, `else` et `elif`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "if heckel == jeckel:\n", " result = \"Égalité\"\n", "else:\n", " if heckel == \"pierre\":\n", " if jeckel == \"ciseaux\":\n", " result = \"Heckel a gagné\"\n", " elif jeckel == \"feuille\":\n", " result = \"Jeckel a gagné\"\n", " elif heckel == \"feuille\":\n", " if jeckel == \"pierre\":\n", " result = \"Heckel a gagné\"\n", " else:\n", " # Jeckel joue forcément ciseaux (enfin s'il respecte les règles)\n", " result = \"Jeckel a gagné\"\n", " # Heckel joue ciseaux\n", " # A vous d'écrire la suite ici" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ce n'est ni élégant ni optimisé mais c'est simple à comprendre et à écrire.\n", "\n", "Prenez garde aux niveaux d'indentation, c'est une erreur commune quand on débute en Python.\n", "La [PEP 8](https://www.python.org/dev/peps/pep-0008/#tabs-or-spaces) recommande l'utilisation des espaces pour l'indentation. Je recommande le visionnage de [cet extrait](https://www.youtube.com/watch?v=SsoOG6ZeyUI) de la série \"Silicon Valley\"." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Le résultat\n", "\n", "Une fois que le vainqueur a été déterminé par nos règles il faut afficher le résultat à l'utilisateur.\n", "L'affichage devra rappeler le coup de chaque joueur et le résulat.\n", "\n", "Nous utiliserons pour cela la fonction `print`. Par défaut `print` affiche ses arguments sur STDOUT (la sortie standard)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "feuille pierre Heckel a gagné\n", "Heckel : feuille, Jeckel : pierre, Heckel a gagné\n" ] } ], "source": [ "# Les variables sont séparées par des virgules\n", "# L'affichage n'est pas très satisfaisant\n", "print(heckel, jeckel, result)\n", "\n", "# Construction d'une longue chaîne de caractères\n", "# à l'aide de l'opérateur de concaténation +\n", "print(\"Heckel : \" + heckel + \", Jeckel : \" + jeckel + \", \" + result)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Deuxième version : un peu mieux" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Les coups" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pour le moment Hekcel et Jeckel jouent toujours le même coup, c'est un peu nul. Nous allons ajouter un peu d'incertitude en choisissant un coup de façon aléatoire.\n", "\n", "Pour cela nous allons utiliser une liste des coups possible associé au module `random` qui permet de générer des nombres aléatoires." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 feuille\n", "feuille ciseaux\n" ] } ], "source": [ "import random\n", "\n", "coups = [\"pierre\", \"feuille\", \"ciseaux\"] # la liste des coups possibles\n", "rand = random.randint(0,2) # fonction randint renvoie un entier, ici entre 0 et 2, bornes incluses\n", "print(rand, coups[rand]) # on accède à l'élément d'indice 'rand' de la liste 'coups'\n", "heckel = coups[rand]\n", "jeckel = coups[random.randint(0,2)] # on peut s'épargner la variable rand\n", "print(heckel, jeckel)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Exécutez la cellule ci-dessus plusieurs fois et vous verrez que les coups varient.\n", "\n", "Plutôt que d'appeler deux fois une suite d'instructions nous allons en faire une fonction.\n", "Nous l'appelerons `draw`, elle ne reçoit pas d'arguments et renvoie un des coups possibles." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def draw():\n", " \"\"\"\n", " Coup aléatoire au chifoumi \n", " pas d'arguments\n", " \"\"\"\n", " coups = [\"pierre\", \"feuille\", \"ciseaux\"]\n", " coup = coups[random.randint(0,2)]\n", " return coup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pensez à éxécuter la cellule ci-dessus pour que la fonction soit connue par l'interpréteur" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ciseaux pierre\n" ] } ], "source": [ "heckel = draw()\n", "jeckel = draw()\n", "print(heckel, jeckel)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Les règles\n", "\n", "Les règles n'ont pas changé mais nous allons essayer de les regrouper dans une fonction pour pouvoir les appeler plus aisément" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "def rules(heckel, jeckel):\n", " \"\"\" \n", " implémentation naïve des règles du chifoumi\n", " args : heckel (str), jeckel (str)\n", " \"\"\"\n", " if heckel == jeckel:\n", " return(\"Égalité\")\n", " else:\n", " if heckel == \"pierre\":\n", " if jeckel == \"ciseaux\":\n", " return(\"Heckel a gagné\")\n", " else:\n", " return(\"Jeckel a gagné\")\n", " elif heckel == \"feuille\":\n", " if jeckel == \"pierre\":\n", " return(\"Heckel a gagné\")\n", " else:\n", " return(\"Jeckel a gagné\")\n", " else:\n", " if jeckel == \"feuille\":\n", " return(\"Heckel a gagné\")\n", " else:\n", " return(\"Jeckel a gagné\")" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Heckel a gagné\n" ] } ], "source": [ "heckel = draw()\n", "jeckel = draw()\n", "result = rules(heckel, jeckel)\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "## Le résultat\n", "\n", "\n", "Nous utiliserons pour cela les fonctions `print` et `format`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Heckel : pierre, Jeckel : ciseaux, Heckel a gagné\n", "heckel : pierre, jeckel : ciseaux, Heckel a gagné\n" ] } ], "source": [ "# Une chaîne plus facile à modifier avec format() (existe depuis Python 2.6)\n", "print(\"Heckel : {}, Jeckel : {}, {}\".format(heckel, jeckel, result))\n", "\n", "# Pour les old-timers, un printf like. Risque de disparaître au profit de format()\n", "print(\"heckel : %s, jeckel : %s, %s\" %(heckel, jeckel, result))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Au final pour cette deuxième version, on a le code suivant (sans les fonctions) :" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Heckel : pierre, Jeckel : ciseaux, Heckel a gagné\n" ] } ], "source": [ "heckel = draw()\n", "jeckel = draw()\n", "result = rules(heckel, jeckel)\n", "print(\"Heckel : {}, Jeckel : {}, {}\".format(heckel, jeckel, result))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Troisième version : un peu plus fun" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Les coups\n", "\n", "Cette fois vous allez pouvoir jouer. Vous incarnez Jeckel.\n", "\n", "Pour récupérer votre coup nous allons utiliser la fonction [`input`](https://docs.python.org/3/library/functions.html?highlight=input#input)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "À vous de jouer [pierre, feuille, ciseaux]feuille\n", "Jeckel joue : feuille\n" ] } ], "source": [ "jeckel = input(\"À vous de jouer [pierre, feuille, ciseaux]\")\n", "print(\"Jeckel joue : {}\".format(jeckel))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En modifiant uniquement la deuxième instruction de notre code vous pouvez commencer à jouer contre Heckel." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "À vous de jouer [pierre, feuille, ciseaux]ciseaux\n", "Heckel : feuille, Jeckel : ciseaux, Jeckel a gagné\n" ] } ], "source": [ "heckel = draw()\n", "jeckel = input(\"À vous de jouer [pierre, feuille, ciseaux]\")\n", "result = rules(heckel, jeckel)\n", "print(\"Heckel : {}, Jeckel : {}, {}\".format(heckel, jeckel, result))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Il y a une faille dans notre implémentation des règles qui peut permettre de gagner à tous les coups. Essayez de la trouver.\n", "\n", "Vous incarnez Jeckel n'oubliez pas. C'est un sacré filou, s'il y a une faille il va en profiter. \n", "Pour éviter la triche il faut vérifier que la valeur soumise par l'utilisateur est bien conforme à ce qui est attendu. Il y a plusieurs manières de faire cette vérification." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys\n", "jeckel = input(\"À vous de jouer [pierre, feuille, ciseaux]\")\n", "if not(jeckel in coups):\n", " sys.exit(\"Tricheur !\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "jeckel = input(\"À vous de jouer [pierre, feuille, ciseaux]\")\n", "try:\n", " coups.index(jeckel)\n", "except:\n", " print('Tricheur !')\n", " raise\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "jeckel = input(\"À vous de jouer [pierre, feuille, ciseaux]\")\n", "if not(jeckel in coups):\n", " raise ValueError(\"Tricheur ! {} ne fait pas partie des coups permis\".format(jeckel))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Les règles\n", "\n", "Vous pouvez ajouter une fonction de vérification de la valeur du coup soumis puis ajoutez cette fonction dans la fonction `rules`.\n", "\n", "Il est également possible d'implémenter les règles avec un dictionnaire. Exemple :" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "heckel a gagné\n" ] } ], "source": [ "winners = {'ciseaux': 'feuille', 'feuille': 'pierre', 'pierre':'ciseaux'}\n", "if jeckel == heckel:\n", " print(\"Égalité\")\n", "elif jeckel in winners.keys() and heckel == winners[jeckel]:\n", " print(\"Jeckel a gagné\")\n", "else:\n", " print(\"heckel a gagné\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vous n'avez plus qu'à modifier la fonction rules()" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "def rules(heckel, jeckel):\n", " \"\"\" \n", " implémentation naïve des règles du chifoumi\n", " args : heckel (str), jeckel (str)\n", " \"\"\"\n", " winners = {'ciseaux': 'feuille', 'feuille': 'pierre', 'pierre':'ciseaux'}\n", " if jeckel == heckel:\n", " return(\"Égalité\")\n", " elif heckel == winners[jeckel]:\n", " return(\"Jeckel a gagné\")\n", " else:\n", " return(\"Heckel a gagné\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Le résultat\n", "\n", "Comment améliorer l'affichage du résultat ? Avec des __emojis__ !\n", "\n", "Vous pouvez récupérer le code Unicode des émojis sur [emojipedia](http://emojipedia.org/). \n", "Pour afficher un caractère d'après son code hexadécimal utilisez `\\u` suivi du code hexa sur 16 bits ou `\\U` suivi du code sur 32 bits ou `\\N` suivi du nom du caractère entre accolades. Exemple :" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "🐼\n", "🐼\n" ] } ], "source": [ "print(\"\\U0001F43C\")\n", "print(\"\\N{PANDA FACE}\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Evidemment ça ne fonctionne que si le glyphe existe dans la police sélectionnée. Sur le navigateur ça devrait aller, dans la console il faut veiller à choisir une police adaptée." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "✊\n", "✋\n", "✌\n" ] } ], "source": [ "pierre = \"\\u270A\"\n", "feuille = \"\\u270B\"\n", "ciseaux = \"\\u270C\"\n", "print(pierre)\n", "print(feuille)\n", "print(ciseaux)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Essayons de modifier l'affichage pour que les coups soit aussi affichées en emojis." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Heckel : ✋, Jeckel : ✊, Heckel a gagné\n" ] } ], "source": [ "emojis = {'pierre': \"\\u270A\", 'feuille': \"\\u270B\", 'ciseaux': \"\\u270C\"}\n", "print(\"Heckel : {}, Jeckel : {}, {}\".format(emojis[heckel], emojis[jeckel], result))\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On récapitule cette troisième version :" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "À vous de jouer [pierre, feuille, ciseaux]feuille\n", "Heckel : ✊, Jeckel : ✋, Jeckel a gagné\n" ] } ], "source": [ "coups = [\"pierre\", \"feuille\", \"ciseaux\"]\n", "emojis = {'pierre': \"\\u270A\", 'feuille': \"\\u270B\", 'ciseaux': \"\\u270C\"}\n", "\n", "heckel = draw()\n", "jeckel = input(\"À vous de jouer [pierre, feuille, ciseaux]\")\n", "if not(jeckel in coups):\n", " raise ValueError(\"Tricheur ! {} ne fait pas partie des coups permis\".format(jeckel))\n", "result = rules(heckel, jeckel)\n", "print(\"Heckel : {}, Jeckel : {}, {}\".format(emojis[heckel], emojis[jeckel], result))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Quatrième (et dernière) version" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Une chance ne suffit, nous allons jouer 3 fois, celui qui a le plus de victoires gagne.\n", "\n", "Pour cela nous aurons besoin d'utiliser une boucle." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.0" } }, "nbformat": 4, "nbformat_minor": 1 }