--- redirect_from: - "/projets/edwigegros-seriousgames/rapport" interact_link: content/projets/EdwigeGros_SeriousGames/rapport.ipynb kernel_name: python3 kernel_path: content/projets/EdwigeGros_SeriousGames has_widgets: false title: |- Rapport de TER pagenum: 3 prev_page: url: /Seance2/index.html next_page: url: suffix: .ipynb search: de la et le les un en des dans pour une est il du par jeu x avec d ce que sur y z au w h joueur qui labyrinthe fourmi ou exemple cruches code jai cette l sont jupyter afin peut deux pas objets plus tre version visualisation programmation trois objet donc pourrait outils boutons chvre faire lobjet cruche permettant permet jeux ne cet partie t contenu librairie valeur modifier faut argument dmonstration gnrer valueplayerwidget outil mme dun cube python blocs ter divers diffrents ipywidgets voici chou loup dimensions ces chaque actions si ainsi volume laide affichageplayer mthode pythreejs comment: "***PROGRAMMATICALLY GENERATED, DO NOT EDIT. SEE ORIGINAL FILES IN /content***" ---
Rapport de TER

Edwige Gros _ Master 1 Informatique

1 Introduction

1.1 Mise en contexte

Ce projet a été réalisé dans le cadre de l'UE de TER du master 1 Informatique. L'objectif général de notre groupe était d'apprendre à maitriser les outils de Jupyter Notebook en modélisant divers algorithmes. Jupyter Notebook est une application serveur-client interactive permettant de réaliser des notebooks (ou calepin) mélangeant à la fois texte et code exécutable. Il permet de travailler avec plus de 40 langages différents en exécutant du code contenu dans une cellule ou appelé dans un fichier à part. Un notebook est en effet divisé en plusieurs cellules exécutables séparemment. De plus jupyter propose divers outils appelés widgets qui permettent d'améliorer la visualisation et l'interactivité du code. La librairie ipywidgets propose de nombreux outils tels que des boutons, des images ou des sliders.

Voici, ci-dessous un exemple de slider proposant de choisir une valeur comprise entre -10 et 10 :

import ipywidgets as widgets
my_slider=widgets.IntSlider(value=0,min=-10,max=10)
my_slider

Les modélisations réalisées pour les projets de ce TER ont par la suite pour vocation à être utilisées à des fins pédagogiques afin d'illustrer des cours ou d'initier des jeunes à la programmation.

1.2 Motivations et objectifs

Pour mon projet personnel j'ai choisi d'utiliser les outils mis à disposition par jupyter afin d'implémenter divers jeux fréquemment rencontrés lors de l'apprentissage de la programmation. L'idée était d'offrir une interface interactive graphique permettant dans un premier temps de se familliariser avec le jeu puis éventuellement de le résoudre par la programmation. Au cours de ce semestre, j'ai pu implémenter trois jeux différents (certains possédant plusieurs versions) :

  • Le jeu Chou-Chèvre-Loup
  • Le jeu des cruches
  • Un jeu de labyrinthe en trois dimensions

J'ai décidé d'utiliser la programmation orientée objet pour implémenter ces jeux afin d'obtenir un jeu plus structuré.

Le code des trois jeux est disponible sur le dépôt gitLab suivant : https://gitlab.u-psud.fr/edwige.gros/ter-jupyter.git

2 Chou-Chèvre-Loup

2.1 Principe

Le but de ce jeu est de faire traverser une rivière à un chou, une chèvre et un loup grâce à une barque qui ne peut en contenir que deux à la fois maximum. La difficulté du jeu provient du fait que la chèvre ne peut pas être laissée seule sur la berge avec le chou. Le loup quant à lui ne peut pas être laissé seul avec la chèvre. Le joueur doit donc trouver une combinaison d'allers et retours pour chacun de manière à ce que tous trois se retrouvent sur la berge opposée sans être mangés.

2.2 Outils découverts

Cet exercice m'a permis de me familiariser avec les widgets les plus courants de Jupyter disponibles dans la librairie ipywidgets tels que la VBox ou HBox, les boutons et l'affichage d'images. L'affichage final est constitué d'une encapsulation de VBox et HBox. La VBox principale contient :

  • Une matrice d'images permettant au joueur de visualiser l'emplacement de chaque animal et de la barque
  • Un ensemble de boutons permettant d'effectuer diverses actions :
    • Faire monter ou descendre un animal de la barque
    • Faire traverser la barque
    • Recommencer le jeu (le remettre à son état initial)
  • Un espace d'affichage pour indiquer au joueur si il a gagné ou perdu la partie

