L'application de Quiz synchrone

Le périmètre fonctionnel de l’application à développer est celui d’un jeu multi-joueurs sur des questions de culture générale, comme "Questions pour un champion". L’application doit donc poser une question et laisser à l'ensemble des joueurs un temps limité pour y répondre de manière synchrone, avant de lancer la question suivante. 
Si, par exemple, 10 millions de joueurs disposent de 5 secondes pour répondre à chaque question, il est clair que l’application doit supporter une très forte montée en charge...

la suite de cette page présente en détail les fonctions à développer.

Cinématique de l'application

L'application sera testée au travers de ses API.
Elle proposera aussi une interface homme machine minimaliste en HTML de base pour pouvoir la tester en mode manuel.
Dans le cas où il ne serait pas possible de départager deux équipes gagnantes en se basant sur le nombre d’utilisateurs, la qualité ergonomique de cette interface Web servira à les départager (cf. "comment on gagne ?").

Les user stories :
  • en tant que jury USI, je peux créer jusqu'à 1 millions de joueurs (prénom, nom, email, mot de passe) via l'API ./user/ en moins d'1 heure.
  • en tant que jury USI, je peux créer une nouvelle partie via l'API ./game/ en précisant les N questions, les NxM réponses, le temps T pour répondre, le nombre J de joueurs pour la partie. Pour cela, je m'authentifie via une clef fournie par l'équipe (cf. Google Maps).
  • en tant que joueur, je peux m'authentifier via email/mot de passe, je reçois alors un cookie de session non persistant.
  • en tant que joueur, j'attends que l'application me transmette la première question (l'application attend que tous les joueurs soient authentifiés pour m'envoyer cette question).
  • en tant que joueur, je dispose de T secondes pour choisir une réponse parmi les M qui me sont proposées.
  • en tant que joueur, je reçois le résultat de ma réponse, mon score,
  • en tant que joueur, j'attends que l'application me transmette la seconde question
  • ...
  • à la fin de la partie (N questions), en tant que joueur je demande mon classement. Je reçois la liste des 100 premiers, la liste des 50 joueurs classés avant moi, la liste des 50 joueurs classés après mois

Valeurs de référence : N = 20 / M = 5 / T = 10
La valeur de J variera entre 0 et 1 million pendant le protocole de test (voir cette page).

le story board pour les interfaces Web :

Architecture de l'application


L'architecture de votre application est à créer par vous, selon vos préférences et expertises.
Vous pouvez construire votre "stack applicative" à partir des technologies de votre choix, comme par exemple des bases NoSQL (MongoDB, Cassandra, etc.), des technologies de Cache dynamique (MemCache), des technologies de map/reduce (Hadoop) pour calculer les résultats, une logique applicative en Java/Python/Erlang/C/Ruby ou même en shell, etc. Bien entendu, vous ne pourrez pas recourir à des services tiers, hébergés en dehors de votre machine virtuelle.
Une seule contrainte est imposée : la faire tourner sur la plateforme Linux fournie par VMware et Steria

Cette architecture devra scaler horizontalement afin de tirer au mieux parti des machines virtuelles fournies par la plateforme : 5 machines virtuelles au début du Challenge, 20 machines virtuelles si vous faites parti des finalistes.

Votre application doit proposer des API respectant le style REST et dialoguant en utilisant le format JSON. Cette API nous permettra de tester la montée en charge des différentes applications candidates avec le même injecteur (qui vous est fourni ici).
Les API suivantes doivent être implémentées (les formats JSON à implémenter sont disponibles ici):
  • yourgame.com/api/user/
  • yourgame.com/api/game/
  • yourgame.com/api/login/
  • yourgame.com/api/question/
  • yourgame.com/api/answer/
  • yourgame.com/api/ranking/

le diagramme de séquence présente l'appel aux API :

 

Persistence des données


