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 !


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