* checkLogin($user, $passwd); * randomCenterNode(); * randomCloudNode(); * cgBuildResultSets($cloudSize, $centerEid, $r1, $r2); * cgChooseRelations(); * cgBuildCloud($centerEid, $cloudSize, $sources, $sumWeights); * cgInsert($centerEid, $cloud, $r1, $r2, $totalDifficulty); * randomGameCore(); * randomGame(); * formatWord($word); * game2json($user, $gameId); * game2array($user, $gameId); * createGame($nbParties, $mode); * createGameCore($cloudSize); * getGame($user, $nbGames, $mode); * computeScore($probas, $difficulty, $answer, $userReputation); * computeUserReputation($score); * normalizeProbas($row); * setGame($user, $pgid, $gid, $answers); * get_game_relations(); * getGameRelationsJSON(); * setGameGetScore($user, $pgid, $gid, $answers); * insertNode($node); * getNodeEid($node); */ /** Vérifie la validité du couple nom d'utilisateur / mot de passe. * @param user : Le nom d'utilisateur. * @param passwd : Le mot de passe. * @return boolean : true si OK sinon false. */ function checkLogin($user, $passwd) { if(isset($_SESSION['userId'])) return true; $db = getDB(); $hashPasswd = md5($passwd); $loginIsOk = ($hashPasswd == $db->querySingle("SELECT hash_passwd FROM user WHERE login='".$user."';")); return $loginIsOk; } /** Selectionne aléatoirement l'eid d'un mot central. * @return eid : Identifiant d'un mot central, NULL en cas d'erreur. */ function randomCenterNode() { $db = getDB(); return $db->querySingle("select eid from random_center_node where rowid = (abs(random()) % (select max(rowid) from random_center_node))+1;"); } /** Selectionne aléatoirement un noeud d'un nuage. * @return eid : L'identifiant du noeud. */ function randomCloudNode() { $db = getDB(); return $db->querySingle("select eid from random_cloud_node where rowid = (abs(random()) % (select max(rowid) from random_cloud_node))+1;"); } /** * @param cloudSize : Taille du nuage. * @param centerEid : Identifiant du mot central. * @param r1 Type de la relation 1. * @param r2 Type de la relation 2. */ function cgBuildResultSets($cloudSize, $centerEid, $r1, $r2) { $db = getDB(); // 'w' => weight (poids), 'd' => difficulté, 's' => select // Le select doit ranvoyer trois colonnes : // eid => l'eid du mot à mettre dans le nuage, // r1 => la probabilité pour que le mot soit dans r1, entre -1 et 1 (négatif = ne devrait pas y être, positif = devrait y être à coup sûr, 0 = on sait pas). $typer1r2 = "type in ($r1, $r2)"; $banned_types = "4, 12, 36, 18, 29, 45, 46, 47, 48, 1000, 1001"; $sources = array( // Voisins 1 saut du bon type (= relations déjà existantes) array('w'=>40, 'd'=>1, 's'=>"select end as eid, type = $r1 as r1, type = $r2 as r2, 0 as r0, 0 as trash from relation where start = $centerEid and $typer1r2 order by random();"), // Voisins 1 saut via r_associated (0), donc qu'on voudrait spécifier si possible. array('w'=>40, 'd'=>2, 's'=>"select end as eid, 0.25 as r1, 0.25 as r2, 0.5 as r0, 0 as trash from relation where start = $centerEid and type = 0 order by random();"), // Voisins 1 saut via les autres relations array('w'=>20, 'd'=>3.1, 's'=>"select end as eid, 0.1 as r1, 0.1 as r2, 0.8 as r0, 0 as trash from relation where start = $centerEid and type not in (0, $r1, $r2, $banned_types) order by random();"), // Voisins 2 sauts, avec un mix de R1 et R2 pour les liens. Par ex [ A -R1-> B -R2-> C ] ou bien [ A -R2-> B -R2-> C ] // Version optimisée de : "select end as eid from relation where $typer1r2 and start in oneHopWithType order by random();" array('w'=>30, 'd'=>3.2, 's'=>"select B.end as eid, ((A.type = $r1) + (B.type = $r1)) / 3. as r1, ((A.type = $r2) + (B.type = $r2)) / 3. as r2, 1/6. as r0, 1/6. as trash from relation as A, relation as B where A.start = $centerEid and A.$typer1r2 and B.start = A.end and B.$typer1r2 order by random();"), // Voisins 1 saut r1/r2 + 1 saut synonyme // Version optimisée de : "select end as eid from relation where start in oneHopWithType and type = 5 order by random()"; array('w'=>20, 'd'=>5, 's'=>"select B.end as eid, (A.type = $r1) * 0.75 as r1, (A.type = $r2) * 0.75 as r2, 0.25 as r0, 0 as trash from relation as A, relation as B where A.start = $centerEid and A.$typer1r2 and B.start = A.end and B.type = 5 order by random();"), // Version optimisée de : "select end as eid from relation where start in (select end from relation where start = $centerEid and type = 5) and $typer1r2 order by random();" array('w'=>20, 'd'=>6, 's'=>"select B.end as eid, (B.type = $r1) * 0.75 as r1, (B.type = $r2) * 0.75 as r2, 0.25 as r0, 0 as trash from relation as A, relation as B where A.start = $centerEid and A.type = 5 and B.start = A.end and B.$typer1r2 order by random();"), // Voisins 2 sauts (tous) // Version optimisée de : "select end as eid, 0.1 as r1, 0.1 as r2, 0.3 as r0, 0.5 as trash from relation where start in (select end from relation where start = $centerEid and type not in ($banned_types)) and type not in ($banned_types) order by random();" array('w'=>10, 'd'=>8, 's'=>"select x as eid, 0.1 as r1, 0.1 as r2, 0.3 as r0, 0.5 as trash from (select x from (select X.eid + Y.dumb as x from (select B.end as eid from relation as A, relation as B where A.type not in ($banned_types) and A.start = $centerEid and B.type not in ($banned_types) and B.start = A.end limit ".($cloudSize*4).") as X, (select 0 as dumb) as Y)) order by random();"), // Centre pointe vers X, M pointe vers X aussi, on prend M. // Version optimisée de : "select start as eid from relation where end in (select end from relation where start = $centerEid) and type not in ($banned_types) order by random();" // Ce n'est toujours pas ça… : "select eid from (select B.start as eid from relation as A, relation as B where A.type not in ($banned_types) and A.start = $centerEid and B.type not in ($banned_types) and B.end = A.end limit 1) order by random();" // Tordu, mais ça marche \o/ . En fait il faut empêcher l'optimiseur de ramener le random avant le limit (et l'optimiseur est malin… :) array('w'=>10, 'd'=>8, 's'=>"select x as eid, 0.1 as r1, 0.1 as r2, 0.2 as r0, 0.6 as trash from (select x from (select X.eid + Y.dumb as x from (select B.start as eid from relation as A, relation as B where A.type not in ($banned_types) and A.start = $centerEid and B.type not in ($banned_types) and B.end = A.end limit ".($cloudSize*4).") as X, (select 0 as dumb) as Y)) order by random();"), 'rand' => array('w'=>5, 'd'=>10, 's'=>false) // random. Le r1 et r2 de random sont juste en-dessous ); $sumWeights = 0; foreach ($sources as $k => $x) { $sumWeights += $x['w']; $sources[$k]['rsPos'] = 0; $sources[$k]['rsSize'] = 0; if ($x['s'] !== false) { $sources[$k]['resultSet'] = array(); $res = $db->query($x['s']); $i = 0; while ($i < $cloudSize && $sources[$k]['resultSet'][] = $res->fetchArray()) { $i++; $sources[$k]['rsSize']++; } } else { $sources[$k]['resultSet'] = array(); for ($i = 0; $i < $cloudSize; $i++) { $sources[$k]['resultSet'][] = array('eid'=>randomCloudNode(), 'r1'=>0, 'r2'=>0, 'r0'=>0, 'trash'=>1); $sources[$k]['rsSize']++; } } } return array($sources, $sumWeights); } /** Sélectionne aléatoirement deux relations. * @return array : Tableau avec la relation 1 et la relation 2. */ function cgChooseRelations() { $relations = array(5, 7, 9, 10, 13, 14, 22); $r1 = rand(0,count($relations)-1); $r2 = rand(0,count($relations)-2); if ($r2 >= $r1) $r2++; $r1 = $relations[$r1]; $r2 = $relations[$r2]; return array($r1, $r2); } /** Génération d'un nuage pour un mot central. * @param cloudSize : Taille du nuage. * @param sources Les sources. * @param sumWeights La somme des poids. * @return array : Tableau avec comme premier élément le nuage et comme second élément le total de difficulté. */ function cgBuildCloud($centerEid, $cloudSize, $sources, $sumWeights) { $db = getDB(); // On boucle tant qu'il n'y a pas eu au moins 2 sources épuisées $cloud = array(); $nbFailed = 0; $i = 0; $totalDifficulty = 0; while ($i < $cloudSize && $nbFailed < 10*$cloudSize) { // On choisit une source aléatoire en tennant compte des poids. $rands = rand(1,$sumWeights); $sumw = 0; if (!isset($sources['rand'])) { break; } $src = $sources['rand']; $srck = 'rand'; // Sélection d'une source aléatoire foreach ($sources as $k => $x) { $sumw += $x['w']; if ($rands < $sumw) { $src = $x; $srck = $k; break; } } // Vérification si on est à la fin du ResultSet de cette source. if ($src['rsPos'] >= $src['rsSize']) { $nbFailed++; $sumWeights -= $src['w']; unset($sources[$srck]); continue; } // On récupère un résultat de cette source. $res = $src['resultSet'][$src['rsPos']]; $sources[$srck]['rsPos']++; // On vérifie si le mot n'a pas déjà été sélectionné. $rejected = false; // Ne pas mettre le mot central dans le nuage. if ($res['eid'] == $centerEid) { continue; } $nodeName = $db->querySingle("select name from node where eid=".$res['eid'].";"); if (substr($nodeName, 0, 2) == "::") { continue; } foreach ($cloud as $c) { if ($c['eid'] == $res['eid']) { $nbFailed++; $rejected = true; break; } } if ($rejected) { continue; } // position dans le nuage, difficulté, eid, probaR1, probaR2 $totalDifficulty += $src['d']; $cloud[$i] = array('pos'=>$i++, 'd'=>$src['d'], 'eid'=>$res['eid'], 'probaR1'=>$res['r1'], 'probaR2'=>$res['r2'], 'probaR0'=>$res['r0'], 'probaTrash'=>$res['trash']); } $res = $sources['rand']['resultSet'][0]; while ($i < $cloudSize) { $totalDifficulty += $sources['rand']['d']; $cloud[$i] = array('pos'=>$i++, 'd'=>$sources['rand']['d'], 'eid'=>randomCloudNode(), 'probaR1'=>$res['r1'], 'probaR2'=>$res['r2'], 'probaR0'=>$res['r0'], 'probaTrash'=>$res['trash']); } return array($cloud, $totalDifficulty); } /** Insère la partie dans la base de données. * @param centerEid : Identifiant du mot central. * @param cloud : Le nuage. * @param r1 : Le type de la relation 1. * @param r2 : Le type de la relation 2. * @param totalDifficulty : La difficulté totale. */ function cgInsert($centerEid, $cloud, $r1, $r2, $totalDifficulty) { $db = getDB(); // Insère dans la base une partie avec le mot central $centerEid, le nuage $cloud et les relations $r1 et $r2 $db->exec("begin transaction;"); $db->exec("INSERT INTO game(gid, eid_central_word, relation_1, relation_2, difficulty) VALUES (null, $centerEid, $r1, $r2, $totalDifficulty);"); $gid = $db->lastInsertRowID(); $t = time(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgid1 = $db->lastInsertRowID(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgid2 = $db->lastInsertRowID(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgid0 = $db->lastInsertRowID(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgidT = $db->lastInsertRowID(); // TODO : R0 et Trash + corrections foreach ($cloud as $c) { $totalWeight = $c['probaR1'] + $c['probaR2'] + $c['probaR0'] + $c['probaTrash']; $db->exec("INSERT INTO game_cloud(gid, num, difficulty, eid_word, totalWeight, probaR1, probaR2, probaR0, probaTrash) VALUES ($gid, ".$c['pos'].", ".$c['d'].", ".$c['eid'].", $totalWeight, ".$c['probaR1'].", ".$c['probaR2'].", ".$c['probaR0'].", ".$c['probaTrash'].");"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgid1, $gid, 0, ".$c['pos'].", $r1, ".$c['probaR1'].", 0);"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgid2, $gid, 0, ".$c['pos'].", $r2, ".$c['probaR2'].", 0);"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgid0, $gid, 0, ".$c['pos'].", 0, ".$c['probaR0'].", 0);"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgidT, $gid, 0, ".$c['pos'].", -1, ".$c['probaTrash'].", 0);"); } $db->exec("commit;"); } function decodeAndInsertGame($user,$game) { $badWords = Array(); $centerEid = getNodeEid($game['center']); $r1 = $game['relations'][0]; $r2 = $game['relations'][1]; if($centerEid === null) $badWords[] = $game['center']; foreach($game['cloud'] as $key => $w) { if ($w['name'] == "") continue; $cloudEid = getNodeEid($w['name']); $cloud[] = Array("eid" => $cloudEid, "pos" => $key, "d" => 5, "probaR1" => $w['relations'][0] ? "1" : "0", "probaR2" => $w['relations'][1] ? "1" : "0", "probaR0" => $w['relations'][2] ? "1" : "0", "probaTrash" => $w['relations'][3] ? "1" : "0"); if($cloudEid === null) $badWords[] = $w['name']; } if(count($badWords) > 0) { echo JSON_encode($badWords); } else if (count($cloud) < 5) { echo "false"; } else { insertCreatedGame($centerEid,$cloud,$r1,$r2,10,$user); echo "true"; } } function insertCreatedGame($centerEid, $cloud, $r1, $r2, $totalDifficulty,$userName) { $db = getDB(); // Insère dans la base une partie avec le mot central $centerEid, le nuage $cloud et les relations $r1 et $r2 $db->exec("begin transaction;"); $db->exec("INSERT INTO game(gid, eid_central_word, relation_1, relation_2, difficulty, author) VALUES (null, $centerEid, $r1, $r2, $totalDifficulty, '$userName');"); $gid = $db->lastInsertRowID(); $t = time(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgid1 = $db->lastInsertRowID(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgid2 = $db->lastInsertRowID(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgid0 = $db->lastInsertRowID(); $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, $gid, null, $t);"); $pgidT = $db->lastInsertRowID(); // TODO : R0 et Trash + corrections foreach ($cloud as $c) { $totalWeight = $c['probaR1'] + $c['probaR2'] + $c['probaR0'] + $c['probaTrash']; $db->exec("INSERT INTO game_cloud(gid, num, difficulty, eid_word, totalWeight, probaR1, probaR2, probaR0, probaTrash) VALUES ($gid, ".$c['pos'].", ".$c['d'].", ".$c['eid'].", $totalWeight, ".$c['probaR1'].", ".$c['probaR2'].", ".$c['probaR0'].", ".$c['probaTrash'].");"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgid1, $gid, 0, ".$c['pos'].", $r1, ".$c['probaR1'].", 0);"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgid2, $gid, 0, ".$c['pos'].", $r2, ".$c['probaR2'].", 0);"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgid0, $gid, 0, ".$c['pos'].", 0, ".$c['probaR0'].", 0);"); $db->exec("INSERT INTO played_game_cloud(pgid, gid, type, num, relation, weight, score) VALUES ($pgidT, $gid, 0, ".$c['pos'].", -1, ".$c['probaTrash'].", 0);"); } $db->exec("commit;"); } /** Retourne un identifiant de partie aléatoire de la liste de parties jouables * @return gid : Identifiant de partie. */ function randomGameCore() { $db = getDB(); return $db->querySingle("select gid from game where gid = (abs(random()) % (select max(gid) from game))+1 or gid = (select max(gid) from game where gid > 0) order by gid limit 1;"); } /** Sélection aléatoire d'une partie de la base de données parmis les parties à jouer. * @return gid : Identifiant de la partie selectionnée. */ function randomGame() { $gid = randomGameCore(); if ($gid === null) { // TODO : séparer ces créations de parties dans une fonction qui détecte le mode toussa. for ($i = 0; $i < 100; $i++) createGameCore(rand(5,15)); $gid = randomGameCore(); if ($gid === null) throw new Exception("Erreur lors de la récupération de la partie. Vérifiez qu'il y a au moins une partie.", 6); } return $gid; } /** Formatage des mots lorsqu'il y a des généralisations/spécifications par le symbole ">". * @param word : Le mot que l'on veut reformater. * @return word : le mot formaté. */ function formatWord($word) { $db = getDB(); $res = ""; $stack = array(); while (($pos = strpos($word, ">")) !== false) { $res .= substr($word,0,$pos) . " ("; $eid = intval(substr($word,$pos+1)); if ($eid == 0) { throw new Exception("Erreur lors du suivi des pointeurs de spécialisation du mot $word.", 7); } if (in_array($eid, $stack)) { throw new Exception("Boucle rencontrée lors du suivi des pointeurs de spécialisation du mot $word.", 8); } if (count($stack) > 10) { throw new Exception("Trop de niveaux de récursions lors du suivi des pointeurs de spécialisation du mot $word.", 9); } $stack[] = $eid; $word = $db->querySingle("select name from node where eid = $eid"); } $res .= $word; for ($depth = count($stack); $depth > 0; $depth--) $res .= ')'; return $res; } /** Formate une partie en JSON en l'imprimant. * @param user : l'utilisateur. * @param gameId : L'identifiant d'une partie. */ function game2json($user, $gameId) { global $stringRelations; $db = getDB(); // TODO : factoriser avec game2array() . // TODO : planter si la requête suivante échoue pour quelque raison que ce soit. $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, ".$gameId.", '$user', -1);"); $pgid = $db->lastInsertRowID(); // TODO Yoann : faire des tests d'erreur pour ces select ? $game = $db->query("select gid, (select name from node where eid = eid_central_word) as name_central_word, eid_central_word, relation_1, relation_2 from game where gid = ".$gameId.";"); $game = $game->fetchArray(); $retstr = ""; $retstr .= '{"gid":'.$gameId.',"pgid":'.$pgid.',"relations":['; $retstr .= '{"id":'.$game['relation_1'].', "name":'.json_encode(''.formatWord($stringRelations[$game['relation_1']])).'}'; $retstr .= ', {"id":'.$game['relation_2'].', "name":'.json_encode(''.formatWord($stringRelations[$game['relation_2']])).'}'; $retstr .= ', {"id":0, "name":'.json_encode(''.formatWord($stringRelations[0])).'}'; $retstr .= ', {"id":-1, "name":'.json_encode(''.formatWord($stringRelations[-1])).'}],'; // $retstr .= '{"gid":'.$gameId.',"pgid":'.$pgid.',"cat1":'.$game['relation_1'].',"cat2":'.$game['relation_2'].',"cat3":0,"cat4":-1,'; $retstr .= '"center":{"id":'.$game['eid_central_word'].',"name":'.json_encode(''.formatWord($game['name_central_word'])).'},'; $cloudsize = 0; $tmp_retstr = ''; $notfirst = false; $res = $db->query("select eid_word,(select name from node where eid=eid_word) as name_word from game_cloud where gid = ".$gameId.";"); while ($x = $res->fetchArray()) { if ($notfirst) $tmp_retstr .= ","; else $notfirst=true; $tmp_retstr .= '{"id":'.$x['eid_word'].',"name":'.json_encode("".formatWord($x['name_word'])).'}'; $cloudsize++; } $retstr .= '"cloudsize":'.$cloudsize.',"cloud":['; $retstr .= $tmp_retstr; $retstr .= "]}"; return $retstr; } /** Récupère une partie sous forme de tableau. * @param db : descripteur de la bdd (obtenu avec getDB()). * @param user : Login de l'utilisateur demandant la partie. * @param gameId : L'identifiant d'une partie. */ function game2array($user, $gameId) { $db = getDB(); // TODO : planter si la requête suivante échoue pour quelque raison que ce soit. $db->exec("INSERT INTO played_game(pgid, gid, login, timestamp) VALUES (null, ".$gameId.", '$user', -1);"); $pgid = $db->lastInsertRowID(); // TODO Yoann : faire des tests d'erreur pour ces select ? $game = $db->query("select gid, (select name from node where eid = eid_central_word) as name_central_word, eid_central_word, relation_1, relation_2 from game where gid = ".$gameId.";"); $game = $game->fetchArray(); $ret = array(); $ret['gid'] = $gameId; $ret['pgid'] = $pgid; $ret['cat1'] = $game['relation_1']; $ret['cat2'] = $game['relation_2']; $ret['cat3'] = 0; $ret['cat4'] = -1; $ret['center'] = array('id' => $game['eid_central_word'], 'name' => formatWord($game['name_central_word'])); $ret['cloud'] = array(); // TODO ! compter dynamiquement. $res = $db->query("select eid_word,(select name from node where eid=eid_word) as name_word, num, difficulty, totalWeight, probaR1, probaR2, probaR0, probaTrash from game_cloud where gid = ".$gameId.";"); while ($x = $res->fetchArray()) { $ret['cloud'][$x['num']] = array( 'id' => $x['eid_word'], 'name' => formatWord($x['name_word']), 'difficulty' => $x['difficulty'], 'totalWeight' => $x['totalWeight'], 'probaR1' => $x['probaR1'], 'probaR2' => $x['probaR2'], 'probaR0' => $x['probaR0'], 'probaTrash' => $x['probaTrash'], 'probas' => normalizeProbas($x) ); } $ret['cloudsize'] = count($ret['cloud']); return $ret; } /** Création d'un lot de parties suivant un mode donnée. * @param nbParties : le nombre de parties à créer. * @param mode : Le mode de jeu pour les parties à créer. */ function createGame($nbParties, $mode) { for ($i = 0; $i < $nbParties; $i++) createGameCore(rand(5,15)); } /** Génère une partie (mode normal pour l'instant) pour une certaine taille de nuage. * @param cloudSize : Taille du nuage. * * Est appelée par randomGame(), donc il faudra adapter quand on aura plusieurs modes, par exemple en ayant une fonction intermédiaire qui puisse être appelée par createGame et randomGame. */ function createGameCore($cloudSize) { // select random node $centerEid = randomCenterNode(); $r1 = cgChooseRelations(); $r2 = $r1[1]; $r1 = $r1[0]; $sources = cgBuildResultSets($cloudSize, $centerEid, $r1, $r2); $sumWeights = $sources[1]; $sources = $sources[0]; $cloud = cgBuildCloud($centerEid, $cloudSize, $sources, $sumWeights); $totalDifficulty = $cloud[1]; $cloud = $cloud[0]; cgInsert($centerEid, $cloud, $r1, $r2, $totalDifficulty); } /** Récupération d'une partie. * @param user : L'identifiant de l'utilisateur. * @param nbGames : Le nombre de parties à récupérer. * @param mode : Le mode de jeu des parties à récupérer. */ function getGame($user) { echo game2json($user, randomGame()); } function computeScore($probas, $difficulty, $answer, $userReputation) { // Calcul du score. Score = proba[réponse de l'utilisateur]*coeff1 - proba[autres reponses]*coeff2 + bonus // score = - proba[autres reponses]*coeff2 // On aura donc -5 <= score <= 0 $score = -5 * (($probas[0] + $probas[1] + $probas[2] + $probas[3]) - $probas[$answer]); // score = proba[réponse de l'utilisateur]*coeff1 - proba[autres reponses]*coeff2 // On aura donc -5 <= score <= 10 $score += 10 * $probas[$answer]; // On est indulgent si la réponse est 3 (poubelle) : if ($answer == 3 && $score < 0) { $score = $score / 2; } // Adapter le score en fonction de la réputation de l'utilisateur (quand il est jeune, augmenter le score pour le motiver). // On aura donc -5 <= score <= 15 if ($score > 3) { $score += max(0, min(5, 5 - $userReputation)); } return round($score); } /** Calcul de la réputation de l'utilisateur selon son score. * @param score : Le score du joueur. */ function computeUserReputation($score) { return max(round(log(max($score/10,1))*100)/100, 0); } /** Formatage des probalitées dans un tableau. * @param row : le vecteur de probabilités. * @return array : Le vecteur de probabilités normalisé. */ function normalizeProbas($row) { return array($row['probaR1']/$row['totalWeight'], $row['probaR2']/$row['totalWeight'], $row['probaR0']/$row['totalWeight'], $row['probaTrash']/$row['totalWeight']); } /** Insertion des données d'une partie joué par un utilisateur. * @param user : L'identifiant de l'utilisateur ayant joué à la partie. * @param pgid : L'identifiant de la partie jouée. * @param gid : L'identifiant du jeu auquel la partie appartient. * @return score : Le score réalisé par le joueur. */ function setGame($user, $pgid, $gid, $answers) { $db = getDB(); if ('ok' !== $db->querySingle("SELECT 'ok' FROM played_game WHERE pgid = $pgid and $gid = $gid and login = '$user' and timestamp = -1;")) { return getGameScores($user, $pgid, $gid); } $userReputation = computeUserReputation($db->querySingle("SELECT score FROM user WHERE login='".SQLite3::escapeString($user)."';")); $db->exec("begin transaction;"); $db->exec("update played_game set timestamp = ".time()." where pgid = $pgid;"); $r0 = 0; $trash = -1; $r1 = $db->querySingle("SELECT relation_1, relation_2 FROM game WHERE gid = $gid;", true); $r2 = $r1['relation_2']; $r1 = $r1['relation_1']; $res = $db->query("SELECT num, difficulty, totalWeight, probaR1, probaR2, probaR0, probaTrash FROM game_cloud WHERE gid = $gid;"); $gameScore = 0; $scores = array(); while ($row = $res->fetchArray()) { $num = intval($row['num']); if (!isset($answers[$num])) { throw new Exception("Cette requête \"Set partie\" ne donne pas de réponse (une relation) pour le mot numéro $num de la partie.", 5); } $relanswer = intval($answers[$num]); switch ($relanswer) { case $r1: $answer = 0; $probaRx = "probaR1"; break; case $r2: $answer = 1; $probaRx = "probaR2"; break; case $r0: $answer = 2; $probaRx = "probaR0"; break; case $trash: $answer = 3; $probaRx = "probaTrash"; break; default: throw new Exception("Réponse ($relanswer) invalide pour le mot $num. Les réponses possibles sont : $r1, $r2, $r0, $trash", 5); } $wordScore = computeScore(normalizeProbas($row), $row['difficulty'], $answer, $userReputation); $gameScore += $wordScore; $scores[$num] = $wordScore; $db->exec("insert into played_game_cloud(pgid, gid, type, num, relation, weight, score) values($pgid, $gid, 1, $num, $r1, ".$userReputation.", ".$wordScore.");"); $db->exec("update game_cloud set $probaRx = $probaRx + ".max(min($userReputation/2,5),0)." where gid = $gid and num = $num;"); $db->exec("update game_cloud set totalWeight = totalWeight + ".max(min($userReputation/2,5),0)." where gid = $gid and num = $num;"); } $db->exec("update user set score = score + ".$gameScore." where login = '$user';"); $db->exec("commit;"); return array( 'scoreTotal' => $gameScore, 'alreadyPlayed' => false, 'scores' => $scores ); } function getGameScores($user, $pgid, $gid) { $db = getDB(); $timestamp = $db->querySingle("SELECT timestamp FROM played_game WHERE pgid = $pgid and $gid = $gid and login = '$user';"); if ($timestamp == -1) { throw new Exception("Cette partie n'a jamais été jouée.", 4); // TODO : code d'erreur en doublon avec celui ci-dessous. } else if ($timestamp == null) { throw new Exception("Cette partie n'est associée à votre nom d'utilisateur.", 4); } $gameScore = 0; $scores = array(); $res = $db->query("SELECT num,score from played_game_cloud where pgid = $pgid and gid = $gid;"); while ($row = $res->fetchArray()) { $gameScore += $row['score']; $scores[$row['num']] = $row['score']; } return array( 'scoreTotal' => $gameScore, 'alreadyPlayed' => true, 'scores' => $scores ); } /** Fourni l'ensembles des relations pouvant apparaître dans le jeu. * @return array : un tableau de realtions. */ function get_game_relations() { $reqlations = array(); $relations[] = 5; $relations[] = 7; $relations[] = 9; $relations[] = 10; $relations[] = 14; $relations[] = 22; return $relations; } function getGameRelationsJSON() { $json = "{"; global $stringRelations; foreach($stringRelations as $id=>$description) if($id == -1) $json .= '"'.$id.'":"'.$description.'"'; else $json .= ',"'.$id.'":"'.$description.'"'; $json .= '}'; return $json; } function setGameGetScore($user, $pgid, $gid, $answers) { $scores = setGame($user, intval($pgid), intval($gid), $answers); // On renvoie une nouvelle partie pour garder le client toujours bien alimenté. $scores['newGame'] = game2json($user, randomGame()); $scores['minScore'] = -5; $scores['maxScore'] = 10; echo json_encode($scores); } /** Insère dans la base de données le noeud si il n'existe pas encore. * @param node : le noeud à ajouter. */ function insertNode($node) { $db = getDB(); if($db->querySingle("SELECT eid FROM node WHERE name='".SQLite3::escapeString($node)."'") == null) { $db->exec("INSERT INTO node(name,type,weight) VALUES('".SQLite3::escapeString($node)."',1,50);"); return true; } return false; } /** retourne l'eid d'un mot. * @param node : le mot dont on veut obtenir l'eid. */ function getNodeEid($node) { $db = getDB(); return $db->querySingle("SELECT eid FROM node WHERE name='".SQLite3::escapeString($node)."';"); } function wordExist($node) { $db = getDB(); return getNodeEid($node) === null ? "0" : "1"; } ?>