La principale difficulté de cet exercice fut de comprendre comment modifier les images contenues dans une encapsulation de VBox et HBox. Pour cela il faut directement modifier les valeurs "value" et "format" de l'objet ipywidgets.Image(). J'ai donc par conséquent regroupé l'ensemble de mes images dans une matrice donnée en argument de ma classe.

Voici un exemple de modification d'une image :

# On a une image initiale
file_name="Brouillons/images/chou.jpg"
im_format="jpg"
file=open(file_name,"rb")
image=file.read()
imageWidget=widgets.Image(
            value=image,
            format=im_format
        )
imageWidget
# On modifie le widget
#On charge une autre image
file_name="Brouillons/images/loup.png"
im_format="png"
file=open(file_name,"rb")
image=file.read()
#On modifie l'image du widget ci-dessus
imageWidget.value=image
imageWidget.format=im_format

2.3 Démonstration

from Chou_Chevre_Loup.Grille import *
jeu=Grille()
jeu.run()

2.4 Pistes d'amélioration

Afin d'améliorer l'ergonomie du jeu, il pourrait être intéressant de désactiver et griser les boutons inutiles pour l'état courant du jeu. Par exemple, griser et désactiver le bouton "Monter Chevre" si la chèvre est déjà à bord du bateau ou sur l'autre rive. Cette amélioration permettrait au joueur de mieux visualiser toutes les actions qui lui sont possibles de réaliser.

3 Les Cruches

3.1 Principe

Pour ce jeu, le joueur dispose de deux cruches non graduées de contenances connues et différentes (par exemple 5 et 7 litres). Ainsi, les seules actions que le joueur puisse effectuer sur les cruches sont :

  • Remplir une cruche au maximum
  • Vider entièrement une cruche
  • Verser le contenu d'une cruche dans l'autre jusqu'à ce que la première soit vide ou la deuxième pleine.

L'objectif du joueur est de réussir à obtenir un maximum de volumes d'eau différents à partir de ces trois actions. Exemple: à partir de deux cruches de 5 et 7 litres, le joueur peut-il obtenir 1L ? 2L? 3L? ...

3.2 Les différentes versions

Dans toutes les versions présentées par la suite, le joueur interagit grâce à des boutons et peut voir le volume d'eau contenu dans chaque cruche dans des Labels affichés en haut à droite de la fenêtre d'affichage.

3.2.1 La version classique

La première version implémentée est une version "classique" qui utilise les mêmes outils que ceux évoqués dans la partie Chou-Chèvre-Loup précédemment. Les deux cruches sont de simples objets ipywidgets.Image(), dont l'image est actualisée en fonction du volume contenu par la cruche. Dans cette version, les contenances des cruches sont fixées à 5 et 7L et ne sont pas modifiables par le joueur car pour chaque cruche, il faut autant d'images que de possibilités de volume d'eau contenu (exemple: il faut 6 images pour la cruche de 5L (vide, avec 1L, 2L, 3L, 4L et pleine)).

Démonstration

from Cruches.Affichage import *
a=Affichage()
a.run()

3.2.2 La version dynamique

La deuxième version implémentée est plus dynamique car elle offre au joueur la possibilité de choisir la contenance des deux cruches. Il a donc fallut générer dynamiquement le rendu graphique en fonction des actions du joueur. Pour cela j'ai utilisé la librairie ipycanvas de Jupyter.

A l'aide de l'objet MultiCanvas j'ai séparé le contour des cruches qui est permanent et l'eau contenue dans les cruches qui doit être constamment redessinée. Les dessiner sur deux épaisseurs différentes permet de complètement effacer la couche du canvas qui a besoin d'être redessinée sans jamais toucher aux contours des cruches.

Démonstration

from Cruches.Affichage_dynamique import *
ad=Affichage_dynamique(6,8)
ad.run()

3.2.3 L'ajout de ValuePlayerWidget

