class Stator():
    """ Create a sprite for a stator """
    def __init__(self, sprites, path, name, x, y, w, h, svg_engine=None,
                 calculate=None, result=None):
        if svg_engine is None:
            self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h))
        else:
            self.spr = Sprite(sprites, x, y,
                              svg_str_to_pixbuf(svg_engine().svg))
        self.spr.type = name
        self.name = name
        self.calculate = calculate
        self.result = result

    def draw(self, layer=1000):
        self.spr.set_layer(layer)

    def match(self, sprite):
        if self.spr == sprite:
            return True
        return False

    def move(self, dx, dy):
        self.spr.move((dx, dy))

    def move_relative(self, dx, dy):
        self.spr.move_relative((dx, dy))

    def hide(self):
        self.spr.hide()
class Slide(Stator):
    """ Create a sprite for a slide """
    def __init__(self, sprites, path, name, x, y, w, h, svg_engine=None,
                 function=None):
        if svg_engine is None:
            self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h))
        else:
            self.spr = Sprite(sprites, x, y,
                              svg_str_to_pixbuf(svg_engine().svg))
        self.tab_dx = [0, SWIDTH - TABWIDTH]
        self.tab_dy = [2 * SHEIGHT, 2 * SHEIGHT]
        self.tabs = []
        self.tabs.append(Tab(sprites, path, 'tab', x + self.tab_dx[0],
                             y + self.tab_dy[0], TABWIDTH, SHEIGHT))
        self.tabs.append(Tab(sprites, path, 'tab', x + self.tab_dx[1],
                             y + self.tab_dy[1], TABWIDTH, SHEIGHT))
        self.calculate = function
        self.name = name

    def add_textview(self, textview, i=0):
        self.tabs[i].textview = textview
        self.tabs[i].textbuffer = textview.get_buffer()

    def set_fixed(self, fixed):
        for tab in self.tabs:
            tab.fixed = fixed

    def match(self, sprite):
        if sprite == self.spr or sprite == self.tabs[0].spr or \
                sprite == self.tabs[1].spr:
            return True
        return False

    def draw(self, layer=1000):
        self.spr.set_layer(layer)
        self.spr.draw()
        for tab in self.tabs:
            tab.draw()

    def move(self, dx, dy):
        self.spr.move((dx, dy))
        for i, tab in enumerate(self.tabs):
            tab.move(dx + self.tab_dx[i], dy + self.tab_dy[i])

    def move_relative(self, dx, dy):
        self.spr.move_relative((dx, dy))
        for i, tab in enumerate(self.tabs):
            tab.move_relative(dx, dy)

    def hide(self):
        self.spr.hide()
        for tab in self.tabs:
            tab.hide()

    def label(self, label, i=0):
        self.tabs[i].label(label)
Exemple #3
0
class Tab():
    """ Create tabs for the slide; include a TextView for OSK input """
    def __init__(self, sprites, path, name, x, y, w, h):
        self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h))
        self.spr.label = "1.0"
        self.spr.type = name
        self.name = name
        self.width = w
        self.textview = None
        self.textbuffer = None
        self.fixed = None
        self.textview_y_offset = 0

    def label(self, label):
        if self.textbuffer is not None:
            self.textbuffer.set_text(label)

    def _move_textview(self, x, y):
        y += self.textview_y_offset
        if self.textview is not None:
            if x > 0 and x < Gdk.Screen.width() - self.width and y > 0:
                self.fixed.move(self.textview, x, y)
                self.textview.show()
            else:
                self.textview.hide()

    def move(self, x, y):
        self.spr.move((x, y))
        self._move_textview(x, y)

    def move_relative(self, dx, dy):
        self.spr.move_relative((dx, dy))
        x, y = self.spr.get_xy()
        self._move_textview(x, y)

    def draw(self, layer=100):
        self.spr.set_layer(layer)
        self.spr.draw()
        x, y = self.spr.get_xy()
        self._move_textview(x, y)

    def hide(self):
        self.spr.hide()
class Tab():
    """ Create tabs for the slide; include a TextView for OSK input """
    def __init__(self, sprites, path, name, x, y, w, h):
        self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h))
        self.spr.label = "1.0"
        self.spr.type = name
        self.name = name
        self.width = w
        self.textview = None
        self.textbuffer = None
        self.fixed = None
        self.textview_y_offset = 0

    def label(self, label):
        if self.textbuffer is not None:
            self.textbuffer.set_text(label)

    def _move_textview(self, x, y):
        y += self.textview_y_offset
        if self.textview is not None:
            if x > 0 and x < Gdk.Screen.width() - self.width and y > 0:
                self.fixed.move(self.textview, x, y)
                self.textview.show()
            else:
                self.textview.hide()

    def move(self, x, y):
        self.spr.move((x, y))
        self._move_textview(x, y)

    def move_relative(self, dx, dy):
        self.spr.move_relative((dx, dy))
        x, y = self.spr.get_xy()
        self._move_textview(x, y)

    def draw(self, layer=100):
        self.spr.set_layer(layer)
        self.spr.draw()
        x, y = self.spr.get_xy()
        self._move_textview(x, y)

    def hide(self):
        self.spr.hide()
Exemple #5
0
class Stator():
    """ Create a sprite for a stator """
    def __init__(self,
                 sprites,
                 path,
                 name,
                 x,
                 y,
                 w,
                 h,
                 svg_engine=None,
                 calculate=None,
                 result=None):
        if svg_engine is None:
            self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h))
        else:
            self.spr = Sprite(sprites, x, y,
                              svg_str_to_pixbuf(svg_engine().svg))
        self.spr.type = name
        self.name = name
        self.calculate = calculate
        self.result = result

    def draw(self, layer=1000):
        self.spr.set_layer(layer)

    def match(self, sprite):
        if self.spr == sprite:
            return True
        return False

    def move(self, dx, dy):
        self.spr.move((dx, dy))

    def move_relative(self, dx, dy):
        self.spr.move_relative((dx, dy))

    def hide(self):
        self.spr.hide()
Exemple #6
0
class Ball():
    ''' The Bounce class is used to define the ball and the user
    interaction. '''

    def __init__(self, sprites, filename):
        self.current_frame = 0
        self.frames = []  # Easter Egg animation
        self.sprites = sprites
        self.ball = Sprite(self.sprites, 0, 0, svg_str_to_pixbuf(
                svg_from_file(filename)))

        self.ball.set_layer(1)
        self.ball.set_label_attributes(24)

        ball = extract_svg_payload(file(filename, 'r'))
        for i in range(8):
            self.frames.append(Sprite(
                    self.sprites, 0, 0, svg_str_to_pixbuf(
                        svg_header(SIZE, SIZE, 1.0) + TRANSFORMS[i] + \
                            ball + PUNCTURE + AIR + '</g>' + svg_footer())))

        for frame in self.frames:
            frame.set_layer(1)
            frame.move((0, -SIZE))  # move animation frames off screen

    def new_ball(self, filename):
        ''' Create a ball object and Easter Egg animation from an SVG file. '''
        self.ball.images[0] = svg_str_to_pixbuf(svg_from_file(filename))

        ball = extract_svg_payload(file(filename, 'r'))
        for i in range(8):
            self.frames[i].images[0] = svg_str_to_pixbuf(
                        svg_header(SIZE, SIZE, 1.0) + TRANSFORMS[i] + \
                            ball + PUNCTURE + AIR + '</g>' + svg_footer())

    def new_ball_from_image(self, filename):
        ''' Just create a ball object from an image file '''
        if filename == '':
            _logger.debug('Image file not found.')
            return
        try:
            self.ball.images[0] = gtk.gdk.pixbuf_new_from_file_at_size(
                filename, SIZE, SIZE)
        except:
            _logger.debug('Could not load image from %s.', filename)

    def ball_x(self):
        return self.ball.get_xy()[0]

    def ball_y(self):
        return self.ball.get_xy()[1]

    def frame_x(self, i):
        return self.frames[i].get_xy()[0]

    def frame_y(self, i):
        return self.frames[i].get_xy()[1]

    def width(self):
        return self.ball.rect[2]

    def height(self):
        return self.ball.rect[3]

    def move_ball(self, pos):
        self.ball.move(pos)

    def move_ball_relative(self, pos):
        self.ball.move_relative(pos)

    def move_frame(self, i, pos):
        self.frames[i].move(pos)

    def move_frame_relative(self, i, pos):
        self.frames[i].move_relative(pos)

    def hide_frames(self):
        for frame in self.frames:
            frame.move((0, -SIZE))  # hide the animation frames

    def next_frame(self, frame_counter):
        if frame_counter in ANIMATION:
            self._switch_frames(ANIMATION[frame_counter])
        return self.current_frame

    def _switch_frames(self, frames):
        ''' Switch between frames in the animation '''
        self.move_frame(frames[1], (self.frame_x(frames[0]),
                                  self.frame_y(frames[0])))
        self.move_frame(frames[0], ((0, -SIZE)))  # hide the frame
        self.current_frame = frames[1]
