CSC 3102 – Introduction aux systèmes d’exploitation

Portail informatique

CSC3102 – Contrôle Final 1 – Année 2017/2018

Consignes :
  • Vous devez exécuter vos scripts pour les tester. Si les scripts ne peuvent pas s'exécuter ou ne produisent pas le résultat escompté, vous perdrez des points ;
  • Vous pouvez vous appuyer sur l'annexe shell qui se trouve ici ;
  • Pour faire le contrôle, vous aurez aussi besoin des scripts et .
  • Le barème est donné à titre indicatif.



Sportifs dans l'âme, nous vous proposons aujourd'hui de jouer au ping-pong. Pour cela, trois exercices sont à réaliser. Le premier est un exercice préparatoire de l'environnement. Le second permet à deux joueurs de disputer une partie de ping-pong. Le troisième a pour but de mettre en place un service de mise en relation de joueurs au sein d'une association sportive.

Vous devez normalement faire les exercices dans l'ordre, mais, si vous bloquez, vous pouvez passer à l'exercice 3 dès que vous avez répondu à la question 2.a.

Préparation (2 points, ∼2mn)

À la racine de votre compte, créez un répertoire Nom_Prenom, où Nom est votre nom et Prenom, votre prénom. Ce répertoire est le répertoire racine de ce que vous devez rendre à la fin du contrôle final. Ainsi, vous devez y placer vos scripts, en suivant l'arborescence qui vous sera décrite dans les exercices.

À la fin du contrôle, vous devez rendre une archive (i.e. un fichier dont l'extension est tgz ou tar.gz) contenant exactement cette arborescence. Nous vous rappelons que vous pouvez créer cette archive de la façon suivante :
$ cd ; tar -f Nom_Prenom.tgz -cz Nom_Prenom

Ping-pong party !!! (12 points, ∼45mn)

Le but de cet exercice est de créer une partie de ping-pong entre deux joueurs identifiés, le tout dans un script pingpongParty.sh, que vous placez à la racine du répertoire créé dans l'exercice préparatoire.

Une partie se déroule en deux étapes :
  • la première étape initialise la partie grâce à un protocole d'accord ;
  • la seconde étape modélise les échanges de balles les joueurs. Ces échanges sont en réalité ni plus ni moins que des échanges de messages "Ping" et "Pong". La partie s'achève quand un des deux joueurs atteint un score fixé.

Chaque étape vous est expliquée par la suite. Pour vous donner une idée, voici le déroulement d'une partie disputée entre deux stars du ping-pong français gatien et lebesson (auprès de qui nous nous excusons pour la piètre qualité des échanges que nous leur faisons effectuer) :
$ ./pingpongParty.sh gatien lebesson 27628 joined gatien(27625) ready to play with lebesson(27628) Pong Pong Point gagné ! Mon score est de 1 point. Pong Ping Point gagné ! Mon score est de 2 points. Victoire!
$ ./pingpongParty.sh lebesson gatien lebesson(27628) ready to play with gatien(27625) Ping Ping Ping Point gagné ! Mon score est de 1 point. Ping Ping Défaite :(

(5 points) - Étape 1 - Are you ready ?


Cette première étape permet soit de démarrer une partie soit de rejoindre une partie. Ce rôle est déterminé en fonction du nombre de paramètres fournis au lancement du script :
  • s'il y a un seul paramètre, le processus lancé est en charge de démarrer la partie (c'est aussi lui qui ouvrira les échanges en envoyant un "Ping" à l'étape suivante). Le paramètre correspond au nom du joueur ;
  • s'il y a deux paramètres, le processus lancé correspond au joueur qui rejoint la partie (c'est le joueur "Pong"). Le premier paramètre correspond au nom du joueur, le second au nom de votre adversaire qui a au préalable initialisé la partie ;
  • sinon le nombre de paramètres est incorrect et le script doit sortir sur une erreur après avoir affiché un message d'erreur type "Paramètres incorrects" sur la sortie standard d'erreur.

Une fois le nombre de paramètres vérifié, les deux joueurs exécutent un protocole permettant d'initialiser la partie :
  • Côté joueur "Ping", vous devez :
    • ouvrir un tube nommé servant de canal de communication entre l'adversaire et le joueur, que vous nommerez $nom_joueur.pipe (n'oubliez pas de rediriger les flux de votre processus sur ce tube grâce à la commande exec) ;
    • se mettre en attente d'un message sur ce tube. Le message reçu contiendra le nom (que vous récupérez dans une variable nommée nom_adversaire) et le pid (récupéré dans une variable du même nom) du joueur adversaire rejoignant la partie. Lors de la réception de message, le script doit afficher "$nom_adversaire joined" sur la sortie standard ;
    • envoyer en retour votre pid sur le canal de communication ouvert par votre adversaire (lisez les instructions relatives au côté Pong pour savoir comment ce canal est ouvert) ;
  • Côté joueur "Pong", vous devez :
    • vérifier que le canal de communication correspondant à la partie que vous voulez rejoindre existe (si vous avez lu avec attention les instructions, vous en avez déduit que ce canal est un tube nommé qui doit se nommer nom_adversaire.pipe, le nom de l'adversaire étant donné en second paramètre de votre script). Si ce n'est pas le cas, votre script doit afficher un message annonçant l'erreur et interrompre le script ;
    • ouvrir un tube nommé $nom_joueur.pipe (de nouveau, n'oubliez pas l'appel à la commande exec) ;
    • envoyer un message contenant votre nom et votre pid sur le tube ouvert par le joueur adversaire de la partie afin de lui indiquer que vous rejoignez la partie ;
    • récupérer sur votre canal le pid de votre adversaire.
  • Que ce soit du côté joueur "Ping" ou joueur "Pong", vous devez afficher un message "$joueur($pid_joueur) ready to play with $adversaire($pid_adversaire)" afin d'être sûr que les deux deux joueurs ont bien achevé la phase d'initialisation.

À titre d'illustration, voici un exemple de scénario de lancement de partie entre les joueurs gatien et lebesson :
$./pingpong.sh gatien lebesson joined gatien (15432) ready to play with lebesson(16754) $
$./pingpong.sh lebesson gatien lebesson(16754) ready to play with gatien(15432) $

La partie a maintenant démarré et vous pouvez passer à la prochaine étape ! Bravo !

(2 points) - Étape 2 (1/3) - Goooooooooo!


Les joueurs disputent à présent une partie enflammée à base d'échanges de ping et de pong transmis au travers des deux canaux de communication ouverts à l'étape précédente.
Complétez votre script pingpongParty.sh pour que :
  • l'initiateur de la partie engage en envoyant un premier message "Ping" sur le canal de son adversaire ;
  • ensuite, les deux joueurs entrent dans une même boucle infinie où il faudra lire depuis son canal, renvoyer "Pong" (respectivement "Ping") si le message reçu est un "Ping" (respectivement "Pong"). Chaque joueur affiche en plus le message envoyé sur la sortie standard afin de pouvoir suivre les échanges (Note : l'utilisation d'une commande spécifique au dédoublement de la redirection de flux sera appréciée).

(3 points) - Étape 2 (2/3) - Décompte des points


Parce que les joueurs ne sont pas des robots, il existe une probabilité non nulle qu'ils loupent la balle.

Afin de déterminer si un échange se poursuit ou non, vous devez modifier votre script de manière à ce qu'à chaque échange (c'est-à-dire à chaque réception), un nombre aléatoire soit tiré. Pour cela, utilisez la commande interne à bash $RANDOM qui donne une valeur aléatoire entre 0 et 32767. Si le premier chiffre du nombre tiré est 3 alors on considère que la balle est loupée, et vous devez envoyer un message "missed" en retour à l'adversaire.

