summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Seguin <guillaume@segu.in>2007-10-06 13:30:21 +0200
committerGuillaume Seguin <guillaume@segu.in>2007-10-06 13:30:21 +0200
commit800bc37c33b6f4e0b375e3e44f6371fb02637c43 (patch)
tree471e9859859b9d90f75052da85f3bc0f8f672544
downloadgol-800bc37c33b6f4e0b375e3e44f6371fb02637c43.tar.gz
gol-800bc37c33b6f4e0b375e3e44f6371fb02637c43.tar.bz2
* Initial import
-rw-r--r--jdlv-logo.pngbin0 -> 392 bytes
-rw-r--r--jdlv.glade124
-rw-r--r--jdlv.pngbin0 -> 180 bytes
-rwxr-xr-xjdlv.py306
4 files changed, 430 insertions, 0 deletions
diff --git a/jdlv-logo.png b/jdlv-logo.png
new file mode 100644
index 0000000..0780f07
--- /dev/null
+++ b/jdlv-logo.png
Binary files differ
diff --git a/jdlv.glade b/jdlv.glade
new file mode 100644
index 0000000..540d9a4
--- /dev/null
+++ b/jdlv.glade
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
+<!--Generated with glade3 3.3.4 on Sat Oct 6 13:26:25 2007 -->
+<glade-interface>
+ <widget class="GtkWindow" id="mainWindow">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="title" translatable="yes">pyGameOfLife</property>
+ <property name="resizable">False</property>
+ <property name="window_position">GTK_WIN_POS_CENTER</property>
+ <property name="icon">jdlv.png</property>
+ <signal name="delete_event" handler="gtk_main_quit"/>
+ <child>
+ <widget class="GtkVBox" id="mainVBox">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <child>
+ <widget class="GtkToolbar" id="toolBar">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="icon_size">GTK_ICON_SIZE_SMALL_TOOLBAR</property>
+ <child>
+ <widget class="GtkToolButton" id="newButton">
+ <property name="visible">True</property>
+ <property name="stock_id">gtk-new</property>
+ <signal name="clicked" handler="new_game"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="quitButton">
+ <property name="visible">True</property>
+ <property name="stock_id">gtk-quit</property>
+ <signal name="clicked" handler="gtk_main_quit"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkToolButton" id="aboutButton">
+ <property name="visible">True</property>
+ <property name="stock_id">gtk-about</property>
+ <signal name="clicked" handler="show_about"/>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkAspectFrame" id="mainFrame">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">GTK_SHADOW_NONE</property>
+ <property name="ratio">1</property>
+ <child>
+ <placeholder/>
+ </child>
+ </widget>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <widget class="GtkStatusbar" id="statusBar">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
+ <property name="has_resize_grip">False</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+ <widget class="GtkAboutDialog" id="aboutDialog">
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">À propos de Glade</property>
+ <property name="resizable">False</property>
+ <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+ <property name="icon">jdlv.png</property>
+ <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+ <property name="has_separator">False</property>
+ <property name="program_name">pyGameOfLife</property>
+ <property name="version">0.1</property>
+ <property name="copyright" translatable="yes">Copyright (c) 2007 Guillaume Seguin &lt;guillaume@segu.in&gt;</property>
+ <property name="logo">jdlv-logo.png</property>
+ <signal name="close" handler="close_about"/>
+ <signal name="delete_event" handler="close_about"/>
+ <signal name="response" handler="close_about"/>
+ <child internal-child="vbox">
+ <widget class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="spacing">2</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="action_area">
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ </widget>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </widget>
+ </child>
+ </widget>
+</glade-interface>
diff --git a/jdlv.png b/jdlv.png
new file mode 100644
index 0000000..fda2ca0
--- /dev/null
+++ b/jdlv.png
Binary files differ
diff --git a/jdlv.py b/jdlv.py
new file mode 100755
index 0000000..fc8b0e5
--- /dev/null
+++ b/jdlv.py
@@ -0,0 +1,306 @@
+#!/usr/bin/env python
+# coding=utf-8
+
+"""
+pyGameOfLife
+Author : Guillaume "iXce" Seguin
+Email : guillaume@segu.in
+
+Copyright (C) 2007 Guillaume Seguin
+
+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.
+"""
+
+import pygtk
+pygtk.require ('2.0')
+import gtk
+import gobject
+import gtk.glade
+
+import random
+import cairo
+import pangocairo
+from math import pi
+
+def autoconnect (ob):
+ '''Autoconnect every member from ob to its glade interface'''
+ handlers = {}
+ for i in dir (ob):
+ handlers[i] = getattr (ob, i)
+ ob.glade.signal_autoconnect (handlers);
+
+def draw_rounded_rectangle (cr, x, y, width, height):
+ '''Draw a rounded rectangle at x, y on Cairo context cr'''
+ radius = 30
+ x0 = x + 10
+ y0 = y + 10
+ x1 = x + width - 10
+ y1 = y + height - 10
+ cr.new_path ()
+ cr.arc (x0 + radius, y1 - radius, radius, pi / 2, pi)
+ cr.line_to (x0, y0 + radius)
+ cr.arc (x0 + radius, y0 + radius, radius, pi, 3 * pi / 2)
+ cr.line_to (x1 - radius, y0)
+ cr.arc (x1 - radius, y0 + radius, radius, 3 * pi / 2, 2 * pi)
+ cr.line_to (x1, y1 - radius)
+ cr.arc (x1 - radius, y1 - radius, radius, 0, pi / 2)
+ cr.close_path ()
+
+def prepare_text (cr, text):
+ '''Prepare text for drawing with Pango/Cairo'''
+ pcr = pangocairo.CairoContext (cr)
+ layout = pcr.create_layout ()
+ layout.set_markup (text)
+ return pcr, layout
+
+class gtkGameOfLife (gtk.DrawingArea):
+
+ _parent = None
+ _surface = None
+ _map = None
+ _finished = False
+ _size = 0
+ _cell_side = 10
+ _timeout = 100
+ _source = None
+
+ def __init__ (self, side = 300, parent = None):
+ '''Prepare widget'''
+ super (gtkGameOfLife, self).__init__ ()
+ self._parent = parent
+ self._size = side / self._cell_side
+ side = self._size * self._cell_side
+ self.add_events (gtk.gdk.BUTTON_PRESS_MASK)
+ self.connect ("expose-event", self.expose)
+ self.connect ("map", self.on_map)
+ self.connect ("button-press-event", self.button_press)
+ self.set_size_request (side, side)
+ self.new_game ()
+
+ def is_finished (self):
+ '''Check if the game is finished, ie the board is empty'''
+ for row in self._map:
+ if len (filter (lambda l: l != 0, row)):
+ return False
+ return True
+
+ def new_game (self):
+ '''Reset game'''
+ self._map = []
+ for i in range (self._size):
+ self._map.append ([0] * self._size)
+ todo = 200
+ while todo > 0:
+ i = random.randint (0, self._size - 1)
+ j = random.randint (0, self._size - 1)
+ if not self._map[i][j]:
+ self._map[i][j] = 1
+ todo -= 1
+ self._finished = False
+ if self._surface:
+ self.redraw (queue = True)
+
+ def on_map (self, *args):
+ '''Initial map callback that starts the timeout'''
+ if self._source:
+ return
+ self.source = gobject.timeout_add (self._timeout, self.update_map)
+
+ def surrounding (self, i, j):
+ '''Counts the surrounding alive points around point (i, j)'''
+ surrounding = 0
+ iplusone = (i + 1) % self._size
+ jplusone = (j + 1) % self._size
+ iminusone = (i - 1 + self._size) % self._size
+ jminusone = (j - 1 + self._size) % self._size
+ # i + 1, j + 1
+ if self._map[iplusone][jplusone]:
+ surrounding += 1
+ # i, j + 1
+ if self._map[i][jplusone]:
+ surrounding += 1
+ # i + 1, j
+ if self._map[iplusone][j]:
+ surrounding += 1
+ # i - 1, j - 1
+ if self._map[iminusone][jminusone]:
+ surrounding += 1
+ # i - 1, j
+ if self._map[iminusone][j]:
+ surrounding += 1
+ # i - 1, j + 1
+ if self._map[iminusone][jplusone]:
+ surrounding += 1
+ # i, j - 1
+ if self._map[i][jminusone]:
+ surrounding += 1
+ # i + 1, j - 1
+ if self._map[iplusone][jminusone]:
+ surrounding += 1
+ return surrounding
+
+ def update_map (self):
+ '''Update the Game Map'''
+ if self._finished:
+ return True
+ new_map = []
+ for i in range (self._size):
+ new_map.append ([0] * self._size)
+ for i in range (self._size):
+ for j in range (self._size):
+ surrounding = self.surrounding (i, j)
+ if (self._map[i][j] and surrounding in (2, 3)) or surrounding == 3:
+ new_map[i][j] = 1
+ self._map = new_map
+ if self.is_finished ():
+ self._finished = True
+ if self._source:
+ gobject.source_remove (self._source)
+ self._source = None
+ if self._surface:
+ self.redraw (queue = True)
+ return True
+
+ def button_press (self, widget, event):
+ '''Check if the cursor clicked an interesting area'''
+ if self._finished:
+ self.new_game ()
+ return
+ '''Add a bunch of dots around event point, just for fun'''
+ alloc = self.get_allocation ()
+ cell_side = self._cell_side
+ i = (event.x - (event.x % cell_side)) / cell_side
+ j = (event.y - (event.y % cell_side)) / cell_side
+ i = int (min (i, self._size - 1))
+ j = int (min (j, self._size - 1))
+ jplusone = (j + 1) % self._size
+ jminusone = (j - 1 + self._size) % self._size
+ self._map[i][j] = self._map[i][jplusone] = 1
+ self._map[i][jminusone] = 1
+
+ def redraw (self, queue = False):
+ '''Redraw internal surface'''
+ alloc = self.get_allocation ()
+ side = min (alloc.width, alloc.height)
+ cell_side = self._cell_side
+ self._surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, side, side)
+ cr = cairo.Context (self._surface)
+ # Draw background
+ cr.set_source_rgb (1, 1, 1)
+ cr.paint ()
+ # Draw cells
+ for i in range (self._size):
+ for j in range (self._size):
+ if not self._map[i][j]:
+ continue
+ # Pretty color!
+ color = 0.75 + float (i + j) / (8 * self._size)
+ cr.set_source_rgb (color, 0, 0)
+ cr.rectangle (cell_side * i, cell_side * j,
+ cell_side, cell_side)
+ cr.fill ()
+ if self._finished:
+ cr.set_source_rgba (0.8, 0.8, 0.8, 0.5)
+ cr.paint ()
+ cr.set_source_rgba (0.2, 0.2, 0.2, 0.6)
+ draw_rounded_rectangle (cr, side * 0.1, side * 0.25,
+ side * 0.8, side * 0.5)
+ cr.fill ()
+ cr.set_source_rgb (1, 1, 1)
+ cr.move_to (side * 0.25, side * 0.43)
+ text = '<span font_desc="Sans 25"><b>Replay ?</b></span>'
+ pcr, layout = prepare_text (cr, text)
+ pcr.show_layout (layout)
+ if queue:
+ self.queue_draw ()
+
+ def expose (self, widget, event):
+ '''Expose event handler'''
+ cr = self.window.cairo_create ()
+ if not self._surface:
+ self.redraw ()
+ cr.set_source_surface (self._surface)
+ cr.rectangle (event.area.x, event.area.y,
+ event.area.width, event.area.height)
+ cr.clip ()
+ cr.paint ()
+ return False
+
+class pyGameOfLife:
+
+ glade = None
+ mainWindow = None
+ aboutDialog = None
+ mainFrame = None
+ statusBar = None
+ messageDialog = None
+ game = None
+
+ def __init__ (self):
+ '''Initialize application'''
+ self.glade = gtk.glade.XML (fname = "jdlv.glade")
+ self.mainWindow = self.glade.get_widget ("mainWindow")
+ self.aboutDialog = self.glade.get_widget ("aboutDialog")
+ self.mainFrame = self.glade.get_widget ("mainFrame")
+ self.statusBar = self.glade.get_widget ("statusBar")
+ self.messageDialog = self.glade.get_widget ("messageDialog")
+ self.glade.get_widget ("toolBar").set_style (gtk.TOOLBAR_ICONS)
+ autoconnect (self)
+ self.game = gtkGameOfLife (parent = self)
+ self.mainFrame.add (self.game)
+
+ def new_game (self, *args):
+ '''Starts a new game'''
+ self.push_status ("")
+ self.game.new_game ()
+
+ def push_status (self, text):
+ '''Update status bar message'''
+ context = self.statusBar.get_context_id ("main")
+ self.statusBar.pop (context)
+ self.statusBar.push (context, text)
+
+ def show (self):
+ '''Show application'''
+ self.mainWindow.show_all ()
+
+ def gtk_main_quit (self, *args):
+ '''Quit gtk main loop'''
+ gtk.main_quit ()
+
+ def show_about (self, *args):
+ '''Show about dialog'''
+ self.aboutDialog.show ()
+
+ def close_about (self, *args):
+ '''Hide about dialog'''
+ self.aboutDialog.hide ()
+ return True
+
+ def open_message_dialog (self, text):
+ '''Open the message dialog and set the message'''
+ text = '''<span font_desc="Sans 16"><b>%s</b></span>''' % text
+ self.messageDialog.set_markup (text)
+ self.messageDialog.show ()
+
+ def close_message_dialog (self, *args):
+ '''Close message dialog'''
+ self.messageDialog.hide ()
+ return False
+
+if __name__ == "__main__":
+ game = pyGameOfLife ()
+ game.show ()
+ gtk.main ()