Les données suivantes doivent être sauvegardées - accessibles après un redémarrage des machines virtuelles :
  • données utilisateurs : nom, prénom, mail, password
  • paramètres d'un jeu : liste des questions, réponses possibles et bonnes réponses, seuil de déclenchement (nombre d'utilisateurs) d'une partie ...
  • historique du jeu précédent : réponses fournies par chaque utilisateur à chaque question, scores finaux et classements finaux

Calcul du score


Le calcul du score est effectué à chaque réponse reçue. Les règles de scoring sont les suivantes
  • Le score pour une question n est : Score(n) = Score(n-1) + Valeur(n) + Bonus. Score(n-1) est le score précédent, Valeur(n) est la valeur de la bonne réponse (voir ci-après) et Bonus est contextuel (voir ci-après)
  • La valeur d'une question n (Valeur(n)) est 1 pour les 5 premières questions, 5 pour les questions 6 à 10, 10 pour les questions 11 à 15, 15 pour les questions 16 à 20, ... 
  • Le bonus est attribué par rapport au nombre de bonnes réponses successives : 1 point pour 2 bonnes réponses successives, 2 points pour 3 bonnes réponses successives, ..., 19 points pour 20 bonnes réponses successives. Ce bonus est calculé et attribué à chaque calcul de score (soit à chaque réponse reçue).
Exemple : 

 index de la question     Valeur de la questionRéponse fournie Bonus Score
 1 1Faux 0 0 
 2 1Vrai 0 1 
 3 1Vrai  1 3
 4 1Faux  0 3      
 5     1Vrai  0  4
 6 5Vrai  1 10
 7 5Vrai  2 17
 8 5Faux  0 17
 9 5Vrai  0 22
 10 5Faux  0 22
 11 10Faux  0 22
 12 10Faux  0 22
 13 10Faux  0 22
 14 10Vrai  0 32
 15 10Faux  0 32
 16 15Vrai  0 47
 17 15Faux  0 47
 18 15Vrai  0 62
 19 15Vrai  1 78
 20 15Vrai  2 95

Le score final est de 95 points.


SLA


Tous les appels qui ne reposent pas sur du long-polling doivent avoir un temps de réponse inférieur à 350 ms.

Resilience


L'application doit être tolérante aux fautes. C'est pourquoi lors des tests, nous comptons arrêter un service de manière aléatoire.

Quel browser ?


L'IHM doit être compatible avec Firefox 3.5 ou équivalent.

Spécification techniques des APIs

Elles sont de type REST et manipulent des données au format JSON.


Création d'un utilisateur

  • URI : .../api/user
  • méthode POST
  • Paramètre d'entrée
{
"firstname" : "string",
"lastname" : "string",
"mail" : "string",
"password" : "string"
}
  • Codes de retour
    • OK : CREATED 201
    • Erreur : 400
  • Commentaires : 
    • si un utilisateur ayant la même adresse mail existe déjà, une erreur est retournée.
    • les longueurs min/max sont les suivantes : de 2 à 50 caractères pour nom, prénom, mail et password.

Génération d'une partie

  • URI : .../api/game
  • méthode POST
  • Paramètres d'entrée
{
"authentication_key" : "string",
"parameters" : "string"
}
  • Codes de retour
    • OK : CREATED 201
    • Clé d'authentification non reconnue : 401
    • Autre erreur : 400
  • Commentaires
    • Parameters est une chaine JSON qui représente le contenu d'un fichier xml pour la configuration de la partie. Parmi les éléments de ce fichier, nous avons:
      • logintimeout : durée maximale du long polling (GET /api/question/1) en secondes. Au bout de cette durée, le serveur répond à toutes les requêtes en attente, si il ne l'a pas déjà fait. Voir la page de séquences.
      • synchrotime : durée entre la fin de la période de réponse pour une question N (POST /api/answer/N) et le retour synchrone des appels à une question N+1 (GET /api/question/N+1) en secondes. Voir la page de séquences.
      • nbusersthreshold : le nombre d'utilisateurs qui doit être atteint pour que la partie commence.
      • questiontimeframe : à partir du moment où le serveur répond aux requêtes question, les utilisateurs ont un temps maximum pour répondre à la question. Ce paramètre indique cette durée en secondes. Voir la page de séquences.
      • nbquestions : le nombre de questions à jouer. Doit être inférieur ou égal au nombre de questions présentes dans le fichier (élement <usi:questions>). Ce paramètre est considéré comme inutile. Nous jouerons toujours 20 questions.
      • flushusertable : un booleen indiquant si toutes les données utilisateurs (liste des utilisateurs, scores et historiques) doivent être supprimées.
      • trackeduseridmail : ne pas tenir compte de ce paramètre.
      • Par ailleurs, les longueurs min/max des questions et réponses sont de 2 à 150 caractères.
    • Chaque équipe de challengers devra fournir à OCTO une clé d'authentification 'authentication_key' pour accéder à cette API.

