Description
Implementation of Conway's game of life
Implementation of Conway's game of life
/* * (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; }