Tas de sable
A propos du code
Règles des tas de sable
Premier essai
static int table[2][SCREEN_WIDTH][SCREEN_HEIGHT];
int currentTable = 0;
On initialise un des tableaux avec des 0, sauf pour la case centrale où on met une grande valeur:
// initialise the table
for (int y = 0; y < SCREEN_HEIGHT; ++y)
for (int x = 0; x < SCREEN_WIDTH; ++x)
table[currentTable][x][y] = 0;
table[currentTable][SCREEN_WIDTH / 2][SCREEN_HEIGHT / 2] = 400000;
Pour afficher les cases on va avoir besoin d'une palette qui assigne une couleur à chaque valeur de case possible.
// init palette
Color palette[5];
palette[0] = Color(0, 0, 255);
palette[1] = Color(0, 255, 255);
palette[2] = Color(255, 255, 0);
palette[3] = Color(255, 0, 0);
palette[4] = Color(255, 255, 255);
La partie affichage est simple, on retrouve simplement la bonne couleur dans la palette pour chaque case:
// draw the current table
for (int y = 0; y < SCREEN_HEIGHT; ++y)
for (int x = 0; x < SCREEN_WIDTH; ++x)
{
int color = table[currentTable][x][y];
if (color > 4)
color = 4;
gfx.setPixel(x, y, palette[color]);
}
gfx.render();
sys.processEvents();
La partie traitement est assez intuitive aussi. D'abord on copie l'ancienne grille dans la courante.
// update the table
for (int y = 0; y < SCREEN_HEIGHT; ++y)
for (int x = 0; x < SCREEN_WIDTH; ++x)
{
table[1 - currentTable][x][y] = table[currentTable][x][y];
}
Puis on boucle sur chaque case pour appliquer la règle d'effondrement si on trouve une valeur supérieur à 3.
for (int y = 0; y < SCREEN_HEIGHT; ++y)
for (int x = 0; x < SCREEN_WIDTH; ++x)
{
if (table[currentTable][x][y] >= 4)
{
table[1 - currentTable][x][y] -= 4;
if (x - 1 >= 0)
table[1 - currentTable][x - 1][y] += 1;
if (x + 1 < SCREEN_WIDTH)
table[1 - currentTable][x + 1][y] += 1;
if (y - 1 >= 0)
table[1 - currentTable][x][y - 1] += 1;
if (y + 1 < SCREEN_HEIGHT)
table[1 - currentTable][x][y + 1] += 1;
}
}
Enfin on échange les grilles.
currentTable = 1 - currentTable;
Télécharger le code source
Télécharger l'exécutable pour Windows
Voici le résultat de ce programme:
Accélération (1)
static int table[2][SCREEN_WIDTH + 2][SCREEN_HEIGHT + 2];
[...]
for (int y = 1; y <= SCREEN_HEIGHT; ++y)
for (int x = 1; x <= SCREEN_WIDTH; ++x)
{
if (table[currentTable][x][y] >= 4)
{
table[1 - currentTable][x][y] -= 4;
table[1 - currentTable][x - 1][y] += 1;
table[1 - currentTable][x + 1][y] += 1;
table[1 - currentTable][x][y - 1] += 1;
table[1 - currentTable][x][y + 1] += 1;
}
}
Et au lieu d'utiliser une boucle pour copier l'ancienne grille dans la courante avant le traitement, on peut
int screenSize = (SCREEN_WIDTH + 2) * (SCREEN_HEIGHT + 2);
memcpy(table[1 - currentTable], table[currentTable], screenSize * sizeof(int));
Télécharger le code source
Télécharger l'exécutable pour Windows
Ce code prend environ 26 minutes et 40s sur mon ordinateur.
Accélération (2)
int counter = 0;
while (sys.isQuitRequested() == false)
{
[...]
counter++;
if ((counter % 16) == 0)
gfx.render();
Télécharger le code source
Télécharger l'exécutable pour Windows
Ce code s'exécute en 18 minutes et 30s.
Accélération (3)
for (int y = 1; y <= SCREEN_HEIGHT; ++y)
for (int x = 1; x <= SCREEN_WIDTH; ++x)
{
while (table[currentTable][x][y] >= 4)
{
table[1 - currentTable][x][y] -= 4;
table[1 - currentTable][x - 1][y] += 1;
table[1 - currentTable][x + 1][y] += 1;
table[1 - currentTable][x][y - 1] += 1;
table[1 - currentTable][x][y + 1] += 1;
table[currentTable][x][y] -= 4;
}
}
Télécharger le code source
Télécharger l'exécutable pour Windows
Remarquez qu'on utilise la case de l'ancienne grille comme un compteur. On la décrémente et on la teste pour sortir
Accélération (4)
for (int y = 1; y <= SCREEN_HEIGHT; ++y)
for (int x = 1; x <= SCREEN_WIDTH; ++x)
{
int height = table[currentTable][x][y] / 4;
table[1 - currentTable][x][y] -= 4 * height;
table[1 - currentTable][x - 1][y] += height;
table[1 - currentTable][x + 1][y] += height;
table[1 - currentTable][x][y - 1] += height;
table[1 - currentTable][x][y + 1] += height;
}
Ensuite, on a vu qu'on utilisait l'ancienne grille comme un compteur.
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
#include "Graphics.h"
#include "System.h"
#define SCREEN_WIDTH 600
#define SCREEN_HEIGHT 600
int main(int argc, char* argv[])
{
// init the window
gfx.init("Sandpiles 5", SCREEN_WIDTH, SCREEN_HEIGHT);
gfx.init2D();
gfx.clearScreen(Color(0, 0, 0, SDL_ALPHA_OPAQUE));
static int table[SCREEN_WIDTH + 2][SCREEN_HEIGHT + 2];
// initialise the table
for (int y = 1; y <= SCREEN_HEIGHT; ++y)
for (int x = 1; x <= SCREEN_WIDTH; ++x)
table[x][y] = 0;
table[SCREEN_WIDTH / 2][SCREEN_HEIGHT / 2] = 400000;
// init palette
Color palette[5];
palette[0] = Color(0, 0, 255);
palette[1] = Color(0, 255, 255);
palette[2] = Color(255, 255, 0);
palette[3] = Color(255, 0, 0);
palette[4] = Color(255, 255, 255);
int counter = 0;
while (sys.isQuitRequested() == false)
{
// draw the current table
for (int y = 0; y < SCREEN_HEIGHT; ++y)
for (int x = 0; x < SCREEN_WIDTH; ++x)
{
int color = table[x + 1][y + 1];
if (color > 4)
color = 4;
gfx.setPixel(x, y, palette[color]);
}
counter++;
if ((counter % 16) == 0)
gfx.render();
sys.processEvents();
// update the table
int screenSize = (SCREEN_WIDTH + 2) * (SCREEN_HEIGHT + 2);
for (int y = 1; y <= SCREEN_HEIGHT; ++y)
for (int x = 1; x <= SCREEN_WIDTH; ++x)
{
int height = table[x][y] / 4;
table[x][y] -= height * 4;
table[x - 1][y] += height;
table[x + 1][y] += height;
table[x][y - 1] += height;
table[x][y + 1] += height;
}
}
gfx.quit();
return EXIT_SUCCESS;
}
Télécharger le code source
Télécharger l'exécutable pour Windows
Et ce programme ne prend que 1min 33s pour dessiner l'image finale. C'est 20 fois plus rapide que le premier
Plusieurs points de départ
int cx = SCREEN_WIDTH / 2 + 1;
int cy = SCREEN_HEIGHT / 2 + 1;
table[cx][cy-70] = 200000;
table[cx-50][cy+50] = 150000;
table[cx+50][cy+50] = 150000;
Télécharger le code source
Télécharger l'exécutable pour Windows
Voilà une image de ce qu'on voit au début de l'exécution:
Essayons avec un carré
int cx = SCREEN_WIDTH / 2 + 1;
int cy = SCREEN_HEIGHT / 2 + 1;
for (int i = -50; i <= 49; ++i)
{
table[cx - 50][cy + i] = 2500;
table[cx + 49][cy + i] = 2500;
table[cx + i][cy - 50] = 2500;
table[cx + i][cy + 49] = 2500;
}
Télécharger le code source
Télécharger l'exécutable pour Windows
Voilà ce qu'on voit au début:
Un espace initial non vide
// initialise the table
for (int y = 1; y <= SCREEN_HEIGHT; ++y)
for (int x = 1; x <= SCREEN_WIDTH; ++x)
table[x][y] = (y > x ? 1 : 0);
int cx = SCREEN_WIDTH / 2;
int cy = SCREEN_HEIGHT / 2;
table[cx][cy] = 400000;
Télécharger le code source
Télécharger l'exécutable pour Windows
Voici ce que ça donne:
Motif
// initialise the table
for (int y = 1; y <= SCREEN_HEIGHT; ++y)
for (int x = 1; x <= SCREEN_WIDTH; ++x)
table[x][y] = (y > x ? (((y+x)&1) ? 2 : 1) : 0);
int cx = SCREEN_WIDTH / 2 + 1;
int cy = SCREEN_HEIGHT / 2 + 1;
table[cx][cy] = 400000;
Télécharger le code source
Télécharger l'exécutable pour Windows
Voici l'image finale: