Dec 09 2009

The Game of Life

Category: C, SDLFaltad @ 1:50 pm

Avant de commencer, il faut définir ce qu’est un automate cellulaire :
Grossièrement, c’est une grille ou tableau qui contient un nombre défini de cellules.
Il y a plusieurs états possible pour une cellule et chaque cellule dépend de l’état de ses voisines.
A chaque génération, on applique un ensemble de règles sur la grille, ce qui donne une nouvelle grille et ainsi de suite.

Parmi les automates cellulaires, on peut s’intéresser plus particulièrement au jeu de la vie.

Inventé en 1970 par John Horton Conway, il possède quatres règles très simple et chaque cellule possède seulement deux états : morte ou vivante.

Les règles sont :

  • Chaque cellule avec moins de 2 voisines vivantes meurt (ou reste morte).
  • Chaque cellule avec plus de 3 voisines vivantes meurt aussi (ou reste morte).
  • Chaque cellule vivante avec 2 ou 3 voisines vivantes reste en vie.
  • Chaque cellule morte avec 3 voisines vivantes ressuscite.

Je me suis amusé à faire une implémentation de ce jeu, à l’aide de la SDL.
Cela se représente facilement à l’aide d’un double tableau, où chaque cellule correspond à une case.

Voici un petit aperçu :
life

Le piège qu’il peut y avoir, c’est de bien faire gaffe au fait que toutes les cellules sont modifiées en même temps. Il faut donc deux tableaux pour avoir l’ancienne génération, et celle à venir.
Avec la SDL, on peut s’amuser à rendre le jeu un peu plus intéractif, grâce aux événements et autre.
J’ai implémenté la mise en pause du programme, ainsi que la modification de la carte à l’aide de la souris.

Ça a l’air un peu brouillon présenté comme cela, mais il y a de nombreuses structures qui reviennent assez souvent :
Par exemple les vaisseaux, c’est un ensemble de cellules qui peut se déplacer, sans se détruire. Le plus connu étant le glider.

On peut avoir aussi des canons, c’est une structure qui générera indéfiniment des vaisseaux, comme par exemple le canon à glider :
glider

Il y a de nombreuses autres structures, plus compliquées, ou plus simple que l’on peut retrouver facilement sur google.

Pour colorier l’écran, je me suis servi de ces deux fonctions :


/* On colorie une zone pixel par pixel */
void            color_cell(int i, int j, int boolean, SDL_Surface *screen)
{
   int          x, y;

   if (boolean)
      boolean = SDL_MapRGB(screen->format, 0, 0, 0);
   else
      boolean = SDL_MapRGB(screen->format, 255, 255, 255);
   for (y = j; y < j + SIZE_CELL; y++)
      for (x = i; x < i + SIZE_CELL; x++)
         setPixel(screen, x, y, boolean);
}
/* Colorie un pixel sur l'écran */
void setPixel(SDL_Surface* screen, int x, int y, Uint32 color)
{
   Uint8 *p;

   if (x < screen->w && y < screen->h)
    {
       p = (Uint8 *)screen->pixels +
	y * screen->pitch + x * screen->format->BytesPerPixel;
      *(Uint32 *)p = color;
    }
}

J'ai préféré utiliser seulement la SDL_Surface répresentant l'écran, en modifiant pixel par pixel, pour mieux gérer les tailles des cellules.
Bon, voilà, si vous voulez vous amuser, les sources sont ici

Il vous faut la libsdl-dev, sinon, ça ne risque pas de compiler !

