+#!/usr/bin/python2.3
+
+# Gnome-hextris; a free rewrite of the xhextris game in Python for Gnome
+# Copyright 2004 Mikko Rauhala <mjr@iki.fi>
+
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# This will be overwritten for the installed version:
+SHAREDIR = "."
+
+VERSION="0.9.0"
+
+import os
+import sys
+
+import pygtk
+pygtk.require("2.0")
+
+import gtk
+import gnome.canvas
+import gnome
+import gobject
+import gtk.glade
+
+import gettext
+
+import whrandom
+
+class Hextris:
+ def __init__(self):
+ self.pieceheight = 24
+ self.piecewidth = 26
+ self.piecenarrow = 14
+# self.piecewidth = 20
+# self.piecenarrow = 20
+ self.rows = 23
+ self.cols = 13 # Must be odd or weirdness ensues
+
+ self.width = (((self.cols/2)+2)*self.piecewidth +
+ ((self.cols/2)+1) * self.piecenarrow) + 2
+ self.height = (self.rows+1) * self.pieceheight + 2
+
+ self.colors = ("blue", "yellow", "red", "orange", "green", "purple",
+ "cyan", "gray45", "magenta", "lightblue")
+ self.deltax = (-1, -1, -1, 0, 0, 0, 0, 0, -1, -1)
+ self.deltay = (-1, -1, -1, 0, 0, 0, 0, 0, -1, -1)
+ self.pieces = (
+ (((0,), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
+ ((0,), (0, 0, 0, 1), (0, 1, 1), (1,)),
+ ((0,), (1, 1), (0, 0, 1, 1)),
+ ((0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
+ ((0,), (0, 0, 0, 1, 1), (0, 1, 1)),
+ ((0,), (0, 1), (0, 0, 1, 1), (0, 0, 0, 0, 1))),
+ (((0,), (0, 0, 1), (0, 0, 1, 1), (0, 0, 0, 1)),
+ ((0,), (0, 0, 0, 1), (0, 0, 1), (0, 1, 1)),
+ ((0,), (0,), (1, 1, 1, 1)),
+ ((0, 1), (0, 1), (0, 0, 1), (0, 0, 1)),
+ ((0, 0, 0, 1), (0, 0, 1), (0, 1, 1)),
+ ((0,), (0, 1, 0, 1), (0, 0, 1, 0, 1))),
+ (((0,), (0, 0, 1), (0, 1, 1), (0, 1)),
+ ((0,), (0, 1, 0, 1), (1, 0, 1, 0)),
+ ((0, 1), (0, 0, 1), (0, 0, 1, 1)),
+ ((0, 0, 0, 1), (0, 0, 0, 1), (0, 0, 1), (0, 0, 1)),
+ ((0,), (0,), (0, 1, 1, 1, 1)),
+ ((0,), (0, 1), (0, 0, 1), (0, 0, 1, 1))),
+ (((0, 1, 0), (0, 1, 0), (1, 0, 1)),
+ ((0,), (1, 1, 1), (0, 1, 0))),
+ (((0, 1), (1,), (1, 1)),
+ ((0, 1), (1, 0, 1), (1,)),
+ ((0, 1), (1, 0, 1), (0, 0, 1)),
+ ((0, 1), (0, 0, 1), (0, 1, 1)),
+ ((0,), (0, 0, 1), (1, 1, 1)),
+ ((0,), (1,), (1, 1, 1))),
+ (((0,), (0, 1), (1, 1, 1)),
+ ((0,), (1, 1), (1, 1, 0)),
+ ((0, 1), (1, 1), (1,)),
+ ((0, 1), (1, 1, 1)),
+ ((0, 1), (0, 1, 1), (0, 0, 1)),
+ ((0,), (0, 1, 1), (0, 1, 1))),
+ (((0, 1), (0, 1), (0, 1, 1)),
+ ((0,), (0, 1, 1), (1, 1)),
+ ((0,), (1, 1), (1, 0, 1)),
+ ((0, 1), (1, 1), (0, 1)),
+ ((0, 1), (0, 1, 1), (1,)),
+ ((0,), (1, 1, 1), (0, 0, 1))),
+ (((0, 1), (0, 1), (1, 1)),
+ ((0,), (1, 1, 1), (1,)),
+ ((0, 1), (1, 1), (0, 0, 1)),
+ ((0, 1), (0, 1, 1), (0, 1)),
+ ((0,), (0, 1, 1), (1, 0, 1)),
+ ((0,), (1, 1), (0, 1, 1))),
+ (((0,), (0, 0, 1), (0, 0, 1), (0, 0, 1, 1)),
+ ((0,), (0, 0, 0, 1), (0, 1, 1), (0, 1)),
+ ((0,), (0, 1), (1, 0, 1, 1)),
+ ((0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
+ ((0, 0, 0, 1), (0, 0, 0, 1), (0, 1, 1)),
+ ((0,), (0, 1), (0, 0, 1, 1, 1))),
+ (((0,), (0, 0, 1), (0, 0, 1), (0, 1, 1)),
+ ((0,), (0, 0, 0, 1), (1, 1, 1)),
+ ((0, 1), (0, 1), (0, 0, 1, 1)),
+ ((0, 0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
+ ((0,), (0, 0, 0, 1), (0, 1, 1, 0, 1)),
+ ((0,), (0, 1), (0, 0, 1, 1), (0, 0, 0, 1)))
+ )
+
+ self.attitude = 0
+ self.speed_orig = 400
+ self.speed = 400
+ self.speed_ratio = 0.99
+ self.speed_reset = gtk.FALSE
+ self.paused = gtk.FALSE
+ self.lost = gtk.TRUE
+ self.score = 0
+ self.hiscore = 0
+ self.nextnum = 0
+
+ self.running = gtk.FALSE
+
+ self.nextpiece = gtk.FALSE
+ self.board = gtk.FALSE
+ self.rowgroups = []
+
+ def draw_hexagon(self, group, color):
+ pts = []
+ pts.append ((self.piecewidth - self.piecenarrow)/2)
+ pts.append (0)
+ pts.append (self.piecewidth - pts[0])
+ pts.append (0)
+ pts.append (self.piecewidth)
+ pts.append (self.pieceheight/2)
+ pts.append (pts[2])
+ pts.append (self.pieceheight)
+ pts.append (pts[0])
+ pts.append (pts[7])
+ pts.append (0)
+ pts.append (pts[5])
+ pts.append (pts[0])
+ pts.append (pts[1])
+ item = group.add ("GnomeCanvasPolygon", points = pts, fill_color = color,
+ outline_color = "black", width_units = 1)
+ return item
+
+ def draw_piece(self, group, matrix, color):
+ for i in range(len(matrix)):
+ for j in range(len(matrix[i])):
+ if matrix[i][j] != 0:
+ g = group.add("GnomeCanvasGroup",
+ x = (j*(self.piecewidth+self.piecenarrow)/2),
+ y = ((i * (self.pieceheight)) +
+ ((j%2) * self.pieceheight/2)))
+ self.draw_hexagon(g, color)
+
+ def place_piece(self, group, matrix, color, x, y):
+ piecegroup = group.add("GnomeCanvasGroup",
+ x = (x+1)*((self.piecewidth+self.piecenarrow)/2),
+ y = ((y * self.pieceheight) -
+ (((x%2)+1) * self.pieceheight/2)))
+ self.draw_piece(piecegroup, matrix, color)
+ return piecegroup
+
+ def init_board(self):
+ board = self.canvas.root()
+ for i in range(self.rows+1):
+ for j in [0, 1]:
+ boardhex = board.add("GnomeCanvasGroup",
+ x = j * (self.piecewidth*(self.cols/2+1) +
+ self.piecenarrow*(self.cols/2+1)),
+ y = i * self.pieceheight)
+ self.draw_hexagon(boardhex, "gray")
+ for i in range(self.cols/2):
+ boardhex = board.add("GnomeCanvasGroup",
+ x = (i+1) * (self.piecewidth +
+ self.piecenarrow),
+ y = self.rows * self.pieceheight)
+ self.draw_hexagon(boardhex, "gray")
+ for i in range(self.cols/2+1):
+ boardhex = board.add("GnomeCanvasGroup",
+ x = (i * (self.piecewidth + self.piecenarrow)+
+ (self.piecewidth + self.piecenarrow)/2),
+ y = ((self.rows*2-1) * self.pieceheight)/2)
+ self.draw_hexagon(boardhex, "gray")
+
+ def on_new_activate(self, event):
+ self.speed = self.speed_orig
+ self.score = 0
+ self.attitude = 0
+ self.lost = gtk.FALSE
+ self.paused = gtk.FALSE
+
+ self.update_appbar()
+
+ if self.board != gtk.FALSE:
+ self.board.destroy()
+ self.board = self.canvas.root().add("GnomeCanvasGroup", x = 0, y = 0)
+
+ self.field = []
+ for i in range(self.rows):
+ self.field.append([])
+ for j in range(self.cols):
+ self.field[i].append(0)
+
+ self.rowgroups = []
+ for i in range(self.rows):
+ self.rowgroups.append(self.board.add("GnomeCanvasGroup", x = 0,
+ y = i * self.pieceheight))
+
+ self.nextnum = whrandom.randint(0, 9)
+ self.next_piece()
+
+ if self.running == gtk.FALSE:
+ gobject.timeout_add(self.speed, self.timer_handler)
+ self.running = gtk.TRUE
+ else:
+ self.speed_reset = gtk.TRUE
+
+ return gtk.TRUE
+
+ def on_about_activate(self, event):
+ aTree = gtk.glade.XML(self.glade, "about")
+ about = aTree.get_widget("about")
+ about.set_property("name", "Ghextris")
+ about.set_property("version", VERSION)
+ return gtk.TRUE
+
+ def on_pause_game_activate(self, event):
+ if self.lost == gtk.TRUE:
+ return gtk.FALSE
+ if self.paused == gtk.TRUE:
+ self.paused = gtk.FALSE
+ else:
+ self.paused = gtk.TRUE
+ return gtk.TRUE
+
+ def on_quit_activate(self, event):
+ gtk.main_quit()
+
+ def main(self):
+ gnome.init("Ghextris", VERSION)
+ gettext.install("ghextris")
+ self.glade = os.path.join(SHAREDIR, "ghextris.glade")
+
+ wTree = gtk.glade.XML(self.glade, "GhextrisApp")
+ dic = {"on_new_activate": self.on_new_activate,
+ "on_pause_game_activate": self.on_pause_game_activate,
+ "on_quit_activate": self.on_quit_activate,
+ "on_about_activate": self.on_about_activate}
+ wTree.signal_autoconnect(dic)
+
+ win = wTree.get_widget("GhextrisApp")
+ self.canvas = wTree.get_widget("canvas")
+ preview = wTree.get_widget("previewcanvas")
+ appbar = wTree.get_widget("appbar").get_children()[0]
+ self.appbar = appbar.get_children()[0]
+
+ win.connect('destroy', self.on_quit_activate)
+
+ self.canvas.set_size_request(self.width, self.height)
+ self.canvas.set_scroll_region(0, 0, self.width, self.height)
+ self.canvas.show()
+
+ preview.set_size_request(self.piecewidth * 4, self.pieceheight * 5)
+ preview.show()
+ self.preview = preview.root()
+
+ win.connect("key-press-event", self.key_handler)
+ self.init_board()
+
+ whrandom.seed()
+
+ self.update_appbar()
+
+ win.show()
+
+ def next_piece(self):
+ self.piecenum = self.nextnum
+ self.nextnum = whrandom.randint(0, 9)
+ self.attitude = 0
+ self.piece_x = (self.cols/2)-1 + self.deltax[self.piecenum]
+ self.piece_y = -3 + self.deltax[self.piecenum]
+ self.piece = self.place_piece(self.board, self.pieces[self.piecenum][0],
+ self.colors[self.piecenum%(len(self.colors)+1)],
+ self.piece_x, self.piece_y)
+
+ if self.nextpiece != gtk.FALSE:
+ self.nextpiece.destroy()
+ self.nextpiece = self.place_piece(self.preview,
+ self.pieces[self.nextnum][0],
+ self.colors[self.nextnum%(len(self.colors)+1)],
+ self.deltax[self.nextnum],
+ self.deltay[self.nextnum])
+
+ def timer_handler(self):
+ if self.lost == gtk.TRUE:
+ self.running = gtk.FALSE
+ return gtk.FALSE
+
+ if self.paused == gtk.TRUE:
+ return gtk.TRUE
+ self.piece_y += 1
+
+ if self.check_collisions() == gtk.TRUE:
+ self.piece_y -= 1
+ self.update_field()
+ if self.top_occupied() != gtk.TRUE:
+ self.next_piece()
+ else:
+ self.lost = gtk.TRUE
+ if self.hiscore < self.score:
+ self.hiscore = self.score
+ self.update_appbar()
+ self.running = gtk.FALSE
+ return gtk.FALSE
+ else:
+ self.piece.move(0, self.pieceheight)
+
+ if self.speed_reset == gtk.TRUE:
+ self.speed_reset = gtk.FALSE
+ gobject.timeout_add(int(self.speed), self.timer_handler)
+ return gtk.FALSE
+
+ return gtk.TRUE
+
+ def update_appbar(self):
+ self.appbar.set_text("%s: %d | %s: %d" % (_("Score"), self.score,
+ _("High score"),
+ self.hiscore))
+
+ def key_handler(self, widget, event=None):
+ if self.lost == gtk.TRUE:
+ return gtk.FALSE
+
+ if event.keyval == gtk.keysyms.p and self.lost == gtk.FALSE:
+ if self.paused == gtk.TRUE:
+ self.paused = gtk.FALSE
+ else:
+ self.paused = gtk.TRUE
+ return gtk.TRUE
+
+ if self.paused == gtk.TRUE:
+ return gtk.FALSE
+
+ if event.keyval == gtk.keysyms.Up or event.keyval == gtk.keysyms.Down:
+ if event.keyval == gtk.keysyms.Up:
+ attitude_change = 1
+ else:
+ attitude_change = -1
+ old_attitude = self.attitude
+ self.attitude = ((self.attitude + attitude_change +
+ len(self.pieces[self.piecenum])) %
+ len(self.pieces[self.piecenum]))
+ if self.check_collisions() == gtk.TRUE:
+ self.attitude = old_attitude
+ return gtk.TRUE
+
+ self.piece.destroy()
+ self.piece = self.place_piece(self.board,
+ self.pieces[self.piecenum][self.attitude],
+ self.colors[self.piecenum%(len(self.colors)+1)],
+ self.piece_x, self.piece_y);
+ return gtk.TRUE
+
+ if event.keyval == gtk.keysyms.Left or event.keyval == gtk.keysyms.Right:
+ if event.keyval == gtk.keysyms.Left:
+ deltax = -1
+ else:
+ deltax = 1
+ self.piece_x += deltax
+
+ deltay = 0
+ if self.piece_x%2 == 0:
+ deltay += self.pieceheight/2
+ else:
+ deltay -= self.pieceheight/2
+
+ if self.check_collisions() == gtk.TRUE:
+ if deltay > 0:
+ self.piece_x -= deltax
+ return gtk.TRUE
+ self.piece_y += 1
+ deltay += self.pieceheight
+ if self.check_collisions() == gtk.TRUE:
+ self.piece_x -= deltax
+ self.piece_y -= 1
+ return gtk.TRUE
+
+ self.piece.move(deltax*(self.piecewidth+self.piecenarrow)/2, deltay)
+ return gtk.TRUE
+
+ if event.keyval == gtk.keysyms.space:
+ orig_piece_y = self.piece_y
+ while self.check_collisions() == gtk.FALSE:
+ self.piece_y += 1
+ self.piece_y -= 1
+ self.piece.move(0, self.pieceheight*(self.piece_y - orig_piece_y))
+ self.score += self.piece_y - orig_piece_y
+ if self.score > self.hiscore:
+ self.hiscore = self.score
+ self.update_field()
+ if self.top_occupied() != gtk.TRUE:
+ self.next_piece()
+ else:
+ self.lost = gtk.TRUE
+ if self.hiscore < self.score:
+ self.hiscore = self.score
+ self.update_appbar()
+
+ return gtk.TRUE
+
+ return gtk.FALSE
+
+ def check_collisions(self):
+ for i in range(len(self.pieces[self.piecenum][self.attitude])):
+ for j in range(len(self.pieces[self.piecenum][self.attitude][i])):
+ if self.pieces[self.piecenum][self.attitude][i][j] != 0:
+ if j%2 == 0 and self.piece_x%2 == 1:
+ deltay = -1
+ else:
+ deltay = 0
+ if j + self.piece_x < 0 or j + self.piece_x >= self.cols:
+ return gtk.TRUE
+ if ((i + self.piece_y + deltay > 0) and
+ (i + self.piece_y + deltay >= self.rows or
+ self.field[i + self.piece_y + deltay][j + self.piece_x] != 0)):
+ return gtk.TRUE
+ return gtk.FALSE
+
+ def update_field(self):
+ for i in range(len(self.pieces[self.piecenum][self.attitude])):
+ for j in range(len(self.pieces[self.piecenum][self.attitude][i])):
+ if self.pieces[self.piecenum][self.attitude][i][j] != 0:
+ if j%2 == 0 and self.piece_x%2 == 1:
+ deltay = -1
+ else:
+ deltay = 0
+ if i+self.piece_y+deltay >= 0:
+ self.field[i + self.piece_y + deltay][j + self.piece_x] = 1
+ self.place_piece(self.rowgroups[i+self.piece_y+deltay],
+ ((0, 1,),),
+ self.colors[self.piecenum%(len(self.colors)+1)],
+ j + self.piece_x - 1, 0)
+ self.piece.destroy()
+ self.collapse_rows()
+
+ def collapse_rows(self):
+ row_points = 50
+ for i in range(self.rows):
+ row_full = gtk.TRUE
+ for j in range(self.cols):
+ if self.field[i][j] == 0:
+ row_full = gtk.FALSE
+ break
+
+ if row_full == gtk.TRUE:
+ row_points *= 2
+ self.speed *= self.speed_ratio
+ self.speed_reset = gtk.TRUE
+ self.rowgroups[i].destroy()
+ for j in range(i-1, -1, -1):
+ self.rowgroups[j].move(0, self.pieceheight)
+ self.rowgroups[j+1] = self.rowgroups[j]
+ self.field[j+1] = self.field[j]
+ self.field[0] = []
+ for j in range(self.cols):
+ self.field[0].append(0)
+ self.rowgroups[0] = self.board.add("GnomeCanvasGroup", x = 0,
+ y = 0)
+ if row_points > 50:
+ self.score += row_points
+ if self.score > self.hiscore:
+ self.hiscore = self.score
+ self.update_appbar()
+
+ def top_occupied(self):
+ for i in range(self.cols):
+ if self.field[0][i] != 0:
+ return gtk.TRUE
+ return gtk.FALSE
+
+if __name__ == '__main__':
+ h = Hextris()
+ h.main()
+ gtk.main()