diff --git a/code/PtiClic/AndroidManifest.xml b/code/PtiClic/AndroidManifest.xml
index b81538f..dd8018f 100644
--- a/code/PtiClic/AndroidManifest.xml
+++ b/code/PtiClic/AndroidManifest.xml
@@ -19,4 +19,6 @@
+
+
diff --git a/code/PtiClic/src/org/pticlic/FrontPage.java b/code/PtiClic/src/org/pticlic/FrontPage.java
index 5485779..5cf39ff 100644
--- a/code/PtiClic/src/org/pticlic/FrontPage.java
+++ b/code/PtiClic/src/org/pticlic/FrontPage.java
@@ -1,8 +1,11 @@
package org.pticlic;
import org.pticlic.games.BaseGame;
+import org.pticlic.model.Network;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
@@ -19,17 +22,23 @@ public class FrontPage extends Activity implements OnClickListener{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frontpage);
-
+
// Écoute des clics sur les différents boutons
((ImageView)findViewById(R.id.prefs)).setOnClickListener(this);
((ImageView)findViewById(R.id.play)).setOnClickListener(this);
((ImageView)findViewById(R.id.infoButton)).setOnClickListener(this);
+
}
-
+
@Override
protected void onStart() {
super.onStart();
-
+
+ if (Network.isConnected(this))
+ System.out.println("Connecter");
+ else
+ System.out.println("Non Connecter");
+
// On récupère le nom du joueur des préférences.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
String loginPref = sp.getString("login", "joueur");
@@ -44,14 +53,34 @@ public class FrontPage extends Activity implements OnClickListener{
public void onClick(View v) {
switch (v.getId()) {
case (R.id.prefs) : startActivity(new Intent(this, Preference.class)); break;
- case (R.id.play) : startActivity(new Intent(this, BaseGame.class)); break;
+ case (R.id.play) : checkNetworkConnection(BaseGame.class); break;
case (R.id.infoButton) : startActivity(new Intent(this, Information.class)); break;
}
}
-
+
+ @SuppressWarnings("rawtypes")
+ private void checkNetworkConnection(Class c) {
+ if (Network.isConnected(this)) {
+ startActivity(new Intent(this, c));
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.app_name))
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage("Problème de connexion au serveur. Vérifiez que vous êtes connecté au réseau.")
+ .setCancelable(false)
+ .setNegativeButton("Ok", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+ }
+
@Override
public void onBackPressed() {
System.exit(0);
}
-
+
}
diff --git a/code/PtiClic/src/org/pticlic/games/BaseGame.java b/code/PtiClic/src/org/pticlic/games/BaseGame.java
index 61e7a57..13a3284 100644
--- a/code/PtiClic/src/org/pticlic/games/BaseGame.java
+++ b/code/PtiClic/src/org/pticlic/games/BaseGame.java
@@ -10,6 +10,8 @@ import org.pticlic.model.Network.Mode;
import org.pticlic.model.Relation;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
@@ -39,12 +41,14 @@ import android.widget.TextView;
* proposer celle qui lui semble le mieux approprier.
*
*/
+
public class BaseGame extends Activity implements OnClickListener, AnimationListener {
private int currentWord = 0;
private TextView currentWordTextView;
private int nbWord = 0;
private DownloadedGame game;
private Match match;
+ private Network network;
/** Called when the activity is first created. */
@Override
@@ -60,6 +64,7 @@ public class BaseGame extends Activity implements OnClickListener, AnimationList
// On initialise la classe permettant la communication avec le serveur.
Network network = new Network(serverURL, Mode.SIMPLE_GAME, id, passwd);
+
game = network.getGames(1);
int nbrel = game.getNbRelation();
nbWord = game.getNbWord();
@@ -68,14 +73,14 @@ public class BaseGame extends Activity implements OnClickListener, AnimationList
match = new Match();
match.setGame(game);
- Relation r = Relation.getInstance();
-
// Boutons des relations
ImageView r1 = ((ImageView)findViewById(R.id.relation1));
ImageView r2 = ((ImageView)findViewById(R.id.relation2));
ImageView r3 = ((ImageView)findViewById(R.id.relation3));
ImageView r4 = ((ImageView)findViewById(R.id.relation4));
-
+
+ Relation r = Relation.getInstance();
+
// TODO : Pour l'instant la poubelle ne fait rien. Il faudra certainement la ranger dans un categorie dans GamePlayed pour calculer le score.
ImageView trash = ((ImageView)findViewById(R.id.trash));
trash.setOnClickListener(this);
@@ -120,7 +125,7 @@ public class BaseGame extends Activity implements OnClickListener, AnimationList
//On recupere le centre de mainWord pour l'animation de translation.
TextView mainWord = (TextView)findViewById(R.id.mainWord);
currentWordTextView = (TextView)findViewById(R.id.currentWord);
-
+
// On defini un ensemble d'animation
AnimationSet set = new AnimationSet(true);
set.setDuration(1000);
@@ -187,18 +192,18 @@ public class BaseGame extends Activity implements OnClickListener, AnimationList
@Override
public void onAnimationEnd(Animation animation) {
-
+
}
@Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
-
+
}
@Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
-
+
}
}
\ No newline at end of file
diff --git a/code/PtiClic/src/org/pticlic/model/Network.java b/code/PtiClic/src/org/pticlic/model/Network.java
index 72d3626..4deb98d 100644
--- a/code/PtiClic/src/org/pticlic/model/Network.java
+++ b/code/PtiClic/src/org/pticlic/model/Network.java
@@ -1,10 +1,18 @@
package org.pticlic.model;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.preference.PreferenceManager;
+
import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
@@ -54,6 +62,55 @@ public class Network {
this.passwd = passwd;
}
+ /**
+ * Permet de savoir si l'application a access a internet ou non
+ *
+ * @param context l'activite permettant de tester l'access a internet
+ * @return true
si on a access a internet false
sinon
+ */
+ public static boolean isConnected(Context context) {
+ ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (cm != null && (cm.getActiveNetworkInfo() == null
+ || !cm.getActiveNetworkInfo().isConnected())) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Permet de verifier que la combinaison login/mdp est correct
+ *
+ * @param context l'activite permettant de tester l'access a internet
+ * @param id l'identifiant de l'utilisateur
+ * @param passwd le mot de passe de l'utilisateur
+ * @return true
si la combinaison login/mdp est correct false
sinon
+ */
+ public static boolean isLoginCorrect(Context context, String id, String passwd) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ String serverURL = sp.getString(Constant.SERVER_URL, "http://dumbs.fr/~bbrun/pticlic.json");
+
+ URL url;
+ boolean res = false;
+ try {
+ url = new URL(serverURL);
+ URLConnection connection = url.openConnection();
+ connection.addRequestProperty("action", "verifyAccess");
+ connection.addRequestProperty("user", id);
+ connection.addRequestProperty("passwd", passwd);
+
+ InputStream in = connection.getInputStream();
+ BufferedReader buf = new BufferedReader(new InputStreamReader(in));
+ res = Boolean.getBoolean(buf.readLine());
+
+ } catch (MalformedURLException e) {
+ return false;
+ } catch (IOException e) {
+ return false;
+ }
+
+ return res;
+ }
+
/**
* Cette méthode permet de récupérer du serveur un certain nombre de parties.
* @param nbGames Le nombre de parties que l'on veut récupérer.
@@ -171,12 +228,12 @@ public class Network {
for (Integer i : game.getTrash()) {
connection.addRequestProperty("trash[]", i.toString());
}
-
+
Gson gson = new Gson();
JsonReader reader = new JsonReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
score = gson.fromJson(reader, TotalScore.class);
-
+
} catch (IOException e) {
return score;
diff --git a/code/serveur/dump2sqlite.sh b/code/serveur/dump2sqlite.sh
index 728da31..b450e02 100755
--- a/code/serveur/dump2sqlite.sh
+++ b/code/serveur/dump2sqlite.sh
@@ -15,7 +15,7 @@ create table node(eid integer primary key autoincrement, name, type, weight);
create table relation(rid integer primary key autoincrement, start, end, type, weight);
create table type_node(name, num);
create table type_relation(name, num, extended_name, info);
-create table user(login primary key, mail, hash_passwd);
+create table user(login primary key, mail, hash_passwd, score);
create table game(gid integer primary key autoincrement, eid_central_word, relation_1, relation_2, difficulty);
create table game_cloud(gid, num, difficulty, eid_word, totalWeight, probaR1, probaR2, probaR0, probaTrash);
create table played_game(pgid integer primary key autoincrement, gid, login);
diff --git a/code/serveur/php/pticlic.php b/code/serveur/php/pticlic.php
index e46be96..10a2152 100644
--- a/code/serveur/php/pticlic.php
+++ b/code/serveur/php/pticlic.php
@@ -12,16 +12,16 @@ function mDie($err,$msg)
exit;
}
-if (!$db = new SQLite3('db')) {
+if (!$db = new SQlite3($SQL_DBNAME)) {
mDie(1,"Erreur lors de l'ouverture de la base de données SQLite3");
}
function initdb() {
global $db;
- $db->exec("insert into user(login, mail, hash_passwd) values('foo', 'foo@isp.com', '".md5('bar')."');");
+ $db->exec("insert into user(login, mail, hash_passwd, score) values('foo', 'foo@isp.com', '".md5('bar')."', 0);");
}
-if ($do_initdb) initdb();
+if ($do_initdb) { initdb(); }
if(!isset($_GET['action']) || !isset($_GET['user']) || !isset($_GET['passwd']))
mDie(2,"La requête est incomplète");
@@ -179,8 +179,6 @@ function create_game($cloudSize) {
cg_insert($centerEid, $cloud, $r1, $r2, $totalDifficulty);
}
-//create_game(10);
-
function random_game() {
global $db;
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;");
@@ -196,15 +194,26 @@ function game2json($game_id) {
echo "cloudsize:10,cloud:["; // TODO ! compter dynamiquement.
$res = $db->query("select eid_word,(select name from node where eid=eid_word) as name_word from game_cloud where gid = ".$game['gid'].";");
+ $notfirst = false;
while ($x = $res->fetchArray()) {
- echo "{id:".$x['eid_word'].",name:".$x['name_word']."}\n";
+ if ($notfirst) { echo ","; } else { $notfirst=true; }
+ echo "{id:".$x['eid_word'].",name:".$x['name_word']."}";
}
echo "]}";
}
-function main() {
+function main($action) {
// Sinon tout est bon on effectu l'opération correspondant à la commande passée.
- if($action == 0) { // "Get partie"
+ // TODO : en production, utiliser : header("Content-Type: application/json; charset=utf-8");
+ header("Content-Type: text/plain; charset=utf-8");
+ if ($action == 2) { // "Create partie"
+ if(!isset($_GET['nb']) || !isset($_GET['mode']))
+ mDie(2,"La requête est incomplète");
+ $nbParties = intval($_GET['nb']);
+ for ($i = 0; $i < $nbParties; $i++) {
+ create_game(10);
+ }
+ } else if ($action == 0) { // "Get partie"
if(!isset($_GET['nb']) || !isset($_GET['mode']))
mDie(2,"La requête est incomplète");
$nbGames = intval($_GET['nb']);
@@ -220,16 +229,26 @@ function main() {
} else if($action == 1) { // "Set partie"
// Requête sql d'ajout d'informations (et calcul de résultat).
// TODO : nettoyer, finir
- $gid = $_GET['gid']; // TODO : vérifier qu'on a bien distribué cette partie à cet utilisateur, et qu'il n'y a pas déjà répondu (répercuter ça sur le random_game).
- $userReputation = 5; // TODO : un nombre entre 0 et 5 environ. Par ex. log(pointsUtilisateur) est un bon choix.
+
+ $gid = $_GET['gid'];
+
+ if(ĝid != $db->querySingle("SELECT gid FROM played_game WHERE login='".$user."'"))
+ mdie(3,"Cette partie n'est associée à votre nom d'utilisateur");
+
+ $userReputation = log($db->querySingle("SELECT score FROM user WHERE login='".$user."'"));
+
$db->exec("begin transaction;");
- $db->exec("insert into played_game(pgid, gid, login) values (null, $gid, null);");
+ $db->exec("INSERT INTO played_game(pgid, gid, login) VALUES (null, $gid, null);");
$pgid = $db->lastInsertRowID();
- for ($i=0; $i < 10; $i++) {
+
+ for($i=0; $i < 10; $i++)
+ {
$x = $_GET['$i'];
+
// TODO : calculer le score. Score = proba[réponse de l'utilisateur]*coeff - proba[autres reponses]*coeff
// TODO : adapter le score en fonction de la réputation de l'utilisateur (plus quand il est jeune, pour le motiver, par ex. avec un terme constant qu'on ajoute).
$score = 1;
+
$db->exec("insert into played_game_cloud(pgid, gid, type, num, relation, weight, score) values($pgid, $gid, 1, ".$c['pos'].", $r1, ".($x*$userReputation).", ".$score.");");
// TODO : game_cloud(probaR_x_) += $réputationJoueur * $coeff
// TODO : game_cloud(totalWeight) += $réputationJoueur * $coeff (NOTE : même coeff que pour game_cloud(probaR_x_))
@@ -241,5 +260,7 @@ function main() {
die("Commande inconnue");
}
}
-
+
+main($action);
+
?>
diff --git a/organisation/notes b/organisation/notes
index ed2661f..19e53d9 100644
--- a/organisation/notes
+++ b/organisation/notes
@@ -1,3 +1,24 @@
- Une classe Constante pour toutes les constantes ("symboles" pour les paramètres, ...).
- Boutons pour les différents modes de jeu directement sur la "page de garde".
- Icônes : http://developer.android.com/guide/practices/ui_guidelines/icon_design.html
+
+NOTES SUITE A LA REUNION
+- addiction -> IMPORTANT ... teasing socialisation
+- choix de la thématique : cadeau thématique
+- game with a purpose GWAP
+- espi
+- liberman (sp?)
+- indexation des images Google
+- code pour Google.com code.google.com/intl/fr~FR/apis/chart/index.html
+- qrcode
+- lire sur site pour installer appli
+- bêta testeurs externes
+- intuitivité
+- réflexion : intuitivité - prototypes -
+- bouton aide -> affiche les icônes en vertical + texte, il y a moins de place pour le mot du nuage, mais on peut continuer à jouer en cliquant sur ces "gros" boutons.
+- Modes de jeu supplémentaires payants par ex.
+
+
+POUR LAFOURCADE
+- lien : code.google.com/intl/fr~FR/apis/chart/index.html
+- note techniques exactes pour le serveur au LIRMM
diff --git a/rapport/rapport.tex b/rapport/rapport.tex
index bae1067..6198586 100644
--- a/rapport/rapport.tex
+++ b/rapport/rapport.tex
@@ -60,6 +60,45 @@ Un grand nombre de développeurs ont créés des applications pour étendre la f
\section{Conception}
+\begin{verbatim}
+NODE(EID integer primary key autoincrement, name string, #type (ref TYPE_NODE.num), weight);
+
+RELATION(RID integer primary key autoincrement, #start (ref NODE.eid), #end (ref NODE.eid), #type (ref TYPE_RELATION.num), weight);
+
+TYPE_NODE(NUM, name string);
+
+TYPE_RELATION(NUM, name string, extended_name string, info string);
+
+USER(LOGIN string primary key, mail string, hash_passwd string (md5sum du password), #score (contrainte : somme de tous les scores des PLAYED_GAME_CLOUD);
+
+GAME(GID integer primary key autoincrement, #eid_central_word (ref NODE.eid, #relation_1 (ref RELATION.rid), #relation_2 ( (ref RELATION.rid), difficulty);
+
+GAME_CLOUD(GID, NUM, difficulty, #eid_word(ref NODE.eid), totalWeight (contrainte : = somme des probas), probaR1 (contrainte : = somme des probas des PLAYED_GAME_CLOUD.weight avec la bonne relation et la même gid et num), probaR2 (idem), probaR0 (idem), probaTrash (idem));
+
+PLAYED_GAME(PGID integer primary key autoincrement, #gid (ref GAME.gid), #login (ref USER.login);
+
+PLAYED_GAME_CLOUD(#PGID (ref PLAYED_GAME.pgid), #GID (ref PLAYED_GAME.gid), NUM, type (contrainte : 0 = partie de référence, 1 = réponse d'un joueur), #relation (ref RELATION.rid), weight (contrainte : probabilité estimée de cette réponse pour les bots (robots), réputation du joueur sinon), score (score donné au joueur, 0 pour les bots);
+
+**INT unless otherwise marked
+\end{verbatim}
+\begin{verbatim}
+create table node(eid integer primary key autoincrement, name, type, weight);
+create table relation(rid integer primary key autoincrement, start, end, type, weight);
+create table type_node(name, num);
+create table type_relation(name, num, extended_name, info);
+create table user(login primary key, mail, hash_passwd, score);
+create table game(gid integer primary key autoincrement, eid_central_word, relation_1, relation_2, difficulty);
+create table game_cloud(gid, num, difficulty, eid_word, totalWeight, probaR1, probaR2, probaR0, probaTrash);
+create table played_game(pgid integer primary key autoincrement, gid, login);
+create table played_game_cloud(pgid, gid, type, num, relation, weight, score);
+
+create index i_relation_start on relation(start);
+create index i_relation_end on relation(end);
+create index i_relation_type on relation(type);
+create index i_relation_start_type on relation(start,type);
+create index i_relation_end_type on relation(end,type);
+\end{verbatim}
+
TODO: UML, diagrammes de classes, Use cases, etc...
@@ -87,6 +126,22 @@ TODO: UML, diagrammes de classes, Use cases, etc...
\item Caractères non échappés dans le dump de la base.% gd
\end{itemize}
+\subsubsection{Itération 1, semaine 3}
+\begin{itemize}
+\item SQLite3 n'est pas capable d'utiliser un index pour la requête extérieure sur une requête du type
+\begin{verbatim}
+select * from (select * from table where condition) where condition
+\end{verbatim}
+Donc nécessité de ré-écrire certaines requêtes avec des jointures à priori beaucoup moins efficaces, mais qui le sont plus grâce aux index.
+\item SQLite3 tranforme les requêtes de la forme~:
+\begin{verbatim}
+select * from table limit 100 order by random();
+\end{verbatim}
+ en une requête qui récupère tout le set de résultats, ajoute une colonne random(), prend les 100 premiers résultats et les trie. Mais cela
+ l'oblige à récupérer tout le set de résultats, et calculer le random() pour chaque ligne, pour ensuite jeter tout ce qui dépasse la ligne
+ 100. Cela est évidemment très coûteux dans le cadre de requêtes avec beaucoup de résultats, et nous avons donc dû isoler la requête avec
+ \verb!limit! de son \verb!order by! avec des «hacks» assez tordus.
+\end{itemize}
\section{Conclusions}