Exemple #7
0
class Game():
    def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']):
        self._activity = parent
        self._colors = colors

        self._canvas = canvas
        parent.show_all()

        self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        self._canvas.connect("draw", self.__draw_cb)
        self._canvas.connect("button-press-event", self._button_press_cb)

        self._width = Gdk.Screen.width()
        self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5)
        self._scale = self._height / (14.0 * DOT_SIZE * 1.2)
        self._scale_gameover = self._height / (4.0 * DOT_SIZE_GAMEOVER * 1.2)
        self._dot_size = int(DOT_SIZE * self._scale)
        self._dot_size_gameover = int(DOT_SIZE_GAMEOVER * self._scale)
        self._turtle_offset = 0
        self._space = int(self._dot_size / 5.)
        self._space_gameover = int(self._dot_size_gameover / 5.)
        self._orientation = 0
        self.level = 0
        self.custom_strategy = None
        self.strategies = [
            BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY,
            self.custom_strategy
        ]
        self.strategy = self.strategies[self.level]
        self._timeout_id = None
        self.best_time = self.load_best_time()
        # Generate the sprites we'll need...
        self._sprites = Sprites(self._canvas)
        self._dots = []
        self._gameover = []
        self._your_time = []
        self._best_time = []
        self._win_lose = []
        for y in range(THIRTEEN):
            for x in range(THIRTEEN):
                offset_x = int((self._width - THIRTEEN * (self._dot_size + \
                                      self._space) - self._space) / 2.)
                if y % 2 == 1:
                    offset_x += int((self._dot_size + self._space) / 2.)
                if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1:
                    self._dots.append(
                        Sprite(self._sprites,
                               offset_x + x * (self._dot_size + self._space),
                               y * (self._dot_size + self._space),
                               self._new_dot('#B0B0B0', self._dot_size)))
                else:
                    self._dots.append(
                        Sprite(
                            self._sprites,
                            offset_x + x * (self._dot_size + self._space),
                            y * (self._dot_size + self._space),
                            self._new_dot(self._colors[FILL], self._dot_size)))
                    self._dots[-1].type = False  # not set

        # Put a turtle at the center of the screen...
        self._turtle_images = []
        self._rotate_turtle(self._new_turtle())
        self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0])
        self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy())

        # ...and initialize.
        self._all_clear()

    def _move_turtle(self, pos):
        ''' Move turtle and add its offset '''
        self._turtle.move(pos)
        self._turtle.move_relative(
            (-self._turtle_offset, -self._turtle_offset))

    def _all_clear(self):
        ''' Things to reinitialize when starting up a new game. '''
        # Clear dots
        for gameover_shape in self._gameover:
            gameover_shape.hide()
        for win_lose_shape in self._win_lose:
            win_lose_shape.hide()
        for your_time_shape in self._your_time:
            your_time_shape.hide()
        for highscore_shape in self._best_time:
            highscore_shape.hide()
        for dot in self._dots:
            if dot.type:
                dot.type = False
                dot.set_shape(self._new_dot(self._colors[FILL],
                                            self._dot_size))
            dot.set_label('')
            dot.set_layer(100)
        self._turtle.set_layer(100)
        # Recenter the turtle
        self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy())
        self._turtle.set_shape(self._turtle_images[0])
        self._set_label('')
        if self._timeout_id is not None:
            GLib.source_remove(self._timeout_id)
            self._timeout_id = None

    def new_game(self, saved_state=None):
        ''' Start a new game. '''
        self.gameover_flag = False
        self.game_lost = False
        self._all_clear()
        # Fill in a few dots to start
        for i in range(15):
            n = int(uniform(0, THIRTEEN * THIRTEEN))
            if self._dots[n].type is not None:
                self._dots[n].type = True
                self._dots[n].set_shape(
                    self._new_dot(self._colors[STROKE], self._dot_size))
        # Calculate the distances to the edge
        self._initialize_weights()
        self.game_start_time = time.time()
        self.strategy = self.strategies[self.level]
        self._timeout_id = None

    def _set_label(self, string):
        ''' Set the label in the toolbar or the window frame. '''
        self._activity.status.set_label(string)

    def _button_press_cb(self, win, event):
        win.grab_focus()
        x, y = list(map(int, event.get_coords()))

        spr = self._sprites.find_sprite((x, y), inverse=True)
        if spr == None:
            return

        if spr.type is not None and not spr.type:
            spr.type = True
            spr.set_shape(self._new_dot(self._colors[STROKE], self._dot_size))
            self._weights[self._dots.index(spr)] = 1000
            self._test_game_over(self._move_the_turtle())
        return True

    def _find_the_turtle(self):
        turtle_pos = self._turtle.get_xy()
        turtle_dot = None
        for dot in self._dots:
            pos = dot.get_xy()
            # Turtle is offset
            if pos[0] == turtle_pos[0] + self._turtle_offset and \
               pos[1] == turtle_pos[1] + self._turtle_offset:
                turtle_dot = self._dots.index(dot)
                break
        if turtle_dot is None:
            _logger.debug('Cannot find the turtle...')
            return None
        return turtle_dot

    def _move_the_turtle(self):
        ''' Move the turtle after each click '''
        self._turtle_dot = self._find_the_turtle()
        if self._turtle_dot is None:
            return

        # Given the col and row of the turtle, do something
        new_dot = self._grid_to_dot(
            self._my_strategy_import(self.strategy,
                                     self._dot_to_grid(self._turtle_dot)))
        self._move_turtle(self._dots[new_dot].get_xy())
        # And set the orientation
        self._turtle.set_shape(self._turtle_images[self._orientation])

        return new_dot

    def _test_game_over(self, new_dot):
        ''' Check to see if game is over '''
        if new_dot is None:
            return
        if self._dots[new_dot].type is None:
            # Game-over feedback
            self._once_around = False
            self.game_stop_time = time.time()
            self.gameover_flag = True
            self._happy_turtle_dance()
            self._timeout_id = GLib.timeout_add(10000, self._game_over)
            return True
        c = int(self._turtle_dot / THIRTEEN) % 2
        if self._dots[
            new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type:
            # Game-over feedback
            for dot in self._dots:
                dot.set_label(':)')
                self.game_stop_time = time.time()
                self.gameover_flag = True
            self._timeout_id = GLib.timeout_add(4000, self._game_over)
            return True
        return False

    def _game_over(self):
        best_seconds = self.best_time % 60
        best_minutes = self.best_time // 60
        self.elapsed_time = int(self.game_stop_time - self.game_start_time)
        second = self.elapsed_time % 60
        minute = self.elapsed_time // 60
        for dot in self._dots:
            dot.hide()
        self._turtle.hide()

        offset_y = int(self._space_gameover / 4.)
        offset_x = int((self._width - 6 * self._dot_size_gameover -
                        5 * self._space_gameover) / 2.)
        y = 1.5
        for x in range(2, 6):
            self._gameover.append(
                Sprite(
                    self._sprites,
                    offset_x + (x - 0.50) * self._dot_size_gameover,
                    y * (self._dot_size + self._space) + offset_y,
                    self._new_dot(self._colors[FILL],
                                  self._dot_size_gameover)))
            self._gameover[-1].type = -1  # No image
            self._gameover[-1].set_label_attributes(72)
        text = ["☻", " Game ", " Over ", "☻"]
        self.rings(len(text), text, self._gameover)
        y = 4.5
        for x in range(2, 6):
            self._win_lose.append(
                Sprite(
                    self._sprites,
                    offset_x + (x - 0.50) * self._dot_size_gameover,
                    y * (self._dot_size + self._space) + offset_y,
                    self._new_dot(self._colors[FILL],
                                  self._dot_size_gameover)))
            self._win_lose[-1].type = -1  # No image
            self._win_lose[-1].set_label_attributes(72)
        text_win_best_time = ["☻", "  YOU  ", "  WON  ", "☻"]
        text_lose = ["☹", "   YOU   ", "  LOST  ", "☹"]
        text_win = ["☻", " GOOD ", "   JOB  ", "☻"]
        if self.game_lost:
            self.rings(len(text_lose), text_lose, self._win_lose)
        elif self.elapsed_time <= self.best_time:
            self.rings(len(text_win_best_time), text_win_best_time,
                       self._win_lose)
        else:
            self.rings(len(text_win), text_win, self._win_lose)
        y = 7.5
        for x in range(2, 5):
            self._your_time.append(
                Sprite(
                    self._sprites, offset_x + x * self._dot_size_gameover,
                    y * (self._dot_size + self._space),
                    self._new_dot(self._colors[FILL],
                                  self._dot_size_gameover)))
            self._your_time[-1].type = -1  # No image
            self._your_time[-1].set_label_attributes(72)
        text = [
            "  your  ", " time:  ", (' {:02d}:{:02d} '.format(minute, second))
        ]
        self.rings(len(text), text, self._your_time)
        y = 10.5
        for x in range(2, 5):
            self._best_time.append(
                Sprite(
                    self._sprites, offset_x + x * self._dot_size_gameover,
                    y * (self._dot_size + self._space),
                    self._new_dot(self._colors[FILL],
                                  self._dot_size_gameover)))
            self._best_time[-1].type = -1  # No image
            self._best_time[-1].set_label_attributes(72)
        if self.elapsed_time <= self.best_time and not self.game_lost:
            self.best_time = self.elapsed_time
            best_seconds = second
            best_minutes = minute
        text = [
            "  best  ", " time:  ",
            (' {:02d}:{:02d} '.format(best_minutes, best_seconds))
        ]
        self.rings(len(text), text, self._best_time)
        self.save_best_time()
        self._timeout_id = GLib.timeout_add(7000, self.new_game)

    def rings(self, num, text, shape):
        i = 0
        for x in range(num):
            shape[x].type = -1
            shape[x].set_shape(
                self._new_dot(self._colors[FILL], self._dot_size_gameover))
            shape[x].set_label(text[i])
            shape[x].set_layer(100)
            i += 1

    def _grid_to_dot(self, pos):
        ''' calculate the dot index from a column and row in the grid '''
        return pos[0] + pos[1] * THIRTEEN

    def _dot_to_grid(self, dot):
        ''' calculate the grid column and row for a dot '''
        return [dot % THIRTEEN, int(dot / THIRTEEN)]

    def _happy_turtle_dance(self):
        ''' Turtle dances along the edge '''
        self.game_lost = True
        i = self._find_the_turtle()
        if i == 0:
            if self._once_around:
                return
            else:
                self._once_around = True
        _logger.debug(i)
        x, y = self._dot_to_grid(i)
        if y == 0:
            x += 1
        if x == 0:
            y -= 1
        if x == THIRTEEN - 1:
            y += 1
        if y == THIRTEEN - 1:
            x -= 1
        i = self._grid_to_dot((x, y))
        self._dots[i].set_label(':)')
        self._move_turtle(self._dots[i].get_xy())
        self._orientation += 1
        self._orientation %= 6
        self._turtle.set_shape(self._turtle_images[self._orientation])
        self._timeout_id = GLib.timeout_add(250, self._happy_turtle_dance)

    def _ordered_weights(self, pos):
        ''' Returns the list of surrounding points sorted by their
        distance to the edge '''
        dots = self._surrounding_dots(pos)
        dots_and_weights = []
        for dot in dots:
            dots_and_weights.append((dot, self._weights[dot]))
        sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1])
        for i in range(6):
            dots[i] = sorted_dots[i][0]
        return dots

    def _daylight_ahead(self, pos):
        ''' Returns true if there is a straight path to the edge from
        the current position/orientation '''
        dots = self._surrounding_dots(pos)
        while True:
            dot_type = self._dots[dots[self._orientation]].type
            if dot_type is None:
                return True
            elif dot_type:
                return False
            else:  # keep looking
                pos = self._dot_to_grid(dots[self._orientation])
                dots = self._surrounding_dots(pos)

    def _surrounding_dots(self, pos):
        ''' Returns dots surrounding a position in the grid '''
        dots = []
        evenodd = pos[1] % 2
        for i in range(6):
            col = pos[0] + CIRCLE[evenodd][i][0]
            row = pos[1] + CIRCLE[evenodd][i][1]
            dots.append(self._grid_to_dot((col, row)))
        return dots

    def _initialize_weights(self):
        ''' How many steps to an edge? '''
        self._weights = []
        for d, dot in enumerate(self._dots):
            if dot.type is None:
                self._weights.append(0)
            elif dot.type:
                self._weights.append(1000)
            else:
                pos = self._dot_to_grid(d)
                pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1])
                self._weights.append(
                    min(min(pos[0], pos2[0]), min(pos[1], pos2[1])))

    def _my_strategy_import(self, f, arg):
        ''' Run Python code passed as argument '''
        userdefined = {}
        try:
            exec(f, globals(), userdefined)
            return userdefined['_turtle_strategy'](self, arg)
        except ZeroDivisionError as e:
            self._set_label('Python zero-divide error: {}'.format(e))
        except ValueError as e:
            self._set_label('Python value error: {}'.format(e))
        except SyntaxError as e:
            self._set_label('Python syntax error: {}'.format(e))
        except NameError as e:
            self._set_label('Python name error: {}'.format(e))
        except OverflowError as e:
            self._set_label('Python overflow error: {}'.format(e))
        except TypeError as e:
            self._set_label('Python type error: {}'.format(e))
        except:
            self._set_label('Python error')
        traceback.print_exc()
        return None

    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()

    def _new_dot(self, color, dot_size):
        ''' generate a dot of a color color '''
        self._stroke = color
        self._fill = color
        self._svg_width = dot_size
        self._svg_height = dot_size
        return svg_str_to_pixbuf(
            self._header() + \
            self._circle(dot_size / 2., dot_size / 2.,
                         dot_size / 2.) + \
            self._footer())

    def _new_turtle(self):
        ''' generate a turtle '''
        self._svg_width = self._dot_size * 2
        self._svg_height = self._dot_size * 2
        self._stroke = '#101010'
        self._fill = '#404040'
        return svg_str_to_pixbuf(
            self._header() + \
            self._turtle() + \
            self._footer())

    def _rotate_turtle(self, image):
        w, h = image.get_width(), image.get_height()
        nw = nh = int(sqrt(w * w + h * h))
        for i in range(6):
            surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, nw, nh)
            context = cairo.Context(surface)
            context.translate(w / 2., h / 2.)
            context.rotate((30 + i * 60) * pi / 180.)
            context.translate(-w / 2., -h / 2.)
            Gdk.cairo_set_source_pixbuf(context, image, 0, 0)
            context.rectangle(0, 0, nw, nh)
            context.fill()
            self._turtle_images.append(surface)
        self._turtle_offset = int(self._dot_size / 2.)

    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 _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'

    def _turtle(self):
        svg = '<g\ntransform="scale(%.1f, %.1f)">\n' % (self._svg_width / 60.,
                                                        self._svg_height / 60.)
        svg += '%s%s%s%s%s%s%s%s' % (
            '  <path d="M 27.5 48.3 ',
            'C 26.9 48.3 26.4 48.2 25.9 48.2 L 27.2 50.5 L 28.6 48.2 ',
            'C 28.2 48.2 27.9 48.3 27.5 48.3 Z" stroke_width="3.5" ', 'fill="',
            self._fill, ';" stroke="', self._stroke, '" />\n')
        svg += '%s%s%s%s%s%s%s%s%s%s' % (
            '   <path d="M 40.2 11.7 ', 'C 38.0 11.7 36.2 13.3 35.8 15.3 ',
            'C 37.7 16.7 39.3 18.4 40.5 20.5 ',
            'C 42.8 20.4 44.6 18.5 44.6 16.2 ',
            'C 44.6 13.7 42.6 11.7 40.2 11.7 Z" stroke_width="3.5" ', 'fill="',
            self._fill, ';" stroke="', self._stroke, '" />\n')
        svg += '%s%s%s%s%s%s%s%s%s%s' % (
            '   <path d="M 40.7 39.9 ', 'C 39.5 42.1 37.9 44.0 35.9 45.4 ',
            'C 36.4 47.3 38.1 48.7 40.2 48.7 ',
            'C 42.6 48.7 44.6 46.7 44.6 44.3 ',
            'C 44.6 42.0 42.9 40.2 40.7 39.9 Z" stroke_width="3.5" ', 'fill="',
            self._fill, ';" stroke="', self._stroke, '" />\n')
        svg += '%s%s%s%s%s%s%s%s%s%s' % (
            '   <path d="M 14.3 39.9 ', 'C 12.0 40.1 10.2 42.0 10.2 44.3 ',
            'C 10.2 46.7 12.2 48.7 14.7 48.7 ',
            'C 16.7 48.7 18.5 47.3 18.9 45.4 ',
            'C 17.1 43.9 15.5 42.1 14.3 39.9 Z" stroke_width="3.5" ', 'fill="',
            self._fill, ';" stroke="', self._stroke, '" />\n')
        svg += '%s%s%s%s%s%s%s%s%s%s' % (
            '   <path d="M 19.0 15.4 ', 'C 18.7 13.3 16.9 11.7 14.7 11.7 ',
            'C 12.2 11.7 10.2 13.7 10.2 16.2 ',
            'C 10.2 18.5 12.1 20.5 14.5 20.6 ',
            'C 15.7 18.5 17.2 16.8 19.0 15.4 Z" stroke_width="3.5" ', 'fill="',
            self._fill, ';" stroke="', self._stroke, '" />\n')
        svg += '%s%s%s%s%s%s%s%s%s%s%s%s' % (
            '   <path d="M 27.5 12.6 ', 'C 29.4 12.6 31.2 13.0 32.9 13.7 ',
            'C 33.7 12.6 34.1 11.3 34.1 9.9 ', 'C 34.1 6.2 31.1 3.2 27.4 3.2 ',
            'C 23.7 3.2 20.7 6.2 20.7 9.9 ',
            'C 20.7 11.3 21.2 12.7 22.0 13.7 ',
            'C 23.7 13.0 25.5 12.6 27.5 12.6 Z" stroke_width="3.5" ', 'fill="',
            self._fill, ';" stroke="', self._stroke, '" />\n')
        svg += '%s%s%s%s%s%s%s%s%s%s%s%s' % (
            '   <path d="M 43.1 30.4 ', 'C 43.1 35.2 41.5 39.7 38.5 43.0 ',
            'C 35.6 46.4 31.6 48.3 27.5 48.3 ',
            'C 23.4 48.3 19.4 46.4 16.5 43.0 ',
            'C 13.5 39.7 11.9 35.2 11.9 30.4 ',
            'C 11.9 20.6 18.9 12.6 27.5 12.6 ',
            'C 36.1 12.6 43.1 20.6 43.1 30.4 Z" stroke_width="3.5" ', 'fill="',
            self._fill, ';" stroke="', self._stroke, '" />\n')
        svg += '%s%s%s%s%s' % (
            '   <path d="M 25.9 33.8 L 24.3 29.1 ',
            'L 27.5 26.5 L 31.1 29.2 L 29.6 33.8 Z" stroke_width="3.5" ',
            'fill="', self._stroke, ';" stroke="none" />\n')
        svg += '%s%s%s%s%s%s' % (
            '   <path d="M 27.5 41.6 ',
            'C 23.5 41.4 22.0 39.5 22.0 39.5 L 25.5 35.4 L 30.0 35.5 ',
            'L 33.1 39.7 C 33.1 39.7 30.2 41.7 27.5 41.6 Z" ',
            'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n')
        svg += '%s%s%s%s%s%s' % (
            '   <path d="M 18.5 33.8 ',
            'C 17.6 30.9 18.6 27.0 18.6 27.0 L 22.6 29.1 L 24.1 33.8 ',
            'L 20.5 38.0 C 20.5 38.0 19.1 36.0 18.4 33.8 Z" ',
            'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n')
        svg += '%s%s%s%s%s%s' % (
            '   <path d="M 19.5 25.1 ', 'C 19.5 25.1 20.0 23.2 22.5 21.3 ',
            'C 24.7 19.7 27.0 19.6 27.0 19.6 L 26.9 24.6 L 23.4 27.3 ',
            'L 19.5 25.1 Z" stroke_width="3.5" fill="', self._stroke,
            ';" stroke="none" />\n')
        svg += '%s%s%s%s%s%s' % (
            '   <path d="M 32.1 27.8 L 28.6 25.0 ',
            'L 29 19.8 C 29 19.8 30.8 19.7 33.0 21.4 ',
            'C 35.2 23.2 36.3 26.4 36.3 26.4 L 32.1 27.8 Z" ',
            'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n')
        svg += '%s%s%s%s%s%s' % (
            '   <path d="M 31.3 34.0 L 32.6 29.6 ',
            'L 36.8 28.0 C 36.8 28.0 37.5 30.7 36.8 33.7 ',
            'C 36.2 36.0 34.7 38.1 34.7 38.1 L 31.3 34.0 Z" ',
            'stroke_width="3.5" fill="', self._stroke, ';" stroke="none" />\n')
        svg += '</g>\n'
        return svg

    def save_best_time(self):
        file_path = os.path.join(get_activity_root(), 'data', 'best-time')
        best_time = [180]
        if os.path.exists(file_path):
            with open(file_path, "r") as fp:
                best_time = fp.readlines()
        int_best_time = int(best_time[0])
        if not int_best_time <= self.elapsed_time and not self.game_lost:
            int_best_time = self.elapsed_time
        with open(file_path, "w") as fp:
            fp.write(str(int_best_time))

    def load_best_time(self):
        file_path = os.path.join(get_activity_root(), 'data', 'best-time')
        if os.path.exists(file_path):
            with open(file_path, "r") as fp:
                highscore = fp.readlines()
            try:
                return int(highscore[0])
            except (ValueError, IndexError) as e:
                logging.exception(e)
                return 0
        return 0