Pour cette dernière version, j'ai modifié la version classique du jeu avec l'aide de Nicolas Hainak et Erwan Cornic afin de la rendre compatible avec l'outil ValuePlayerWidget qu'ils ont développé. Cet outil permet d'enregistrer les divers états du jeu depuis son lancement et de visualiser ces états étape par étape afin de mieux comprendre comment le joueur a obtenu l'état final. Afin d'intégrer ValuePlayerWidget il a fallut modifier la classe Affichage du jeu et créer une classe Visua qui hérite de HBox et qui sera la visualisation prise en argument par l'objet ValuePlayerWidget lors de son initialisation. J'ai donc découpé l'affichage en deux parties: la partie donnée à ValuePlayerWidget (les images des cruches et les labels d'indication de leur volumes) et la partie avec les boutons permettant de modifier le dernier état des cruches. La classe AffichagePlayer hérite donc de traitlets.HasTraits et possède en argument un objet Visua dont la valeur est liée à la valeur de l'objet AffichagePlayer. Cet objet Visua est ensuite donné en argument à l'objet ValuePlayerWidget qui est un argument de AffichagePlayer. Ainsi, lorsqu'un bouton est appuyé, la méthode ValuePlayerWidget.set_value() est appelée, la valeur de l'objet est donc modifiée et ajoutée à l'historique des valeurs et celle de AffichagePlayer est modifiée par la même occasion (car elles sont liées). La méthode traitlets._observe_value() détecte ensuite que la valeur de l'objet AffichagePlayer a été modifiée et met alors à jour l'affichage en fonction de la nouvelle valeur. Cette fameuse valeur des objets AffichagePlayer et Visua correspond à un tuple (c1, c2) et dont c1 indique le volume contenu par la première cruche et c2 le volume contenu par la deuxième cruche.

Démonstration

from Cruches.AffichagePlayer import *
afPlayer=AffichagePlayer()
afPlayer.visualisation

3.3 Pistes d'amélioration

Afin d'améliorer l'expérience du joueur, nous pourrions ajouter à droite de la visualisation, un tableau de valeurs mémorisant pour le joueur les différents volumes qu'il a déjà réussi à obtenir.

Exemple pour des cruches de 5 et 7 litres :

Volume Obtenu
1L
2L X
3L
4L
5L X
6L
7L X

Une fois le tableau complété, un message de victoire pourrait s'afficher. Par ailleurs, pour la version avec canvas du jeu, deux widgets de type dropdown et un bouton pourraient être affichés à droite de la visualisation afin de permettre au joueur de choisir la contenance des deux cruches.

Exemple:

exemple=widgets.VBox([widgets.Dropdown(options=[str(i) for i in range (1,11)],value='5',description='Cruche n°1 :',disabled=False,),widgets.Dropdown(options=[str(i) for i in range (1,11)],value='7',description='Cruche n°2 :',disabled=False,),widgets.Button(description="Valider")])
exemple

4 Le labyrinthe

4.1 Principe

Le principe de ce jeu est semblable au jeu Laby qui nous a été présenté dans ce TER : faire évoluer une fourmi dans un labyrinthe. Cependant dans ce jeu, la fourmi doit évoluer dans un labyrinthe en 3 dimensions.

4.2 La découverte de Pythreejs

J'ai utilisé la librairie pythreejs afin de générer la visualisation 3D de la fourmi et du labyrinthe. Cette librairie permet de modéliser des formes géométriques en trois dimensions, de leur ajouter une texture et de les placer sur une scène. Cette librairie est semblable à la librairie threejs disponible pour JavaScript. Il s'agit d'un outil complexe proposant de régler l'emplacement de la caméra et ses déplacements ainsi que l'origine et le type de lumière.

4.2.1 Générer le labyrinthe

Le labyrinthe est composé de plusieurs objets statiques ou mobiles. Les objets mobiles sont les trois sphères qui servent à représenter la fourmi. Les autres objets statiques sont les parois du labyrinthe. Le labyrinthe est généré à partir d'un fichier JSON dans lequel la position et les dimensions des objets sont spécifiées. Voici un exemple de fichier JSON :

{
    "view":{"w":600, "h":400},
    "camera":{"x":0,"y":12,"z":12},
    "key_light":{"x":0,"y":10,"z":10},
    "perso" : {"x" : -4 , "y" : 0.5, "z":-4, "w": 1 , "h": 1, "d":1 ,"orientation":"S"},
    "sol" : {"x" : 0 , "y" : -0.5, "z":0, "w": 10 , "h": 1, "d":10 },
    "bloc" : [
            {"x" : -4.75 , "y" : 0.5, "z":0, "w": 0.5 , "h": 1, "d":10 },
            {"x" : 0 , "y" : 0.5, "z":-4.75, "w": 9 , "h": 1, "d":0.5 },
            {"x" : 4.75 , "y" : 0.5, "z":0, "w": 0.5 , "h": 1, "d":10 },
            {"x" : -1 , "y" : 0.5, "z":4.75, "w": 8 , "h": 1, "d":0.5 },
            {"x" :-4.75 , "y" :0.5, "z":-3, "w":0.5 , "h":1, "d":3 },
            {"x" :-3 , "y" :0.5, "z":-0.25, "w":3 , "h":1, "d":0.5 },
            {"x" :-1.75 , "y" :0.5, "z":1.25, "w":0.5 , "h":1, "d":2.5 },
            {"x" :-4 , "y" :0.5, "z":2, "w":1 , "h":1, "d":0.5 },
            {"x" :-1.25 , "y" :0.5, "z":-3.5, "w":0.5 , "h":1, "d":2 },
            {"x" :0.75 , "y" :0.5, "z":4, "w":0.5 , "h":1, "d":1 },
            {"x" :0.25 , "y" :0.5, "z":-2.25, "w":3.5 , "h":1, "d":0.5 },
            {"x" :0.75 , "y" :0.5, "z":-0.5, "w":0.5 , "h":1, "d":3 },
            {"x" :2.75 , "y" :0.5, "z":-4, "w":0.5 , "h":1, "d":1 },
            {"x" :3.75 , "y" :0.5, "z":1.75, "w":2 , "h":1, "d":0.5 },
            {"x" :3 , "y" :0.5, "z":0.5, "w":0.5 , "h":1, "d":2 },
            {"x" :-3.25 , "y" :0.5, "z":-3, "w":0.5 , "h":1, "d":3 },
            {"x" : 0 , "y" : 1.5, "z":0, "w": 10 , "h": 1, "d":10 }
        ]
}

Les deux premières lignes permettent régler l'orientation et l'emplacement de la caméra à l'état initial du jeu. La deuxième ligne quant à elle, donne la position de la lumière sur la scène.

Les informations sur le personnage sont données dans la troisième ligne intitulée "perso". La fourmi, bien qu'étant composée en réalité de trois petites sphères, est considérée comme un cube dans l'algorithme lors des détections de collision. Cette particularité vient en partie du fait que, initialement, la fourmi était un cube. Afin d'aider le joueur à se repérer dans l'espace, nous avons remplacé ce cube par une fourmi qui, contrairement au cube, dispose d'une tête et d'un corps permettant ainsi de l'orienter dans l'espace.

Les catégories "sol" et "bloc" listent par la suite les paroies du labyrinthe en donnant leurs coordonnées, largeur, hauteur et profondeur. Les coordonnées d'un bloc correspondent au centre de ce dernier. Par exemple pour un cube de coordonnées (0,0,0) et de largeur 1, ses paroies droites et gauches seront aux coordonnées d'abscisse x=-0.5 et x=0.5 .

4.2.2 Les objets

Tous les objets du labyrinthe sont des objets de type Mesh auxquels on peut donner une géométrie, une matière et une position. On peut ainsi créer divers objets en trois dimensions et leur donner une texture. J'ai rencontré beaucoup de difficultés pour appliquer une texture à un objet Mesh car la documentation de Pythreejs est malheureusement incomplète. Voici donc un exemple de sphère avec une texture :

from pythreejs import *
imageTexture = ImageTexture(imageUri='Brouillons/images/zombiskin.JPG')
texture=MeshPhysicalMaterial(map=imageTexture)
sphere=Mesh(geometry=SphereBufferGeometry(radius=1),
            material=texture,
            position=[-2,0,0]
        )
sphere

Tous ces objets sont ensuite donnés à un objet Scene, lui même donné à un objet Renderer qui renvoie la visualisation 3D de tous les objets dans une même scène.

4.2.3 Les mouvements

Il existe deux méthodes possibles pour générer le mouvement des objets :

  • On peut avoir un affichage avec un mouvement continu (c'est le cas pour la démonstration 4.3). On crée ainsi une liste de N positions qui correspondent à un ensemble de points sur la trajectoire de la fourmi. On associe à cette liste, une liste de taille N/3. Le $n^{èm}$ élément de cette liste correspond au timing de la $n^{èm}$ position dans la première liste. Ces deux listes sont ensuite fournies à un objet VectorKeyframeTrack, lui même donné en argument à un objet AnimationClip. Ce dernier est donné en argument à un objet AnimationAction qui permet de modéliser un mouvement continu de la fourmi. Un exemple étant probablement plus parlant, voici un déplacement de notre sphere créée précédemment (un cube immobile a été ajouté dans la scène pour mieux percevoir le mouvement de la sphère):
view_width = 300
view_height = 400
#création d'un référentiel par rapport à la sphere
cube = Mesh(
    BoxBufferGeometry(1, 1, 1),
    MeshPhysicalMaterial(color='blue'),
    position=[2, 0, 2]
)
#Placement de la caméra et des lumières
camera = PerspectiveCamera( position=[10, 6, 10], aspect=view_width/view_height)
key_light = DirectionalLight(position=[0, 10, 10])
ambient_light = AmbientLight()
#Liste des positions et temps
positon_track = VectorKeyframeTrack(name='.position',
    times=[0, 2, 5],
    values=[-2, 0, 0,
            2,3,2,
            3,3,5
           ])
# Création de la visualisation
scene = Scene(children=[cube,sphere, camera, key_light, ambient_light])
controller = OrbitControls(controlling=camera)
renderer = Renderer(camera=camera, scene=scene, controls=[controller],
                    width=view_width, height=view_height)
# Générateur de mouvements
clip = AnimationClip(tracks=[positon_track])
action = AnimationAction(AnimationMixer(sphere), clip, sphere)
#
widgets.VBox([renderer,action])
  • On peut sinon préférer un affichage avec déplacement discret. Pour cela il suffit de modifier directement la position de l'objet. Cette version a également été implémentée dans l'optique d'une future amélioration possible et sera évoquée dans la partie 4.4 .

4.3 Démonstration

from Labyrinthe.Affichage import *
myLab=Affichage('Labyrinthe/lab/lab.json')
myLab.renderer
myLab.reset()
for i in range (0,3):
    myLab.avancer()
myLab.gauche()
for i in range (0,3):
    myLab.avancer()
myLab.droite()
for i in range (0,4):
    myLab.avancer()
myLab.gauche()
for i in range (0,5):
    myLab.avancer()
myLab.droite()
for i in range(0,4):
    myLab.avancer()
myLab.lancer()

4.4 Les améliorations possibles

J'ai identifié deux améliorations qu'il serait intéressant d'ajouter :

  • Ajouter ViewPlayerWidget : Dans la partie 3.2.3, j'ai évoqué l'outil ViewPlayerWidget que Nicolas Hainak et Erwan Cornic ont adapté en Python. De la même manière qu'il a été ajouté au jeu des cruches, il pourrait être intéressant de l'ajouter au jeu de labyrinthe afin de pouvoir retrasser les pas de la fourmi. Dans ce cas précis, il pourrait être intéressant de revenir en arrière et pouvoir évoluer à partir de ce point (dans le jeu des cruches, j'ai pris la décision de ne pas rendre cette action possible). Pour intégrer ViewPlayerWidget, il faudrait utiliser la version du labyrinthe avec des déplacements discret suivante :
from Labyrinthe.AffichageHistorique import *
l=AffichageHistorique('Labyrinthe/lab/laby3d.json')
l.renderer
for i in range (0,3):
    l.avancer()
l.droite()
for i in range (0,4):
    l.avancer()
l.gauche()
for i in range (0,4):
    l.avancer()
l.gauche()
l.avancer()
l.droite()
l.avancer()
l.avancer()
l.droite()
for i in range (0,6):
    l.avancer()
l.droite()
for i in range (0,3):
    l.avancer()
l.monter()
l.avancer()
l.avancer()
l.horizontal()
l.droite()
  • Ajouter une fonction regarder() : Dans le jeu de programmation Laby, la fourmi possède une méthode regarder() qui renvoie True si la fourmi est face à un obstacle et False sinon. Cette méthode permet de faire sortir la fourmi du labyrinthe de manière automatique grâce à un algorithme. On pourrait intégrer une méthode semblable au labyrinthe 3D cependant peut-être serait-il intéressant de modifier un peu cette méthode afin de pouvoir regarder tous les côtés de la fourmi sans avoir à l'orienter d'abord. En effet, le labyrinthe étant en 3 dimensions, il faudrait tourner la fourmi non seulement à droite et à gauche mais également vers le haut et le bas. Permettre à la fourmi de regarder sans se tourner éviterait des manipulations longues et compliquées.

5 Des pistes à explorer dans le futur

5.1 La tentative Blockly

Blockly est un langage de programmation visuel (VPL) développé par un ingénieur Google en 2011 permettant de faire de la programmation par blocs au lieu de coder au clavier. Chaque bloc représente une instruction et ces blocs peuvents s'assembler et s'imbriquer les uns dans les autres pour former un algorithme. Il s'agit d'un outil très utilisé pour l'apprentissage de la programmation car il permet d'éviter les fautes de frappe et donc limite la frustration de l'utilisateur. Voici, ci-dessous un exemple de blockly :

%%HTML
<script src="../blockly/google-blockly/blockly_compressed.js"></script>
<script src="../blockly/google-blockly/blocks_compressed.js"></script>
<script src="../blockly/google-blockly/msg/js/en.js"></script>

<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>

  <xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
    <block type="controls_if"></block>
    <block type="logic_compare"></block>
    <block type="controls_repeat_ext"></block>
    <block type="math_number">
      <field name="NUM">123</field>
    </block>
    <block type="math_arithmetic"></block>
    <block type="text"></block>
    <block type="text_print"></block>
  </xml>

  <script>
    var demoWorkspace = Blockly.inject('blocklyDiv',
        {media: '../../media/',
         toolbox: document.getElementById('toolbox')});
  </script>

J'avais pour projet d'intégrer cet outil aux trois jeux que j'ai implémentés. Il existe d'ailleurs une version de Blockly adaptée pour Jupyter appelée Delpy. Cet outil permet de créer une fenêtre de Blockly, d'y ajouter ses propres fonctions, de créer des blocs de code et les exécuter ou générer le code Python équivalent. Il était possible, à partir de cette fenêtre, d'interargir avec le jeu cependant il fallait tout d'abord générer le code Python puis l'exécuter. Par ailleurs, Delpy n'hérite pas de la classe widget, il n'est donc pas possible de l'intégrer à une VBox pour que la visualisation du jeu et la fenêtre Blockly soient situées l'une au dessus de l'autre dans la même cellule.

Démonstration :

from Labyrinthe.AffichageHistorique import *
myLab=AffichageHistorique('Labyrinthe/lab/lab.json')
myLab.renderer
from delpy import Delpy, delpy_method
class MyDelpy(Delpy):
    def __init__(self,lab):
        super().__init__()
        self.__lab=lab
    
    @delpy_method("Labyrinthe")
    def avancer(self):
        self.__lab.avancer()
    
    @delpy_method("Labyrinthe")
    def droite(self):
        self.__lab.droite()
    
    @delpy_method("Labyrinthe")
    def gauche(self):
        self.__lab.gauche()
    
    @delpy_method("Labyrinthe")
    def monter(self):
        self.__lab.monter()
    
    @delpy_method("Labyrinthe")
    def descendre(self):
        self.__lab.descendre()
    
    @delpy_method("Labyrinthe")
    def horizontal(self):
        self.__lab.horizontal()
    
    @delpy_method("Labyrinthe")
    def reset(self):
        self.__lab.reset()
    
p = MyDelpy(myLab)
p
from delpy import Delpy


self = Delpy.get(1482220223888)

for count in range(3):
    self.avancer()
self.gauche()
for count2 in range(3):
    self.avancer()
self.droite()
for count3 in range(4):
    self.avancer()

Delpy est un outil avec un fort potentiel pour Jupyter cependant il comporte de nombreux défauts comme le fait de ne pas être un widget ou de ne pas pouvoir directement exécuter le code en cliquant sur Run quand il existe des dépendances avec du code présent dans les cellules précédentes. Un autre défaut sur le plan ergonomique est l'utilisation peu intuitive des blocs. En effet, pour générer le code au dessus, il faut assembler les blocs de la manière suivante :

Delpy for Lab

Il faut donc laisser les différents blocs séparés et non imbriqués comme l'on pourrait le penser instinctivement. En effet, si les blocs sont imbriqués, il n'y a pas de retour à la ligne entre les instructions.

Pour observer le résultat de ce code il faut cliquer sur le bouton "Generate Python Code" en bas à gauche. Un code Python semblable à la cellule de code au-dessus apparaitra et en l'éxécutant, la fourmi avancera dans la visualisation du labyrinthe.

5.2 Modifier le mode d'interaction

L'ergonomie des jeux pourrait être améliorée en modifiant les modes d'interaction. Actuellement cette interaction se fait soit avec des boutons soit avec des instructions saisies dans les cellules sous la visualisation.

Dans un premier temps, on pourrait envisager de déclencher les actions en cliquant directement sur l'image avec des clics simples ou clic-and-drop. Par exemple on pourrait envisager pour le jeux Chou-Chèvre-Loup, que le joueur fasse glisser la chèvre dans la barque pour la faire monter à bord. Ce mode d'interaction a pour avantage d'être non seulement instinctif mais aussi plus accessible pour des enfants ne sachant pas lire ou pas correctement. De cette manière, le jeu serait accessible pour des enfants de 5 à 7 ans.

On pourrait également envisager un mode d'interaction plus original, par exemple avec une manette de console. Ce mode d'interaction serait particulièrement adapté au labyrinthe 3D. A l'aide des divers joysticks de la manette, le joueur pourrait contrôller à la fois la fourmi et la caméra.

6 Conclusion et retour d'expérience

A travers les trois jeux conçus pour ce TER j'ai pu m'initier à plusieurs librairies de Jupyter : ipywidgets, ipycanvas et pythreejs. Je n'ai bien entendu pu exploiter qu'une partie de leur potentiel mais j'ai tout de même pu constater qu'il s'agit d'un excellent outil pour une programmation interactive. Un premier atout est la possibilité de coder cellule par cellule et donc de coder progressivement en testant à chaque étape le code ajouté ou modifié. En effet, il est beaucoup plus simple de tester son code sur Jupyter que sur une interface Python classique. Par ailleurs, toutes les librairies disponibles sur Jupyter offrent de nombreux outils d'aide à la visualisation et à la saisie. Grâce aux boutons, zones de saisies et sliders, on peut facilement demander à une personnes non-initiée à la programmation de rentrer les variables qu'elle désire. Et à l'aide d'autres widgets, on peut facilement créer une visualisation plus parlante qu'une liste de nombres ou qu'un texte.

Si vous êtes un étudiant voulant s'initier à la programmation en Python sur Jupyter, je vous conseillerais de commencer en reproduisant les exemples donnés dans la documentation de ipywidgets. Il s'agit d'exemples simples permettant de prendre en main les différents outils. Une fois que vous aurez en tête l'ensemble des outils à votre disposition, vous pourrez prendre un peu d'indépendance en programmant de petits algorithmes simples dont les paramètres d'entrée sont saisis à l'aide de sliders ou boutons et la sortie visualisable dans un autre widget.

En revanche si vous avez un objectif plus précis et complexe en tête, votre expérience avec Jupyter peut devenir plus compliquée. Surtout lorsque qu'aucun exemple semblable à ce que vous voulez faire n'est disponible dans la documentation. Pour des librairies moins classiques qu'ipywidgets telles que pythreejs, il existe peu d'information sur internet et la documentation est pauvre. Il faut évoluer à l'aide de la documentation de threejs (qui est pour JavaScript) et d'exemples en JavaScript donnés sur des forums. Or le passage de JavaScript à Pyhton n'est pas toujours simple. Par exemple, en threejs, il est possible d'attribuer des textures différentes pour chaque face d'un cube grâce à l'objet CubeTexture. Cet objet existe bien en pythreejs cependant il m'a été impossible de le faire fonctionner et la documentation disponible sur cet objet est trop faible pour pouvoir résoudre ce problème.

Bien entendu avec un peu de persévérence et en empruntant d'autres chemins, il est toujours possible d'obtenir ce que l'on souhaite.