diff options
-rw-r--r-- | TODO | 62 | ||||
-rw-r--r-- | balloongame.py | 94 | ||||
-rwxr-xr-x | lessonbuilder | 7 | ||||
-rwxr-xr-x | lessons/en_US/MAKELESSONS | 6 | ||||
-rw-r--r-- | lessons/en_US/badwords.txt | 2 | ||||
-rw-r--r-- | lessonscreen.py | 62 |
6 files changed, 130 insertions, 103 deletions
@@ -1,47 +1,38 @@ Typing Turtle +Notes + +How to handle capitals and punctuation? + +Are capitals and punctuation universal concepts? Certainly for all Roman languages. +Do other languages have capital-like and punctuation-like concepts with different rules? +Is there a generic way that punctuation could just be handled naturally? + It depends on how nicely we want them to work. For example knowing that you put quotes + around words, we can randomly enquote words. Knowing that semicolons, periods and + commas appear after words also allows us to randomly append them. + If we don't care, we can just suggest that a) punctuation be taught after the rest + of the alphabet, and b) native text be given which includes plenty of punctuation. +Is there a generic way that we can handle capitalization? + It would be nice to just be able to capitalize the beginning of any word. + OTOH, acronyms and stuff would be nice to include and those will only come from + wordlists. + +The answer to both: Right now, I think it's best to just encourage expansive word lists. +Note that this means that we have top *stop* stripping non-alpha characters from words +as we read them in! + +BTW, after all that work, when are we going to teach Enter? + First Release -- Write to Loser, Wes about developing artwork. -- Draw incorrect characters in red. -- Support backspace, backspace to previous lines. -- Scrolling TextView in lesson. -- Missing spaces at the end of some lines thanks to dodgy word wrap. -- Handle ends of line in a sane manner. -- Implement two step types: key learning and text copying. -- Split text into lines for long lessons. -- Try out an insensitive gtk.Entry instead of the gtk.Label. -- Better flow at the end of a level. Report the result on the Lesson screen: Need more work, Medal received, etc. -- Ability of lessons to list medals in other lessons as prerequisites. Disable unavailable lessons. -- Some sort of lesson sorting criteria. -- Split into file-per-screen. -- Scroll lessons list to the first non-medaled lesson at startup. Or just remember scroll position. + Status message on the main screen. "You unlocked a new lesson!" for example. Eventually have the turtle 'say' it. -- Implement a long text copying lesson and fix bugs in the scrolling and typing. + Graphical WPM and accuracy meters. -- WPM meter updated in 1sec timer in addition to on keypress. -- Working medals assignment: "You got a medal!" popup, display next to lesson. -- Nice looking keyboard. -- Highlighted keyboard keys when pressed. -- Support for displaying modifier keys in Keyboard. -- Change key shown when modified is held. -- Indicate next key to press on keyboard. -- Translate keyboard to native key layout. + Make medal WPM adjustable somehow? Perhaps a settable Goal WPM? + Highlight regions of keyboard, color by finger. -+ Artwork and animations. - + Speed meter picture? - + Accuracy meter picture? + Sound effects. + Welcome to the activity sound. + Speed up / slow down sounds when WPM crosses threshold: Slow, Medium, Fast. + Medal award sounds for each medal type: None, Bronze, Silver, Gold. Applause sound. + Incorrect key pressed tick sound. -+ Develop lessons. - - Continue to develop lessons for all keys on the keyboard. - - Develop 'focus' lessons e.g. fj. - - Mark some lessons as "locational" versus "textual" and translate from the English keyboard to native. Ex: Home row, Left hand, Numbers, etc. - - Give each lesson criteria for each medal type based on Accuracy, WPM. -- Automatically generate lessons similar to 'home row' based on a list of keys. Future Release + Goal support with progress reporting. WPM, Accuracy, Entire keyboard learned, etc. @@ -52,13 +43,6 @@ Future Release Balloon Game -- Create BalloonGameScreen class (use gtk.Layout?). -- Generate a list of random words, or read from lesson dictionary. -- Score display. -- Floating balloons with words on them. Random velocities, "floaty" look. -- Balloon letters disappear when typed. -- Balloon pops when word typed, score increased. -- Rate of balloons increases over time. + Game finished popup, displays score and medal text. + Fix flickering + Improve graphics. diff --git a/balloongame.py b/balloongame.py index 2550a0b..65ea6f8 100644 --- a/balloongame.py +++ b/balloongame.py @@ -23,9 +23,8 @@ import gobject, pygtk, gtk, pango # parameters about them. BALLOON_STAGES = [ { 'count': 10, 'delay': 80 }, -# { 'count': 20, 'delay': 40 }, -# { 'count': 80, 'delay': 20 }, -# { 'count': 100, 'delay': 10 }, + { 'count': 20, 'delay': 60 }, + { 'count': 70, 'delay': 40 }, ] class Balloon: @@ -35,7 +34,8 @@ class Balloon: self.vx = vx self.vy = vy self.word = word - + self.size = 100 + class BalloonGame(gtk.VBox): def __init__(self, lesson, activity): gtk.VBox.__init__(self) @@ -84,17 +84,17 @@ class BalloonGame(gtk.VBox): self.finished = False # Start the animation loop running. - self.update_timer = gobject.timeout_add(20, self.tick) + self.update_timer = gobject.timeout_add(20, self.tick, priority=gobject.PRIORITY_HIGH_IDLE+30) def realize_cb(self, widget): self.activity.add_events(gtk.gdk.KEY_PRESS_MASK) self.key_press_cb_id = self.activity.connect('key-press-event', self.key_cb) # Clear the mouse cursor. - #pixmap = gtk.gdk.Pixmap(self.activity.window, 1, 1) + #pixmap = gtk.gdk.Pixmap(widget.window, 10, 10) #color = gtk.gdk.Color() - #cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 0, 0) - #self.area.window.set_cursor(cursor) + #cursor = gtk.gdk.Cursor(pixmap, pixmap, color, color, 5, 5) + #widget.window.set_cursor(cursor) def unrealize_cb(self, widget): self.activity.disconnect(self.key_press_cb_id) @@ -124,16 +124,22 @@ class BalloonGame(gtk.VBox): for b in self.balloons: if b.word[0] == key: b.word = b.word[1:] - self.score += 10 + self.add_score(10) # Pop the balloon if it's been typed. if len(b.word) == 0: self.balloons.remove(b) - self.score += 100 + self.add_score(100) + + self.queue_draw_balloon(b) + + break return False def update_balloon(self, b): + self.queue_draw_balloon(b) + b.x += b.vx b.y += b.vy @@ -142,6 +148,8 @@ class BalloonGame(gtk.VBox): if b.y < -100: self.balloons.remove(b) + + self.queue_draw_balloon(b) def tick(self): if self.finished: @@ -170,7 +178,7 @@ class BalloonGame(gtk.VBox): y = self.bounds.height + 100 vx = random.uniform(-2, 2) - vy = random.uniform(-5, -3) + vy = -3 #random.uniform(-5, -3) b = Balloon(x, y, vx, vy, word) self.balloons.append(b) @@ -180,10 +188,8 @@ class BalloonGame(gtk.VBox): if len(self.balloons) == 0 and self.stage_idx >= len(BALLOON_STAGES): self.finished = True + self.queue_draw() - #self.queue_draw() - self.draw() - return True def draw_results(self, gc): @@ -210,13 +216,20 @@ class BalloonGame(gtk.VBox): ty = y+h/2-(size[1]/pango.SCALE)/2 self.area.window.draw_layout(gc, tx, ty, layout) + def queue_draw_balloon(self, b): + x1 = int(b.x - b.size/2) + y1 = int(b.y - b.size/2) + x2 = int(b.x + b.size/2) + y2 = int(b.y + b.size/2) + self.queue_draw_area(x1, y1, x2, y2) + def draw_balloon(self, gc, b): x = int(b.x) y = int(b.y) # Draw the balloon. gc.foreground = self.area.get_colormap().alloc_color(65535,0,0) - self.area.window.draw_arc(gc, True, x-50, y-50, 100, 100, 0, 360*64) + self.area.window.draw_arc(gc, True, x-b.size/2, y-b.size/2, b.size, b.size, 0, 360*64) # Draw the text. gc.foreground = self.area.get_colormap().alloc_color(0,0,0) @@ -225,6 +238,37 @@ class BalloonGame(gtk.VBox): tx = x-(size[0]/pango.SCALE)/2 ty = y-(size[1]/pango.SCALE)/2 self.area.window.draw_layout(gc, tx, ty, layout) + + def add_score(self, num): + self.score += num + self.queue_draw_score() + + def queue_draw_score(self): + layout = self.area.create_pango_layout(_('SCORE: %d') % self.score) + layout.set_font_description(pango.FontDescription('Times 14')) + size = layout.get_size() + x = self.bounds.width-20-size[0]/pango.SCALE + y = 20 + self.queue_draw_area(x, y, x+size[0], y+size[1]) + + def draw_score(self, gc): + layout = self.area.create_pango_layout(_('SCORE: %d') % self.score) + layout.set_font_description(pango.FontDescription('Times 14')) + size = layout.get_size() + x = self.bounds.width-20-size[0]/pango.SCALE + y = 20 + self.area.window.draw_layout(gc, x, y, layout) + + def draw_instructions(self, gc): + # Draw instructions. + gc.foreground = self.area.get_colormap().alloc_color(0,0,0) + + layout = self.area.create_pango_layout(_('Type the words to pop the balloons!')) + layout.set_font_description(pango.FontDescription('Times 14')) + size = layout.get_size() + x = (self.bounds.width - size[0]/pango.SCALE)/2 + y = self.bounds.height-20 - size[1]/pango.SCALE + self.area.window.draw_layout(gc, x, y, layout) def draw(self): self.bounds = self.area.get_allocation() @@ -243,23 +287,9 @@ class BalloonGame(gtk.VBox): self.draw_results(gc) else: - # Draw instructions. - gc.foreground = self.area.get_colormap().alloc_color(0,0,0) - - layout = self.area.create_pango_layout(_('Type the words to pop the balloons!')) - layout.set_font_description(pango.FontDescription('Times 14')) - size = layout.get_size() - x = (self.bounds.width - size[0]/pango.SCALE)/2 - y = self.bounds.height-20 - size[1]/pango.SCALE - self.area.window.draw_layout(gc, x, y, layout) - - # Draw score. - layout = self.area.create_pango_layout(_('SCORE: %d') % self.score) - layout.set_font_description(pango.FontDescription('Times 14')) - size = layout.get_size() - x = self.bounds.width-20-size[0]/pango.SCALE - y = 20 - self.area.window.draw_layout(gc, x, y, layout) + self.draw_instructions(gc) + + self.draw_score(gc) def expose_cb(self, area, event): self.draw() diff --git a/lessonbuilder b/lessonbuilder index df35fa1..83cc40b 100755 --- a/lessonbuilder +++ b/lessonbuilder @@ -202,8 +202,9 @@ def filter_pairs(pairs, required_keys, keys): return good_pairs def get_weighted_random_pair(pairs): - # TODO: I'm currently ignoring the weighting because it's preventing keys from - # ever appearing, for example j never appears in the home row lesson. + # TODO: I'm currently ignoring the weighting because it's preventing certain keys + # from ever appearing due to their unpopularity, for example j never appears in the + # home row lesson. return random.choice(pairs) #n = random.uniform(0, 1) #for p in pairs: @@ -387,7 +388,7 @@ def build_intro_steps(): steps = [] text = '' - text += _('Hihowareyah! Ready to learn the secret of fast typing?\n') + text += _('Hihowahyah! Ready to learn the secret of fast typing?\n') text += _('Always use the correct finger to press each key!\n\n') text += _('Now, place your hands on the keyboard just like the picture below.\n') text += _('When you\'re ready, press the SPACE bar with your thumb!') diff --git a/lessons/en_US/MAKELESSONS b/lessons/en_US/MAKELESSONS index c5d4718..b5b8a23 100755 --- a/lessons/en_US/MAKELESSONS +++ b/lessons/en_US/MAKELESSONS @@ -12,6 +12,7 @@ --desc="This lesson teaches you the a, s, d, f, g, h, j, k and l keys \nin the middle of the keyboard.\nThese keys are called the Home Row." \ --keys="asdfghjkl" \ --wordlist=2of12.txt \ + --badwordlist=badwords.txt \ --order=1 \ --output=homerow.lesson @@ -21,6 +22,7 @@ --keys="asdfghjkl" \ --game='balloon' \ --wordlist=2of12.txt \ + --badwordlist=badwords.txt \ --order=2 \ --output=homerowballoon.lesson @@ -29,6 +31,7 @@ --desc="This lesson teaches you the q, w, e, r, t, y, u, i, o and p keys \non the top row of the keyboard." \ --keys="qwertyuiop" --base-keys="asdfghjkl" \ --wordlist=2of12.txt \ + --badwordlist=badwords.txt \ --order=3 \ --output=toprow.lesson @@ -37,6 +40,7 @@ --desc="This lesson teaches you the z, x, c, v, b, n and m keys \non the bottom row of the keyboard." \ --keys="zxcvbnm" --base-keys="asdfghjklqwertyuiop" \ --wordlist=2of12.txt \ + --badwordlist=badwords.txt \ --order=4 \ --output=bottomrow.lesson @@ -47,6 +51,7 @@ --keys="QWERTASDFGZXCVB" \ --base-keys="abcdefghijklmnopqrstuvwxyz" \ --wordlist=2of12.txt \ + --badwordlist=badwords.txt \ --order=5 \ --output=leftcapital.lesson @@ -56,5 +61,6 @@ --keys="YUIOPHJKLBNM" \ --base-keys="abcdefghijklmnopqrstuvwxyzQWERTASDFGZXCVB" \ --wordlist=2of12.txt \ + --badwordlist=badwords.txt \ --order=6 \ --output=rightcapital.lesson diff --git a/lessons/en_US/badwords.txt b/lessons/en_US/badwords.txt index 2a549a7..ba09943 100644 --- a/lessons/en_US/badwords.txt +++ b/lessons/en_US/badwords.txt @@ -1,2 +1,4 @@ coitus ass +re +ll diff --git a/lessonscreen.py b/lessonscreen.py index 7facc55..2fb9435 100644 --- a/lessonscreen.py +++ b/lessonscreen.py @@ -112,7 +112,6 @@ class LessonScreen(gtk.VBox): self.lessontext.set_right_margin(20) self.lessontext.set_wrap_mode(gtk.WRAP_WORD) self.lessontext.modify_base(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffffcc')) - #self.lessontext.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffff80')) self.lessonscroll = gtk.ScrolledWindow() self.lessonscroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS) @@ -134,17 +133,11 @@ class LessonScreen(gtk.VBox): self.connect('unrealize', self.unrealize_cb) self.show_all() - - # Load hand overlay SVGs. - bundle_path = sugar.activity.activity.get_bundle_path() - #for o in KEY_OVERLAYS.keys(): - # pass + + self.timer_id = None self.begin_lesson() - # Initialize stats update timer. - gobject.timeout_add(1000, self.timer_cb) - def realize_cb(self, widget): self.activity.add_events(gtk.gdk.KEY_PRESS_MASK|gtk.gdk.KEY_RELEASE_MASK) self.key_press_cb_id = self.activity.connect('key-press-event', self.key_cb) @@ -153,33 +146,41 @@ class LessonScreen(gtk.VBox): def unrealize_cb(self, widget): self.activity.disconnect(self.key_press_cb_id) self.activity.disconnect(self.key_release_cb_id) - + + def start_timer(self): + self.start_time = time.time() + self.timer_id = gobject.timeout_add(1000, self.timer_cb) + + def stop_timer(self): + if self.timer_id: + gobject.source_remove(self.timer_id) + self.start_time = None + self.timer_id = None + + def timer_cb(self): + self.update_stats() + return True + def update_stats(self): if self.lesson_finished: return if self.start_time: - self.total_time = time.time() - self.start_time - - if self.total_time >= 1.0: - self.wpm = 60 * (self.correct_keys / 5) / self.total_time - else: - self.wpm = 1.0 - + self.total_time += time.time() - self.start_time + self.start_time = time.time() + + if self.total_time >= 1.0: + self.wpm = 60 * (self.correct_keys / 5) / self.total_time self.wpmlabel.set_markup(_('<b>WPM:</b> %(wpm)d') % { 'wpm': int(self.wpm) } ) + else: - self.total_time = 0.0 - self.wpm = 100.0 - + self.wpm = 1.0 + if self.total_keys: self.accuracy = 100.0 * self.correct_keys / self.total_keys self.accuracylabel.set_markup(_('<b>Accuracy:</b> %(accuracy)d%%') % { 'accuracy' : int(self.accuracy) } ) - def timer_cb(self): - self.update_stats() - return True - def begin_lesson(self): self.lesson_finished = False @@ -190,7 +191,8 @@ class LessonScreen(gtk.VBox): self.incorrect_keys = 0 self.start_time = None - + self.total_time = 0 + self.step = None self.next_step_idx = 0 @@ -216,6 +218,9 @@ class LessonScreen(gtk.VBox): return new_lines def advance_step(self): + # Stop the WPM timer. + self.stop_timer() + # Clear step related variables. self.step = None @@ -324,7 +329,6 @@ class LessonScreen(gtk.VBox): self.line_idx = 0 self.begin_line() - def begin_line(self): self.line = self.lines[self.line_idx] self.line_mark = self.line_marks[self.line_idx] @@ -373,9 +377,9 @@ class LessonScreen(gtk.VBox): self.total_keys += 1 elif self.mode == 'text': - # Timer starts with first text mode keypress. - if not self.start_time: - self.start_time = time.time() + # WPM timer starts with first text mode keypress. + if not self.timer_id: + self.start_timer() # Handle backspace by deleting text and optionally moving up lines. if key_name == 'BackSpace': |