(Vous pouvez aussi regarder du côté de golly, c'est "un peu" plus complet)

Avec un peu de random de temps à autre, ça peut faire un joli écran de veille :D
N'hésitez pas à creuser le sujet, ceci n'était qu'un très simple aperçu !


Nov 06 2009

Python – Curses – Wrapper

Category: PythonFaltad @ 1:50 pm

Bonjour

La semaine passée, j’ai découvert et apprécié la toute puissance de Python.
Dans le cadre d’un mini projet, je me suis intéressé plus particulièrement à la bibliothèque curses.
Elle permet d’intéragir facilement avec son terminal, voir la page de la documentation pour plus d’information.

Alors, pour bien commencer, il faut importer curses, afin de pouvoir l’utiliser:

import curses

#On initialise curses
screen = curses.initscr()
screen.addstr(3, 3, "Hello World")
c = 0
#On boucle jusqu'à ce que l'utilisateur tappe la lettre 'q'.
#Attention, getch renvoit un int (pour les touches supérieures à 255
#Il faut donc convertir q en int
while c != ord('q'):
    c = screen.getch()

#ici on remet le terminal dans son état normal
curses.endwin()

Si vous essayer ce code, vous allez voir les caractères que vous tapez s’afficher à l’écran.
Il faut donc désactiver le mode echo du terminal.
Au passage, on peut empêcher la bufferisation du terminal ainsi que permettre à curses de gérer les
touches renvoyant plusieurs octets comme Home, End, Page Up, Page Down…

import curses

screen = curses.initscr()
#On initialise donc les options
curses.noecho()
curses.cbreak()
screen.keypad(1)

screen.addstr(3, 3, "Hello World")
c = 0
while c != ord('q'):
    c = screen.getch()

#Et on remet le term comme il faut
curses.echo()
curses.nocbreak()
screen.keypad(0)

curses.endwin()

Maintenant, un petit problème : si l’utilisateur appuie sur ctrl+c ou s’amuse à tuer notre application, elle ne réinitialisera pas le term… et il sera tout bousillé :(
La solution serait de faire comme ceci :
(On peut apprécier la belle syntaxe de Python sans accolade :) )

import curses
#On importe le module de gestion des erreurs
import traceback

#On definit une fonction qui reset le term, et si une erreur est definie
#on l'affiche.
def reset_term(screen, error = 0):
    curses.echo()
    curses.nocbreak()
    screen.keypad(0)
    curses.endwin()
    if error == 1:
        print traceback.format_exc()

screen = curses.initscr()
curses.noecho()
curses.cbreak()
screen.keypad(1)
#Initialisation des couleurs
curses.start_color()

#Hop, on attrape toutes les exceptions et on les renvoie
# sur la fonction reset_term.
try:
    #On initialise une paire de couleur.
    curses.init_pair(1, curses.COLOR_RED, 0)
    screen.addstr(3, 3, "Hello World", curses.color_pair(1))
    screen.refresh()
    c = 0
    while c != ord('q'):
        c = screen.getch()
    reset_term(screen)
except:
    reset_term(screen, 1)

Bon, le code commence à s’étoffer, avec presque que des initalisations et gestion d’erreur, ça fait beaucoup pour rien !
C’est là qu’intervient la fabuleuse fonction curses.wrapper !
Une petite explication s’impose :
curses.wrapper va s’occuper d’initialiser le terminal toute seule (avec la couleur aussi), et gérer les erreurs aussi.
Elle prends comme argument une fonction qu’elle s’occupera d’appeler une fois l’initialisation effectuée, ainsi que le nombre d’arguments que vous voulez lui envoyer.

import curses

# La fonction recevra obligatoirement une variable qui
# contiendra le retour de curses.initscr()
def main(screen, str):
    curses.init_pair(1, curses.COLOR_RED, 0)
    screen.addstr(3, 3, str, curses.color_pair(1))
    screen.refresh()
    c = 0
    while c != ord('q'):
        c = screen.getch()

curses.wrapper(main, str)

Beaucoup plus simple non ?
Voili voilou, premier article sur python soyez indulgent :)
Merci !

Tags: ,


Oct 14 2009

Headers d'un mail.

Category: Réseau, sécuritéFaltad @ 11:32 pm

Bonjour !

Ça commence à faire un bout de temps que je n’ai rien écrit. Je vais essayer de changer un peu ça, notamment avec ce que j’ai découvert aujourd’hui.

En bref, je me suis rendu compte qu’il faut toujours vérifier les en-têtes des messages importants/bizarres que l’on reçoit.

Et ceci parce qu’il est très facile d’envoyer un message dont l’adresse de provenance est falsifié. Démonstration

Il suffit de se connecter par le biais de telnet sur un serveur smtp : (port 25 par défaut)