Exemple #8
0
class Slide(Stator):
    """ Create a sprite for a slide """
    def __init__(self,
                 sprites,
                 path,
                 name,
                 x,
                 y,
                 w,
                 h,
                 svg_engine=None,
                 function=None):
        if svg_engine is None:
            self.spr = Sprite(sprites, x, y, file_to_pixbuf(path, name, w, h))
        else:
            self.spr = Sprite(sprites, x, y,
                              svg_str_to_pixbuf(svg_engine().svg))
        self.tab_dx = [0, SWIDTH - TABWIDTH]
        self.tab_dy = [2 * SHEIGHT, 2 * SHEIGHT]
        self.tabs = []
        self.tabs.append(
            Tab(sprites, path, 'tab', x + self.tab_dx[0], y + self.tab_dy[0],
                TABWIDTH, SHEIGHT))
        self.tabs.append(
            Tab(sprites, path, 'tab', x + self.tab_dx[1], y + self.tab_dy[1],
                TABWIDTH, SHEIGHT))
        self.calculate = function
        self.name = name

    def add_textview(self, textview, i=0):
        self.tabs[i].textview = textview
        self.tabs[i].textbuffer = textview.get_buffer()

    def set_fixed(self, fixed):
        for tab in self.tabs:
            tab.fixed = fixed

    def match(self, sprite):
        if sprite == self.spr or sprite == self.tabs[0].spr or \
                sprite == self.tabs[1].spr:
            return True
        return False

    def draw(self, layer=1000):
        self.spr.set_layer(layer)
        self.spr.draw()
        for tab in self.tabs:
            tab.draw()

    def move(self, dx, dy):
        self.spr.move((dx, dy))
        for i, tab in enumerate(self.tabs):
            tab.move(dx + self.tab_dx[i], dy + self.tab_dy[i])

    def move_relative(self, dx, dy):
        self.spr.move_relative((dx, dy))
        for i, tab in enumerate(self.tabs):
            tab.move_relative(dx, dy)

    def hide(self):
        self.spr.hide()
        for tab in self.tabs:
            tab.hide()

    def label(self, label, i=0):
        self.tabs[i].label(label)
