This commit is contained in:
Bertrand BRUN 2011-02-04 13:30:03 +01:00
commit e02b802ea8
8 changed files with 219 additions and 29 deletions

View File

@ -19,4 +19,6 @@
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
</manifest>

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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 <code>true</code> si on a access a internet <code>false</code> 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 <code>true</code> si la combinaison login/mdp est correct <code>false</code> 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;

View File

@ -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);

View File

@ -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);
?>

View File

@ -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

View File

@ -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}