Après un mois de silence, dûs au travail sur mon projet de groupe pour SUPINFO, j'ai enfin décidé de refaire un nouvel article, encore sur PHP. Promis, la prochaine fois, je le fais sur autre chose :p . Aujourd'hui, l'article sera sur la recherche de texte similaire. Vous savez, lorsque vous cherchez sur Google et que vous faites une faute de frappe, il vous propose une expression approchante. Et bien c'est ça. Bien sûr, je ne vais pas faire une fonction aussi étoffée que celle de Google (déjà parce qu'il faut une base de données énorme pour pouvoir sortir un truc comme eux et que je suis pas aussi intelligent que les mecs qui bossent là-bas), mais je vais approcher les bases du concept (enfin de ce que j'ai pu trouver seul, je ne me suis pas documenté là-dessus au préalable).
Tout d'abord, je vous explique pourquoi j'ai eu besoin de faire cet algorithme : c'est encore une fois pour mon MUD, pour pouvoir proposer une correction lorsque le joueur se trompe de commande/de mot (par exemple, lorsqu'il veut prendre un objet, mais qu'il se trompe dans le nom). C'est histoire de faire en sorte que ce soit ergonomique et que le joueur ne soit pas perdu si il tape mal son mot et que le jeu l'aide à trouver ce qu'il cherchait.
Mon algorithme se base sur 2 fonctions : metaphone() et levenshtein().
Pour le cas de metaphone(), j'aurais pu aussi utiliser soundex() qui était disponible, et décrite par Donald Knuth dans son TAOCP (vol. 3), mais j'ai décidé de choisir metaphone() car elle est plus précise, notamment sur les mots en anglais (elle intègre plusieurs règles de prononciation anglaise), et permet de faire de la recherche de proximité sur les clés qu'elles renvoie (car soundex() ne renvoie que des clés de 4 caractères, alors que pour metaphone(), ce sont des clés de taille variable qui sont renvoyées).
Bref, au final, l'algorithme fait une recherche de similarité phonétique, puis ensuite (si la recherche n'a rien donné) passe à une recherche lettre-par-lettre.
Avant de commencer, je tiens juste à spécifier une chose : la fonction prendra pour acquis que les deux mots sont différents. Il faudra mettre une condition/boucle avant l'appel à la fonction pour vérifier l'égalité des variables. Enfin bref.
Tout d'abord, on va faire une vérification phonétique exacte, c'est-à-dire que l'on va vérifier si une de nos chaînes en données (i.e. celles dans lesquelles on doit chercher) correspond phonétiquement à la chaîne "mystère". On va donc utiliser les clés metaphone pour ça, et au final, ça ressemble beaucoup à une recherche d'égalité de chaîne :
$needleMetaphone = metaphone($needle); $metaphoneKeys = array(); //Analyse phonétique Metaphone foreach($haystack as $item) { //C'est pour utiliser plus tard, histoire de pas faire 15 fois les mêmes clés. $metaphoneKeys[$item] = metaphone($item); if($metaphoneKeys[$item] == $needleMetaphone) return $item; }
Bon, pour le return, faut vous imaginer qu'on utilise ce code dans un contexte de fonction (si si, parce qu'on va rajouter des trucs derrière, donc dans une fonction c'est bien).
Alors ici, rien de plus simple : on calcule les 2 clés metaphone (enfin on en calcule une avant, puis l'autre), et on regarde si elles sont exactes. Le cas échéant, on retourne la valeur demandée. Comme je l'ai commenté dans le code, j'ai mis une petite feature pour les clés metaphone, c'est de les garder en mémoire. En effet, on va s'en servir plus tard, ça ne sert à rien de les recalculer à chaque fois (surtout la clé metaphone de la chaîne recherchée, elle est la même durant toute la fonction).
Bon, maintenant, imaginons que la recherche phonétique exacte ne marche pas : le mec qui fait la recherche sait ni parler, ni écrire, l'algorithme metaphone marche bien sur l'anglais mais moins sur le reste, etc. Nous allons alors élargir un peu notre champ de recherche en incluant la recherche phonétique approximative.
Alors, pour faire de la recherche phonétique approximative, on va encore avoir besoin des clés metaphone des différentes chaînes de caractères que l'on cherche. Voilà pourquoi j'ai fait une "mise en cache" des variables traitées dans la boucle d'au-dessus, elle vont nous éviter de tout recalculer et de perdre du temps pour rien.
Ici, le but sera de juste calculer la distance entre les clés metaphone (la distance Levenshtein, oui, c'est utile pour faire des comparaisons de chaînes de caractères, alors pourquoi pas de clés, qui sont des chaînes de caractères ?), puis de prendre la clé avec la distance la plus petite par rapport à celle cherchée, donc logiquement correspondant à l'entrée la plus proche de la valeur recherchée.
Au final, le code qui permet de gérer ça est juste une simple boucle :
$levenshtein = 0; $ret = ''; //Analyse de proximité metaphone foreach($haystack as $item) { if(!$levenshtein || $levenshtein > levenshtein($metaphoneKeys[$item], $needleMetaphone)) { $ret = $item; $levenshtein = levenshtein($metaphoneKeys[$item], $needleMetaphone); } }
Au final, après cette boucle, la variable $ret contiendra la valeur de notre liste la plus approchante de la valeur fournie.
Bon c'est cool, maintenant on peut faire des recherches phonétiques, et donc donner des suggestions aux mecs qui font des fautes d'orthographe, mais on ne peut toujours pas remédier au problème le plus fréquent lors d'une saisie de texte : les fautes de frappe. C'est pourquoi l'on a besoin de rajouter une petite boucle encore pour que la fonction soit plus précise.
Bon, après les clés metaphone, cette partie c'est de la touchette : le truc, ça va juste être de modifier la partie précédente (qui fait une recherche de similarité sur les clés Metaphone) pour qu'elle fasse une recherche directement sur le texte dont il est question.
Pas besoin d'épiloguer sur le contenu de notre boucle cette fois-ci, je l'ai déjà fait précédemment. Le résultat, c'est ça :
foreach($haystack as $item) { if(!$levenshtein || $levenshtein > levenshtein($item, $needle)) { $ret = $item; $levenshtein = levenshtein($item, $needle); } }
Voilà, grâce à cette boucle, $ret contiendra au final la valeur qui est la plus proche au niveau des caractères de $needle (donc la suggestion la plus concordante).
Maintenant qu'on a toutes les parties, le temps est venu de faire un mix de tout ça pour avoir une belle fonction qui nous calcule tout ça automatiquement et nous renvoie la meilleure solution.
Allez, on y va, on colle tout, et j'explique après :
function getMostSimilarString($needle, $haystack) { $needleMetaphone = metaphone($needle); $metaphoneKeys = array(); //Analyse phonétique Metaphone exacte foreach($haystack as $item) { $metaphoneKeys[$item] = metaphone($item); if($metaphoneKeys[$item] == $needleMetaphone) return $item; } $levenshtein = 0; $ret = ''; //Analyse de proximité metaphone foreach($haystack as $item) { if(!$levenshtein || $levenshtein > levenshtein($metaphoneKeys[$item], $needleMetaphone)) { $ret = $item; $levenshtein = levenshtein($metaphoneKeys[$item], $needleMetaphone); } } //Analyse de la distance de Levenshtein, si l'analyse de la clé Metaphone n'a rien donné. foreach($haystack as $item) { if(!$levenshtein || $levenshtein > levenshtein($item, $needle)) { $ret = $item; $levenshtein = levenshtein($item, $needle); } } return $ret; }
Bon, alors on remarque bien les 3 blocs, et on voit bien maintenant que la mise en mémoire lors de la première boucle des clés metaphone est utile, vu qu'on a pas à les recalculer lors de la seconde boucle. Ensuite, je dois m'expliquer sur la raison pour laquelle j'ai mis l'analyse approximative metaphone avant l'analyse textuelle approximative (l'analyse allant normalement du plus précis au plus vague, l'ordre aurait dû être inversé).
Les raisons pour lesquelles j'ai fait ça, c'est tout d'abord parce que j'ai souhaité mettre en valeur la phonétique avant la rigueur d'écriture. Je l'ai fait aussi parce que metaphone, comme indiqué sur la doc' PHP, est une fonction programmée avec les règles de prononciation anglaise (en tout cas metaphone simple, apparement, double metaphone serait capable de comprendre le français, mais je divague). Enfin, je l'ai fait parce que je souhaitais donner une priorité à la recherche directe de caractère par rapport à la recherche phonétique, mais seulement si celle-ci est plus concluante (ce qui peut être le cas lors des fautes de frappe), car, comme une clé metaphone est plus courte que le mot (j'ai compté environ 2 lettres par syllabe), il faut que le nombre de lettres d'écarts entre la suggestion et le mot mal orthographié soit relativement petite (et que la différence phonétique soit énorme) pour qu'elle prenne le pas.
Voilà, c'est fini. J'espère que vous avez appris quelques trucs (au moins autant que j'ai appris en écrivant cette petite fonction) en lisant cet article.
Sinon, le point hors-sujet du jour : la semaine dernière, je suis allé au Hellfest, c'était juste trop génial. Les concerts de groupes trop awesome (notamment Maximum The Hormone), voir les copains bourrés, l'ambiance qui tue, discuter avec des étrangers qui ont la même passion... Mais bon mon compte en banque n'a pas trop aimé lui (150€ en tout pour 1 jour, ça fait mal). Maintenant, je vais devoir faire des économies, et, qui sait, peut-être que l'année prochaine, j'irai encore :D .
Bref, je vous dis à une prochaine fois.
Parfait, parfait, j'exagère un peu. Mais il faut avouer que c'est bien fun.
Bon alors on va remettre les choses en place pour les deux qui dorment au fond : Lua (Lune en portugais), c'est un langage de programmation créé par 3 personnes de l'université de Rio de Janeiro. Il est libre, et a été conçu dans le but d'être très léger, ce qui fait que son interpréteur pèse environ 100 Ko (comparé à PHP qui pèse 15 Mo...). Il est tellement compact que certains petits jeux indépendants ont décidé de l'utiliser comme moteur de scripts : WoW, Garry's mod, Far Cry... Et il a même été porté sur la Nintendo DS (avec MicroLua DS, fait par un français) et la PSP. L'article Wikipédia sur Lua vous en dira plus sur le sujet.
Maintenant, venons en au faits, c'est à dire l'interaction entre PHP et Lua. Eh ouais, parce que, avant d'avoir été un langage interprété en standalone, il a été écrit sous forme d'une bibliothèque C, afin de rendre l'interaction avec les programmes C plus facile (pour créer un système d'extension simplement par exemple, comme sur WoW). Par la suite, quelques développeurs d'extensions PHP qui passaient par là on eu l'idée de développer une extension PHP pour utiliser Lua depuis notre site web ou, comme moi, depuis un programme, et c'est de là qu'on est arrivé à PHP-Lua. Bref, c'est fini pour la petite histoire, on passe à l'installation.
Néanmoins, il faut savoir que l'extension officielle est en alpha, et donc considérée comme non-stable. Cependant, je travaille pour ma part sur un fork de cette extension, disponible ici. Ce fork est plus à jour que la version officielle de PHP-lua (le dernier commit date de 6 jours au moment ou j'écris), et un peu plus évolué et propose quelques fonctionnalités en plus dont j'ai eu besoin. Je vous conseille de faire un clonage du dépôt Git de phplua, plutôt que de télécharger l'archive qu'il propose, le dépôt étant bien évidemment plus à jour.
Pour la compilation, rien de plus simple (penser à avoir installé Lua au préalable est une bonne idée aussi) :
phpize ./configure make make install
Mais, comme je tourne sur un système Debian, ça ne marche pas aussi simplement. En effet, Debian appelle la bibliothèque Lua d'une autre façon que les autres, ce qui implique de changer un peu le Makefile avant de lancer make. J'ai dû par conséquent aller chercher dans le Makefile la variable LUA_SHARED_LIBADD, pour remplacer sa valeur par :
LUA_SHARED_LIBADD = -llua5.1
Et ensuite, l'extension a compilé correctement. Si vous avez essayé et que vous n'y arrivez pas, je vous propose quand même ma build, réalisée sur Debian, avec Lua 5.1 d'installé : c'est ici. Copiez-le dans /usr/lib/php5/le dossier de votre version/ et ajoutez "extension=lua.so" à votre php.ini et c'est bon !
Une fois que l'extension a été installée, il me reste plus qu'à tester son fonctionnement. Pour ça, un tout bête script :
<?php $lua = new lua(); $lua->evaluate('print("Hello World !\n")');
Et là, magie ! Un "Hello World" apparaît, signalant que tout fonctionne bien. Bon, maintenant, on va passer au coeur du sujet : faire des trucs marrant en Lua avec PHP.
Au tout début, j'ai pas fait des trucs hyper évolués, j'ai juste essayé d'exécuter un script Lua tout banal. Fallait que je me fasse un peu au concept de programmer sur 2 langages en même temps et tout. Bref. Donc après avoir fait le programme de test que j'ai écrit quelques lignes plus haut, qui évalue une chaîne écrite en Lua, et qui affiche via Lua un Hello World. Très puissant comme programme... J'ai un peu corsé le jeu donc en mettant le script dans un fichier à part, et en rajoutant quelques trucs :
Fichier Lua :
fp = io.open('tmpfile', 'a+') msg = io.stdin:read() for i = 0, 100 do fp:write(msg.."\n") end fp:close()
Fichier PHP :
<?php $lua = new lua(); $lua->evaluatefile('test.lua');
Bon, c'est vrai que c'est pas beaucoup plus compliqué que l'autre script, mais ça m'a permis de tester un certain truc de Lua qui aurait pu ne pas marcher avec PHP, c'est à dire la lecture à partir de l'entrée clavier (c'est la fonction io.stdin:read() ). Concrètement, ce programme prend en entrée une chaîne de caractères et l'écrit 100 fois dans le fichier tmpfile. Enfin, rien de bien excitant. On peut donc passer à la suite, parce que ce que j'ai fait, on peut le faire avec un shell_exec('lua test.lua'), et donc que pour l'instant ici cette extension est un peu inutile.
Ensuite, j'ai décidé de donner enfin une utilité à cette extension, en appelant des fonctions Lua depuis PHP. A première vue, ça peut avoir l'air de ne pas servir à grand chose, mais mine de rien c'est utile. Mais on le verra un peu plus bas. Pour l'instant, un exemple simple d'appel de fonction Lua depuis PHP, avec la méthode call_function() :
Fichier Lua :
function copyLines(msg) fp = io.open('tmpfile', 'a+') for i = 0, 100 do fp:write(msg.."\n") end fp:close() end function flushTmpFile() fp = io.open('tmpfile', 'w+') fp:close() end
Fichier PHP :
<?php $lua = new lua(); $lua->evaluatefile('test.lua'); $lua->call_function('flushTmpFile', array()); $lua->call_function('copyLines', array('some creepy messages...'));
Donc avec ça, on commence à devenir utile, en appelant des fonctions Lua depuis PHP, ce qui nous permet de faire des trucs plutôt fun, comme par exemple de faire des programmes PHP scriptables en Lua. Ou encore de pouvoir recharger tout le contexte programmé en Lua sans avoir à redémarrer le programme (il suffit de détruire et de rdéclarer l'objet). Mais, bon, pour pouvoir profiter à fond de ça, il va encore falloir rajouter quelque chose.
Après que j'aie réussi à lancer des fonctions Lua depuis PHP, j'ai décidé d'aller plus loin en voyant comment lancer des fonctions PHP depuis Lua. En réalité, c'est pas vraiment plus compliqué que de lancer des fonctions Lua depuis PHP. Pour pouvoir le faire, il suffit de dire à l'objet Lua que la fonction "truc" en PHP correspond à la fonction "machin" en Lua. Et ensuite, il suffit juste d'appeler la fonction Lua, et l'extension fait le reste (les paramètres Lua sont les mêmes qu'en PHP).
Note : Cette fonctionnalité n'est pas disponible dans la version PECL de l'extension, elle a été ajoutée uniquement dans la version forkée. Une raison de plus de passer sur cette extension :)
Bon, au final, ça rend un truc comme ça :
Fichier Lua :
money = getMoney() print(money.."\n") setMoney(125) money = getMoney() print(money.."\n")
Fichier PHP :
<?php $money = 0; function moneySetValue($val) { global $money; $money = $val; } function moneyGetValue() { global $money; return $money; } $lua = new lua(); $lua->expose_function("setMoney", 'moneySetValue'); $lua->expose_function("getMoney", 'moneyGetValue'); $lua->evaluatefile('test.lua');
Et comme résultat, on le voit bien, il change la valeur de la variable PHP. C'est super. Et après avoir vu tout ça, j'ai décidé de mettre tout ça en application dans un truc assez sympa.
Comme j'aime pas mal le concept de la programmation évènementielle, j'ai voulu faire un système de gestion d'events pour Lua, pour faire par exemple un système de plugins rechargeables dynamiquement. Un truc bien utile quoi. Le tout consiste en une classe :
<?php /* * Lua Events Class * * Copyright 2011 Yohann Lorant <yohann.lorant@gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * */ class LuaEvent { private $_lua; private $_events; public function __construct($files) { $this->_files = $files; $this->reload(); } public function reload() { $this->flushLua(); $this->loadLua($this->_files); $this->initLua(); } public function initLua() { $this->_lua->call_function('__init', array()); } public function loadLua($files) { foreach($files as $file) { if(is_file($file)) $this->_lua->evaluatefile($file); } return TRUE; } public function flushLua() { $this->_events = array(); $this->_lua = new lua(); $this->_lua->expose_function('addEvent', array($this, 'addEvent')); $this->_lua->expose_function('delEvent', array($this, 'delEvent')); } public function addEvent($event, $callback) { if(!isset($this->_events[$event])) $this->_events[$event] = array(); $this->_events[$event][] = $callback; return TRUE; } public function delEvent($event, $callback) { if(!isset($this->_events[$event])) return FALSE; if(!$search = array_search($callback, $this->_events[$event])) return FALSE; unset($this->_events[$event][$search]); if(empty($this->_events[$event])) unset($this->_events[$event]); } public function execEvents($cmd) { if(isset($this->_events[$cmd[0]])) { foreach($this->_events[$cmd[0]] as $ev) @$this->_lua->call_function($ev, array($cmd)); } } }
Et pour tester ça, j'ai utilisé un petit script PHP qui reproduit un shell et appelle les fonctions Lua associées aux events Lua déclarés dans la fonction __init() (en Lua) :
<?php $files = array('test.lua'); $events = new LuaEvent($files); $continue = 1; while($continue) { echo "> "; $cmd = trim(fgets(STDIN)); $cmd = explode(' ', $cmd); $events->execEvents($cmd); if($cmd[0] == 'quit') $continue = 0; elseif($cmd[0] == 'reload') $events->reload(); echo "\n"; }
Et le fichier test.lua associé, qui contient nos commandes (bien sûr, comme les commandes sont complètement désolidarisées du PHP, vous pouvez écrire le vôtre avec vos propres commandes, ça marchera) :
function __init() addEvent('hello', 'cmdHello') addEvent('quit', 'cmdQuit') print('Init done.'.."\n") end function cmdHello(cmd) print('Oh, Hello '..cmd[1]..'. It\'s been a loooong time...'.."\n") end function cmdQuit(cmd) print('You know what ? You have won. Just go.'.."\n") end
Et lorsque l'on exécute, oh magie !
[yohann@Scruffy:~/Programmes]$ php testlua.php Init done. > hello john Oh, Hello john. It's been a loooong time... > reload Init done. > hello I've been very very busy being dead... Since you murdered me. > quit You know what ? You have won. Just go. [yohann@Scruffy:~/Programmes]$
Voilà, cet article (plutôt long) sur PHP et Lua arrive à sa fin. Grâce à ça, j'ai pu commencer à développer un MUD en tant que plugin de mon serveur IRC, qui utilise des scripts Lua pour gérer les events du jeu, ainsi que pour créer des nouvelles commandes et donc permettre au programmeur du MUD de customiser ton jeu sans toucher au moteur en PHP (mais bon, il manque encore plein de trucs, il est absolument pas utilisable en l'état).
Bref, avant de finir, je vais faire un petit HS en vous parlant d'un artiste que vous devriez écouter : c'est allemand, ça mixe de la musique 8-bit avec de la guitare, et ça s'appelle Pornophonique. Je vous conseille vraiment d'écouter, c'est un super groupe, et ils font de la bonne musique. En plus, c'est de la musique libre, donc gratuite à écouter. Que du bon quoi. Si vous voulez en savoir plus, rendez-vous sur leur site officiel.
A plus les gens.
Ceci est votre premier billet. Quand vous serez prêt à bloguer, connectez-vous pour le modifier ou le supprimer.
Eh ouais, je fais un blog. J'en avais jamais fait avant, je découvre quelque chose. En même temps, ma vie personnelle est un peu inintéressante, vu que je suis tout le temps sur mon PC, donc l'utilité d'un blog ne m'est pas tout de suite venue à l'esprit. Mais bon quand je cherche des infos sur Internet, je tombe pas mal de fois sur des blogs, donc je me dis à la fin "Je fais que prendre, faut bien donner des fois.".
Bon, tout d'abord, je me présente : Yohann, 19 ans, un mec boutonneux. Je suis pour l'instant en B1 (première année) à SUPINFO (on va éviter de troller sur le bien/mal de cette école, les sujets sont déjà légion sur l'interwebz) sur Nantes, où je passe mon temps à programmer (bon, il n'y a pas que programmer dans les cours mais...), parce que j'aime ça. Ouais, je suis plus programmeur qu'admin système, même si j'aime bien quand le système que j'utilise est bien configuré. Par contre, j'aurais tendance à faire un tas de scripts pour que toutes les tâches d'administration que j'aurais besoin de faire soient faisables facilement, si j'avais été admin système.
Personnellement, j'ai un faible pour le PHP comme langage de programmation. C'est pas que j'aime faire des sites web, mais j'aime ce langage, et je fais des programmes avec. Mais genre des vrais programmes, que tu lances pas dans un navigateur mais dans une console (d'ailleurs mon plus gros projet en est un, Leelabot. Et même des fois des programmes graphiques, avec l'aide de PHP-GTK. On peut donc déduire de ça que ce blog contiendra quelques articles sur PHP, les programmes en PHP, et PHP-GTK. Mais cela n'empêche pas que je mette mes yeux du côté d'autres langages : C, Lua, XHTML, CSS, Javascript (j'ai regardé du côté de MongoDB d'ailleurs, c'est sympa)... J'ai même commandé Programming Perl il y a quelques jours (le bouquin de référence sur Perl si vous ne saviez pas).
Sinon, à part la programmation, j'aime GNU/Linux. C'est un super système, libre et gratuit, et super pratique quand tu sais t'en servir. La plupart de mes PCs tournent sous Debian Sid, et je garde mon PC le plus puissant sous Fedora (l'installation des pilotes graphiques propriétaires sous Debian m'ayant quelque peu ennuyé, j'ai décidé de switcher, l'installation sous Fedora étant beaucoup plus simple : yum install kmod-nvidia, après avoir activé les dépôts RPMFusion). J'utilise comme gestionnaire de fenêtres Openbox, avec comme barre des tâches AWN et un petit conky qui me sert pas à grand chose, vu que je met pas souvent le nez sur mon bureau.

L'état actuel de mon bureau, en compressé JPEG moche.
Je posterai donc à l'occasion des trucs sur GNU/Linux, mais je suis loin d'être expert de ce système. Très loin.
Sinon, dans la vie, j'aime regarder des séries télé et des anime. Enfin pas tous. Niveau série télé, je regarde How I Met Your Mother (que pas mal de monde aime dans les personnes que je fréquente sur Internet apparement), The Big Bang Theory et quelques séries à droite à gauche. Je suis également un assez grand fan de Stargate. Sinon niveau anime, j'en ai regardé quelques uns, car je n'aime pas les endless anime (genre Naruto et One Piece). Comme le montre mon fond d'écran quelques lignes plus haut, j'ai aimé Puella Magi Madoka Magica, un anime de magical girl assez sympa. Sinon, pêle mêle, on peut trouver : Neon Genesis Evangelion, Gurren Lagann, Lucky Star, K-On!, Star driver...
Ensuite, j'ai un faible pour les jeux vidéo. Des jeux vidéo récents (Portal 2 est awesome, j'attends avec impatience Duke Nukem Forever), mais pas beaucoup. Je suis plus orienté anciens jeux vidéo, le retrogaming, comme ils disent aux US. Je suis assez fan de Nintendo (je dispose de toutes leurs consoles excepté la Wii et le Virtual Boy qui est devenu un article de collection), et j'apprécie les jeux de SEGA (notamment la série des Sonic, enfin jusqu'à Sonic 3, côté 3D, je n'aime que Sonic Adventure 2).
Cette préférence pour le retrogaming vient rencontrer un autre de mes légers centres d'intérêts que je n'ai pas pu mettre en oeuvre encore (faute de moyens) : l'électronique. En effet, j'aime bidouiller les circuits électroniques, et le hardware. J'envisage de me payer un arduino dès que j'aurai les moyens pour faire des trucs sympas avec, notamment au niveau de l'interaction avec d'autres circuits.
Sinon, que dire de plus sur moi ? Je suis un amateur de musique Metal (mais je n'écoute pas toujours ça), touchant un peut à tout. J'aime souvent faire les choses d'une manière qui est détournée, ou détourner des concepts (comme par exemple le forum de discussions en full-CLI). J'ai des tendances légères au troll, mais la plupart du temps, ce n'est que pour m'amuser.
A plus (c'est pourri comme conclusion, mais j'ai pas trouvé mieux, mais bon vu l'introduction, ça fait ton sur ton, c'est harmonisé).