Exemple #9
0
class Bar():
    ''' The Bar class is used to define the bars at the bottom of the
    screen '''

    def __init__(self, sprites, ball_size, colors=['#FFFFFF', '#AAAAAA']):
        ''' Initialize the 2-segment bar, labels, and mark '''
        self._sprites = sprites
        self._colors = colors[:]
        self.bars = {}

        self._width = Gdk.Screen.width()
        self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE
        self._scale = Gdk.Screen.height() / 900.0

        self._ball_size = ball_size

        self.make_bar(2)
        self._make_wedge_mark()

    def resize_all(self):
        self._width = Gdk.Screen.width()
        self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE
        self._scale = Gdk.Screen.height() / 900.0

        for bar in list(self.bars.keys()):
            self.bars[bar].hide()
        self.mark.hide()

        for bar in list(self.bars.keys()):
            self.make_bar(bar)
        self._make_wedge_mark()

    def _make_wedge_mark(self):
        ''' Make a mark to show the fraction position on the bar. '''
        dx = self._ball_size / 2.
        n = (self._width - self._ball_size) / dx
        dy = (BAR_HEIGHT * self._scale) / n
        s = 3.5
        i = int(n / 2) - 1
        mark = svg_header(self._ball_size,
                          BAR_HEIGHT * self._scale + s, 1.0)
        mark += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                          s,
                          i * 2 * dy + s, (i * 2 + 1) * dy + s,
                          '#FF0000', '#FFFFFF')
        mark += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                          dx + s,
                          (i * 2 + 1) * dy + s, (i * 2 + 2) * dy + s,
                          '#FF0000', '#FFFFFF')
        mark += svg_footer()
        self.mark = Sprite(self._sprites, 0,
                           self._height,  # hide off bottom of screen
                           svg_str_to_pixbuf(mark))
        self.mark.set_layer(1)

    def mark_width(self):
        return self.mark.rect[2]

    def bar_x(self):
        return self.bars[2].get_xy()[0]

    def bar_y(self):
        if self.bars[2].get_xy()[1] < 0:
            return self.bars[2].get_xy()[1] + 3000
        else:
            return self.bars[2].get_xy()[1]

    def width(self):
        return self.bars[2].rect[2]

    def show_bar(self, n):
        if n in self.bars:
            self.bars[n].move([self.bar_x(), self.bar_y()])

    def bump_bars(self, direction='up'):
        ''' when the toolbars expand and contract, we need to move the bar '''
        if direction == 'up':
            dy = -style.GRID_CELL_SIZE
        else:
            dy = style.GRID_CELL_SIZE
        for bar in self.bars:
            self.bars[bar].move_relative([0, dy])
        self.mark.move_relative([0, dy])

    def hide_bars(self):
        ''' Hide all of the bars '''
        for bar in self.bars:
            if self.bars[bar].get_xy()[1] > 0:
                self.bars[bar].move_relative([0, -3000])

    def get_bar(self, nsegments):
        ''' Return a bar with n segments '''
        if nsegments not in self.bars:
            self.make_bar(nsegments)
        return self.bars[nsegments]

    def make_bar(self, nsegments):
        return self._make_wedge_bar(nsegments)

    def _make_wedge_bar(self, nsegments):
        ''' Create a wedged-shaped bar with n segments '''
        s = 3.5  # add provision for stroke width
        svg = svg_header(self._width, BAR_HEIGHT * self._scale + s, 1.0)
        dx = self._width / float(nsegments)
        dy = (BAR_HEIGHT * self._scale) / float(nsegments)
        for i in range(int(nsegments) // 2):
            svg += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                             i * 2 * dx + s,
                             i * 2 * dy + s, (i * 2 + 1) * dy + s,
                             '#000000', '#FFFFFF')
            svg += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                             (i * 2 + 1) * dx + s,
                             (i * 2 + 1) * dy + s, (i * 2 + 2) * dy + s,
                             '#000000', '#FFFFFF')
        if int(nsegments) % 2 == 1:  # odd
            svg += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                             (i * 2 + 2) * dx + s,
                             (i * 2 + 2) * dy + s,
                             BAR_HEIGHT * self._scale + s,
                             '#000000', '#FFFFFF')
        svg += svg_footer()

        self.bars[nsegments] = Sprite(self._sprites, 0, 0,
                                      svg_str_to_pixbuf(svg))
        self.bars[nsegments].set_layer(2)
        self.bars[nsegments].set_label_attributes(18, horiz_align="left", i=0)
        self.bars[nsegments].set_label_attributes(18, horiz_align="right", i=1)
        self.bars[nsegments].set_label_color('black', i=0)
        self.bars[nsegments].set_label_color('white', i=1)
        self.bars[nsegments].set_label(' 0', i=0)
        self.bars[nsegments].set_label('1 ', i=1)
        self.bars[nsegments].move(
            (0, self._height - BAR_HEIGHT * self._scale))
