diff options
author | Philip Withnall <philip@tecnocode.co.uk> | 2013-08-18 19:43:15 (GMT) |
---|---|---|
committer | Philip Withnall <philip@tecnocode.co.uk> | 2013-08-18 19:43:15 (GMT) |
commit | 1f1c8f55b7cbee6ccb4c2588f62239d72718536f (patch) | |
tree | 9361aaafd2791c9ad40ff2476efa5015147500f5 /PascalTriangle.activity/pascaltriangle.py | |
parent | 543f746a20340d7355b1b74c993c2262baac4c9c (diff) |
Add support for entering new values in cells
This makes the game almost functionally complete.
Diffstat (limited to 'PascalTriangle.activity/pascaltriangle.py')
-rwxr-xr-x | PascalTriangle.activity/pascaltriangle.py | 135 |
1 files changed, 125 insertions, 10 deletions
diff --git a/PascalTriangle.activity/pascaltriangle.py b/PascalTriangle.activity/pascaltriangle.py index 48441fc..fa4782e 100755 --- a/PascalTriangle.activity/pascaltriangle.py +++ b/PascalTriangle.activity/pascaltriangle.py @@ -1,6 +1,6 @@ from sugar3.activity import activity from sugar3.graphics.toolbarbox import ToolbarBox -import math +import math, random from gi.repository import Gtk, Gdk import cairo @@ -15,14 +15,19 @@ class PascalTriangleActivity(activity.Activity): # Create a new GTK+ drawing area drawing_area = Gtk.DrawingArea() - drawing_area.add_events(Gdk.EventMask.BUTTON_PRESS_MASK) + drawing_area.add_events(Gdk.EventMask.BUTTON_PRESS_MASK | \ + Gdk.EventMask.KEY_PRESS_MASK) + drawing_area.set_can_focus(True) drawing_area.connect('button-press-event', self._drawing_area_button_press_cb, None) + drawing_area.connect('key-press-event', + self._drawing_area_key_press_cb, None) drawing_area.connect('draw', self._drawing_area_draw_cb, None) # Parent and show the drawing area. self.set_canvas(drawing_area) drawing_area.show() + drawing_area.grab_focus() # Set the initial size of the Pascal triangle to be drawn. This is # the number of cells on its base (equivalently, the number of rows in @@ -35,6 +40,34 @@ class PascalTriangleActivity(activity.Activity): # Set the currently selected cell (which the user's clicked on). # Default to no selection (-1, -1). self._current_cell = (-1, -1) + self._current_cell_text = '' + + # Generate a list of blank cells which the user needs to fill in. + self._blank_cells = self._generate_blank_cell_list() + + + """ + Calculate the number of cells in the triangle. This is the Nth triangle + number, where N is the triangle_size. The formula for this is 1/2*N*(N+1). + """ + def _calculate_number_of_cells(self): + return self._triangle_size * (self._triangle_size + 1) / 2 + + + def _generate_blank_cell_list(self): + blank_cells = [] + + # Generate a number of coordinates for blank cells, between 1 cell and + # the entire triangle. + num_blanks = random.randint(1, self._calculate_number_of_cells()) + for _ in range(num_blanks): + row_index = random.randint(0, self._triangle_size - 1) + column_index = random.randint(0, row_index) + blank_cells.append((row_index, column_index)) + + # Remove duplicates from the list. We're guaranteed to have a non-empty + # list after this. + return list(set(blank_cells)) """ @@ -79,16 +112,67 @@ class PascalTriangleActivity(activity.Activity): # Found the cell. Update the current cell and queue a # redraw. self._current_cell = (row_index, column_index) + self._current_cell_text = '' widget.queue_draw() + return True # No cell found? Clear the current cell and queue a redraw. self._current_cell = (-1, -1) + self._current_cell_text = '' widget.queue_draw() return True + def _drawing_area_key_press_cb(self, widget, event, data = None): + if event.type != Gdk.EventType.KEY_PRESS: + return False + + # Give up if any modifiers are set. + if event.state != 0: + return True + + digit_keyvals = [ + Gdk.KEY_0, + Gdk.KEY_1, + Gdk.KEY_2, + Gdk.KEY_3, + Gdk.KEY_4, + Gdk.KEY_5, + Gdk.KEY_6, + Gdk.KEY_7, + Gdk.KEY_8, + Gdk.KEY_9, + ] + control_keyvals = [ + # Only backspace is supported at the moment. + Gdk.KEY_BackSpace, + ] + + # Handle digit presses. Note we don't currently support infix editing + # and we clamp to 2 digits. + if event.keyval in digit_keyvals: + if len(self._current_cell_text) < 2: + digit = digit_keyvals.index(event.keyval) + self._current_cell_text += '%i' % digit + widget.queue_draw() + + # Check whether the answer is correct. + self._check_current_cell_text(widget) + + return True + # Otherwise, handle the control character + elif event.keyval in control_keyvals: + if event.keyval == Gdk.KEY_BackSpace: + self._current_cell_text = self._current_cell_text[:-1] + widget.queue_draw() + return True + + # If the key pressed wasn't a digit or control character, ignore it. + return True + + def _drawing_area_draw_cb(self, widget, ctx, data = None): # Widget allocation and sizes. The cell_height is calculated weirdly # because the cells interlock as they tesselate; so for 2 rows, the @@ -156,12 +240,43 @@ class PascalTriangleActivity(activity.Activity): ctx.set_source(self._get_cell_background(row_index, column_index)) ctx.fill() - # Write its number. - cell_number = self._calculate_pascal_number(row_index, - column_index) - extents = ctx.text_extents(str(cell_number)) - ctx.move_to(centre[0] - extents[2] / 2.0, centre[1] + extents[3] / 2.0) + # Write its number if it's a non-empty cell. If it's an empty cell, + # write a question mark unless it's the selected cell. + cell_text = None + if not (row_index, column_index) in self._blank_cells: + cell_text = str(self._calculate_pascal_number(row_index, + column_index)) + ctx.set_source_rgb(0.0, 0.0, 0.0) # black + elif (row_index, column_index) != self._current_cell: + cell_text = '?' + ctx.set_source_rgb(0.4, 0.4, 0.4) # grey + else: + cell_text = self._current_cell_text + ctx.set_source_rgb(1.0, 0.0, 0.0) # red + + if cell_text != None: + extents = ctx.text_extents(cell_text) + ctx.move_to(centre[0] - extents[2] / 2.0, centre[1] + extents[3] / 2.0) + ctx.set_font_size(50) + ctx.show_text(cell_text) - ctx.set_font_size(50) - ctx.set_source_rgb(0.0, 0.0, 0.0) - ctx.show_text(str(cell_number)) + + """ + Check whether the user-entered text for the current cell matches the + expected value. If so, also check to see if the user's filled out all blank + cells and hence has won. + """ + def _check_current_cell_text(self, widget): + # Check whether the answer is correct. If so, change the cell to be + # uneditable. + expected_num = self._calculate_pascal_number(self._current_cell[0], + self._current_cell[1]) + if int(self._current_cell_text) == expected_num: + self._blank_cells.remove(self._current_cell) + self._current_cell = (-1, -1) + self._current_cell_text = '' + widget.queue_draw() + + # Check whether all blank cells have been filled. + if len(self._blank_cells) == 0: + print('Well done!') |