.-(~)-------------------------------------------------(faltad@Lily)-
`--> telnet smtp.free.fr 25
Trying 212.27.48.4...
Connected to smtp.free.fr.
Escape character is '^]'.
220 smtp2-g21.free.fr ESMTP Postfix

Une fois le lien établi, normalement, sur la plupart des smtp, il faut s’authentifier.

auth plain
503 5.5.1 Error: authentication not enabled

Mais ici, on s’aperçoit que l’authentification n’est pas activé !
Essayons alors d’envoyer un mail :

mail from: hadopi@assemblee-nationale.fr
250 2.1.0 Ok
rcpt to: mon_mail@hote.fr
250 2.1.5 Ok
data
354 End data with .
On tappe notre super message ici. Et pour finir, on
utilise un \n suivi d'un point suivi d'un autre \n.
.
250 2.0.0 Ok: queued as D492A4B1C64

Et hop, magie, le message a été envoyé !

Et maintenant, citation d’un client de messagerie normal :

From: hadopi@assemblee-nationale.fr
Sent: Wed 14/10/09 23:07
—-

On tappe notre super message ici. Et pour finir, on
utilise un \n suivi d’un point suivi d’un autre \n.

On peut par contre voir la provenance d’un tel message en regardant les headers, notamment cette ligne :
(Ou l’adresse IP de l’envoyeur ainsi que l’identité du serveur smtp utilisé sont affichés)

Received: from lgp44-4-88-160-58-65.fbx.proxad.net (lgp44-4-88-160-58-65.fbx.proxad.net [88.160.58.65])

	by smtp2-g21.free.fr (Postfix) with SMTP id 1E0C04B1E25

Mais, un novice en informatique, se fera prendre au piège, surtout s’il ne sait pas ce que sont les headers d’un mail.
C’est la porte ouverte au phishing !

Je ne parlerais pas des moyens de camouflage des “pirates” utilisables, car n’y connaissant que très peu de choses, j’aurais peur de dire des stupidités :)

En conclusion, il faut toujours vérifier les headers d’un message important/bizarre, et si cela s’avère nécessaire, se renseigner auprès de l’expéditeur si possible.

Tags: , , ,


Aug 12 2009

Botirc, mon copain

Category: C, RéseauFaltad @ 12:56 am

Cette dernière semaine m’est venu l’envie de coder un bot, pour irc, comme ça, sur un coup de tête.

Pour être honnête, je ne savais pas du tout par où commencer, mais j’avais jeté mon dévolu sur le langage C.
Puis, je suis tombé sur cette merveilleuse chose qu’est la RFC.

Donc, si on abrège cette divine documentation, pour se connecter à un serveur, il faut, outre se connecter au serveur lui même, envoyer dans un certain ordre des commandes pour s’identifier.
On se connecte :

/*
** On se connecte d'abord au serveur. Ici, c'est se.quakenet.org
*/

sock = xsocket(AF_INET, SOCK_STREAM, 0);
sin.sin_addr.s_addr = inet_addr(host);
sin.sin_family = AF_INET;
sin.sin_port = htons(6667);
if (connect(sock, (struct sockaddr *) sin, sizeof(struct sockaddr)) == -1)
{
        perror("connect()");
        exit(errno);
}

Ensuite, la RFC nous dit qu’il faut envoyer une commande NICK, suivi d’un USER.

/*
** Envoi des identifiants au serveur.
** Le NICK est de la forme suivante : "NICK :pseudo\n"
** Et le USER, lui, est : "USER nom hôte nom de serveur :pseudo\n"
** un snprintf dans un buffer alloué suffisament peut-être intéressant.
*/

snprintf(nick, SIZE_BUFFER, "NICK :%s\n", nickname);
snprintf(user, SIZE_BUFFER, "USER %s %s %s :%s\n", name, hostname, server_name, nickname);
xsend(sock, nick, strlen(nick), 0);
xsend(sock, user, strlen(user), 0);

Attention, n’oubliez pas de mettre un caractère ‘:’ avant votre pseudo, sinon le serveur ne prendra que le dernier mot.
Maintenant que nous sommes identifié, il faut juste rejoindre le channel voulu.

/*
** Un simple message du style : "JOIN CHANNEL" suffira amplement
*/

snprintf(join, SIZE_BUFFER, "JOIN %s", channel);
xsend(sock, join, strlen(join), 0);

Bon, reste plus qu’à compiler, et à tester. En surveillant le chan avec l’aide d’un client normal, on devrait pouvoir voir le bot se connecter.
Mais non, il ne se passe rien ! C’est quoi ce binz ?

Allons voir ça de plus près avec telnet : (on rentre les commandes par l’entrée standard, telnet se charge de les envoyer au serveur)

telnet irc.quakenet.org 6667
Trying 66.225.225.66...
Connected to irc.quakenet.org.
Escape character is '^]'.
NOTICE AUTH :*** Looking up your hostname
NOTICE AUTH :*** Checking Ident
NOTICE AUTH :*** Found your hostname
NOTICE AUTH :*** No ident response
NICK Faltad
PING :4085172480
USER Faltad test test faltad
JOIN #epinantes
:servercentral.il.us.quakenet.org 451 Faltad Faltad :Register first.

On voit donc, que le serveur nous demande de nous enregistrer ! Mais comment ?
Et bien en fait, il a aussi envoyé un message de ping (ici PING :4085172480).
Il faut donc lui répondre par un PONG :4085172480.
Et hop, ca marche beaucoup mieux !

Un petit dernier truc, pour envoyer un message à une personne ou à un channel, il faut utiliser la commande suivante :
Et les messages que nous envoient les autres utilisateurs arrivent sous cette forme aussi.
:4085172480

/*
** Syntaxe : "PRIVMSG CHANNEL :message\n"
** Ne pas oublier le ':'
*/

snprintf(message, SIZE_BUFFER, "PRIVMSG %s :%s\n", channel, msg);
xsend(sock, message, strlen(message), 0);

Le bot est maintenant prêt à régner sur l’IRC, il va falloir lui trouver une occupation, et ça, c’est plus dur :)
N’hésitez pas a fouiller la RFC, pour utiliser des commandes plus ciblés (du genre KICK).

Voilà, j’espère que mon premier article n’est pas trop confus, ni moche..
Merci de m’avoir lu ! À bientôt.

PS : Le chan #epinantes sur quakenet vous attends ;)


Aug 09 2009

Bienvenue

Category: Non classéFaltad @ 3:25 pm

Bonjour,

Et oui, cela faisait quelques temps que je n’avais pas touché a ce blog.

Mais, ces derniers temps, l’envie m’est revenue, notamment avec mes nouvelles expériences que j’ai eu dans le domaine des technologies.

Cependant, peut-être que certains articles que j’écrirais ne seront pas forcément lié à l’informatique (même pas du tout), mais à ce que je pense en ce moment :)

Donc, à tout de suite pour des articles !