Exemple #10
0
class Ball():
    ''' The Bounce class is used to define the ball and the user
    interaction. '''
    def __init__(self, sprites, filename):
        self._current_frame = 0
        self._frames = []  # Easter Egg animation
        self._sprites = sprites
        self.ball = Sprite(self._sprites, 0, 0,
                           svg_str_to_pixbuf(svg_from_file(filename)))

        self.ball.set_layer(3)
        self.ball.set_label_attributes(24, vert_align='top')

        ball = extract_svg_payload(file(filename, 'r'))
        for i in range(8):
            self._frames.append(
                Sprite(
                    self._sprites, 0, 0,
                    svg_str_to_pixbuf(
                        svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] +
                        ball + PUNCTURE + AIR + '</g>' + svg_footer())))

        for frame in self._frames:
            frame.set_layer(3)
            frame.move((0, -SIZE[1]))  # move animation frames off screen

    def new_ball(self, filename):
        ''' Create a ball object and Easter Egg animation from an SVG file. '''
        self.ball.set_shape(svg_str_to_pixbuf(svg_from_file(filename)))
        ball = extract_svg_payload(file(filename, 'r'))
        for i in range(8):
            self._frames[i].set_shape(
                svg_str_to_pixbuf(
                    svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] + ball +
                    PUNCTURE + AIR + '</g>' + svg_footer()))

    def new_ball_from_image(self, filename, save_path):
        ''' Just create a ball object from an image file '''
        if filename == '':
            _logger.debug('Image file not found.')
            return
        try:
            pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
            if pixbuf.get_width() > pixbuf.get_height():
                size = pixbuf.get_height()
                x = int((pixbuf.get_width() - size) / 2)
            else:
                size = pixbuf.get_width()
                x = int((pixbuf.get_height() - size) / 2)
            crop = GdkPixbuf.Pixbuf.new(0, True, 8, size, size)
            pixbuf.copy_area(x, 0, size, size, crop, 0, 0)
            scale = crop.scale_simple(85, 85, GdkPixbuf.InterpType.BILINEAR)
            scale.savev(save_path, 'png', [], [])
            self.ball.set_shape(svg_str_to_pixbuf(
                generate_ball_svg(save_path)))
        except Exception as e:
            _logger.error('Could not load image from %s: %s' % (filename, e))

    def new_ball_from_fraction(self, fraction):
        ''' Create a ball with a section of size fraction. '''
        r = SIZE[0] / 2.0
        self.ball.set_shape(
            svg_str_to_pixbuf(
                svg_header(SIZE[0], SIZE[1], 1.0) +
                svg_sector(r, r + BOX[1], r - 1, 1.999 *
                           pi, COLORS[0], COLORS[1]) +
                svg_sector(r, r + BOX[1], r - 1, fraction * 2 *
                           pi, COLORS[1], COLORS[0]) +
                svg_rect(BOX[0], BOX[1], 4, 4, 0, 0, '#FFFFFF', 'none') +
                svg_footer()))

    def ball_x(self):
        return self.ball.get_xy()[0]

    def ball_y(self):
        return self.ball.get_xy()[1]

    def frame_x(self, i):
        return self._frames[i].get_xy()[0]

    def frame_y(self, i):
        return self._frames[i].get_xy()[1]

    def width(self):
        return self.ball.rect[2]

    def height(self):
        return self.ball.rect[3]

    def move_ball(self, pos):
        self.ball.move(pos)

    def move_ball_relative(self, pos):
        self.ball.move_relative(pos)

    def move_frame(self, i, pos):
        self._frames[i].move(pos)

    def move_frame_relative(self, i, pos):
        self._frames[i].move_relative(pos)

    def hide_frames(self):
        for frame in self._frames:
            frame.move((0, -SIZE[1]))  # hide the animation frames

    def next_frame(self, frame_counter):
        if frame_counter in ANIMATION:
            self._switch_frames(ANIMATION[frame_counter])
        return self._current_frame

    def _switch_frames(self, frames):
        ''' Switch between frames in the animation '''
        self.move_frame(frames[1],
                        (self.frame_x(frames[0]), self.frame_y(frames[0])))
        self.move_frame(frames[0], ((0, -SIZE[1])))  # hide the frame
        self._current_frame = frames[1]
