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