Le gain de point étant maintenant détecté, il reste à compter les points gagnés. Pour cela, vous devez modifier votre script de manière à différencier les messages pouvant être reçus. Si vous recevez un message "missed", incrémentez votre score (stocké dans une variable score), affichez-le et engagez un nouvel échange en envoyant un "Ping" à votre adversaire.

(2 points) - Étape 2 (3/3) - Victoire !


La partie s'achève quand un des deux joueurs atteint un score fixé par une variable victoire. Une partie de ping-pong se jouant en 11 points, fixez cette variable à 11 (la variable avait été fixée à 2 pour l'illustration du début d'exercice pour des questions de lisibilité).

Chaque joueur comptant son propre score, le joueur vainqueur doit informer son adversaire afin de clore proprement la rencontre. Pour cela,
  • Côté gagnant :
    • envoyez un signal (au sens propre d'un point de vue système d'exploitation) notifiant sa défaite à votre adversaire ;
    • affichez un message "Victoire !" sur votre sortie standard ;
    • fermez votre canal de communication ;
    • quittez le script ;
  • Côté perdant, sur réception du signal :
    • fermez votre canal de communication ;
    • affichez un message "Défaite :(" sur votre sortie standard ;
    • quittez le script.

Association sportive de Ping-pong (6 points, ∼30mn)

L'association sportive de ping-pong locale se démène pour apporter joie et dynamisme à ses membres. Pour cela, elle porte assistance aux joueurs n'ayant pas d'adversaire en mettant à disposition un service de mise en relation de joueurs.

(2 points) - Organisation des informations internes


Mettez en place l'arborescence permettant l'organisation des différentes données internes de l'association sportive suivant le schéma suivant (Nom_Prenom correspondant au répertoire mis en place dans l'exercice 1) :
~ - Nom_Prenom - Assos - Scripts - pingpongParty.sh \ \ letsPlay.sh \ \ EnCours - partiesOuvertes.txt

(4 points) - Recherche d'un partenaire


Afin de porter assistance aux joueurs esseulés, l'association propose de maintenir une liste des joueurs en attente d'adversaire dans le fichier partiesOuvertes.txt (chaque ligne du fichier correspond au nom d'un joueur en attente d'adversaire). Ainsi, il est possible de venir consulter cette liste afin de débuter une partie au plus vite. Même si le le script que vous allez écrire dans cet exercice ne permet de n'avoir qu'un seul joueur en attente à la fois, il est facile d'imaginer des scénarios où plusieurs joueurs sont en attente : par exemple, un joueur peut absolument vouloir jouer sur un terrain ombragé quitte à attendre, un joueur peut ne pas vouloir rencontrer de joueurs ayant un niveau trop élevé, etc. Ainsi, nous vous demandons de considérer que le fichier partiesOuvertes.txt doit pouvoir contenir plusieurs lignes.

À l'emplacement décrit dans l'arborescence de la question précédente, ajoutez un script, nommé letsPlay.sh, qui :
  • prend en paramètre le nom du joueur voulant jouer ;
  • consulte le fichier partiesOuvertes.txt dont l'emplacement est décrit dans l'arborescence mise en place précédemment. Si le fichier est vide, lancez une partie en tant qu'initiateur grâce au script écrit à l'exercice précédent et enregistrer la partie comme étant en attente en l'ajoutant à la fin du fichier partiesOuvertes.txt. Sinon, récupérez le premier joueur dans la liste (i.e. la première ligne du fichier), le supprimez et rejoignez sa partie.

Votre script letsPlay.sh ne doit pas attendre la fin de la partie de ping-pong pour se terminer.

Pensez que plusieurs joueurs peuvent essayer de s'enregistrer simultanément dans le fichier partiesOuvertes.txt.