Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/PascalTriangle.activity/pascaltriangle.py
diff options
context:
space:
mode:
authorPhilip 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)
commit1f1c8f55b7cbee6ccb4c2588f62239d72718536f (patch)
tree9361aaafd2791c9ad40ff2476efa5015147500f5 /PascalTriangle.activity/pascaltriangle.py
parent543f746a20340d7355b1b74c993c2262baac4c9c (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-xPascalTriangle.activity/pascaltriangle.py135
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!')