diff options
author | Walter Bender <walter@sugarlabs.org> | 2010-09-09 17:48:15 (GMT) |
---|---|---|
committer | Walter Bender <walter@sugarlabs.org> | 2010-09-09 17:48:15 (GMT) |
commit | 611ceff0d5bf0a53fc596ebd7ad01d7ca43b7f91 (patch) | |
tree | ecf45354ac36371f4ad83f475f96167a63bbdd82 | |
parent | a37532dc04b42aa05517559946f0a6596a1bbb64 (diff) |
added triangle game
-rw-r--r-- | PukllanapacActivity.py | 101 | ||||
-rw-r--r-- | card.py | 72 | ||||
-rw-r--r-- | constants.py | 4 | ||||
-rw-r--r-- | grid.py | 88 | ||||
-rw-r--r-- | sprites.py | 5 | ||||
-rw-r--r-- | window.py | 78 |
6 files changed, 282 insertions, 66 deletions
diff --git a/PukllanapacActivity.py b/PukllanapacActivity.py index c01a0a1..8247b4e 100644 --- a/PukllanapacActivity.py +++ b/PukllanapacActivity.py @@ -17,15 +17,18 @@ import gobject import sugar from sugar.activity import activity -try: # 0.86+ toolbar widgets +try: + from sugar.graphics.toolbarbox import ToolbarBox + _have_toolbox = True +except ImportError: + _have_toolbox = False + +if _have_toolbox: from sugar.bundle.activitybundle import ActivityBundle from sugar.activity.widgets import ActivityToolbarButton from sugar.activity.widgets import StopButton - from sugar.graphics.toolbarbox import ToolbarBox from sugar.graphics.toolbarbox import ToolbarButton - _new_sugar_system = True -except ImportError: - _new_sugar_system = False + from sugar.graphics.toolbutton import ToolButton from sugar.graphics.menuitem import MenuItem from sugar.graphics.icon import Icon @@ -42,6 +45,7 @@ SERVICE = 'org.sugarlabs.PukllanapacActivity' IFACE = SERVICE PATH = '/org/augarlabs/PukllanapacActivity' LEVEL_ICONS = ['level1', 'level2', 'level3'] +GAME_ICONS = ['rectangle', 'hexagon'] def _button_factory(icon_name, tooltip, callback, toolbar, cb_arg=None, @@ -93,7 +97,8 @@ class PukllanapacActivity(activity.Activity): super(PukllanapacActivity,self).__init__(handle) self._play_level = 0 - self._setup_toolbars(_new_sugar_system) + self._play_mode = 0 + self._setup_toolbars(_have_toolbox) # Create a canvas canvas = gtk.DrawingArea() @@ -103,35 +108,42 @@ class PukllanapacActivity(activity.Activity): canvas.show() self.show_all() - # Initialize the canvas self.tw = Game(canvas, os.path.join(activity.get_bundle_path(), 'images'), self) # Restore game state from Journal or start new game - try: # Try reading restored settings from the Journal. - self._play_level = int(self.metadata['play_level']) + if 'play_level' in self.metadata: + self.change_play_level_cb(play_level=int( + self.metadata['play_level'])) + if 'play_mode' in self.metadata: + self.change_play_mode_cb(play_mode=int( + self.metadata['play_mode'])) grid = [] for i in range(24): - grid.append(int(self.metadata['card'+str(i)])) - self._play_level = int(self.metadata['play_level']) - # print "restoring: " + str(grid) - self.tw.grid.set_grid(grid) - except KeyError: - pass - print "restoring play level %d " % (self._play_level) + if 'card' + str(i) in self.metadata: + grid.append(int(self.metadata['card' + str(i)])) + self.tw.grid.set_grid(grid, self.tw.mode) + for i in range(24): + if 'rotate' + str(i) in self.metadata: + print "restoring %d to %d" % (i, + int(self.metadata['rotate' + str(i)])) + self.tw.grid.card_table[i].set_orientation( + int(self.metadata['rotate' + str(i)])) self.tw.mask(self._play_level) def write_file(self, file_path): """ Write the grid status to the Journal """ + self.metadata['play_mode'] = self._play_mode self.metadata['play_level'] = self._play_level - # print "saving " + str(self.tw.grid.grid) for i in range(24): - self.metadata['card'+str(i)] = str(self.tw.grid.grid[i]) + self.metadata['card' + str(i)] = str(self.tw.grid.grid[i]) + self.metadata['rotate' + str(i)] = str( + self.tw.grid.card_table[self.tw.grid.grid[i]].orientation) - def _setup_toolbars(self, new_sugar_system): + def _setup_toolbars(self, have_toolbox): """ Setup the toolbars.. """ - if new_sugar_system: + if have_toolbox: toolbox = ToolbarBox() # Activity toolbar @@ -157,11 +169,18 @@ class PukllanapacActivity(activity.Activity): # Add the buttons and labels to the toolbars self.level_button = _button_factory(LEVEL_ICONS[self._play_level], _('Set difficulty level.'), - self.level_cb, toolbar) + self.change_play_level_cb, toolbar) + mode = self._play_mode + mode += 1 + if mode == len(GAME_ICONS): + mode = 0 + self.game_button = _button_factory(GAME_ICONS[mode], + _('Select game.'), + self.change_play_mode_cb, toolbar) _separator_factory(toolbar, True, False) self.status_label = _label_factory(_("drag to swap"), toolbar) - if _new_sugar_system: + if _have_toolbox: _separator_factory(toolbox.toolbar, False, True) stop_button = StopButton(self) @@ -169,11 +188,39 @@ class PukllanapacActivity(activity.Activity): toolbox.toolbar.insert(stop_button, -1) stop_button.show() - def level_cb(self, button): + def change_play_level_cb(self, button=None, play_level=None): """ Cycle between levels """ - self._play_level += 1 - if self._play_level == len(LEVEL_ICONS): - self._play_level = 0 + if self._play_mode > 0: + return + if play_level is None: + self._play_level += 1 + if self._play_level == len(LEVEL_ICONS): + self._play_level = 0 + else: + self._play_level = play_level self.level_button.set_icon(LEVEL_ICONS[self._play_level]) - self.tw.grid.reset() + self.tw.grid.reset(GAME_ICONS[self._play_mode]) self.tw.mask(self._play_level) + + def change_play_mode_cb(self, button=None, play_mode=None): + """ Cycle between game modes """ + if play_mode is None: + self._play_mode += 1 + if self._play_mode == len(GAME_ICONS): + self._play_mode = 0 + else: + self._play_mode = play_mode + mode = self._play_mode + mode += 1 + if mode == len(GAME_ICONS): + mode = 0 + self.game_button.set_icon(GAME_ICONS[mode]) + self.tw.mode = GAME_ICONS[self._play_mode] + self.tw.grid.initialize_cards(self.tw.sprites, self.tw.path, + self.tw.card_dim, self.tw.scale, + GAME_ICONS[self._play_mode]) + if self._play_mode > 0: + self._play_level = len(LEVEL_ICONS) - 1 + self.level_button.set_icon(LEVEL_ICONS[self._play_level]) + self.tw.mask(self._play_level) + self.tw.grid.reset(GAME_ICONS[self._play_mode]) @@ -18,19 +18,75 @@ from sprites import Sprite def load_image(file, w, h): - """ convert from file to pixbuf at size w, h """ + """ Convert from file to pixbuf at size w, h """ return gtk.gdk.pixbuf_new_from_file_at_size(file, int(w), int(h)) class Card: - """ class for defining individual cards """ + """ Class for defining individual cards """ - def __init__(self, tw, c, x, y): - """ Load a card from a precomputed svg """ + def __init__(self, sprites, path, card_dim, scale, c, x, y, shape='circle'): + """ Load a card from a precomputed SVG. """ self.images = [] - file = "%s/card-%d.svg" % (tw.path, c) - self.images.append(load_image(file, tw.card_dim * tw.scale, - tw.card_dim * tw.scale)) + self.orientation = 0 + self.increment = 90 + + if shape == 'triangle': + file = "%s/triangle-r0-%d.svg" % (path, c) + self.increment = 60 + else: + file = "%s/card-%d.svg" % (path, c) + + self.images.append(load_image(file, card_dim * scale, + card_dim * scale)) + + if shape == 'triangle': + file = "%s/triangle-r60-%d.svg" % (path, c) + self.images.append(load_image(file, card_dim * scale, + card_dim * scale)) + file = "%s/triangle-r120-%d.svg" % (path, c) + self.images.append(load_image(file, card_dim * scale, + card_dim * scale)) + file = "%s/triangle-r180-%d.svg" % (path, c) + self.images.append(load_image(file, card_dim * scale, + card_dim * scale)) + file = "%s/triangle-r240-%d.svg" % (path, c) + self.images.append(load_image(file, card_dim * scale, + card_dim * scale)) + file = "%s/triangle-r300-%d.svg" % (path, c) + self.images.append(load_image(file, card_dim * scale, + card_dim * scale)) + else: + for r in range(3): + self.images.append(self.images[r].rotate_simple(90)) + # create sprite from svg file - self.spr = Sprite(tw.sprites, x, y, self.images[0]) + self.spr = Sprite(sprites, x, y, self.images[0]) + self.spr.draw() + + def reset_image(self, tw): + """ Reset image to orientation 0. """ + while self.orientation != 0: + self.rotate_ccw() + + def set_orientation(self, r): + """ Set orientation to 0, 90, 180, or 270. """ + if r in [0, 60, 90, 120, 180, 240, 270, 300]: + while r != self.orientation: + self.rotate_ccw() + + def rotate_180(self): + """ Rotate a tile 180 degrees """ + print "rotate 180" + r = 0 + while r < 180: + self.rotate_ccw() + r += self.increment + + def rotate_ccw(self): + """ Set the card image to correspond to rotation r. """ + self.orientation += self.increment + if self.orientation == 360: + self.orientation = 0 + self.spr.images[0] = self.images[int(self.orientation/self.increment)] self.spr.draw() diff --git a/constants.py b/constants.py index 8ea6eb7..57bd402 100644 --- a/constants.py +++ b/constants.py @@ -24,9 +24,7 @@ C = [[Y, B, Y, R], [R, Y, Y, R], [R, R, Y, R], [R, R, R, R], [B, B, B, Y], [Y, B H = [[G, B, G], [B, Y, R], [B, R, B], [Y, G, Y], [R, G, B], [G, B, B], [R, B, G], [B, B, B], [R, R, R], [R, R, B], [G, R, R], [Y, R, R], [R, G, Y], [G, G, Y], [G, R, Y], [B, Y, G], [Y, R, Y], [G, G, R], [G, G, G], [B, Y, Y], [R, Y, B], [Y, B, G], [Y, Y, Y], [Y, B, B]] -RT = [[B, B, Y], [Y, B, G], [G, R, B], [Y, G, Y], [R, G, G], [Y, G, B], [B, G, G], [R, B, Y], [R, R, G], [R, Y, G], [B, B, R], [Y, Y, Y]] - -LT = [[B, B, G], [R, Y, Y], [G, G, G], [G, Y, G], [G, R, B], [B, Y, Y], [B, B, B], [B, R, R], [Y, R, R], [R, R, R], [R, B, Y], [R, Y, G]] +RT = [[B, B, Y], [Y, B, G], [G, B, R], [Y, G, Y], [R, G, G], [Y, G, B], [B, G, G], [R, Y, B], [R, R, G], [R, G, Y], [B, B, R], [Y, Y, Y], [B, B, G], [R, Y, Y], [G, G, G], [G, Y, G], [G, R, B], [B, Y, Y], [B, B, B], [B, R, R], [Y, R, R], [R, R, R], [R, B, Y], [R, Y, G]] MASKS = [[0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21, 22, 23], [0, 1, 2, 3, 4, 5, 6, 11, 12, 17, 18, 19, 20, 21, 22, 23], []] @@ -16,43 +16,78 @@ import gtk import gobject from sprites import Sprite -from card import Card, load_image +from card import Card +HEX_TO_GRID = [-1, 0, 1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, -1, 23, 24, -1] +GRID_TO_HEX = [1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 25, 26] +HEX_ORIENTATION = [-1, 180, 0, -1, 180, 0, 180, 0, 0, 180, 0, 180, 180, 0, 180, + 0, 0, 180, 0, 180, 180, 0, 180, 0, -1, 180, 0, -1] class Grid: - """ Class for defining 6x4 matrix of cards """ - def __init__(self, tw): - """ Grid positions """ + """ Class for defining matrix of cards """ + + def __init__(self, tw, shape='rectangle'): + """ Set initial grid positions: either a rectangle or a hexgaon """ self.grid = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] - self.card_table = [] # Stuff to keep around for the graphics self.w = int(tw.width) self.h = int(tw.height) self.d = int(tw.card_dim * tw.scale) + self.dx = self.d * 0.85 + self.dy = self.d * 0.5 self.s = tw.scale - # Initialize the cards + self.initialize_cards(tw.sprites, tw.path, tw.card_dim, tw.scale, shape) + + def initialize_cards(self, sprites, path, card_dim, scale, shape): + if hasattr(self, 'card_table'): + for c in self.card_table: + c.spr.hide() + self.card_table = [] for i in self.grid: - x, y = self.i_to_xy(i) - self.card_table.append(Card(tw, i, x, y)) + x, y = self.i_to_xy(i, shape) + if shape == 'hexagon': + self.card_table.append(Card(sprites, path, card_dim, scale, i, + x, y, 'triangle')) + self.card_table[i].set_orientation( + HEX_ORIENTATION[GRID_TO_HEX[i]]) + else: + self.card_table.append(Card(sprites, path, card_dim, scale, i, + x, y)) - def i_to_xy(self, i): + def i_to_xy(self, i, shape='rectangle'): """ Convert a grid index to an x, y position """ - return int((self.w - (self.d * 6)) / 2) + \ - (i % 6) * self.d - 10 + i % 6 * 4, \ - int((self.h - (self.d * 4)) / 2) + \ - int( i / 6) * self.d - 6 + int(i/6)*4 + if shape == 'hexagon': # 4 x 7 with empty corners + return int((self.w - (self.dx * 4)) / 2) + \ + (GRID_TO_HEX[i] % 4) * self.dx, \ + int((self.h - (self.dy * 7)) / 2) + \ + int(GRID_TO_HEX[i] / 4) * self.dy + else: # 6 x 4 + return int((self.w - (self.d * 6)) / 2) + \ + (i % 6) * self.d - 10 + i % 6 * 4, \ + int((self.h - (self.d * 4)) / 2) + \ + int( i / 6) * self.d - 6 + int(i / 6) * 4 - def xy_to_i(self, x, y): + def xy_to_i(self, x, y, shape='rectangle'): """ Convert an x, y position to a grid index """ - return (x - int((self.w - (self.d * 6)) / 2)) / self.d + \ - ((y - int((self.h - (self.d * 4)) / 2)) / self.d) * 6 + if shape == 'hexagon': + return HEX_TO_GRID[(x - int((self.w - (self.d * 6)) / 2)) \ + / self.d + \ + ((y - int((self.h - (self.d * 4)) / 2)) / self.d) * 6] + else: + return (x - int((self.w - (self.d * 6)) / 2)) / self.d + \ + ((y - int((self.h - (self.d * 4)) / 2)) / self.d) * 6 - def set_grid(self, newgrid): + def set_grid(self, newgrid, shape='rectangle'): """ Move cards to x, y positions specified in grid """ for i, c in enumerate(newgrid): - x, y = self.i_to_xy(i) + x, y = self.i_to_xy(i, shape) self.card_table[c].spr.move((x, y)) + if shape == 'hexagon': + self.card_table[c].set_orientation( + HEX_ORIENTATION[GRID_TO_HEX[c]]) self.grid[i] = c def show_all(self): @@ -65,16 +100,21 @@ class Grid: for i in list: self.card_table[i].spr.hide() - def reset(self): + def reset(self, shape='rectangle'): """ Reset everything to initial layout """ self.show_all() self.grid = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] for i in self.grid: - x, y = self.i_to_xy(i) + x, y = self.i_to_xy(i, shape) self.card_table[i].spr.move((x, y)) + if shape == 'hexagon': + self.card_table[i].set_orientation( + HEX_ORIENTATION[GRID_TO_HEX[i]]) + else: + self.card_table[i].set_orientation(0) - def swap(self, a, b): + def swap(self, a, b, shape='rectangle'): """ swap grid elements and x,y positions of sprites """ ai = self.spr_to_i(a) bi = self.spr_to_i(b) @@ -87,6 +127,12 @@ class Grid: bx, by = b.get_xy() a.move((bx, by)) b.move((ax, ay)) + if shape == 'hexagon': + if HEX_ORIENTATION[GRID_TO_HEX[ai]] != \ + HEX_ORIENTATION[GRID_TO_HEX[bi]]: + print 'rotating 180: ', ai, bi, self.grid[ai], self.grid[bi] + self.card_table[self.grid[ai]].rotate_180() + self.card_table[self.grid[bi]].rotate_180() def spr_to_i(self, spr): """ Find a card index from a sprite """ @@ -127,13 +127,14 @@ class Sprites: if spr in self.list: self.list.remove(spr) - def find_sprite(self, pos): + def find_sprite(self, pos, alpha=True): """ Search based on (x, y) position. Return the 'top/first' one. """ list = self.list[:] list.reverse() for spr in list: if spr.hit(pos): - return spr + if not alpha or spr.get_pixel(pos)[3] == 255: + return spr return None def refresh(self, event): @@ -28,6 +28,9 @@ from grid import Grid from sprites import Sprites from constants import C, MASKS, CARD_DIM +import logging +_logger = logging.getLogger('pukllanapac-activity') + LEVEL_BOUNDS = [[[1, 2], [0, 1], [2, 3], [1, 2]], [[1, 2], [0, 1], [1, 4], [0, 3]], [[0, 3], [-1, 2], [1, 5], [-1, 4]]] @@ -59,6 +62,7 @@ class Game(): self.canvas.connect("expose-event", self._expose_cb) self.canvas.connect("button-press-event", self._button_press_cb) self.canvas.connect("button-release-event", self._button_release_cb) + self.canvas.connect("key_press_event", self._keypress_cb) self.width = gtk.gdk.screen_width() self.height = gtk.gdk.screen_height() - GRID_CELL_SIZE self.card_dim = CARD_DIM @@ -68,7 +72,8 @@ class Game(): self.sprites = Sprites(self.canvas) # Initialize the grid - self.grid = Grid(self) + self.mode = 'rectangle' + self.grid = Grid(self, self.mode) self.bounds = LEVEL_BOUNDS[0] self.level = 0 @@ -102,11 +107,23 @@ class Game(): self.release = spr # if the same card (click) then rotate if self.press == self.release: - # check to see if it was an aborted move - if distance(self.start_drag, [x, y]) < 20: - pass + if distance(self.start_drag, [x, y]) > 20: + print "rotating card ", self.grid.grid[self.grid.spr_to_i( + self.press)], 'was (', self.grid.card_table[ + self.grid.grid[self.grid.spr_to_i( + self.press)]].orientation, ')' + self.grid.card_table[self.grid.grid[self.grid.spr_to_i( + self.press)]].rotate_ccw() + if self.mode == 'hexagon': # Rotate a second time + self.grid.card_table[self.grid.grid[self.grid.spr_to_i( + self.press)]].rotate_ccw() + self.press.set_layer(0) + self.press.set_layer(100) else: - self.grid.swap(self.press, self.release) + print "swapping: ", self.grid.grid[self.grid.spr_to_i( + self.press)], self.grid.grid[self.grid.spr_to_i( + self.release)] + self.grid.swap(self.press, self.release, self.mode) self.press = None self.release = None if self.test() == True: @@ -114,6 +131,15 @@ class Game(): gobject.timeout_add(3000, self.activity.level_cb, None) return True + def _keypress_cb(self, area, event): + """ Keypress is used to switch between games """ + k = gtk.gdk.keyval_name(event.keyval) + u = gtk.gdk.keyval_to_unicode(event.keyval) + if k == '1': + print 'game 1' + elif k == '2': + print 'game 2' + def _expose_cb(self, win, event): self.sprites.refresh(event) return True @@ -144,26 +170,68 @@ class Game(): return False if C[self.grid.grid[i]][3] != C[self.grid.grid[i - 6]][2]: return False + ''' if row > self.bounds[1][0] and row <= self.bounds[1][1]: if C[self.grid.grid[i]][1] != C[self.grid.grid[i + 6]][0]: return False if C[self.grid.grid[i]][2] != C[self.grid.grid[i + 6]][3]: return False + ''' if col > self.bounds[2][0] and col <= self.bounds[2][1]: if C[self.grid.grid[i]][3] != C[self.grid.grid[i - 1]][0]: return False if C[self.grid.grid[i]][2] != C[self.grid.grid[i - 1]][1]: return False + ''' if col > self.bounds[3][0] and col <= self.bounds[3][1]: if C[self.grid.grid[i]][0] != C[self.grid.grid[i + 1]][3]: return False if C[self.grid.grid[i]][1] != C[self.grid.grid[i + 1]][2]: return False + ''' return True + def solver(self): + """ Permutate until a solution is found (useless since 24! is >>>) """ + self.grid.reset(self.mode) + counter = 0 + a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23] + for i in Permutation(a): + self.grid.set_grid(i, self.mode) + if self.test() is True: + return True + counter += 1 + if (counter/10000)*10000 == counter: + _logger.debug('%d' % counter) + self.activity.status_label.set_text('%d' % (counter)) + self.activity.status_label.set_text(_("no solution found")) + return True def distance(start, stop): """ Measure the length of drag between button press and button release. """ dx = start[0] - stop[0] dy = start[1] - stop[1] return sqrt(dx * dx + dy * dy) + + +class Permutation: + """Permutaion class for checking for all possible matches on the grid """ + + def __init__(self, elist): + self._data = elist[:] + self._sofar = [] + + def __iter__(self): + return self.next() + + def next(self): + for e in self._data: + if e not in self._sofar: + self._sofar.append(e) + if len(self._sofar) == len(self._data): + yield self._sofar[:] + else: + for v in self.next(): + yield v + self._sofar.pop() |