Login

  • URI : .../api/login
  • méthode POST
  • Paramètres d'entrée
{
"mail" : "string",
"password" : "string"
}
  • Codes de retour
    • OK : CREATED 201S
    • Si l'utilisateur est déjà loggé : 400
    • Problème d'authentification (l'utilisateur n'existe pas, mauvais mot de passe)  : 401
  • Commentaire : lorsque le code de retour est OK, un cookie de session non persistant, nommé 'session_key' est placé dans l'entête HTTP. La valeur de ce cookie est une clé générée pour authentifier l'utilisateur lors des différents appels faits pendant la suite du jeu.

Obtenir la question N

  • URI : .../api/question/N
  • méthode GET
  • Paramètre retourné
{
"question" : "string",
"answer_1" : "string",
"answer_2" : "string",
"answer_3" : "string",
"answer_4" : "string",
"score" : number
}
  • Codes de retours
    • OK : 200
    • Clé de session non reconnue : 401
    • Non respect de la séquence ou autre erreur : 400
  • Commentaires
    • Retourne la question N (de 1 à 20) ainsi que les réponses possibles et le score actuel de l'utilisateur.
    • Cette API utilise un mécanisme de long polling : le serveur ne retourne sa réponse qu'au bout d'un certain temps. Le serveur attend qu'il y ait un nombre X de joueurs. Ce nombre X est défini dans les paramètres du jeu fournis lors d'un appel à 'game'.
    • Le serveur doit contrôler que la requête est temporellement valide, c'est à dire qu'elle ne dépasse pas la fenêtre temporelle de la question N. Par exemple, une erreur doit être retournée si l'utilisateur demande la question 10 alors que c'est la question 1 qui doit être jouée.
    • L'authentification de l'utilisateur se fait par le cookie 'session_key'.

Répondre à une question N

  • URI : .../api/answer/N
  • méthode POST
  • Paramètre d'entrée
{
"answer" : number
}
  • Paramètre de retour
{
"are_u_right" : "boolean",
"good_answer" : "string",
"score" : number
}
  • Codes de retour
    • OK : CREATED 201
    • Clé de session non reconnue : 401
    • Autre erreur : 400
  • Commentaires
    • Enregistre la réponse de l'utilisateur et retourne la bonne réponse et le statut de l'utilisateur. Chaque participant ne peut répondre qu'une fois à chaque question. Par ailleurs, le serveur doit valider que la réponse n'arrive pas trop tard ou trop tôt par rapport à l'intervalle de réponse possible.
    • Si un joueur ne répond pas dans le temps imparti, le système considère qu'il a mal répondu: son score n'augmente pas et le numéro de la réponse enregistrée est 0 (toute réponse reçue doit normalement être supérieure à 1)
    • L'authentification de l'utilisateur se fait par le cookie 'session_key'.