Exemple #11
0
class Game():
    def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']):
        self._activity = parent
        self._colors = colors

        self._canvas = canvas
        parent.show_all()

        self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        self._canvas.connect("draw", self.__draw_cb)
        self._canvas.connect("button-press-event", self._button_press_cb)

        self._width = Gdk.Screen.width()
        self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5)
        self._scale = self._height / (14.0 * DOT_SIZE * 1.2)
        self._dot_size = int(DOT_SIZE * self._scale)
        self._turtle_offset = 0
        self._space = int(self._dot_size / 5.)
        self._orientation = 0
        self.level = 0
        self.custom_strategy = None
        self.strategies = [
            BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY, EXPERT_STRATEGY,
            self.custom_strategy
        ]
        self.strategy = self.strategies[self.level]
        self._timeout_id = None

        # Generate the sprites we'll need...
        self._sprites = Sprites(self._canvas)
        self._dots = []
        for y in range(THIRTEEN):
            for x in range(THIRTEEN):
                xoffset = int((self._width - THIRTEEN * (self._dot_size + \
                                      self._space) - self._space) / 2.)
                if y % 2 == 1:
                    xoffset += int((self._dot_size + self._space) / 2.)
                if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1:
                    self._dots.append(
                        Sprite(self._sprites,
                               xoffset + x * (self._dot_size + self._space),
                               y * (self._dot_size + self._space),
                               self._new_dot('#B0B0B0')))
                else:
                    self._dots.append(
                        Sprite(self._sprites,
                               xoffset + x * (self._dot_size + self._space),
                               y * (self._dot_size + self._space),
                               self._new_dot(self._colors[FILL])))
                    self._dots[-1].type = False  # not set

        # Put a turtle at the center of the screen...
        self._turtle_images = []
        self._rotate_turtle(self._new_turtle())
        self._turtle = Sprite(self._sprites, 0, 0, self._turtle_images[0])
        self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy())

        # ...and initialize.
        self._all_clear()

    def _move_turtle(self, pos):
        ''' Move turtle and add its offset '''
        self._turtle.move(pos)
        self._turtle.move_relative(
            (-self._turtle_offset, -self._turtle_offset))

    def _all_clear(self):
        ''' Things to reinitialize when starting up a new game. '''
        # Clear dots
        for dot in self._dots:
            if dot.type:
                dot.type = False
                dot.set_shape(self._new_dot(self._colors[FILL]))
            dot.set_label('')

        # Recenter the turtle
        self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy())
        self._turtle.set_shape(self._turtle_images[0])
        self._set_label('')
        if self._timeout_id is not None:
            GObject.source_remove(self._timeout_id)

    def new_game(self, saved_state=None):
        ''' Start a new game. '''
        self._all_clear()

        # Fill in a few dots to start
        for i in range(15):
            n = int(uniform(0, THIRTEEN * THIRTEEN))
            if self._dots[n].type is not None:
                self._dots[n].type = True
                self._dots[n].set_shape(self._new_dot(self._colors[STROKE]))

        # Calculate the distances to the edge
        self._initialize_weights()
        self.strategy = self.strategies[self.level]

    def _set_label(self, string):
        ''' Set the label in the toolbar or the window frame. '''
        self._activity.status.set_label(string)

    def _button_press_cb(self, win, event):
        win.grab_focus()
        x, y = map(int, event.get_coords())

        spr = self._sprites.find_sprite((x, y), inverse=True)
        if spr == None:
            return

        if spr.type is not None and not spr.type:
            spr.type = True
            spr.set_shape(self._new_dot(self._colors[STROKE]))
            self._weights[self._dots.index(spr)] = 1000
            self._test_game_over(self._move_the_turtle())
        return True

    def _find_the_turtle(self):
        turtle_pos = self._turtle.get_xy()
        turtle_dot = None
        for dot in self._dots:
            pos = dot.get_xy()
            # Turtle is offset
            if pos[0] == turtle_pos[0] + self._turtle_offset and \
               pos[1] == turtle_pos[1] + self._turtle_offset:
                turtle_dot = self._dots.index(dot)
                break
        if turtle_dot is None:
            _logger.debug('Cannot find the turtle...')
            return None
        return turtle_dot

    def _move_the_turtle(self):
        ''' Move the turtle after each click '''
        self._turtle_dot = self._find_the_turtle()
        if self._turtle_dot is None:
            return

        # Given the col and row of the turtle, do something
        new_dot = self._grid_to_dot(
            self._my_strategy_import(self.strategy,
                                     self._dot_to_grid(self._turtle_dot)))
        self._move_turtle(self._dots[new_dot].get_xy())
        # And set the orientation
        self._turtle.set_shape(self._turtle_images[self._orientation])

        return new_dot

    def _test_game_over(self, new_dot):
        ''' Check to see if game is over '''
        if new_dot is None:
            return
        if self._dots[new_dot].type is None:
            # Game-over feedback
            self._once_around = False
            self._happy_turtle_dance()
            return True
        c = int(self._turtle_dot / THIRTEEN) % 2
        if self._dots[
            new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type:
            # Game-over feedback
            for dot in self._dots:
                dot.set_label(':)')
            return True
        return False

    def _grid_to_dot(self, pos):
        ''' calculate the dot index from a column and row in the grid '''
        return pos[0] + pos[1] * THIRTEEN

    def _dot_to_grid(self, dot):
        ''' calculate the grid column and row for a dot '''
        return [dot % THIRTEEN, int(dot / THIRTEEN)]

    def _happy_turtle_dance(self):
        ''' Turtle dances along the edge '''
        i = self._find_the_turtle()
        if i == 0:
            if self._once_around:
                return
            else:
                self._once_around = True
        _logger.debug(i)
        x, y = self._dot_to_grid(i)
        if y == 0:
            x += 1
        if x == 0:
            y -= 1
        if x == THIRTEEN - 1:
            y += 1
        if y == THIRTEEN - 1:
            x -= 1
        i = self._grid_to_dot((x, y))
        self._dots[i].set_label(':)')
        self._move_turtle(self._dots[i].get_xy())
        self._orientation += 1
        self._orientation %= 6
        self._turtle.set_shape(self._turtle_images[self._orientation])
        self._timeout_id = GObject.timeout_add(250, self._happy_turtle_dance)

    def _ordered_weights(self, pos):
        ''' Returns the list of surrounding points sorted by their
        distance to the edge '''
        dots = self._surrounding_dots(pos)
        dots_and_weights = []
        for dot in dots:
            dots_and_weights.append((dot, self._weights[dot]))
        sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1])
        for i in range(6):
            dots[i] = sorted_dots[i][0]
        return dots

    def _daylight_ahead(self, pos):
        ''' Returns true if there is a straight path to the edge from
        the current position/orientation '''
        dots = self._surrounding_dots(pos)
        while True:
            dot_type = self._dots[dots[self._orientation]].type
            if dot_type is None:
                return True
            elif dot_type:
                return False
            else:  # keep looking
                pos = self._dot_to_grid(dots[self._orientation])
                dots = self._surrounding_dots(pos)

    def _surrounding_dots(self, pos):
        ''' Returns dots surrounding a position in the grid '''
        dots = []
        evenodd = pos[1] % 2
        for i in range(6):
            col = pos[0] + CIRCLE[evenodd][i][0]
            row = pos[1] + CIRCLE[evenodd][i][1]
            dots.append(self._grid_to_dot((col, row)))
        return dots

    def _initialize_weights(self):
        ''' How many steps to an edge? '''
        self._weights = []
        for d, dot in enumerate(self._dots):
            if dot.type is None:
                self._weights.append(0)
            elif dot.type:
                self._weights.append(1000)
            else:
                pos = self._dot_to_grid(d)
                pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1])
                self._weights.append(
                    min(min(pos[0], pos2[0]), min(pos[1], pos2[1])))

    def _my_strategy_import(self, f, arg):
        ''' Run Python code passed as argument '''
        userdefined = {}
        try:
            exec f in globals(), userdefined
            return userdefined['_turtle_strategy'](self, arg)
        except ZeroDivisionError, e:
            self._set_label('Python zero-divide error: %s' % (str(e)))
        except ValueError, e:
            self._set_label('Python value error: %s' % (str(e)))
Exemple #12
0
class Ball():
    ''' The Bounce class is used to define the ball and the user
    interaction. '''

    def __init__(self, sprites, filename):
        self._current_frame = 0
        self._frames = []  # Easter Egg animation
        self._sprites = sprites
        self.ball = Sprite(self._sprites, 0, 0, svg_str_to_pixbuf(
            svg_from_file(filename)))

        self.ball.set_layer(3)
        self.ball.set_label_attributes(24, vert_align='top')

        ball = extract_svg_payload(file(filename, 'r'))
        for i in range(8):
            self._frames.append(Sprite(
                self._sprites, 0, 0, svg_str_to_pixbuf(
                    svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] +
                    ball + PUNCTURE + AIR + '</g>' + svg_footer())))

        for frame in self._frames:
            frame.set_layer(3)
            frame.move((0, -SIZE[1]))  # move animation frames off screen

    def new_ball(self, filename):
        ''' Create a ball object and Easter Egg animation from an SVG file. '''
        self.ball.set_shape(svg_str_to_pixbuf(svg_from_file(filename)))
        ball = extract_svg_payload(file(filename, 'r'))
        for i in range(8):
            self._frames[i].set_shape(svg_str_to_pixbuf(
                svg_header(SIZE[0], SIZE[1], 1.0) + TRANSFORMS[i] +
                ball + PUNCTURE + AIR + '</g>' + svg_footer()))

    def new_ball_from_image(self, filename, save_path):
        ''' Just create a ball object from an image file '''
        if filename == '':
            _logger.debug('Image file not found.')
            return
        try:
            pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
            if pixbuf.get_width() > pixbuf.get_height():
                size = pixbuf.get_height()
                x = int((pixbuf.get_width() - size) / 2)
            else:
                size = pixbuf.get_width()
                x = int((pixbuf.get_height() - size) / 2)
            crop = GdkPixbuf.Pixbuf.new(0, True, 8, size, size)
            pixbuf.copy_area(x, 0, size, size, crop, 0, 0)
            scale = crop.scale_simple(85, 85, GdkPixbuf.InterpType.BILINEAR)
            scale.savev(save_path, 'png', [], [])
            self.ball.set_shape(
                svg_str_to_pixbuf(generate_ball_svg(save_path)))
        except Exception as e:
            _logger.error('Could not load image from %s: %s' % (filename, e))

    def new_ball_from_fraction(self, fraction):
        ''' Create a ball with a section of size fraction. '''
        r = SIZE[0] / 2.0
        self.ball.set_shape(svg_str_to_pixbuf(
            svg_header(SIZE[0], SIZE[1], 1.0) +
            svg_sector(r, r + BOX[1], r - 1, 1.999 * pi,
                       COLORS[0], COLORS[1]) +
            svg_sector(r, r + BOX[1], r - 1, fraction * 2 * pi,
                       COLORS[1], COLORS[0]) +
            svg_rect(BOX[0], BOX[1], 4, 4, 0, 0, '#FFFFFF', 'none') +
            svg_footer()))

    def ball_x(self):
        return self.ball.get_xy()[0]

    def ball_y(self):
        return self.ball.get_xy()[1]

    def frame_x(self, i):
        return self._frames[i].get_xy()[0]

    def frame_y(self, i):
        return self._frames[i].get_xy()[1]

    def width(self):
        return self.ball.rect[2]

    def height(self):
        return self.ball.rect[3]

    def move_ball(self, pos):
        self.ball.move(pos)

    def move_ball_relative(self, pos):
        self.ball.move_relative(pos)

    def move_frame(self, i, pos):
        self._frames[i].move(pos)

    def move_frame_relative(self, i, pos):
        self._frames[i].move_relative(pos)

    def hide_frames(self):
        for frame in self._frames:
            frame.move((0, -SIZE[1]))  # hide the animation frames

    def next_frame(self, frame_counter):
        if frame_counter in ANIMATION:
            self._switch_frames(ANIMATION[frame_counter])
        return self._current_frame

    def _switch_frames(self, frames):
        ''' Switch between frames in the animation '''
        self.move_frame(frames[1], (self.frame_x(frames[0]),
                                    self.frame_y(frames[0])))
        self.move_frame(frames[0], ((0, -SIZE[1])))  # hide the frame
        self._current_frame = frames[1]
