Conway's Game of Life

Gtk+ 2.0

Description

Implementation of Conway's game of life


Source

/*
 * (C) Davide F. Merico <hds619@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.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <gtk/gtk.h>

typedef int ** _MAP;
typedef int *  _ROW;
typedef int    _POINT;

GtkWidget *window, *gen_label, *drawing_area, *speed_time;
_MAP map[2];
int x, y, status_sim = 0;

_MAP make_map (char *raw_map, int size, int *ext_x, int *ext_y)
{
 int j, k, chk = 0, x, y, end_x, end_y;
 _MAP l_map;

 for ( j = x = y = 0; j < size; j++ ) {
        if ( raw_map[j] == '\n' ) {
             chk = 1;
             y++;
        }

        if ( !chk )
             x++;
 }

 l_map = (_MAP) calloc (120, sizeof (_ROW));
 for ( j = 0; j < 120; j++ )
       l_map[j] = (_ROW) calloc (160, sizeof (_POINT));

 for ( j = (120 - y) / 2, end_y = j + y, chk = 0; j < end_y; j++ ) {
       for ( k = (160 - x) / 2, end_x = k + x; k < end_x; k++, chk++ )
             l_map[j][k] = ( raw_map[chk] == '#' ) ? 1 : 0;
       chk++;
 }

 *ext_x = 160;
 *ext_y = 120;

 return l_map;
}

int compare_map (_MAP map_1, _MAP map_2, int mx, int my)
{
 int x, y;

 for ( y = 0; y < my; y++ ) {
       for ( x = 0; x < mx; x++ ) {
             if ( map_1[y][x] != map_2[y][x] )
                  return 0;
       }
 }

 return 1;
}

void free_map (_MAP map, int k)
{
 int j;

 for ( j = 0; j < k; j++ )
        free (map[j]);

 free (map);
}

void clone_map (_MAP *map_1, _MAP map_2, int mx, int my)
{
 int x, y;

 for ( y = 0; y < my; y++ )
       for ( x = 0; x < mx; x++ )
             (*map_1)[y][x] = map_2[y][x];
}

int closest_life (_MAP map, int px, int py, int mx, int my)
{
 int x, y, cnt = 0;

 for ( y = py - 1; y <= (py + 1) && y < my; y++ ) {
       for ( x = px - 1; x <= (px + 1) && x < mx; x++ ) {
             if ( y < 0 || x < 0 || (px == x && py == y) )
                  continue;

             cnt += map[y][x];
       }
 }

 return cnt;
}

void next_step (_MAP map_prev, _MAP *map_next, int mx, int my)
{
 int x, y, life;

 for ( y = 0; y < my; y++ ) {
       for ( x = 0; x < mx; x++ ) {
             life = closest_life (map_prev, x, y, mx, my);

             if ( !map_prev[y][x] && life == 3 ) {
                  (*map_next)[y][x] = 1;
                  continue;
             }

             if ( !(map_prev[y][x] && ( life == 2 || life == 3 )) ) {
                  (*map_next)[y][x] = 0;
             }
       }
 }
}

gboolean start_simulation (gpointer user_data)
{
 static int gen = 1;
 char *string;

 if ( status_sim ) {
      string = g_strdup_printf ("Generazione: %d", ++gen);
      gtk_statusbar_pop (GTK_STATUSBAR (gen_label), 0);
      gtk_statusbar_push (GTK_STATUSBAR (gen_label), 0, string);
      g_free (string);

      next_step (map[0], &map[1], x, y);
      if ( compare_map (map[0], map[1], x, y) ) {
           status_sim = 0;
           gtk_widget_set_sensitive (GTK_WIDGET ((GtkButton *) user_data), FALSE);

           return FALSE;
      }
      clone_map (&map[0], map[1], x, y);

      gtk_widget_queue_draw_area (drawing_area, 0, 0,
                                  gtk_widget_get_allocated_width (drawing_area),
                                  gtk_widget_get_allocated_height (drawing_area));

      return TRUE;
 }

 return FALSE;
}

void check_start_simulation (GtkButton *button, gpointer data)
{
 status_sim = !status_sim;

 if ( status_sim )
      gtk_button_set_label (button, "Ferma Simulazione");
 else gtk_button_set_label (button, "Avvia Simulazione");

 gtk_widget_set_sensitive (speed_time, FALSE);

 g_timeout_add (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (speed_time)),
                start_simulation, (gpointer *) button);
}

gboolean draw_map (GtkWidget *widget, cairo_t *cr, gpointer data)
{
 guint width, height, j, k, end_x, end_y, kx, ky;
 GdkRGBA background = { 0.1f, 0.1f, 0.1f, 1.f };
 GdkRGBA life_color = { 0.f, 1.f, 0.f, 1.f };

 width  = gtk_widget_get_allocated_width (widget);
 height = gtk_widget_get_allocated_height (widget);

 gdk_cairo_set_source_rgba (cr, &background);

 cairo_rectangle (cr, 0, 0, width, height);
 cairo_fill (cr);

 gdk_cairo_set_source_rgba (cr, &life_color);
 for ( j = 0; j < height; j += 4 ) {
       for ( k = 0; k < width; k += 4 ) {
             cairo_move_to (cr, k, j);

             if ( map[0][j / 4][k / 4] ) {
                  cairo_line_to (cr, k + 2, j);
             }
       }
 }
 cairo_stroke (cr);

 return FALSE;
}

int main (int argc, char *argv[])
{
 char *raw_map, *string;
 int size, gen = 1;
 FILE *of;
 GtkWidget *main_vbox, *hbox, *start_button, *label;

 if ( !(of = fopen (argv[1], "rb")) ) {
      fprintf (stderr, "Cannot open file.\n");
      return 1;
 }

 fseek (of, 0, SEEK_END);
 size = ftell (of);
 fseek (of, 0, SEEK_SET);
 raw_map = (char *) malloc (size);
 fread (raw_map, sizeof (char), size, of);
 fclose (of);

 map[0] = make_map (raw_map, size, &x, &y);
 map[1] = make_map (raw_map, size, &x, &y);
 free (raw_map);

 gtk_init (&argc, &argv);

 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 gtk_window_set_title (GTK_WINDOW (window), "Game of Life");
 gtk_window_set_default_size (GTK_WINDOW (window), 640, -1);
 gtk_window_set_resizable (GTK_WINDOW (window), FALSE);

 main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
 gtk_box_set_homogeneous (GTK_BOX (main_vbox), FALSE);
 gtk_container_add (GTK_CONTAINER (window), main_vbox);

 start_button = gtk_button_new_with_label ("Avvia Simulazione");
 gtk_container_add (GTK_CONTAINER (main_vbox), start_button);
 g_signal_connect (G_OBJECT (start_button), "clicked",
                   G_CALLBACK (check_start_simulation), NULL);

 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
 gtk_container_add (GTK_CONTAINER (main_vbox), hbox);

 drawing_area = gtk_drawing_area_new ();

 gtk_widget_set_size_request (drawing_area, 640, 480);
 gtk_container_add (GTK_CONTAINER (hbox), drawing_area);
 g_signal_connect (G_OBJECT (drawing_area), "draw",
                   G_CALLBACK (draw_map), NULL);

 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
 gtk_container_add (GTK_CONTAINER (main_vbox), hbox);

 label = gtk_label_new ("Velocità Simulazione ( ms ): ");
 gtk_container_add (GTK_CONTAINER (hbox), label);

 speed_time = gtk_spin_button_new_with_range (10, 1000, 10);
 gtk_spin_button_set_value (GTK_SPIN_BUTTON (speed_time), 300);
 gtk_container_add (GTK_CONTAINER (hbox), speed_time);

 string = g_strdup_printf ("Generazione: %d", gen);
 gen_label = gtk_statusbar_new  ();
 gtk_statusbar_push (GTK_STATUSBAR (gen_label), 0, string);
 g_free (string);
 gtk_container_add (GTK_CONTAINER (main_vbox), gen_label);

 gtk_widget_show_all (window);
 g_signal_connect (G_OBJECT (window), "delete-event",
                   G_CALLBACK (gtk_main_quit), NULL);

 gtk_main ();

 return 0;
}