- Published on
Exemple de projet de jeu en C - Cursedinal
- Authors
- Name
- Anthony Rabine
Cet article tente de décrire comment réaliser proprement un projet de programmation en langage C dans les règles de l'art. Le jeu présenté ici a été l'objet d'un projet d'étudiants en programmation suite à une série de cours de langage C. Nous tenterons donc d'expliquer en détail les attendus d'un projet et ce qui est demandé en terme de connaissances pour être à l'aise avec ce langage. C'est une sorte de correction en sommes !
Parenthèse sur la pédagogie
L'objectif de vos enseignants est de vous former, vous faire passer leur savoir. Ils vous apprennent votre futur métier. Pour cela, ils font appel à la pédagogie : ils vont créer des situations, des travaux pour assurer le transfert du savoir.
Donc, ce n'est pas pour vous embêter si on vous demande plusieurs types de travaux ; ils ont tous un objectif derrière. Gardez cela en tête et surtout la finalité.
Dans le cadre des études informatique de type programmation, on va former généralement des ingénieurs. Un ingénieur, selon ma définition, est un technicien qui possède des compétences plus transversales :
- humaines : relations avec son équipe, supervision d'autres développeurs
- pédagogiques (aussi!): savoir présenter facilement et rapidement un problème, une architecture
- rédactionneles : mail, documentation, présentations powerpoint, il doit savoir rédiger en termes clairs, présenter un avancement
- organisationnelles : plannifier son développement, débusquer les parties compliquées
J'ajoute donc à la fin de chaque chapitre un dernier paragraphe expliquant l'objectif pédagogique sur ce que l'on vous demande ; en comprenant pourquoi l'enseignant vous demande de réaliser un travail barbant, il est mieux accepté car l'objectif est connu.
L'attendu : découverte du cahier des charges
La découverte et la compréhension d'un sujet fait parti intégrante du processus d'apprentissage. On semande ainsi à l'étdudiant se se comporter comme il le fera en entreprise plus tard, en tant qu'ingénieur informaticien, qui découvre le cahier des charges d'un client (liste non exhaustive) :
- Bien tout lire
- Estimer le temps de développement des différentes parties
- Identifier les parties difficiles
- Anticiper et plannifier le travail
- Comprendre les rendus (livrables)
Bien entendu, la majorité des étudiants travailleront au dernier moment. Attention, c'est le piège lorsque l'on apprend un nouveau langage : on se retrouve très vite bloqué, perdant plusieurs heures à trouver l'origine d'un bug car on ne maîtrise et on ne comprend pas le langage.
Les classiques entendus le jour de la présentation orale : « Monsieur ! Je suis resté bloqué des heures à cause des pointeurs ! » Les fameux pointeurs coupables et non leur utilisation !
« Mais, tu n'as pas rendu de documentation technique ?
- Non parce que je ne savais pas quoi mettre dedans !! »
Ok, voyons ce qui leur avait été demandé : de développer un clone du jeu Cardinal Chains dans un terminal (console) ou graphiquement pour les plus courageux (en utilisant une librairie tierce genre SDL2 ou Gtk).
Le jeu doit avoir plusieurs niveaux dans des fichiers séparés de l'exécutable et dès qu'un niveau est terminé on passe au suivant. L'ensemble tourne dans une console, avec des couleurs, une difficulté croissante et plusieurs chaînes. Une interface utilisateur minimale permet de basculer entre les chaînes, choisir un niveau, quitter ... bref, tout ce que l'on attend d'un vrai jeu.
Le code source doit être livré dans une archive contenant le code source, une documentation technique et une documentation utilisateur.
Voilà en simplifié le cahier des charges. La livraison elle seule est tout un roman, j'en ai vu des erreurs ! Je vais y consacrer un chapître à la fin de cet article.
Les grandes erreurs obervées
Globalement, les étudiants ont eu beaucoup de mal concernant les points suivants :
- La liste des livrables : oublis, incompréhension, faible qualité documentaire
- Le respect du cahier des charges : une fonctions ne leur plaît pas ? Hop ! elle passe à la trappe, tant pis si le client ne l'a pas
- La partie documentaire technique : comment documenter du code, comment l'expliquer
- La partie présentation orale et Power Point : trop de détails, trop de code présenté
- La qualité du code : aucun commentaire, variables aux noms exotiques, pas de séparation en modules, bugs de pointeurs, oublis de désallocation de mémoire
- Globalement des projets un peu livré à la méthode LaRache
Bref, les élèves de première année ne savent pas comment organiser les livraisons, écire des documents professionnels et présenter leur travail. C'est normal, ils sont là pour apprendre, mais certaines compétences que je pensais acquises ne l'étaient pas du tout ! (genre Word ...)
Ce que l'on attend d'un programme en langage C
Je vais tenter de décrire ici ce que j'ai appris en terme d'organisation de code et d'architecture statique en langage C depuis que je développe. Ce sont des connaissances qui ne font généralement pas parties du programme enseigné, ce dernier se contentant de se focaliser sur la grammaire du langage.
Globalement
- Il doit être organisé sous la forme de modules ; un module est un couple .c/.h regroupant les fonctions d'une même entité logique (l'affichage, le moteur applicatif...)
- Bien séparer ce qui doit être dans le fichier d'en-tête et dans le .c
- Il doit respecter des règles de codage et avoir une bonne proportion de commentaires. Pas nécessairement partout, mais essentiellement au niveau des interfaces
La NASA a ses propres règles de codage (http://web.archive.org/web/20190125125043/http://homepages.inf.ed.ac.uk/dts/pm/Papers/nasa-c-style.pdf)
Utilisez la votre, prendez des exemples connus dans la nature (Linux, Gnome, GNU, Microsoft ...).
Un autre exemple avec de bonnes justifications : indhill-cstyle.pdf.
Voici un exemple d'organisation par modules pour notre petit jeu en lignes de commandes :
- Un module pour l'affichage (gfx)
- Un module manipulant les fichiers (sauvegarde, niveaux)
- le moteur de jeu, totalement indépendant (cursedinal)
Les contextes
La modélisation par structures est la base du langage C qui est de type procédural. Les fonctions vont travailler sur ce que l'on nomme des contextes : des structures regroupant les données nécessaires à un module particuler (moteur de jeu, graphisme, réseau ...).
Les gros avantages de travailler de base avec des structures sont les suivants :
- Il suffit de passer un pointeur vers un contexte pour passer toutes les variables nécessaires en même temps, en plus d'être léger (un pointeur prend 4 ou 8 octets en mémoire selon les plateformes)
- La structure est évolutive : ajoutez, renommez, supprimez des variables, le prototype des fonctions ne changeront pas.
- La déclaration en mémoire d'une variable de contexte se fait en dehors du module C : c'est au rôle de l'appelant (celui qui appelle, l'extérieur) de réserver quelque part ce contexte, où ça l'arrange le mieux.
- Il est possible de gérer plusieurs contextes : vos fonctions, si elles se limitent à utiliser que les pariables de ce contexte, sont ré-entrentent par design et peuvent ainsi être appelées à plusieurs endroit, avec des contextes différents.
Donc, ne lésinez pas, de base sortez la structure pour représenter la plupart des éléments de votre logiciel, même s'il possède une petite variable. Demain, l'évolution sera plus aisée.
Commençons par la fin : quoi livrer et comment
Posons nous dès le départ sur l'objectif du projet et réfléchissons sur les livrables, c'est à dire ce que l'on va envoyer au Client. Dans le cadre de l'école, le Client c'est le professeur. Notez que dans le cadre professionnel le Client peut aussi être interne à votre entreprise, par exemple l'équipe chargée des tests.
Connaître les fichiers à livrer va nous aider à plannifier les différents travaux : le code, mais aussi la documentation, à ne pas négliger, ainsi que les éventuels tests (à coder aussi !).
Lister les livrables va nous permettre de commencer à réfléchir sur les outils :
- Quel(s) système(s) d'exploitation(s) doit-on utiliser (que veut le Client ?)
- Quel IDE nous allons utiliser ?
- Quels tests allons-nous devoir faire manuellement au à développer ?
- Où va-t-on héberger le code ? Gihub ? Gitlab ? autre ?
- Quelles documentations écrire ? Quel format et quel logiciel ?
Le code source
Placez tous les fichiers dans un répertoire dédié (nommé 'src' ou 'sources', comme toujours un nom explicite). On va demander à tourver plusieurs modules (couples .c / .h) :
- L'affichage
- Le moteur de jeu
- Eventuellement un module sur les I/O (chargement des niveaux à partir d'un fichier, sauvegarde)
- Un fichier 'main.c' qui contiendra la fonction d'entrée (main())
- Un fichier de projet de construction (CMake, Make, script shell...)
Le fichier main contenant :
- init
- game loop
- nettoyage mémoire et code de sortie
Le binaire
Un répertoire contenant le binaire (exécutable) et fichiers associés (images, sons, fichiers de configuration ...). S'il nécessite des DLL, les placer dans ce répertoire.
Astuce : Votre ordinateur de développeur n'est pas représentatif d'un système lambda ; vous possédez toutes les librairies pour compiler votre code. L'idée est de tester votre exécutable sur un OS vierge, fraichement installé, voire un peu différent (par exemple une Fedora si vous développez sur Ubuntu).
Le fichier de projet
La somme des fichiers .c et .h de votre projet ne suffit pas à savoir comment construire votre projet (enfin, pas toujours). Il faut, comme beaucoup de langage, un fichier de projet ou constructeur d'application.
Certains IDE jouent le rôle de constructeur d'application mais ce n'est pas l'idéal ; il faudra installer le même logiciel (lourd) et s'il est propriétaire et payant ... ça sera difficile pour les autres de reconstruire votre application. De plus, en milieu professionnel, il n'est pas rare d'utiliser des machines de "build" (construction) automatisé totalement sans interface graphique.
En C/C++, généralement on va séparer l'IDE, qui sert à éditer le code dans de bonne conditions, du constructeur de projet. Il existe plusieurs outils assez populaires :
- Make (et sont fichier Makefile), l'ancêtre
- CMake, le successeur de Make, plus moderne
Voici un exemple de CMakeLists.txt classique, propre et simple :
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(cursedinal VERSION 1.0)
find_package(Curses REQUIRED)
include_directories(${CURSES_INCLUDE_DIR})
set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
# add the executable
add_executable(${PROJECT_NAME}
src/main.c
src/gfx.c
src/gfx.h
src/cursedinal.c
src/cursedinal.h
src/files_io.c
src/files_io.h
)
target_link_libraries(${PROJECT_NAME} ${CURSES_LIBRARIES} )
Points bonus
Éventuellement, pour des points bonus :
- Un projet et du code de tests unitaires
- Un README.md à la racine
- Un fichier de licence : toujours choisir une licence pour votre code
- Une archive de livraison bien organisée
- Une documentation utilisateur (au format PDF)
- Une documentation technique (au format PDF)
- Tout est en anglais, c'est parfait
- Le code compile sans Warning sur au moins deux systèmes bien différents (Windows et un Unix)
Voici un exemple classique d'organisation de votre archive :
Points malus
- Ne livrez pas les fichiers générés par le compilateur (fichiers objets .o, fichiers temporaires ...)
- L'archive est mal rangée, on doit fouiller pour trouver ce que l'on veut
- Il manque des éléments prévus
- des fichiers mal nommés (nom confu, ou avec des restes de copier/coller du genre ma_doc(1).pdf)
- On doit fouiller pour trouver la fonction 'main()'
- Mélange français/anglais un peu partout
- Le programme le compile pas
- Trop de warnings affichés
Objectif pédagogique : Le produit livré sera la première chose que votre client verra. Il convient de soigner sa présentation, cela montrera votre sérieux. Généralement, le même sérieux sera apporté à tous les éléments donc vorte Client sera dans de bonnes dispositions.
Comment bien commenter mon code ?
Alors concernant les commentaires, je vous donne mon avis qui n'est que le miens. Selon ma conception, un commentaire est nécessaire pour aider l'autre.
L'autre c'est :
- Vous dans 6 mois
- Un collègue de boulot ou de projet
- Votre successeur lorsque vous démissionnerez
- Les utilisateurs de votre API (code)
Donc, déjà si vous appelez convenablement vos fonctions et variables, il n'y a pas trop besoin de commentaire. Ensuite, le classique : ne commentez pas le code trivial (i++; // incrémentation de i, sans blague ?).
Décrivez donc en priorité toutes les interfaces publiques : les fonctions qui sont dans le header et qui sont utilisées par les autres modules.
Vous pouvez utiliser la syntaxe Doxygen.
Le reste, l'implémentation, commentez uniquement les parties difficiles.
Voici un exemple de fichier C selon moi bien écrit :
- Séparation de diverses zones
- Les inclusions en haut
- Un commentaire au début de chaque fichier avec toutes les informations nécessaires
Voici l'en-tête associée :
- Les fonctions sont bien documentées, ici avec Doxygen
- Usage de structures de contextes
- Notez la présence des header guards (#ifndef GFX_H...)
À l'aide, c'est quoi une doc utilisateur ?
Mettez-vous à la place de tati Ginette qui ne connaît rien en informatique. Pour cette personne, rien n'est évident, de la décompression d'une archive au lancement de l'exécutable. C'est une notice d'utilisation, la même notice que vous trouvez avec tout appareil neuf que vous achetez.
Une équipe d'étudiants a créé un manuel en une seule page. C'est parfait, c'est concis, c'est exactement ce qui est demandé.
Objectif pédagogique : Savoir expliquer avec des mots simple, non techniques et de façon concise votre logiciel. Typiquement ce que vous aurez à faire professionnellement lorsque votre interlocuteur ne sera pas ingénieur informaticien et ce sera souvent votre Client.
Comment modéliser un langage procédural comme le C ?
Les langages objets ont leur standard de représentation "graphique" : l'UML. Même si je ne suis pas ultra fan, cela est pratique pour documenter un code de façon "haut niveau", c'est-à-dire sans entrer dans les détails, de façon macroscopique. Surtout lorsque vous devez expliquer l'architecture d'un logiciel rapidement à un collègue, c'est pratique. Le reste, le détail, pour moi c'est de la perte de temps surtout que le code d'un logiciel évolue très vite.
Donc, oui, l'UML peut servir à décrire un programme C !
- Les diagrammes de classe documentent votre API : les structures (classe sans méthode !)
- Utilisez les relations "use" entre les modules
- Une structure en contient une autre ? C'est de la composition !
- Un pointeur dans une structure qui appartient à un autre module ? Une aggrégation !
- Les diagrammes de séquence ont toute leur place ; c'est la modélisation dynamique de votre logiciel, pas de problème pour le C
- Les diagrammes de package montrent les modules C, les librairies et leurs relations/dépendances
- Diagramme d'activité : modélise les algorithmes importants ou un fonctionnement haut niveau
- Diagrammes de machine à état : bon ... évidemment on peut en faire !
La documentation technique : votre rapport (ou mémoire)
Il porte des noms différents : rapport, mémoire. C'est votre présentation en plus précis. Ce document suit exactement les mêmes chapitres. Le but est de vous faire réfléchir, à tête reposée, sur les éléments de votre projet et apporter un regard critique.
On y trouve :
- Une introduction / objectifs
- Une présentation technique de votre projet (à haut niveau !!)
- Choix techniques (outils, architecture), critique, analyse et bilan
- Une conclusion et une ouverture
- Des références où trouver le code par exemple (github...)
Concernant la forme, utilisez un modèle de document convenablement paramétré. Un epeu de professionalisme ! Dans le monde du travail, vous aurez à utiliser les modèles de votre équipe et de votre entreprise; c'est une question d'image, de transmission des connaissances, d'archivage et de qualité.
Un modèle de document doit avoir :
- Une page de garde complète (titre, date, contexte, nom/prénom, lieu)
- Chaque page avec en-tête et pied de page (reprise des éléments de la page de garde + le chapitre en cours + la page / pages totales)
- Un sommaire
- Puis les chapitres
- Éventuellement une bibliographie, des références et la listes des tables/illustrations si votre rapport est épais
Les informations redondantes sur toutes les feuilles ont plusieurs utilités notamment celle de retrouver l'auteur d'une feuille perdue.
Objectif pédagogique : Laisser une trace de votre savoir à l'entreprise, ou à vous même. Il est plus facile de prendre en main un code complexe en lisant d'abord la documentation technique.
Dans ma présentation (slides, diapositives), je mets quoi ?
Voici quelques points importants à assimiler pour produire des slides de bonne qualité. L'examinateur devine assez vite la qualité de ce qu'il va voir à partir de la page de garde et du sommaire (quand il existe !!).
Quand rédiger la présentation
Dans l'idée, rédigez votre présentation en toute fin : ce n'est qu'un résumé de votre documentation technique encore plus abstraite.
De plus, il vous faudra des captures d'écran de votre logiciel donc, mieux vaut repousser.
Combien de pages
Généralement, il faut passer une à deux minutes par écran de slide maximum. Si votre exposé oral dure 15 minutes, il vous faudra environ 10 slides, pas plus ! (hors sommaire et denière page de Q&A)
Entrainez-vous deux ou trois fois en conditions réelles en vous chronométrant. Peaufinez votre phrases : simples, concises, allez droit à l'objectif sans utiliser de vocabulaire trop technique.
Architecture de votre présentation.
Il faut aller du général au particulier et terminer par une ouverture. Une sorte de double entonoir. Gardez cette recette pour toutes vos présentation, à vie !
Il doit contenir :
- Une page de garde (nom-prénom, titre, date, contexte (entreprise, école...))
- Un sommaire
- Un chapitrage avec sur chaque slide : en-tête (chapitre en cours) et pied de page (date, page courante, pages totales)
- Une conclusion
- Un dernier slide de fin (+ remerciements, Q&A) sans forcément dire autre chose qu'une phrase
Le sommaire
Voici un exemple de sommaire assez classique :
Le contenu
Pas de code.
Non, aucun code, quasiment. Au secours. Personne ne veut voir sur un slide une fonction de 300 lignes écrit en tout petit avec des fautes d'orthographe, de nommage et des bugs gros comme une maison.
Pourquoi ? Parce que ce n'est pas intéressant pour la partie "orale" de votre exposé. Le code sera revu et noté à part. Ce qui va intéresser le jury est la réflexion, votre analyse, l'architecture du code. Élevez-vous. Éloignez-vous de la technique. Regardez votre code vue d'avion : on ne voit pas les détails mais le fonctionnement global.
Lisez le chapitre sur la modélisation d'un code en langage C pour savoir quoi mettre.
Exemple de contenu :
- Vous allez commencer par décrire l'objectif, le cahier des charges (1 slide)
- Ensuite, décrivez votre planning, organisation de travail, répartition des tâches (1 slide)
- Présentez l'architecture générale (diagrammes) (2 slides)
- Présentez l'architecture particulière d'un ou deux points intéressants (2 slides)
- Présentez le contenu de la livraison (1 slide)
Conclusion / perspective
Exemple d'éléments à placer dans votre conclusion :
- Le résultat obtenu : ça marche ou pas
- La complétude du cahier des charges : complet ou il manque des fonctions ?
- Les difficultés rencontrées
- Les évolutions possibles
- Où trouver le code (le cas échéant)
- L'usage potentiel de votre travail
Points bonus
- Vocabulaire simple, explications claires et précises
- Des captures d'écran
- Une modélisation haut niveau
- Une description des outils utilisés (compilateur, Git)
- Une description sur l'oganisation / répartition des tâches
- Un gantt / planning simplifié
Objectif pédagogique : Savoir décrire le fonctionnement de votre logiciel à quelqu'un. Par exemple, à des collègues de travail qui arrivent sur votre projet, ou à un nouvel embauché. Votre auditoire est relativement technique mais il va s'intéresser à avoir une vue globale du système, il verra les détails seuls par la suite.
De l'importance de l'ergonomie de votre programme
Ne pas négliger l'interface utilisateur, surtout pour une application en ligne de commandes !
- Gérer les minuscules et majuscules pour les lettres si l'utilisateur doit appuyer sur des touches du clavier
- Utiliser les touches classiques (flèches ou ZQSD pour l'orientation, echap pour quitter, enter pour valider, F1 pour l'aide ...)
- Listez toujours les touches possibles et leur action
- Un peu de couleur ! c'est possible avec un terminal Windows ou Unix ...
Conclusion
Voici un exemple de résultat attentu vous octroyant une bonne note :