Exemple #13
0
class Game():

    def __init__(self, canvas, parent=None, colors=['#A0FFA0', '#FF8080']):
        self._activity = parent
        self._colors = colors

        self._canvas = canvas
        parent.show_all()

        self._canvas.add_events(Gdk.EventMask.BUTTON_PRESS_MASK)
        self._canvas.connect("draw", self.__draw_cb)
        self._canvas.connect("button-press-event", self._button_press_cb)

        self._width = Gdk.Screen.width()
        self._height = Gdk.Screen.height() - (GRID_CELL_SIZE * 1.5)
        self._scale = self._height / (14.0 * DOT_SIZE * 1.2)
        self._dot_size = int(DOT_SIZE * self._scale)
        self._turtle_offset = 0
        self._space = int(self._dot_size / 5.)
        self._orientation = 0
        self.level = 0
        self.custom_strategy = None
        self.strategies = [BEGINNER_STRATEGY, INTERMEDIATE_STRATEGY,
                           EXPERT_STRATEGY, self.custom_strategy]
        self.strategy = self.strategies[self.level]
        self._timeout_id = None

        # Generate the sprites we'll need...
        self._sprites = Sprites(self._canvas)
        self._dots = []
        for y in range(THIRTEEN):
            for x in range(THIRTEEN):
                xoffset = int((self._width - THIRTEEN * (self._dot_size + \
                                      self._space) - self._space) / 2.)
                if y % 2 == 1:
                    xoffset += int((self._dot_size + self._space) / 2.)
                if x == 0 or y == 0 or x == THIRTEEN - 1 or y == THIRTEEN - 1:
                    self._dots.append(
                        Sprite(self._sprites,
                               xoffset + x * (self._dot_size + self._space),
                               y * (self._dot_size + self._space),
                               self._new_dot('#B0B0B0')))
                else:
                    self._dots.append(
                        Sprite(self._sprites,
                               xoffset + x * (self._dot_size + self._space),
                               y * (self._dot_size + self._space),
                               self._new_dot(self._colors[FILL])))
                    self._dots[-1].type = False  # not set

        # Put a turtle at the center of the screen...
        self._turtle_images = []
        self._rotate_turtle(self._new_turtle())
        self._turtle = Sprite(self._sprites, 0, 0,
                              self._turtle_images[0])
        self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy())

        # ...and initialize.
        self._all_clear()

    def _move_turtle(self, pos):
        ''' Move turtle and add its offset '''
        self._turtle.move(pos)
        self._turtle.move_relative(
            (-self._turtle_offset, -self._turtle_offset))

    def _all_clear(self):
        ''' Things to reinitialize when starting up a new game. '''
        # Clear dots
        for dot in self._dots:
            if dot.type:
                dot.type = False
                dot.set_shape(self._new_dot(self._colors[FILL]))                
            dot.set_label('')

        # Recenter the turtle
        self._move_turtle(self._dots[int(THIRTEEN * THIRTEEN / 2)].get_xy())
        self._turtle.set_shape(self._turtle_images[0])
        self._set_label('')
        if self._timeout_id is not None:
            GObject.source_remove(self._timeout_id)

    def new_game(self, saved_state=None):
        ''' Start a new game. '''
        self._all_clear()

        # Fill in a few dots to start
        for i in range(15):
            n = int(uniform(0, THIRTEEN * THIRTEEN))
            if self._dots[n].type is not None:
                self._dots[n].type = True
                self._dots[n].set_shape(self._new_dot(self._colors[STROKE]))

        # Calculate the distances to the edge
        self._initialize_weights()
        self.strategy = self.strategies[self.level]

    def _set_label(self, string):
        ''' Set the label in the toolbar or the window frame. '''
        self._activity.status.set_label(string)

    def _button_press_cb(self, win, event):
        win.grab_focus()
        x, y = map(int, event.get_coords())

        spr = self._sprites.find_sprite((x, y), inverse=True)
        if spr == None:
            return

        if spr.type is not None and not spr.type:
            spr.type = True
            spr.set_shape(self._new_dot(self._colors[STROKE]))
            self._weights[self._dots.index(spr)] = 1000
            self._test_game_over(self._move_the_turtle())
        return True

    def _find_the_turtle(self):
        turtle_pos = self._turtle.get_xy()
        turtle_dot = None
        for dot in self._dots:
            pos = dot.get_xy()
            # Turtle is offset
            if pos[0] == turtle_pos[0] + self._turtle_offset and \
               pos[1] == turtle_pos[1] + self._turtle_offset:
                turtle_dot = self._dots.index(dot)
                break
        if turtle_dot is None:
            _logger.debug('Cannot find the turtle...')
            return None
        return turtle_dot

    def _move_the_turtle(self):
        ''' Move the turtle after each click '''
        self._turtle_dot = self._find_the_turtle()
        if self._turtle_dot is None:
            return

        # Given the col and row of the turtle, do something
        new_dot = self._grid_to_dot(
            self._my_strategy_import(self.strategy,
                                     self._dot_to_grid(self._turtle_dot)))
        self._move_turtle(self._dots[new_dot].get_xy())
        # And set the orientation
        self._turtle.set_shape(self._turtle_images[self._orientation])

        return new_dot

    def _test_game_over(self, new_dot):
        ''' Check to see if game is over '''
        if new_dot is None:
            return
        if self._dots[new_dot].type is None:
            # Game-over feedback
            self._once_around = False
            self._happy_turtle_dance()
            return True
        c = int(self._turtle_dot / THIRTEEN) % 2
        if self._dots[
            new_dot + CIRCLE[c][0][0] + THIRTEEN * CIRCLE[c][0][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][1][0] + THIRTEEN * CIRCLE[c][1][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][2][0] + THIRTEEN * CIRCLE[c][2][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][3][0] + THIRTEEN * CIRCLE[c][3][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][4][0] + THIRTEEN * CIRCLE[c][4][1]].type and \
           self._dots[
            new_dot + CIRCLE[c][5][0] + THIRTEEN * CIRCLE[c][5][1]].type:
           # Game-over feedback
           for dot in self._dots:
               dot.set_label(':)')
           return True
        return False

    def _grid_to_dot(self, pos):
        ''' calculate the dot index from a column and row in the grid '''
        return pos[0] + pos[1] * THIRTEEN

    def _dot_to_grid(self, dot):
        ''' calculate the grid column and row for a dot '''
        return [dot % THIRTEEN, int(dot / THIRTEEN)]

    def _happy_turtle_dance(self):
        ''' Turtle dances along the edge '''
        i = self._find_the_turtle()
        if i == 0: 
            if self._once_around:
                return
            else:
                self._once_around = True
        _logger.debug(i)
        x, y = self._dot_to_grid(i)
        if y == 0:
            x += 1
        if x == 0:
            y -= 1
        if x == THIRTEEN - 1:
            y += 1
        if y == THIRTEEN - 1:
            x -= 1
        i = self._grid_to_dot((x, y))
        self._dots[i].set_label(':)')
        self._move_turtle(self._dots[i].get_xy())
        self._orientation += 1
        self._orientation %= 6
        self._turtle.set_shape(self._turtle_images[self._orientation])
        self._timeout_id = GObject.timeout_add(250, self._happy_turtle_dance)

    def _ordered_weights(self, pos):
        ''' Returns the list of surrounding points sorted by their
        distance to the edge '''
        dots = self._surrounding_dots(pos)
        dots_and_weights = []
        for dot in dots:
            dots_and_weights.append((dot, self._weights[dot]))
        sorted_dots = sorted(dots_and_weights, key=lambda foo: foo[1])
        for i in range(6):
            dots[i] = sorted_dots[i][0]
        return dots

    def _daylight_ahead(self, pos):
        ''' Returns true if there is a straight path to the edge from
        the current position/orientation '''
        dots = self._surrounding_dots(pos)
        while True:
            dot_type = self._dots[dots[self._orientation]].type
            if dot_type is None:
                return True
            elif dot_type:
                return False
            else:  # keep looking
                pos = self._dot_to_grid(dots[self._orientation])
                dots = self._surrounding_dots(pos)

    def _surrounding_dots(self, pos):
        ''' Returns dots surrounding a position in the grid '''
        dots = []
        evenodd = pos[1] % 2
        for i in range(6):
            col = pos[0] + CIRCLE[evenodd][i][0]
            row = pos[1] + CIRCLE[evenodd][i][1]
            dots.append(self._grid_to_dot((col, row)))
        return dots

    def _initialize_weights(self):
        ''' How many steps to an edge? '''
        self._weights = []
        for d, dot in enumerate(self._dots):
            if dot.type is None:
                self._weights.append(0)
            elif dot.type:
                self._weights.append(1000)
            else:
                pos = self._dot_to_grid(d)
                pos2 = (THIRTEEN - pos[0], THIRTEEN - pos[1])
                self._weights.append(min(min(pos[0], pos2[0]),
                                         min(pos[1], pos2[1])))

    def _my_strategy_import(self, f, arg):
        ''' Run Python code passed as argument '''
        userdefined = {}
        try:
            exec f in globals(), userdefined
            return userdefined['_turtle_strategy'](self, arg)
        except ZeroDivisionError, e:
            self._set_label('Python zero-divide error: %s' % (str(e)))
        except ValueError, e:
            self._set_label('Python value error: %s' % (str(e)))
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()
Exemple #15
0
class Bar():
    ''' The Bar class is used to define the bars at the bottom of the
    screen '''

    def __init__(self, sprites, ball_size, colors=['#FFFFFF', '#AAAAAA']):
        ''' Initialize the 2-segment bar, labels, and mark '''
        self._sprites = sprites
        self._colors = colors[:]
        self.bars = {}

        self._width = Gdk.Screen.width()
        self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE
        self._scale = Gdk.Screen.height() / 900.0

        self._ball_size = ball_size

        self.make_bar(2)
        self._make_wedge_mark()

    def resize_all(self):
        self._width = Gdk.Screen.width()
        self._height = Gdk.Screen.height() - style.GRID_CELL_SIZE
        self._scale = Gdk.Screen.height() / 900.0

        for bar in self.bars.keys():
            self.bars[bar].hide()
        self.mark.hide()

        for bar in self.bars.keys():
            self.make_bar(bar)
        self._make_wedge_mark()

    def _make_wedge_mark(self):
        ''' Make a mark to show the fraction position on the bar. '''
        dx = self._ball_size / 2.
        n = (self._width - self._ball_size) / dx
        dy = (BAR_HEIGHT * self._scale) / n
        s = 3.5
        i = int(n / 2) - 1
        mark = svg_header(self._ball_size,
                          BAR_HEIGHT * self._scale + s, 1.0)
        mark += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                          s,
                          i * 2 * dy + s, (i * 2 + 1) * dy + s,
                          '#FF0000', '#FFFFFF')
        mark += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                          dx + s,
                          (i * 2 + 1) * dy + s, (i * 2 + 2) * dy + s,
                          '#FF0000', '#FFFFFF')
        mark += svg_footer()
        self.mark = Sprite(self._sprites, 0,
                           self._height,  # hide off bottom of screen
                           svg_str_to_pixbuf(mark))
        self.mark.set_layer(1)

    def _make_mark(self):
        ''' Make a mark to show the fraction position on the bar. '''
        mark = svg_header(self._ball_size / 2.,
                          BAR_HEIGHT * self._scale + 4, 1.0)
        mark += svg_rect(self._ball_size / 2.,
                         BAR_HEIGHT * self._scale + 4, 0, 0, 0, 0,
                         '#FF0000', '#FF0000')
        mark += svg_rect(1, BAR_HEIGHT * self._scale + 4, 0, 0,
                         self._ball_size / 4., 0, '#000000', '#000000')
        mark += svg_footer()
        self.mark = Sprite(self._sprites, 0,
                           self._height,  # hide off bottom of screen
                           svg_str_to_pixbuf(mark))
        self.mark.set_layer(1)

    def mark_width(self):
        return self.mark.rect[2]

    def bar_x(self):
        return self.bars[2].get_xy()[0]

    def bar_y(self):
        if self.bars[2].get_xy()[1] < 0:
            return self.bars[2].get_xy()[1] + 3000
        else:
            return self.bars[2].get_xy()[1]

    def width(self):
        return self.bars[2].rect[2]

    def height(self):
        return self.bars[2].rect[3]

    def show_bar(self, n):
        if n in self.bars:
            self.bars[n].move([self.bar_x(), self.bar_y()])

    def bump_bars(self, direction='up'):
        ''' when the toolbars expand and contract, we need to move the bar '''
        if direction == 'up':
            dy = -style.GRID_CELL_SIZE
        else:
            dy = style.GRID_CELL_SIZE
        for bar in self.bars:
            self.bars[bar].move_relative([0, dy])
        self.mark.move_relative([0, dy])

    def hide_bars(self):
        ''' Hide all of the bars '''
        for bar in self.bars:
            if self.bars[bar].get_xy()[1] > 0:
                self.bars[bar].move_relative([0, -3000])

    def get_bar(self, nsegments):
        ''' Return a bar with n segments '''
        if nsegments not in self.bars:
            self.make_bar(nsegments)
        return self.bars[nsegments]

    def make_bar(self, nsegments):
        return self._make_wedge_bar(nsegments)

    def _make_rect_bar(self, nsegments):
        ''' Create a bar with n segments '''
        svg = svg_header(self._width - self._ball_size,
                         BAR_HEIGHT * self._scale, 1.0)
        dx = self._width / float(nsegments)
        for i in range(int(nsegments) / 2):
            svg += svg_rect(dx, BAR_HEIGHT * self._scale, 0, 0,
                            i * 2 * dx, 0, self._colors[0], self._colors[0])
            svg += svg_rect(dx, BAR_HEIGHT * self._scale, 0, 0,
                            (i * 2 + 1) * dx, 0,
                            self._colors[1], self._colors[1])
        if int(nsegments) % 2 == 1:  # odd
            svg += svg_rect(dx, BAR_HEIGHT * self._scale, 0, 0,
                            (i * 2 + 2) * dx, 0, self._colors[0],
                            self._colors[0])
        svg += svg_footer()

        self.bars[nsegments] = Sprite(self._sprites, 0, 0,
                                      svg_str_to_pixbuf(svg))
        self.bars[nsegments].move(
            (0, self._height -
             int((self._ball_size + self._height()) / 2)))

    def _make_wedge_bar(self, nsegments):
        ''' Create a wedged-shaped bar with n segments '''
        s = 3.5  # add provision for stroke width
        svg = svg_header(self._width, BAR_HEIGHT * self._scale + s, 1.0)
        dx = self._width / float(nsegments)
        dy = (BAR_HEIGHT * self._scale) / float(nsegments)
        for i in range(int(nsegments) / 2):
            svg += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                             i * 2 * dx + s,
                             i * 2 * dy + s, (i * 2 + 1) * dy + s,
                             '#000000', '#FFFFFF')
            svg += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                             (i * 2 + 1) * dx + s,
                             (i * 2 + 1) * dy + s, (i * 2 + 2) * dy + s,
                             '#000000', '#FFFFFF')
        if int(nsegments) % 2 == 1:  # odd
            svg += svg_wedge(dx, BAR_HEIGHT * self._scale + s,
                             (i * 2 + 2) * dx + s,
                             (i * 2 + 2) * dy + s,
                             BAR_HEIGHT * self._scale + s,
                             '#000000', '#FFFFFF')
        svg += svg_footer()

        self.bars[nsegments] = Sprite(self._sprites, 0, 0,
                                      svg_str_to_pixbuf(svg))
        self.bars[nsegments].set_layer(2)
        self.bars[nsegments].set_label_attributes(18, horiz_align="left", i=0)
        self.bars[nsegments].set_label_attributes(18, horiz_align="right", i=1)
        self.bars[nsegments].set_label_color('black', i=0)
        self.bars[nsegments].set_label_color('white', i=1)
        self.bars[nsegments].set_label(' 0', i=0)
        self.bars[nsegments].set_label('1 ', i=1)
        self.bars[nsegments].move(
            (0, self._height - BAR_HEIGHT * self._scale))