]> git.siccegge.de Git - ghextris.git/blob - ghextris.py
5fb037d4fec43fa415587074f0c5ff24a7ae8537
[ghextris.git] / ghextris.py
1 #!/usr/bin/python2
2
3 # Gnome-hextris; a free rewrite of the xhextris game in Python for Gnome
4 # Copyright 2004 Mikko Rauhala <mjr@iki.fi>
5
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 # This will be overwritten for the installed version:
21 SHAREDIR = "."
22
23 VERSION="0.9.0"
24
25 import os
26 import sys
27
28 import gi
29 gi.require_version("Gtk", "3.0")
30
31 from gi.repository import Gtk
32 import gnomecanvas
33 import gnome
34 import gnome.ui
35 from gi.repository import GObject
36
37 import gettext
38
39 import random
40
41 class Hextris:
42 def __init__(self):
43 self.pieceheight = 24
44 self.piecewidth = 26
45 self.piecenarrow = 14
46 # self.piecewidth = 20
47 # self.piecenarrow = 20
48 self.rows = 23
49 self.cols = 13 # Must be odd or weirdness ensues
50
51 self.width = (((self.cols/2)+2)*self.piecewidth +
52 ((self.cols/2)+1) * self.piecenarrow) + 2
53 self.height = (self.rows+1) * self.pieceheight + 2
54
55 self.colors = ("blue", "yellow", "red", "orange", "green", "purple",
56 "cyan", "gray45", "magenta", "lightblue")
57 self.deltax = (-1, -1, -1, 0, 0, 0, 0, 0, -1, -1)
58 self.deltay = (-1, -1, -1, 0, 0, 0, 0, 0, -1, -1)
59 self.pieces = (
60 (((0,), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
61 ((0,), (0, 0, 0, 1), (0, 1, 1), (1,)),
62 ((0,), (1, 1), (0, 0, 1, 1)),
63 ((0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
64 ((0,), (0, 0, 0, 1, 1), (0, 1, 1)),
65 ((0,), (0, 1), (0, 0, 1, 1), (0, 0, 0, 0, 1))),
66 (((0,), (0, 0, 1), (0, 0, 1, 1), (0, 0, 0, 1)),
67 ((0,), (0, 0, 0, 1), (0, 0, 1), (0, 1, 1)),
68 ((0,), (0,), (1, 1, 1, 1)),
69 ((0, 1), (0, 1), (0, 0, 1), (0, 0, 1)),
70 ((0, 0, 0, 1), (0, 0, 1), (0, 1, 1)),
71 ((0,), (0, 1, 0, 1), (0, 0, 1, 0, 1))),
72 (((0,), (0, 0, 1), (0, 1, 1), (0, 1)),
73 ((0,), (0, 1, 0, 1), (1, 0, 1, 0)),
74 ((0, 1), (0, 0, 1), (0, 0, 1, 1)),
75 ((0, 0, 0, 1), (0, 0, 0, 1), (0, 0, 1), (0, 0, 1)),
76 ((0,), (0,), (0, 1, 1, 1, 1)),
77 ((0,), (0, 1), (0, 0, 1), (0, 0, 1, 1))),
78 (((0, 1, 0), (0, 1, 0), (1, 0, 1)),
79 ((0,), (1, 1, 1), (0, 1, 0))),
80 (((0, 1), (1,), (1, 1)),
81 ((0, 1), (1, 0, 1), (1,)),
82 ((0, 1), (1, 0, 1), (0, 0, 1)),
83 ((0, 1), (0, 0, 1), (0, 1, 1)),
84 ((0,), (0, 0, 1), (1, 1, 1)),
85 ((0,), (1,), (1, 1, 1))),
86 (((0,), (0, 1), (1, 1, 1)),
87 ((0,), (1, 1), (1, 1, 0)),
88 ((0, 1), (1, 1), (1,)),
89 ((0, 1), (1, 1, 1)),
90 ((0, 1), (0, 1, 1), (0, 0, 1)),
91 ((0,), (0, 1, 1), (0, 1, 1))),
92 (((0, 1), (0, 1), (0, 1, 1)),
93 ((0,), (0, 1, 1), (1, 1)),
94 ((0,), (1, 1), (1, 0, 1)),
95 ((0, 1), (1, 1), (0, 1)),
96 ((0, 1), (0, 1, 1), (1,)),
97 ((0,), (1, 1, 1), (0, 0, 1))),
98 (((0, 1), (0, 1), (1, 1)),
99 ((0,), (1, 1, 1), (1,)),
100 ((0, 1), (1, 1), (0, 0, 1)),
101 ((0, 1), (0, 1, 1), (0, 1)),
102 ((0,), (0, 1, 1), (1, 0, 1)),
103 ((0,), (1, 1), (0, 1, 1))),
104 (((0,), (0, 0, 1), (0, 0, 1), (0, 0, 1, 1)),
105 ((0,), (0, 0, 0, 1), (0, 1, 1), (0, 1)),
106 ((0,), (0, 1), (1, 0, 1, 1)),
107 ((0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
108 ((0, 0, 0, 1), (0, 0, 0, 1), (0, 1, 1)),
109 ((0,), (0, 1), (0, 0, 1, 1, 1))),
110 (((0,), (0, 0, 1), (0, 0, 1), (0, 1, 1)),
111 ((0,), (0, 0, 0, 1), (1, 1, 1)),
112 ((0, 1), (0, 1), (0, 0, 1, 1)),
113 ((0, 0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)),
114 ((0,), (0, 0, 0, 1), (0, 1, 1, 0, 1)),
115 ((0,), (0, 1), (0, 0, 1, 1), (0, 0, 0, 1)))
116 )
117
118 self.attitude = 0
119 self.speed_orig = 400
120 self.speed = 400
121 self.speed_ratio = 0.99
122 self.speed_reset = False
123 self.paused = False
124 self.lost = True
125 self.score = 0
126 self.hiscore = 0
127 self.nextnum = 0
128
129 self.running = False
130
131 self.nextpiece = False
132 self.board = False
133 self.rowgroups = []
134
135 def draw_hexagon(self, group, color):
136 pts = []
137 pts.append ((self.piecewidth - self.piecenarrow)/2)
138 pts.append (0)
139 pts.append (self.piecewidth - pts[0])
140 pts.append (0)
141 pts.append (self.piecewidth)
142 pts.append (self.pieceheight/2)
143 pts.append (pts[2])
144 pts.append (self.pieceheight)
145 pts.append (pts[0])
146 pts.append (pts[7])
147 pts.append (0)
148 pts.append (pts[5])
149 pts.append (pts[0])
150 pts.append (pts[1])
151 item = group.add ("GnomeCanvasPolygon", points = pts, fill_color = color,
152 outline_color = "black", width_units = 1)
153 return item
154
155 def draw_piece(self, group, matrix, color):
156 for i in range(len(matrix)):
157 for j in range(len(matrix[i])):
158 if matrix[i][j] != 0:
159 g = group.add("GnomeCanvasGroup",
160 x = (j*(self.piecewidth+self.piecenarrow)/2),
161 y = ((i * (self.pieceheight)) +
162 ((j%2) * self.pieceheight/2)))
163 self.draw_hexagon(g, color)
164
165 def place_piece(self, group, matrix, color, x, y):
166 piecegroup = group.add("GnomeCanvasGroup",
167 x = (x+1)*((self.piecewidth+self.piecenarrow)/2),
168 y = ((y * self.pieceheight) -
169 (((x%2)+1) * self.pieceheight/2)))
170 self.draw_piece(piecegroup, matrix, color)
171 return piecegroup
172
173 def init_board(self):
174 board = self.canvas.root()
175 for i in range(self.rows+1):
176 for j in [0, 1]:
177 boardhex = board.add("GnomeCanvasGroup",
178 x = j * (self.piecewidth*(self.cols/2+1) +
179 self.piecenarrow*(self.cols/2+1)),
180 y = i * self.pieceheight)
181 self.draw_hexagon(boardhex, "gray")
182 for i in range(self.cols/2):
183 boardhex = board.add("GnomeCanvasGroup",
184 x = (i+1) * (self.piecewidth +
185 self.piecenarrow),
186 y = self.rows * self.pieceheight)
187 self.draw_hexagon(boardhex, "gray")
188 for i in range(self.cols/2+1):
189 boardhex = board.add("GnomeCanvasGroup",
190 x = (i * (self.piecewidth + self.piecenarrow)+
191 (self.piecewidth + self.piecenarrow)/2),
192 y = ((self.rows*2-1) * self.pieceheight)/2)
193 self.draw_hexagon(boardhex, "gray")
194
195 def on_new_activate(self, event):
196 self.speed = self.speed_orig
197 self.score = 0
198 self.attitude = 0
199 self.lost = False
200 self.paused = False
201
202 self.update_appbar()
203
204 if self.board != False:
205 self.board.destroy()
206 self.board = self.canvas.root().add("GnomeCanvasGroup", x = 0, y = 0)
207
208 self.field = []
209 for i in range(self.rows):
210 self.field.append([])
211 for j in range(self.cols):
212 self.field[i].append(0)
213
214 self.rowgroups = []
215 for i in range(self.rows):
216 self.rowgroups.append(self.board.add("GnomeCanvasGroup", x = 0,
217 y = i * self.pieceheight))
218
219 self.nextnum = random.randint(0, 9)
220 self.next_piece()
221
222 if self.running == False:
223 GObject.timeout_add(self.speed, self.timer_handler)
224 self.running = True
225 else:
226 self.speed_reset = True
227
228 return True
229
230 def on_about_activate(self, event):
231 aTree = self.builder.get_object("about")
232 about = aTree.get_widget("about")
233 about.set_property("name", "Ghextris")
234 about.set_property("version", VERSION)
235 return True
236
237 def on_pause_game_activate(self, event):
238 if self.lost == True:
239 return False
240 if self.paused == True:
241 self.paused = False
242 else:
243 self.paused = True
244 return True
245
246 def on_quit_activate(self, event):
247 Gtk.main_quit()
248
249 def main(self):
250 gnome.init("Ghextris", VERSION)
251 gettext.install("ghextris")
252 self.builder = Gtk.Builder()
253 self.builder.add_from_file(os.path.join(SHAREDIR, "ghextris.builder"))
254 wTree =self.builder.get_object("GhextrisApp")
255 dic = {"on_new_activate": self.on_new_activate,
256 "on_pause_game_activate": self.on_pause_game_activate,
257 "on_quit_activate": self.on_quit_activate,
258 "on_about_activate": self.on_about_activate}
259 wTree.signal_autoconnect(dic)
260
261 win = wTree.get_widget("GhextrisApp")
262 self.canvas = wTree.get_widget("canvas")
263 preview = wTree.get_widget("previewcanvas")
264 appbar = wTree.get_widget("appbar").get_children()[0]
265 self.appbar = appbar.get_children()[0]
266
267 win.connect('destroy', self.on_quit_activate)
268
269 self.canvas.set_size_request(self.width, self.height)
270 self.canvas.set_scroll_region(0, 0, self.width, self.height)
271 self.canvas.show()
272
273 preview.set_size_request(self.piecewidth * 4, self.pieceheight * 5)
274 preview.show()
275 self.preview = preview.root()
276
277 win.connect("key-press-event", self.key_handler)
278 self.init_board()
279
280 random.seed()
281
282 self.update_appbar()
283
284 win.show()
285
286 def next_piece(self):
287 self.piecenum = self.nextnum
288 self.nextnum = random.randint(0, 9)
289 self.attitude = 0
290 self.piece_x = (self.cols/2)-1 + self.deltax[self.piecenum]
291 self.piece_y = -3 + self.deltax[self.piecenum]
292 self.piece = self.place_piece(self.board, self.pieces[self.piecenum][0],
293 self.colors[self.piecenum%(len(self.colors)+1)],
294 self.piece_x, self.piece_y)
295
296 if self.nextpiece != False:
297 self.nextpiece.destroy()
298 self.nextpiece = self.place_piece(self.preview,
299 self.pieces[self.nextnum][0],
300 self.colors[self.nextnum%(len(self.colors)+1)],
301 self.deltax[self.nextnum],
302 self.deltay[self.nextnum])
303
304 def timer_handler(self):
305 if self.lost == True:
306 self.running = False
307 return False
308
309 if self.paused == True:
310 return True
311 self.piece_y += 1
312
313 if self.check_collisions() == True:
314 self.piece_y -= 1
315 self.update_field()
316 if self.top_occupied() != True:
317 self.next_piece()
318 else:
319 self.lost = True
320 if self.hiscore < self.score:
321 self.hiscore = self.score
322 self.update_appbar()
323 self.running = False
324 return False
325 else:
326 self.piece.move(0, self.pieceheight)
327
328 if self.speed_reset == True:
329 self.speed_reset = False
330 GObject.timeout_add(int(self.speed), self.timer_handler)
331 return False
332
333 return True
334
335 def update_appbar(self):
336 self.appbar.set_text("%s: %d | %s: %d" % (_("Score"), self.score,
337 _("High score"),
338 self.hiscore))
339
340 def key_handler(self, widget, event=None):
341 if self.lost == True:
342 return False
343
344 if event.keyval == Gdk.KEY_p and self.lost == False:
345 if self.paused == True:
346 self.paused = False
347 else:
348 self.paused = True
349 return True
350
351 if self.paused == True:
352 return False
353
354 if event.keyval == Gdk.KEY_Up or event.keyval == Gdk.KEY_Down:
355 if event.keyval == Gdk.KEY_Up:
356 attitude_change = 1
357 else:
358 attitude_change = -1
359 old_attitude = self.attitude
360 self.attitude = ((self.attitude + attitude_change +
361 len(self.pieces[self.piecenum])) %
362 len(self.pieces[self.piecenum]))
363 if self.check_collisions() == True:
364 self.attitude = old_attitude
365 return True
366
367 self.piece.destroy()
368 self.piece = self.place_piece(self.board,
369 self.pieces[self.piecenum][self.attitude],
370 self.colors[self.piecenum%(len(self.colors)+1)],
371 self.piece_x, self.piece_y);
372 return True
373
374 if event.keyval == Gdk.KEY_Left or event.keyval == Gdk.KEY_Right:
375 if event.keyval == Gdk.KEY_Left:
376 deltax = -1
377 else:
378 deltax = 1
379 self.piece_x += deltax
380
381 deltay = 0
382 if self.piece_x%2 == 0:
383 deltay += self.pieceheight/2
384 else:
385 deltay -= self.pieceheight/2
386
387 if self.check_collisions() == True:
388 if deltay > 0:
389 self.piece_x -= deltax
390 return True
391 self.piece_y += 1
392 deltay += self.pieceheight
393 if self.check_collisions() == True:
394 self.piece_x -= deltax
395 self.piece_y -= 1
396 return True
397
398 self.piece.move(deltax*(self.piecewidth+self.piecenarrow)/2, deltay)
399 return True
400
401 if event.keyval == Gdk.KEY_space:
402 orig_piece_y = self.piece_y
403 while self.check_collisions() == False:
404 self.piece_y += 1
405 self.piece_y -= 1
406 self.piece.move(0, self.pieceheight*(self.piece_y - orig_piece_y))
407 self.score += self.piece_y - orig_piece_y
408 if self.score > self.hiscore:
409 self.hiscore = self.score
410 self.update_field()
411 if self.top_occupied() != True:
412 self.next_piece()
413 else:
414 self.lost = True
415 if self.hiscore < self.score:
416 self.hiscore = self.score
417 self.update_appbar()
418
419 return True
420
421 return False
422
423 def check_collisions(self):
424 for i in range(len(self.pieces[self.piecenum][self.attitude])):
425 for j in range(len(self.pieces[self.piecenum][self.attitude][i])):
426 if self.pieces[self.piecenum][self.attitude][i][j] != 0:
427 if j%2 == 0 and self.piece_x%2 == 1:
428 deltay = -1
429 else:
430 deltay = 0
431 if j + self.piece_x < 0 or j + self.piece_x >= self.cols:
432 return True
433 if ((i + self.piece_y + deltay > 0) and
434 (i + self.piece_y + deltay >= self.rows or
435 self.field[i + self.piece_y + deltay][j + self.piece_x] != 0)):
436 return True
437 return False
438
439 def update_field(self):
440 for i in range(len(self.pieces[self.piecenum][self.attitude])):
441 for j in range(len(self.pieces[self.piecenum][self.attitude][i])):
442 if self.pieces[self.piecenum][self.attitude][i][j] != 0:
443 if j%2 == 0 and self.piece_x%2 == 1:
444 deltay = -1
445 else:
446 deltay = 0
447 if i+self.piece_y+deltay >= 0:
448 self.field[i + self.piece_y + deltay][j + self.piece_x] = 1
449 self.place_piece(self.rowgroups[i+self.piece_y+deltay],
450 ((0, 1,),),
451 self.colors[self.piecenum%(len(self.colors)+1)],
452 j + self.piece_x - 1, 0)
453 self.piece.destroy()
454 self.collapse_rows()
455
456 def collapse_rows(self):
457 row_points = 50
458 for i in range(self.rows):
459 row_full = True
460 for j in range(self.cols):
461 if self.field[i][j] == 0:
462 row_full = False
463 break
464
465 if row_full == True:
466 row_points *= 2
467 self.speed *= self.speed_ratio
468 self.speed_reset = True
469 self.rowgroups[i].destroy()
470 for j in range(i-1, -1, -1):
471 self.rowgroups[j].move(0, self.pieceheight)
472 self.rowgroups[j+1] = self.rowgroups[j]
473 self.field[j+1] = self.field[j]
474 self.field[0] = []
475 for j in range(self.cols):
476 self.field[0].append(0)
477 self.rowgroups[0] = self.board.add("GnomeCanvasGroup", x = 0,
478 y = 0)
479 if row_points > 50:
480 self.score += row_points
481 if self.score > self.hiscore:
482 self.hiscore = self.score
483 self.update_appbar()
484
485 def top_occupied(self):
486 for i in range(self.cols):
487 if self.field[0][i] != 0:
488 return True
489 return False
490
491 if __name__ == '__main__':
492 h = Hextris()
493 h.main()
494 Gtk.main()