class Tile: def __init__(self, sprites, svg, svgs, tile_type='tile', number=0): self.highlight = [svg_str_to_pixbuf(svg)] self.spr = Sprite(sprites, 0, 0, self.highlight[0]) for s in svgs: self.highlight.append(svg_str_to_pixbuf(s)) self.paths = [] # [[N, E, S, W], [N, E, S, W]] self.shape = None self.orientation = 0 self.type = tile_type self.number = number self.value = 1 self.spr.set_label_color('#FF0000') def set_value(self, value): self.value = value def get_value(self): return self.value def set_paths(self, paths): for c in paths: self.paths.append(c) def get_paths(self): return self.paths def reset(self): self.spr.set_layer(HIDE) self.shape = None self.spr.set_shape(self.highlight[0]) while self.orientation != 0: self.rotate_clockwise() def set_shape(self, path): if self.shape is None: self.spr.set_shape(self.highlight[path + 1]) self.shape = path elif self.shape != path: self.spr.set_shape(self.highlight[-1]) def rotate_clockwise(self): """ rotate the tile and its paths """ for i in range(len(self.paths)): west = self.paths[i][WEST] self.paths[i][WEST] = self.paths[i][SOUTH] self.paths[i][SOUTH] = self.paths[i][EAST] self.paths[i][EAST] = self.paths[i][NORTH] self.paths[i][NORTH] = west for h in range(len(self.highlight)): self.highlight[h] = self.highlight[h].rotate_simple(270) self.spr.set_shape(self.highlight[0]) self.orientation += 90 self.orientation %= 360 def show_tile(self): self.spr.set_layer(CARDS) def hide(self): self.spr.move((-self.spr.get_dimensions()[0], 0))
class Tile: def __init__(self, sprites, svg, svgs, tile_type='tile', number=0): self.highlight = [svg_str_to_pixbuf(svg)] self.spr = Sprite(sprites, 0, 0, self.highlight[0]) for s in svgs: self.highlight.append(svg_str_to_pixbuf(s)) self.paths = [] # [[N, E, S, W], [N, E, S, W]] self.shape = None self.orientation = 0 self.type = tile_type self.number = number self.value = 1 self.spr.set_label_color('#FF0000') def set_value(self, value): self.value = value def get_value(self): return self.value def set_paths(self, paths): for c in paths: self.paths.append(c) def get_paths(self): return self.paths def reset(self): self.spr.set_layer(HIDE) self.shape = None self.spr.set_shape(self.highlight[0]) while self.orientation != 0: self.rotate_clockwise() def set_shape(self, path): if self.shape is None: self.spr.set_shape(self.highlight[path + 1]) self.shape = path elif self.shape != path: self.spr.set_shape(self.highlight[-1]) def rotate_clockwise(self): """ rotate the tile and its paths """ for i in range(len(self.paths)): west = self.paths[i][WEST] self.paths[i][WEST] = self.paths[i][SOUTH] self.paths[i][SOUTH] = self.paths[i][EAST] self.paths[i][EAST] = self.paths[i][NORTH] self.paths[i][NORTH] = west for h in range(len(self.highlight)): self.highlight[h] = self.highlight[h].rotate_simple(270) self.spr.set_shape(self.highlight[0]) self.orientation += 90 self.orientation %= 360 def show_tile(self): self.spr.set_layer(CARDS) def hide(self): self.spr.move((-self.spr.get_dimensions()[0], 0))
class Game(): def __init__(self, canvas, parent=None, path=None): self._canvas = canvas self._parent = parent self._parent.show_all() self._path = path self._canvas.connect("draw", self.__draw_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.KEY_PRESS_MASK) self._canvas.connect('key-press-event', self._keypress_cb) self._canvas.set_can_focus(True) self._canvas.grab_focus() self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() self._scale_x = self._width / 1200.0 self._scale_y = self._height / 900.0 self._first_time = True self._loco_pos = (0, 0) self._loco_dim = (0, 0) self._loco_quadrant = 3 self._drag_pos = [0, 0] self._counter = 0 self._correct = 0 self._timeout_id = None self._pause = 200 self._press = None self._clicked = False self._dead_key = None self._waiting_for_delete = False self._waiting_for_enter = False self._seconds = 0 self._timer_id = None self.level = 0 self.score = 0 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._BG = [ 'background0.jpg', 'background0.jpg', 'background0.jpg', 'background1.jpg', 'background2.jpg', 'background2.jpg', 'background2.jpg' ] self._backgrounds = [] for bg in self._BG: pixbuf = GdkPixbuf.Pixbuf.new_from_file( os.path.join(self._path, 'images', bg)) pixbuf = pixbuf.scale_simple(self._width, self._height, GdkPixbuf.InterpType.BILINEAR) self._backgrounds.append(Sprite(self._sprites, 0, 0, pixbuf)) self._backgrounds[-1].type = 'background' self._backgrounds[-1].hide() self._panel = Sprite( self._sprites, int(400 * self._scale_x), int(400 * self._scale_y), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'ventana.png'), int(720 * self._scale_x), int(370 * self._scale_y))) self._panel.type = 'panel' self._panel.set_label(LABELS[0]) self._panel.set_label_attributes(20) self._panel.hide() self._LOCOS = glob.glob(os.path.join(self._path, 'images', 'loco*.png')) self._loco_cards = [] for loco in self._LOCOS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._loco_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._loco_cards[-1].type = 'loco' self._loco_dim = (int(150 * self._scale_x), int(208 * self._scale_y)) self._MEN = glob.glob(os.path.join(self._path, 'images', 'man*.png')) self._man_cards = [] for loco in self._MEN: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._man_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._man_cards[-1].type = 'loco' self._TAUNTS = glob.glob( os.path.join(self._path, 'images', 'taunt*.png')) self._taunt_cards = [] for loco in self._TAUNTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._taunt_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._taunt_cards[-1].type = 'loco' self._GHOSTS = glob.glob( os.path.join(self._path, 'images', 'ghost*.png')) self._ghost_cards = [] for loco in self._GHOSTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale_x), int(208 * self._scale_y)) self._ghost_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._ghost_cards[-1].type = 'loco' self._sticky_cards = [] self._loco_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._LOCOS[0], int(150 * self._scale_x), int(208 * self._scale_y)) self._man_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._MEN[0], int(150 * self._scale_x), int(208 * self._scale_y)) self._ghost_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._GHOSTS[0], int(150 * self._scale_x), int(208 * self._scale_y)) for i in range(len(MSGS[1])): # Check re i18n self._sticky_cards.append( Sprite(self._sprites, 0, 0, self._loco_pixbuf)) self._sticky_cards[-1].type = 'loco' self._sticky_cards[-1].set_label_attributes(24, vert_align='bottom') self._all_clear() def _time_increment(self): ''' Track seconds since start_time. ''' self._seconds = int(GLib.get_current_time() - self._start_time) self.timer_id = GLib.timeout_add(1000, self._time_increment) def _timer_reset(self): ''' Reset the timer for each level ''' self._start_time = GLib.get_current_time() if self._timer_id is not None: GLib.source_remove(self._timer_id) self._timer_id = None self.score += self._seconds self._time_increment() def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' for p in self._loco_cards: p.hide() for p in self._man_cards: p.hide() for p in self._taunt_cards: p.hide() for p in self._ghost_cards: p.hide() for p in self._sticky_cards: p.set_shape(self._loco_pixbuf) p.set_label('') p.set_label_color('white') p.hide() self._backgrounds[self.level].set_layer(BG_LAYER) def _show_time(self): self.level = 0 self._all_clear() x = int(self._width / 4.) y = int(self._height / 8.) for i in range(len(str(self.score))): self._sticky_cards[i].move((x, y)) self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(str(self.score)[i]) x += int(self._loco_dim[0] / 2.) self.score = 0 self._parent.fullscreen() aplay.play(os.path.join(self._path, 'sounds', 'sonar.ogg')) GLib.timeout_add(5000, self.new_game, True) def new_game(self, first_time): ''' Start a new game at the current level. ''' self._first_time = first_time self._clicked = False # It may be time to advance to the next level. if (self.level == 6 and self._counter == len(MSGS)) or \ self._counter > 4: self._first_time = True self.level += 1 self._counter = 0 self._correct = 0 self._pause = 200 if self.level == len(self._backgrounds): self._show_time() return self._all_clear() if self._first_time: # Every game starts by putting up a panel with instructions # The panel disappears on mouse movement self._panel.set_label(LABELS[self.level]) self._panel.set_layer(PANEL_LAYER) aplay.play(os.path.join(self._path, 'sounds', 'drip.ogg')) self._timer_reset() if self.level == 0: # Choose a random location for the Loco self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) aplay.play(os.path.join(self._path, 'sounds', 'bark.ogg')) self._loco_cards[0].move((x, y)) self._loco_pos = (x, y) elif self.level == 1: aplay.play(os.path.join(self._path, 'sounds', 'glass.ogg')) elif self.level == 2: aplay.play(os.path.join(self._path, 'sounds', 'glass.ogg')) # Place some Locos on the canvas for i in range(self._counter + 1): self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 3: aplay.play(os.path.join(self._path, 'sounds', 'bark.ogg')) # Place some Locos on the left-side of the canvas for i in range(self._counter + 1): self._loco_quadrant = int(uniform(2, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 4: # Place some Locos on the canvas with letters as labels # Just lowercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(ALPHABETLC[int( uniform(0, len(ALPHABETLC)))]) elif self.level == 5: # Place some Locos on the canvas with letters as labels # Uppercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(ALPHABETUC[int( uniform(0, len(ALPHABETUC)))]) elif self.level == 6: x = 0 y = 0 c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': y += self._loco_dim[1] x = 0 else: self._sticky_cards[c].move((x, y)) self._sticky_cards[c].type = i self._sticky_cards[c].set_layer(LOCO_LAYER) self._sticky_cards[c].set_label(MSGS[self._counter][i]) c += 1 x += int(self._loco_dim[0] / 2.) if self.level in [0, 1]: self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) if self.level == 0: self._move_loco(x, y, 0) else: self._taunt(x, y, 0) def _quad_to_xy(self, q): x = int(max(0, (self._width / 2.) * uniform(0, 1) - self._loco_dim[0])) if q in [0, 1]: x += int(self._width / 2.) y = int(max(0, (self._height / 2.) * uniform(0, 1) - self._loco_dim[1])) if q in [1, 2]: y += int(self._height / 2.) return x, y def _taunt(self, x, y, i): n = len(self._taunt_cards) self._taunt_cards[(i + 1) % n].hide() if self._clicked: self._timeout_id = None return True else: self._taunt_cards[i % n].move((x, y)) self._taunt_cards[i % n].set_layer(LOCO_LAYER) self._timeout_id = GLib.timeout_add(200, self._taunt, x, y, i + 1) def _move_loco(self, x, y, i): j = (i + 1) % len(self._loco_cards) cx, cy = self._loco_cards[i].get_xy() dx = cx - x dy = cy - y if dx * dx + dy * dy < 100: self._loco_cards[j].move((x, y)) self._loco_pos = (x, y) self._loco_cards[j].hide() self._loco_cards[i].hide() self._man_cards[0].move((x, y)) self._man_cards[0].set_layer(LOCO_LAYER) self._timeout_id = None if self._pause > 50: self._pause -= 10 return True else: if dx > 0: cx -= 5 elif dx < 0: cx += 5 if dy > 0: cy -= 5 elif dy < 0: cy += 5 self._loco_cards[j].move((cx, cy)) self._loco_pos = (cx, cy) self._loco_cards[j].set_layer(LOCO_LAYER) self._loco_cards[i].hide() self._timeout_id = GLib.timeout_add(self._pause, self._move_loco, x, y, j) def _keypress_cb(self, area, event): ''' Keypress ''' # Games 4, 5, and 6 use the keyboard if self.level not in [4, 5, 6]: return True k = Gdk.keyval_name(event.keyval) if self._waiting_for_enter: if k == 'Return': self._waiting_for_enter = False self._panel.hide() self._counter += 1 self._correct = 0 GLib.timeout_add(1000, self.new_game, False) return if k in NOISE_KEYS or k in WHITE_SPACE: return True if self.level == 6 and self._waiting_for_delete: if k in ['BackSpace', 'Delete']: self._waiting_for_delete = False self._sticky_cards[self._correct].set_label_color('white') self._sticky_cards[self._correct].set_label(MSGS[ self._counter][self._sticky_cards[self._correct].type]) self._panel.hide() self._panel.set_label_color('black') return if k[0:5] == 'dead_': self._dead_key = k[5:] return if self.level == 6: n = len(MSGS[self._counter]) else: n = self._counter + 1 if self.level == 6: i = self._correct if self._dead_key is not None: k = DEAD_DICTS[DEAD_KEYS.index(self._dead_key)][k] self._dead_key = None elif k in PUNCTUATION: k = PUNCTUATION[k] elif k in SPECIAL: k = SPECIAL[k] elif len(k) > 1: return True if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label_color('blue') self._sticky_cards[i].set_label(k) self._correct += 1 else: self._sticky_cards[i].set_label_color('red') self._sticky_cards[i].set_label(k) self._panel.set_label_color('red') self._panel.set_label(ALERTS[1]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_delete = True aplay.play(os.path.join(self._path, 'sounds', 'glass.ogg')) else: for i in range(n): if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label('') self._sticky_cards[i].hide() break # Test for end condition if self.level == 6 and \ self._correct == len(MSGS[self._counter]) - \ MSGS[self._counter].count(' '): c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': continue elif MSGS[self._counter][i] != self._sticky_cards[c].labels[0]: return True c += 1 self._panel.set_label(ALERTS[0]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_enter = True aplay.play(os.path.join(self._path, 'sounds', 'drip.ogg')) return else: for i in range(n): if len(self._sticky_cards[i].labels[0]) > 0: return True self._counter += 1 self._correct = 0 GLib.timeout_add(1000, self.new_game, False) def _mouse_move_cb(self, win, event): ''' Move the mouse. ''' # Games 0, 3, 4, and 5 use move events x, y = list(map(int, event.get_coords())) if self._seconds > 1: self._panel.hide() if not self._clicked and self.level == 0: # For Game 0, see if the mouse is on the Loco dx = x - self._loco_pos[0] - self._loco_dim[0] / 2. dy = y - self._loco_pos[1] - self._loco_dim[1] / 2. if dx * dx + dy * dy < 200: self._clicked = True if self._timeout_id is not None: GLib.source_remove(self._timeout_id) # Play again self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._correct += 1 self._counter += 1 GLib.timeout_add(1000, self.new_game, False) elif self.level in [4, 5]: # For Game 4 and 5, we allow dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] elif self.level == 3: # For Game 3, we are dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] if x > self._width / 2.: self._press.set_shape(self._man_pixbuf) if self._press.type == 'loco': self._correct += 1 self._press.type = 'man' return True def _button_release_cb(self, win, event): # Game 3 uses release if self.level == 3: # Move to release if self._correct == self._counter + 1: self._counter += 1 self._correct = 0 GLib.timeout_add(2000, self.new_game, False) self._press = None self._drag_pos = [0, 0] return True def _button_press_cb(self, win, event): self._press = None x, y = list(map(int, event.get_coords())) if self.level == 0: return spr = self._sprites.find_sprite((x, y)) if spr is None: return if spr.type != 'loco': return if self.level < 2 and self._timeout_id is None: return if self._clicked: return # Games 1, 2, and 3 involve clicks; Games 4 and 5 allow click to drag if self.level == 1: self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._clicked = True self._counter += 1 self._correct += 1 if self._timeout_id is not None: GLib.source_remove(self._timeout_id) GLib.timeout_add(2000, self.new_game, False) elif self.level == 2: spr.set_shape(self._ghost_pixbuf) spr.type = 'ghost' if self._correct == self._counter: self._counter += 1 self._correct = 0 GLib.timeout_add(2000, self.new_game, False) else: self._correct += 1 elif self.level in [3, 4, 5]: # In Games 4 and 5, dragging is used to remove overlaps self._press = spr self._drag_pos = [x, y] return True def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def do_expose_event(self, event): ''' Handle the expose-event by drawing ''' # Restrict Cairo to the exposed area cr = self._canvas.window.cairo_create() cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self._sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit()
class Game(): def __init__(self, canvas, parent=None, path=None): self._canvas = canvas self._parent = parent self._parent.show_all() self._path = path self._canvas.connect("draw", self.__draw_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.add_events(Gdk.EventMask.POINTER_MOTION_MASK) self._canvas.connect("motion-notify-event", self._mouse_move_cb) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect('button-release-event', self._button_release_cb) self._canvas.add_events(Gdk.EventMask.KEY_PRESS_MASK) self._canvas.connect('key-press-event', self._keypress_cb) self._canvas.set_can_focus(True) self._canvas.grab_focus() self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() self._scale = self._width / 1200. self._first_time = True self._loco_pos = (0, 0) self._loco_dim = (0, 0) self._loco_quadrant = 3 self._drag_pos = [0, 0] self._counter = 0 self._correct = 0 self._timeout_id = None self._pause = 200 self._press = None self._clicked = False self._dead_key = None self._waiting_for_delete = False self._waiting_for_enter = False self._seconds = 0 self._timer_id = None self.level = 0 self.score = 0 # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._BG = ['background0.jpg', 'background0.jpg', 'background0.jpg', 'background1.jpg', 'background2.jpg', 'background2.jpg', 'background2.jpg'] self._backgrounds = [] for bg in self._BG: self._backgrounds.append(Sprite( self._sprites, 0, 0, GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', bg), self._width, self._height))) self._backgrounds[-1].type = 'background' self._backgrounds[-1].hide() self._panel = Sprite( self._sprites, int(400 * self._scale), int(400 * self._scale), GdkPixbuf.Pixbuf.new_from_file_at_size( os.path.join(self._path, 'images', 'ventana.png'), int(720 * self._scale), int(370 * self._scale))) self._panel.type = 'panel' self._panel.set_label(LABELS[0]) self._panel.set_label_attributes(20) self._panel.hide() self._LOCOS = glob.glob( os.path.join(self._path, 'images', 'loco*.png')) self._loco_cards = [] for loco in self._LOCOS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._loco_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._loco_cards[-1].type = 'loco' self._loco_dim = (int(150 * self._scale), int(208 * self._scale)) self._MEN = glob.glob( os.path.join(self._path, 'images', 'man*.png')) self._man_cards = [] for loco in self._MEN: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._man_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._man_cards[-1].type = 'loco' self._TAUNTS = glob.glob( os.path.join(self._path, 'images', 'taunt*.png')) self._taunt_cards = [] for loco in self._TAUNTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._taunt_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._taunt_cards[-1].type = 'loco' self._GHOSTS = glob.glob( os.path.join(self._path, 'images', 'ghost*.png')) self._ghost_cards = [] for loco in self._GHOSTS: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( loco, int(150 * self._scale), int(208 * self._scale)) self._ghost_cards.append(Sprite(self._sprites, 0, 0, pixbuf)) self._ghost_cards[-1].type = 'loco' self._sticky_cards = [] self._loco_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._LOCOS[0], int(150 * self._scale), int(208 * self._scale)) self._man_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._MEN[0], int(150 * self._scale), int(208 * self._scale)) self._ghost_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( self._GHOSTS[0], int(150 * self._scale), int(208 * self._scale)) for i in range(len(MSGS[1])): # Check re i18n self._sticky_cards.append(Sprite(self._sprites, 0, 0, self._loco_pixbuf)) self._sticky_cards[-1].type = 'loco' self._sticky_cards[-1].set_label_attributes(24, vert_align='bottom') self._all_clear() def _time_increment(self): ''' Track seconds since start_time. ''' self._seconds = int(GObject.get_current_time() - self._start_time) self.timer_id = GObject.timeout_add(1000, self._time_increment) def _timer_reset(self): ''' Reset the timer for each level ''' self._start_time = GObject.get_current_time() if self._timer_id is not None: GObject.source_remove(self._timer_id) self._timer_id = None self.score += self._seconds self._time_increment() def _all_clear(self): ''' Things to reinitialize when starting up a new game. ''' for p in self._loco_cards: p.hide() for p in self._man_cards: p.hide() for p in self._taunt_cards: p.hide() for p in self._ghost_cards: p.hide() for p in self._sticky_cards: p.set_shape(self._loco_pixbuf) p.set_label('') p.set_label_color('white') p.hide() self._backgrounds[self.level].set_layer(BG_LAYER) def _show_time(self): self.level = 0 self._all_clear() x = int(self._width / 4.) y = int(self._height / 8.) for i in range(len(str(self.score))): self._sticky_cards[i].move((x, y)) self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label(str(self.score)[i]) x += int(self._loco_dim[0] / 2.) self.score = 0 self._parent.unfullscreen() GObject.idle_add(play_audio_from_file, self, os.path.join( self._path, 'sounds', 'sonar.ogg')) GObject.timeout_add(5000, self.new_game, True) def new_game(self, first_time): ''' Start a new game at the current level. ''' self._first_time = first_time self._clicked = False # It may be time to advance to the next level. if (self.level == 6 and self._counter == len(MSGS)) or \ self._counter > 4: self._first_time = True self.level += 1 self._counter = 0 self._correct = 0 self._pause = 200 if self.level == len(self._backgrounds): self._show_time() return self._all_clear() if self._first_time: # Every game starts by putting up a panel with instructions # The panel disappears on mouse movement self._panel.set_label(LABELS[self.level]) self._panel.set_layer(PANEL_LAYER) play_audio_from_file(self, os.path.join( self._path, 'sounds', 'drip.ogg')) self._timer_reset() if self.level == 0: # Choose a random location for the Loco self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) play_audio_from_file(self, os.path.join( self._path, 'sounds', 'bark.ogg')) self._loco_cards[0].move((x, y)) self._loco_pos = (x, y) elif self.level == 1: play_audio_from_file(self, os.path.join( self._path, 'sounds', 'glass.ogg')) elif self.level == 2: play_audio_from_file(self, os.path.join( self._path, 'sounds', 'glass.ogg')) # Place some Locos on the canvas for i in range(self._counter + 1): self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 3: play_audio_from_file(self, os.path.join( self._path, 'sounds', 'bark.ogg')) # Place some Locos on the left-side of the canvas for i in range(self._counter + 1): self._loco_quadrant = int(uniform(2, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) elif self.level == 4: # Place some Locos on the canvas with letters as labels # Just lowercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label( ALPHABETLC[int(uniform(0, len(ALPHABETLC)))]) elif self.level == 5: # Place some Locos on the canvas with letters as labels # Uppercase for i in range(self._counter + 1): self._loco_quadrant = int(uniform(0, 4)) x, y = self._quad_to_xy(self._loco_quadrant) self._sticky_cards[i].move((x, y)) self._sticky_cards[i].type = 'loco' self._sticky_cards[i].set_layer(LOCO_LAYER) self._sticky_cards[i].set_label( ALPHABETUC[int(uniform(0, len(ALPHABETUC)))]) elif self.level == 6: x = 0 y = 0 c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': y += self._loco_dim[1] x = 0 else: self._sticky_cards[c].move((x, y)) self._sticky_cards[c].type = i self._sticky_cards[c].set_layer(LOCO_LAYER) self._sticky_cards[c].set_label(MSGS[self._counter][i]) c += 1 x += int(self._loco_dim[0] / 2.) if self.level in [0, 1]: self._loco_quadrant += int(uniform(1, 4)) self._loco_quadrant %= 4 x, y = self._quad_to_xy(self._loco_quadrant) if self.level == 0: self._move_loco(x, y, 0) else: self._taunt(x, y, 0) def _quad_to_xy(self, q): x = int(max(0, (self._width / 2.) * uniform(0, 1) - self._loco_dim[0])) if q in [0, 1]: x += int(self._width / 2.) y = int(max(0, (self._height / 2.) * uniform(0, 1) - self._loco_dim[1])) if q in [1, 2]: y += int(self._height / 2.) return x, y def _taunt(self, x, y, i): n = len(self._taunt_cards) self._taunt_cards[(i + 1) % n].hide() if self._clicked: self._timeout_id = None return True else: self._taunt_cards[i % n].move((x, y)) self._taunt_cards[i % n].set_layer(LOCO_LAYER) self._timeout_id = GObject.timeout_add( 200, self._taunt, x, y, i + 1) def _move_loco(self, x, y, i): j = (i + 1) % len(self._loco_cards) cx, cy = self._loco_cards[i].get_xy() dx = cx - x dy = cy - y if dx * dx + dy * dy < 100: self._loco_cards[j].move((x, y)) self._loco_pos = (x, y) self._loco_cards[j].hide() self._loco_cards[i].hide() self._man_cards[0].move((x, y)) self._man_cards[0].set_layer(LOCO_LAYER) self._timeout_id = None if self._pause > 50: self._pause -= 10 return True else: if dx > 0: cx -= 5 elif dx < 0: cx += 5 if dy > 0: cy -= 5 elif dy < 0: cy += 5 self._loco_cards[j].move((cx, cy)) self._loco_pos = (cx, cy) self._loco_cards[j].set_layer(LOCO_LAYER) self._loco_cards[i].hide() self._timeout_id = GObject.timeout_add( self._pause, self._move_loco, x, y, j) def _keypress_cb(self, area, event): ''' Keypress ''' # Games 4, 5, and 6 use the keyboard print 'keypress event' if self.level not in [4, 5, 6]: return True k = Gdk.keyval_name(event.keyval) u = Gdk.keyval_to_unicode(event.keyval) if self._waiting_for_enter: if k == 'Return': self._waiting_for_enter = False self._panel.hide() self._counter += 1 self._correct = 0 GObject.timeout_add(1000, self.new_game, False) return if k in NOISE_KEYS or k in WHITE_SPACE: return True if self.level == 6 and self._waiting_for_delete: if k in ['BackSpace', 'Delete']: self._waiting_for_delete = False self._sticky_cards[self._correct].set_label_color('white') self._sticky_cards[self._correct].set_label( MSGS[self._counter][ self._sticky_cards[self._correct].type]) self._panel.hide() self._panel.set_label_color('black') return if k[0:5] == 'dead_': self._dead_key = k[5:] return if self.level == 6: n = len(MSGS[self._counter]) else: n = self._counter + 1 if self.level == 6: i = self._correct if self._dead_key is not None: k = DEAD_DICTS[DEAD_KEYS.index(self._dead_key)][k] self._dead_key = None elif k in PUNCTUATION: k = PUNCTUATION[k] elif k in SPECIAL: k = SPECIAL[k] elif len(k) > 1: return True if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label_color('blue') self._sticky_cards[i].set_label(k) self._correct += 1 else: self._sticky_cards[i].set_label_color('red') self._sticky_cards[i].set_label(k) self._panel.set_label_color('red') self._panel.set_label(ALERTS[1]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_delete = True play_audio_from_file(self, os.path.join( self._path, 'sounds', 'glass.ogg')) else: for i in range(n): if self._sticky_cards[i].labels[0] == k: self._sticky_cards[i].set_label('') self._sticky_cards[i].hide() break # Test for end condition if self.level == 6 and \ self._correct == len(MSGS[self._counter]) - \ MSGS[self._counter].count(' '): c = 0 for i in range(len(MSGS[self._counter])): if MSGS[self._counter][i] == ' ': continue elif MSGS[self._counter][i] != self._sticky_cards[c].labels[0]: return True c += 1 self._panel.set_label(ALERTS[0]) self._panel.set_layer(PANEL_LAYER) self._waiting_for_enter = True GObject.idle_add(play_audio_from_file, self, os.path.join( self._path, 'sounds', 'drip.ogg')) return else: for i in range(n): if len(self._sticky_cards[i].labels[0]) > 0: return True self._counter += 1 self._correct = 0 GObject.timeout_add(1000, self.new_game, False) def _mouse_move_cb(self, win, event): ''' Move the mouse. ''' # Games 0, 3, 4, and 5 use move events x, y = map(int, event.get_coords()) if self._seconds > 1: self._panel.hide() if not self._clicked and self.level == 0: # For Game 0, see if the mouse is on the Loco dx = x - self._loco_pos[0] - self._loco_dim[0] / 2. dy = y - self._loco_pos[1] - self._loco_dim[1] / 2. if dx * dx + dy * dy < 200: self._clicked = True if self._timeout_id is not None: GObject.source_remove(self._timeout_id) # Play again self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._correct += 1 self._counter += 1 GObject.timeout_add(1000, self.new_game, False) elif self.level in [4, 5]: # For Game 4 and 5, we allow dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] elif self.level == 3: # For Game 3, we are dragging if self._press is None: self._drag_pos = [0, 0] return True dx = x - self._drag_pos[0] dy = y - self._drag_pos[1] self._press.move_relative((dx, dy)) self._drag_pos = [x, y] if x > self._width / 2.: self._press.set_shape(self._man_pixbuf) if self._press.type == 'loco': self._correct += 1 self._press.type = 'man' return True def _button_release_cb(self, win, event): # Game 3 uses release if self.level == 3: # Move to release if self._correct == self._counter + 1: self._counter += 1 self._correct = 0 GObject.timeout_add(2000, self.new_game, False) self._press = None self._drag_pos = [0, 0] return True def _button_press_cb(self, win, event): self._press = None x, y = map(int, event.get_coords()) if self.level == 0: return spr = self._sprites.find_sprite((x, y)) if spr is None: return if spr.type != 'loco': return if self.level < 2 and self._timeout_id is None: return if self._clicked: return # Games 1, 2, and 3 involve clicks; Games 4 and 5 allow click to drag if self.level == 1: self._all_clear() self._man_cards[0].move((x - int(self._loco_dim[0] / 2.), y - int(self._loco_dim[1] / 2.))) self._man_cards[0].set_layer(LOCO_LAYER) self._clicked = True self._counter += 1 self._correct += 1 if self._timeout_id is not None: GObject.source_remove(self._timeout_id) GObject.timeout_add(2000, self.new_game, False) elif self.level == 2: spr.set_shape(self._ghost_pixbuf) spr.type = 'ghost' if self._correct == self._counter: self._counter += 1 self._correct = 0 GObject.timeout_add(2000, self.new_game, False) else: self._correct += 1 elif self.level in [3, 4, 5]: # In Games 4 and 5, dragging is used to remove overlaps self._press = spr self._drag_pos = [x, y] return True def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def do_expose_event(self, event): ''' Handle the expose-event by drawing ''' # Restrict Cairo to the exposed area cr = self._canvas.window.cairo_create() cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() # Refresh sprite list self._sprites.redraw_sprites(cr=cr) def _destroy_cb(self, win, event): Gtk.main_quit()
class Game(): def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']): self._activity = parent self._colors = [colors[0]] self._colors.append(colors[1]) self._colors.append('#FFFFFF') self._colors.append('#000000') self._colors.append('#FF0000') self._colors.append('#FF8080') self._colors.append('#FFa0a0') self._colors.append('#FFc0c0') self._colors.append('#FFFF00') self._colors.append('#FFFF80') self._colors.append('#FFFFa0') self._colors.append('#FFFFe0') self._colors.append('#0000FF') self._colors.append('#8080FF') self._colors.append('#80a0FF') self._colors.append('#c0c0FF') self._canvas = canvas if parent is not None: parent.show_all() self._parent = parent self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) self._canvas.add_events(Gdk.EventMask.BUTTON_RELEASE_MASK) self._canvas.connect("draw", self.__draw_cb) self._canvas.connect("button-press-event", self._button_press_cb) self._canvas.connect("button-release-event", self._button_release_cb) self._global_scale = 1 self._width = Gdk.Screen.width() self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5) self._scale = self._width / (10 * DOT_SIZE * 1.2) self._dot_size = int(DOT_SIZE * self._scale) self._space = int(self._dot_size / 5.) self._press = False self._release = None self._rubbing = False self._tapped = None self._pausing = False self._count = 0 self._targets = None # click target self._shake = None # accelerometer target self._next = None self.last_spr = None self._timer = None self.roygbiv = False # Generate the sprites we'll need... self._sprites = Sprites(self._canvas) self._svg_width = self._width self._svg_height = self._height self._lightbg = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf( self._header() + \ self._rect(self._width, self._height, 0, 0) + \ self._footer())) self._lightbg.set_label_attributes(24) self._lightbg._vert_align = ["bottom"] self._darkbg = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf( self._header() + \ self._rect(self._width, self._height, 0, 0, color='#000000') + \ self._footer())) self._darkbg.set_label_attributes(24) self._darkbg._vert_align = ["bottom"] self._darkbg.set_label_color('yellow') self._darkbg.set_layer(0) self._dots = [] for y in range(FIVE): for x in range(NINE): xoffset = int((self._width - NINE * self._dot_size - \ (NINE - 1) * self._space) / 2.) self._dots.append( Sprite(self._sprites, xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space), self._new_dot(self._colors[WHITE]))) self._dots[-1].type = DOT self._dots[-1].set_label_attributes(40) n = FIVE / 2. # and initialize a few variables we'll need. self._yellow_dot() def _clear_pause(self): self._pausing = False if self._rubbing and self._release is not None: if self._next is not None: self._next() def _yellow_dot(self): for y in range(FIVE): for x in range(NINE): xoffset = int((self._width - NINE * self._dot_size - \ (NINE - 1) * self._space) / 2.) self._dots[x + y * NINE].move( (xoffset + x * (self._dot_size + self._space), y * (self._dot_size + self._space))) self._dots[x + y * NINE].set_shape( self._new_dot(self._colors[WHITE])) self._dots[x + y * NINE].type = DOT self._dots[x + y * NINE].set_layer(100) self._lightbg.set_label(_('Tap on the yellow dot.')) self._targets = [int(uniform(0, NINE * FIVE))] self._next = self._yellow_dot_too self._dots[self._targets[0]].set_shape( self._new_dot(self._colors[YELLOW])) self._dots[self._targets[0]].type = YELLOW self._rubbing = False self._shake = None def _yellow_dot_too(self, append=True): ''' Things to reinitialize when starting up a new game. ''' if append: i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._lightbg.set_label( _('Well done! \ Now tap on the other yellow dot.')) self._next = self._yellow_dots_three self._dots[self._targets[1]].set_shape( self._new_dot(self._colors[YELLOW])) self._dots[self._targets[1]].type = YELLOW self._rubbing = False def _yellow_dots_three(self): ''' Things to reinitialize when starting up a new game. ''' if self._release == self._targets[0]: self._yellow_dot_too(append=False) self._lightbg.set_label(_('The other yellow dot!')) return i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._lightbg.set_label(_('Great! Now rub on one of the yellow dots.')) self._next = self._red_dot self._dots[self._targets[2]].set_shape( self._new_dot(self._colors[YELLOW])) self._dots[self._targets[2]].type = YELLOW self._rubbing = True def _red_dot(self): if self._release is None: return self._lightbg.set_label( _('Good job! \ Now rub on another one of the yellow dots.')) self._next = self._blue_dot self._dots[self._release].set_shape(self._new_dot(self._colors[RED])) self._dots[self._release].type = RED self._rubbing = True def _blue_dot(self): if self._release is None: return if self._dots[self._release].type != YELLOW: return self._lightbg.set_label( _('Now gently tap on the yellow dot five times.')) self._next = self._yellow_tap self._dots[self._release].set_shape(self._new_dot(self._colors[BLUE])) self._dots[self._release].type = BLUE self._rubbing = False self._count = 0 def _yellow_tap(self): if self._dots[self._release].type != YELLOW: if self._count == 0: self._lightbg.set_label( _('Now gently tap on the yellow dot five times.')) else: self._lightbg.set_label(_('Tap on a yellow dot.')) return self._count += 1 if self._count > 4: self._count = 0 self._next = self._red_tap self._lightbg.set_label( _('Now gently tap on the red dot five times.')) else: self._lightbg.set_label(_('Keep tapping.')) i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._dots[i].set_shape(self._new_dot(self._colors[YELLOW])) self._dots[i].type = YELLOW def _red_tap(self): if self._dots[self._release].type != RED: if self._count == 0: self._lightbg.set_label( _('Now gently tap on the red dot five times.')) else: self._lightbg.set_label(_('Tap on a red dot.')) return self._count += 1 if self._count > 4: self._count = 0 self._next = self._blue_tap self._lightbg.set_label( _('Now gently tap on the blue dot five times.')) else: self._lightbg.set_label(_('Keep tapping.')) i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._dots[i].set_shape(self._new_dot(self._colors[RED])) self._dots[i].type = RED def _blue_tap(self): if self._dots[self._release].type != BLUE: if self._count == 0: self._lightbg.set_label( _('Now gently tap on the blue dot five times.')) else: self._lightbg.set_label(_('Tap on a blue dot.')) return self._count += 1 if self._count > 4: self._count = 0 self._next = self._shake_it self._lightbg.set_label('') # Since we don't end up in the button press GObject.timeout_add(500, self._next) else: self._lightbg.set_label(_('Keep tapping.')) i = self._targets[0] while i in self._targets: i = int(uniform(0, NINE * FIVE)) self._targets.append(i) self._dots[i].set_shape(self._new_dot(self._colors[BLUE])) self._dots[i].type = BLUE def _shake_it(self): self._lightbg.set_label(_('OK. Now, shake the computer!!')) for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: dot.set_layer(200) self._next = self._shake_it_more self._shake = 'random' self._pausing = True GObject.timeout_add(5000, self._clear_pause) GObject.timeout_add(100, read_accelerometer, self) def _shake_it_more(self): self._lightbg.set_label(_('Shake it harder!!')) self._next = self._turn_left self._shake = 'random2' self._pausing = True GObject.timeout_add(5000, self._clear_pause) def _turn_left(self): self._lightbg.set_label( _('See what happens if you turn it to the left.')) self._next = self._turn_right self._shake = 'left' self._pausing = True def _turn_right(self): self._lightbg.set_label(_('Now turn it to the right.')) self._next = self._align self._pausing = True self._shake = 'right' def _align(self): self._lightbg.set_label(_('Shake it some more.')) self._next = self._tap_six self._pausing = True self._shake = 'align' def _tap_six(self): self._shake = None self._lightbg.set_label(_('OK. Now press each of the yellow dots.')) if self._tapped == None: self._tapped = [] if self._dots[self._release].type != YELLOW: self._lightbg.set_label(_('Press the yellow dots.')) return else: if not self._release in self._tapped: self._tapped.append(self._release) self._dots[self._release].set_label(':)') if len(self._tapped) == 6: self._darkbg.set_layer(100) self._lightbg.set_layer(0) for dot in self._dots: if dot.type != YELLOW: dot.set_layer(0) self._darkbg.set_label(_('Press all of the yellow dots again!')) self._tapped = None self._next = self._tap_six_too def _tap_six_too(self): self._shake = None if self._tapped == None: self._tapped = [] if self._dots[self._release].type != YELLOW: return else: if not self._release in self._tapped: self._tapped.append(self._release) self._dots[self._release].set_label('') if len(self._tapped) == 6: self._lightbg.set_layer(100) self._darkbg.set_layer(0) for dot in self._dots: if dot.type in [RED, BLUE]: dot.set_layer(100) pos1 = self._dots[self._targets[1]].get_xy() pos2 = self._dots[self._targets[2]].get_xy() self._dots[self._targets[1]].move(pos2) self._dots[self._targets[2]].move(pos1) self._lightbg.set_label( _('Tap on the two dots that switched positions.')) self._tapped = None self._next = self._tap_two def _tap_two(self): self._shake = None if self._tapped == None: self._tapped = [] if not self._release in [self._targets[1], self._targets[2]]: self._lightbg.set_label(_('Keep trying.')) return else: if not self._release in self._tapped: self._tapped.append(self._release) self._dots[self._release].set_label(':)') if len(self._tapped) == 2: pos1 = self._dots[self._targets[1]].get_xy() pos2 = self._dots[self._targets[2]].get_xy() self._dots[self._targets[1]].move(pos2) self._dots[self._targets[2]].move(pos1) self._lightbg.set_label(_("Good job! Now let's shake again.")) self._shake = 'random2' self._next = self._shake_three # Since we don't end up in the button press GObject.timeout_add(500, self._next) for i in self._tapped: self._dots[i].set_label('') elif len(self._tapped) == 1: self._lightbg.set_label( _('You found one. Now find the other one.')) def _shake_three(self): self._next = self._fade_it self._shake = 'random2' GObject.timeout_add(100, read_accelerometer, self) self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _fade_it(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 1) self._lightbg.set_label(_('Going')) self._shake = 'random2' self._next = self._fade_it_again self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _fade_it_again(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 2) self._lightbg.set_label(_('Going') + '..') self._shake = 'random2' self._next = self._and_again self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _and_again(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 3) self._lightbg.set_label(_('Going') + '...') self._shake = 'random2' self._next = self._one_last_time self._pausing = True GObject.timeout_add(2000, self._clear_pause) def _one_last_time(self): for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: self._fade_dot(dot, 4) self._lightbg.set_label(_('Gone!')) self._shake = None self._next = self._yellow_dot GObject.timeout_add(500, self._next) def _fade_dot(self, dot, i): if i == 4: dot.set_shape(self._new_dot(self._colors[WHITE])) else: dot.set_shape(self._new_dot(self._colors[dot.type + i])) def _set_label(self, string): ''' Set the label in the toolbar or the window frame. ''' self._activity.status.set_label(string) def motion_cb(self, x, y, z): if read_accelerometer.device_path is None: jiggle_factor = 5 else: jiggle_factor = 3 if self._shake is None: return elif self._shake in ['random', 'random2']: if self._shake == 'random2': jiggle_factor *= 2 for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: x += int(uniform(-jiggle_factor, jiggle_factor)) z += int(uniform(-jiggle_factor, jiggle_factor)) # Randomize z drift, which tends toward up... if int(uniform(0, 2)) == 0: z = -z dot.move_relative((x, z)) elif self._shake == 'align': docked = True yellow = 0 red = 0 blue = 0 for dot in self._dots: if dot.type == YELLOW: docked = self._dock_dot(dot, yellow + 1, 1, jiggle_factor, docked) yellow += 1 elif dot.type == RED: docked = self._dock_dot(dot, red + 2, 2, jiggle_factor, docked) red += 1 elif dot.type == BLUE: docked = self._dock_dot(dot, blue + 3, 3, jiggle_factor, docked) blue += 1 if docked: self._lightbg.set_label(_('Interesting.')) self._pausing = False elif self._shake == 'left': right = False for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: pos = dot.get_xy() if pos[0] < 0: if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) x = int(uniform(0, 10)) dot.move_relative((x, z)) elif x < 0: x += int(uniform(-10, 0)) if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) if pos[0] > -x: dot.move_relative((x, z)) pos = dot.get_xy() if pos[0] > 100: right = True if not right: self._lightbg.set_label(_('Hmm')) self._pausing = False elif self._shake == 'right': left = False for dot in self._dots: if dot.type in [RED, YELLOW, BLUE]: pos = dot.get_xy() if pos[0] > self._width - self._dot_size: if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) x = int(uniform(-10, 0)) dot.move_relative((x, z)) elif x < self._width - self._dot_size: x += int(uniform(0, 10)) if pos[1] > self._height: z = int(uniform(-20, 0)) elif pos[1] < 0: z = int(uniform(0, 20)) if pos[0] < self._width - x - self._dot_size: dot.move_relative((x, z)) pos = dot.get_xy() if pos[0] < self._width - self._dot_size - 100: left = True if not left: self._lightbg.set_label(_('Hmm')) self._pausing = False if not self._pausing: if self._next is not None: GObject.timeout_add(1000, self._next) else: self._lightbg.set_label('') self._shake = None GObject.timeout_add(100, read_accelerometer, self) return def _dock_dot(self, dot, n, m, jiggle_factor, docked): x = (self._dot_size + self._space) * n y = (self._dot_size + self._space) * m pos = dot.get_xy() dx = x - pos[0] dy = y - pos[1] if abs(dx) < 11 and abs(dy) < 11: dot.move((x, y)) return docked else: if dx < 0: dx = max(-10, dx) elif dx > 0: dx = min(10, dx) if dy < 0: dy = max(-10, dy) elif dy > 0: dy = min(10, dy) dx += int(uniform(-jiggle_factor, jiggle_factor)) dy += int(uniform(-jiggle_factor, jiggle_factor)) dot.move_relative((dx, dy)) return False def _button_press_cb(self, win, event): if self._shake is not None: return True win.grab_focus() x, y = map(int, event.get_coords()) self._press = True self._release = None spr = self._sprites.find_sprite((x, y)) if spr == None: return True self.last_spr = spr if self._rubbing: self._pausing = True if spr in self._dots: for target in self._targets: if self._dots.index(spr) == target: self._release = target GObject.timeout_add(1000, self._clear_pause) return True def _button_release_cb(self, win, event): if self._shake is not None: return True self._press = False self._release = None if self._pausing: self._lightbg.set_label(_('Rub a little longer.')) return True x, y = map(int, event.get_coords()) spr = self._sprites.find_sprite((x, y)) if spr.type is not None: if spr in self._dots: for target in self._targets: if self._dots.index(spr) == target: self._release = target if self._next is not None: GObject.timeout_add(200, self._next) def _smile(self): for dot in self._dots: dot.set_label(':)') def __draw_cb(self, canvas, cr): self._sprites.redraw_sprites(cr=cr) def _grid_to_dot(self, pos): ''' calculate the dot index from a column and row in the grid ''' return pos[0] + pos[1] * NINE def _dot_to_grid(self, dot): ''' calculate the grid column and row for a dot ''' return [dot % NINE, int(dot / NINE)] def _destroy_cb(self, win, event): Gtk.main_quit() def _new_dot(self, color): ''' generate a dot of a color color ''' self._dot_cache = {} if not color in self._dot_cache: self._stroke = color self._fill = color self._svg_width = self._dot_size self._svg_height = self._dot_size pixbuf = svg_str_to_pixbuf( self._header() + \ self._circle(self._dot_size / 2., self._dot_size / 2., self._dot_size / 2.) + \ self._footer()) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self._svg_width, self._svg_height) context = cairo.Context(surface) Gdk.cairo_set_source_pixbuf(context, pixbuf, 0, 0) context.rectangle(0, 0, self._svg_width, self._svg_height) context.fill() self._dot_cache[color] = surface return self._dot_cache[color] def _line(self, vertical=True): ''' Generate a center line ''' if vertical: self._svg_width = 3 self._svg_height = self._height return svg_str_to_pixbuf( self._header() + \ self._rect(3, self._height, 0, 0) + \ self._footer()) else: self._svg_width = self._width self._svg_height = 3 return svg_str_to_pixbuf( self._header() + \ self._rect(self._width, 3, 0, 0) + \ self._footer()) def _header(self): return '<svg\n' + 'xmlns:svg="http://www.w3.org/2000/svg"\n' + \ 'xmlns="http://www.w3.org/2000/svg"\n' + \ 'xmlns:xlink="http://www.w3.org/1999/xlink"\n' + \ 'version="1.1"\n' + 'width="' + str(self._svg_width) + '"\n' + \ 'height="' + str(self._svg_height) + '">\n' def _rect(self, w, h, x, y, color='#ffffff'): svg_string = ' <rect\n' svg_string += ' width="%f"\n' % (w) svg_string += ' height="%f"\n' % (h) svg_string += ' rx="%f"\n' % (0) svg_string += ' ry="%f"\n' % (0) svg_string += ' x="%f"\n' % (x) svg_string += ' y="%f"\n' % (y) svg_string += 'style="fill:%s;stroke:none;"/>\n' % (color) return svg_string def _circle(self, r, cx, cy): return '<circle style="fill:' + str(self._fill) + ';stroke:' + \ str(self._stroke) + ';" r="' + str(r - 0.5) + '" cx="' + \ str(cx) + '" cy="' + str(cy) + '" />\n' def _footer(self): return '</svg>\n'
class AbacusGeneric(): """ A generic abacus: a frame, rods, and beads. """ def __init__(self, abacus): """ Specify parameters that define the abacus """ self.abacus = abacus self.set_parameters() self.create() def set_parameters(self): """ Define the physical paramters. """ self.name = "suanpan" self.num_rods = 15 self.bot_beads = 5 self.top_beads = 2 self.base = 10 self.top_factor = 5 def create(self): """ Create and position the sprites that compose the abacus """ # Width is a function of the number of rods self.frame_width = self.num_rods*(BWIDTH+BOFFSET)+FSTROKE*2 # Height is a function of the number of beads if self.top_beads > 0: self.frame_height = (self.bot_beads+self.top_beads+5)*BHEIGHT +\ FSTROKE*2 else: self.frame_height = (self.bot_beads+2)*BHEIGHT + FSTROKE*2 # Draw the frame... x = (self.abacus.width-(self.frame_width*self.abacus.scale))/2 y = (self.abacus.height-(self.frame_height*self.abacus.scale))/2 _frame = _svg_header(self.frame_width, self.frame_height, self.abacus.scale) +\ _svg_rect(self.frame_width, self.frame_height, FSTROKE/2, FSTROKE/2, 0, 0, "#000000", "#000000") +\ _svg_rect(self.frame_width-(FSTROKE*2), self.frame_height-(FSTROKE*2), 0, 0, FSTROKE, FSTROKE, "#808080", "#000000") +\ _svg_footer() self.frame = Sprite(self.abacus.sprites, x, y, _svg_str_to_pixbuf(_frame)) self.frame.type = 'frame' # and then the rods and beads. self.rods = [] self.beads = [] x += FSTROKE*self.abacus.scale y += FSTROKE*self.abacus.scale self.draw_rods_and_beads(x, y) # Draw the dividing bar... _bar = _svg_header(self.frame_width-(FSTROKE*2), BHEIGHT, self.abacus.scale) +\ _svg_rect(self.frame_width-(FSTROKE*2), BHEIGHT, 0, 0, 0, 0, "#000000", "#000000") +\ _svg_footer() if self.top_beads > 0: self.bar = Sprite(self.abacus.sprites, x, y+(self.top_beads+2)*BHEIGHT*self.abacus.scale, _svg_str_to_pixbuf(_bar)) else: self.bar = Sprite(self.abacus.sprites, x, y-FSTROKE*self.abacus.scale, _svg_str_to_pixbuf(_bar)) self.bar.type = 'frame' self.bar.set_label_color('white') # and finally, the mark. _mark = _svg_header(20, 15, self.abacus.scale) +\ _svg_indicator() +\ _svg_footer() dx = (BWIDTH+BOFFSET)*self.abacus.scale self.mark = Sprite(self.abacus.sprites, x+(self.num_rods-1)*dx, y-((FSTROKE/2)*self.abacus.scale), _svg_str_to_pixbuf(_mark)) self.mark.type = 'mark' def draw_rods_and_beads(self, x, y): """ Draw the rods and beads """ _white = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffffff", "#000000") +\ _svg_footer() _yellow1 = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffffcc", "#000000") +\ _svg_footer() _yellow2 = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffff88", "#000000") +\ _svg_footer() _yellow3 = _svg_header(BWIDTH, BHEIGHT, self.abacus.scale) +\ _svg_bead("#ffff00", "#000000") +\ _svg_footer() self.colors = [_svg_str_to_pixbuf(_white), _svg_str_to_pixbuf(_yellow1), _svg_str_to_pixbuf(_yellow2), _svg_str_to_pixbuf(_yellow3)] dx = (BWIDTH+BOFFSET)*self.abacus.scale bo = (BWIDTH-BOFFSET)*self.abacus.scale/4 ro = (BWIDTH+5)*self.abacus.scale/2 for i in range(self.num_rods): _rod = _svg_header(10, self.frame_height-(FSTROKE*2), self.abacus.scale) +\ _svg_rect(10, self.frame_height-(FSTROKE*2), 0, 0, 0, 0, ROD_COLORS[i%len(ROD_COLORS)], "#404040") +\ _svg_footer() self.rods.append(Sprite(self.abacus.sprites, x+i*dx+ro, y, _svg_str_to_pixbuf(_rod))) for b in range(self.top_beads): self.beads.append(Bead(Sprite(self.abacus.sprites, x+i*dx+bo, y+b*BHEIGHT*self.abacus.scale, self.colors[0]), 2*BHEIGHT*self.abacus.scale, self.top_factor*(pow(self.base, self.num_rods-i-1)))) for b in range(self.bot_beads): if self.top_beads > 0: self.beads.append(Bead(Sprite(self.abacus.sprites, x+i*dx+bo, y+(self.top_beads+5+b)*\ BHEIGHT*self.abacus.scale, self.colors[0]), 2*BHEIGHT*self.abacus.scale, pow(self.base,self.num_rods-i-1))) else: self.beads.append(Bead(Sprite(self.abacus.sprites, x+i*dx+bo, y+(2+b)*BHEIGHT\ *self.abacus.scale, self.colors[0]), 2*BHEIGHT*self.abacus.scale, pow(self.base,self.num_rods-i-1))) for rod in self.rods: rod.type = "frame" def hide(self): """ Hide the rod, beads, mark, and frame. """ for rod in self.rods: rod.hide() for bead in self.beads: bead.hide() self.bar.hide() self.frame.hide() self.mark.hide() def show(self): """ Show the rod, beads, mark, and frame. """ self.frame.set_layer(FRAME_LAYER) for rod in self.rods: rod.set_layer(ROD_LAYER) for bead in self.beads: bead.show() self.bar.set_layer(BAR_LAYER) self.mark.set_layer(MARK_LAYER) def set_value(self, string): """ Set abacus to value in string """ _logger.debug("restoring %s: %s" % (self.name, string)) # String has two bytes per column. v = [] for r in range(self.num_rods): v.append(0) # Convert string to column values. if len(string) == 2*self.num_rods: for i in range(self.num_rods): v[self.num_rods-i-1] = int( string[2*self.num_rods-i*2-1:2*self.num_rods-i*2]) else: _logger.debug("bad saved string %s (%d != 2*%d)" % (string, len(string), self.num_rods)) # Move the beads to correspond to column values. for r in range(self.num_rods): self.set_rod_value(r, v[r]) def set_rod_value(self, r, v): """ Move beads on rod r to represent value v """ bot = v % self.top_factor top = (v-bot)/self.top_factor top_bead_index = r*(self.top_beads+self.bot_beads) bot_bead_index = r*(self.top_beads+self.bot_beads)+self.top_beads # Clear the top. for i in range(self.top_beads): if self.beads[top_bead_index+i].get_state() == 1: self.beads[top_bead_index+i].move_up() # Clear the bottom. for i in range(self.bot_beads): if self.beads[bot_bead_index+i].get_state() == 1: self.beads[bot_bead_index+i].move_down() # Set the top. for i in range(top): self.beads[top_bead_index+self.top_beads-i-1].move_down() # Set the bottom for i in range(bot): self.beads[bot_bead_index+i].move_up() def value(self, count_beads=False): """ Return a string representing the value of each rod. """ string = '' v = [] for r in range(self.num_rods+1): # +1 for overflow v.append(0) # Tally the values on each rod. for i, bead in enumerate(self.beads): r = i/(self.top_beads+self.bot_beads) j = i % (self.top_beads+self.bot_beads) if bead.get_state() == 1: if j < self.top_beads: v[r+1] += self.top_factor else: v[r+1] += 1 if count_beads: # Save the value associated with each rod as a 2-byte integer. for j in v[1:]: string += "%2d" % (j) else: sum = 0 for bead in self.beads: sum += bead.get_value() string = str(sum) return(string) def label(self, string): """ Label the crossbar with the string. (Used with self.value) """ self.bar.set_label(string) def move_mark(self, dx): """ Move indicator horizontally across the top of the frame. """ self.mark.move_relative((dx, 0)) def fade_colors(self): """ Reduce the saturation level of every bead. """ for bead in self.beads: if bead.get_level() > 0: bead.set_color(self.colors[bead.get_level()-1]) bead.set_level(bead.get_level()-1) def move_bead(self, sprite, dy): """ Move a bead (or beads) up or down a rod. """ # find the bead associated with the sprite i = -1 for bead in self.beads: if sprite == bead.spr: i = self.beads.index(bead) break if i == -1: print "bead not found" return b = i % (self.top_beads+self.bot_beads) if b < self.top_beads: if dy > 0 and bead.get_state() == 0: self.fade_colors() bead.set_color(self.colors[3]) bead.move_down() # Make sure beads below this bead are also moved. for ii in range(self.top_beads-b): if self.beads[i+ii].state == 0: self.beads[i+ii].set_color(self.colors[3]) self.beads[i+ii].move_down() elif dy < 0 and bead.state == 1: self.fade_colors() bead.set_color(self.colors[3]) bead.move_up() # Make sure beads above this bead are also moved. for ii in range(b+1): if self.beads[i-ii].state == 1: self.beads[i-ii].set_color(self.colors[3]) self.beads[i-ii].move_up() else: if dy < 0 and bead.state == 0: self.fade_colors() bead.set_color(self.colors[3]) bead.move_up() # Make sure beads above this bead are also moved. for ii in range(b-self.top_beads+1): if self.beads[i-ii].state == 0: self.beads[i-ii].set_color(self.colors[3]) self.beads[i-ii].move_up() elif dy > 0 and bead.state == 1: self.fade_colors() bead.set_color(self.colors[3]) bead.move_down() # Make sure beads below this bead are also moved. for ii in range(self.top_beads+self.bot_beads-b): if self.beads[i+ii].state == 1: self.beads[i+ii].set_color(self.colors[3]) self.beads[i+ii].move_down()