Obtenir le classement d'un utilisateur

  • URI : .../api/ranking
  • méthode GET
  • Paramètre de retour
{
        "score" : number,
"top_scores" : {
"mail" : [ "string", "string", ...],
"scores" : [ number, number, ...],
"firstname" : ["string", "string", ...],
"lastname" : ["string", "string", ...]
}
"before" : {
"mail" : ["string", "string", ...],
"scores" : [number, number, ...],
                        "firstname" : ["string", "string", ...],
                        "lastname" : ["string", "string", ...]
}
"after" : {
"mail" : ["string", "string", ...],
"scores" : [number, number, ...],
"firstname" : ["string", "string", ...],
"lastname" : ["string", "string", ...]
}
}
  • Codes de retour
    • OK 200
    • Clé de session non reconnue : 401
    • Autre : 400
  • Commentaires
    • Retourne les 100 premiers scores (couple mail/score) et les 10 scores avoisinant l'utilisateur (5 avant et 5 après). Si l'utilisateur courant est classé 4ème, (donc dans le 5 premiers) seuls les 3 utilisateurs le précédant et les 5 suivants seront retournés.
    • Les listes "top_scores", "before" et "after" sont triées par lastname/firstname/mail dans le cas d'ex-aequo.
    • L'authentification de l'utilisateur se fait par le cookie 'session_key'.

Obtenir le classement d'un utilisateur (mode admin)

  • URI : .../api/score
  • méthode GET
  • Paramètre d'URL
    • user_mail : string
    • authentication_key
  • Paramètre de retour
{
        "score" : number,
"top_scores" : { 
"mail" : [ "string", "string", ...],
"scores" : [ number, number, ...],
"firstname" : ["string", "string", ...],
"lastname" : ["string", "string", ...]
}
"before" : { 
"mail" : ["string", "string", ...],
"scores" : [number, number, ...],
                        "firstname" : ["string", "string", ...],
                        "lastname" : ["string", "string", ...]
}
"after" : { 
"mail" : ["string", "string", ...],
"scores" : [number, number, ...],
"firstname" : ["string", "string", ...],
"lastname" : ["string", "string", ...]
}
}
  • Codes de retour
    • OK 200
    • Problème d'authentification : 401
    • Autre : 400
  • Commentaires
    • Retourne le score et le classement d'un utilisateur à la fin d'une partie (toutes les questions ont été jouées).
    • Cet appel n'authentifie pas un joueur mais l'administrateur. L'authentification de l'appelant est analogue à celle faite par .../api/game : par le paramètre "authentication_key", communiqué par chaque équipe à l'organisation OCTO.

Obtenir les réponses d'un utilisateur

  • URI : .../api/audit
  • méthode GET
  • Paramètre d'URL
    • user_mail : string
    • authentication_key
  • Paramètre de retour
{
"user_answers" : [number, number, ...]
"good_answers" :  [number, number, ...]
}
  • Codes de retour
    • OK 200
    • Problème d'authentification : 401
    • Autre : 400
  • Commentaires
    • Pour la dernière partie exécutée, retourne les réponses faites par un utilisateur identifié par son mail. En parallèle, les bonnes réponses sont aussi retournées.
    • Cet appel n'authentifie pas un joueur mais l'administrateur. L'authentification de l'appelant est analogue à celle faite par .../api/game : par le paramètre "authentication_key", communiqué par chaque équipe à l'organisation OCTO.

Obtenir une réponse d'un utilisateur

  • URI : .../api/audit/n
  • méthode GET
  • Paramètre d'URL
    • user_mail : string
    • authentication_key
  • Paramètre de retour
{
"user_answer" : number,
"good_answer" :  number,
"question" : string
}
  • Codes de retour
    • OK 200
    • Problème d'authentification : 401
    • Autre : 400
  • Commentaires
    • Pour la dernière partie exécutée, retourne la réponse à la question identifiée par son numéro (/n dans l'URI). En parallèle, la bonne réponse et l'intitulé de la question sont retournés.
    • Cet appel n'authentifie pas un joueur mais l'administrateur. L'authentification de l'appelant est analogue à celle faite par .../api/game : par le paramètre "authentication_key", communiqué par chaque équipe à l'organisation OCTO.
Si vous avez des questions sur les specs, merci d'utiliser SourceForge avec le préfixe [SPECS].
Sous-pages (1) : Séquences d'appels