# Currently # * Displaying Categories in set language, # * Pick English Questions # To Do # change to update Leitner info # display questions based on box # fix accessed wav # structure # class CurrentQuestion # class Answer # class Questions # functions # show_answer # finished # startgame # ask_subcat # ask_category # display_points # click_on_ # next_question # next_question # spot_found # spot_not_found # load import pygame from pygame import * from sugar.activity import activity import os import random from layout import * from frontend import hex2rgb import time import threading __PLUGIN_NAME__ = 'single player' #set up paths to for adding images and sounds DATAPATH = os.path.join(activity.get_activity_root(), "data") ACTIVITYPATH = activity.get_bundle_path() IMAGEPATH = os.path.join(DATAPATH, 'image') SOUNDPATH = os.path.join(DATAPATH, 'sound') ICONPATH = os.path.join(ACTIVITYPATH, 'images') CORRECT_SOUNDS = ['i-like-it.wav', 'ooh-yeah.wav', 'impressive.wav', 'dramatic_chord.wav', 'sweet.wav', 'brain-yes.wav', 'mk-excellent.wav', 'that-was-cool.wav', 'bt-excellent.wav', 'groovy.wav', 'yes-i-like-it.wav', 'burns-excellent.wav', 'oh-yeah.wav'] WRONG_SOUNDS = ['db_forgetaboutit.wav', 'alf_wrong.wav', 'doh.wav', 'sorry.wav', 'awh_man.wav', 'metal_clang_2.wav', 'excuse_me.wav', 'negative.wav', 'bunny_awful.wav', 'gwarsh.wav', 'not.wav', 'haha.wav', 'oh_no.wav', 'compute.wav', 'hoo-ah.wav'] clock = pygame.time.Clock() class CurrentQuestion: id = 0 prompt = u'' response = u'' imgfn = u'' sndfn = u'' map = u'' answer_link = u'' class Imagequiz_question: id = 0 map = u'' cat = 0 subcat = 0 text = u'' answer = u'' answer_link = u'' class Answer: display = True display_line = True sound_enabled = True link = u'' text = u'' img_found_count = 0 img_notfound_count = 0 imgfn_found = ["Emotes/face-grin.png"] imgfn_notfound = ["Emotes/face-devil-grin.png"] found_straight = 0 straight_count = 0 icon_left = 0 bg_icons = pygame.Surface((500, 32)) bg_icons_straight = pygame.Surface((500, 32)) def __init__(self): self.sound_found = sf.sound_load("accessed.wav") self.sound_notfound = sf.sound_load("sorry.wav") def reset_points(self): self.found_straight = 0 self.straight_count = 0 self.icon_left = 0 self.bg_icons.fill(hex2rgb(Layout().Toolbar.background)) self.bg_icons_straight.fill(hex2rgb(Layout().Toolbar.background)) sf.display_surface(self.bg_icons, (540, 10), "toolbar") sf.display_surface(self.bg_icons_straight, (538, 50), "toolbar") sf.refresh() def add_straight(self): # 5 in a row -> special reward self.straight_count += 1 im, im_rect = sf.image_load("images/%s" % "Icons/bulb.png") self.bg_icons_straight.blit(im, ((self.straight_count-1) * (im_rect[2] + 4), 0)) sf.display_surface(self.bg_icons_straight, (538, 50), "toolbar") sf.refresh() def display_icon(self, icon_name): if icon_name == "found": self.found_straight += 1 if self.found_straight == 5: # Found Straight 5! self.bg_icons.fill(hex2rgb(Layout().Toolbar.background)) sf.display_surface(self.bg_icons, (540, 10), "toolbar") self.add_straight() self.icon_left = 0 self.found_straight = 1 fn = self.imgfn_found[self.img_found_count % len(self.imgfn_found)] self.img_found_count += 1 elif icon_name == "not-found": self.found_straight = 0 fn = self.imgfn_notfound[self.img_notfound_count % len(self.imgfn_notfound)] self.img_notfound_count += 1 img, img_rect = sf.image_load("images/%s" % fn) self.bg_icons.blit(img, (self.icon_left, 0)) sf.display_surface(self.bg_icons, (540, 10), "toolbar") sf.refresh() self.icon_left += img_rect[2] + 4 class PlaySoundThread (threading.Thread): def set(self, sound, interval): self.sound = sound self.i = interval def run(self): time.sleep(self.i) self.sound.play() def play_sound(self, i): if self.sound_enabled: t = self.PlaySoundThread() if i == 1: t.set(self.sound_found, 0) else: t.set(self.sound_notfound, 0.5) t.start() def display_answer(self): if self.display_line: sf.draw_lines() if self.display == False: return False # Get Top Right Spot of list map_arr = Q.question.map.split(", ") # Widths & Heights bubble_width = 400 bubble_height = 300 textfield_width = 270 textfield_height = 200 # Extremas of the Polygone will be saved in here: x_max = 0 y_max = 0 x_min = 1000 y_min = 1000 # Extract Extremas from the polygon o = [] a = "x" for s in map_arr: if a == "x": a = s else: # Take care of border pixels if int(a) > x_max: x_max = int(a) if int(s) > y_max: y_max = int(s) if int(a) < x_min: x_min = int(a) if int(s) < y_min: y_min = int(s) a = "x" # Set x and y for the Answer Bubble y_med = (y_min + y_max) / 2 x_max -= 5 x_max += Layout().Question.x + Layout().Question.Image.x + 2 y_med += Layout().Question.y + Layout().Question.Image.y + 2 # sf.draw_polygon() # Draw Answer Bubble Image & Text im, im_rect = sf.image_load('images/bubble.gif') text_arr = self.text.split(' ') cur_line = '' cur_x = 0 cur_y = 0 # 'textfield' contains the answer text textfield = pygame.Surface((textfield_width, textfield_height)) textfield.fill((255, 255, 255)) # Make line breaks after reaching width of 'textfield' for t in text_arr: cur_line = "%s %s" % (cur_line, t) font = pygame.font.Font(None, 38) n_text = font.render(cur_line, 1, (0,0,0)) textpos = n_text.get_rect() # print cur_line, x,y,w,h = list(textpos) if w > (textfield_width): textfield.blit(text, (cur_x, cur_y)) cur_line = t cur_y += 30 written = 1 else: written = 0 text = n_text # print textpos # Draw leftovers on textfield if written == 0: textfield.blit(n_text, (cur_x, cur_y)) else: font = pygame.font.Font(None, 38) n_text = font.render(cur_line, 1, (0,0,0)) textfield.blit(n_text, (cur_x, cur_y)) # print "draw" # Draw on Screen sf.display_surface(im, (x_max, y_med)) sf.display_surface(textfield, (x_max+25, y_med+20)) pygame.display.update() class Questions: def __init__(self): in_question = False played = 0 count_won = 0 count_lost = 0 id = u'' questions = [] question = CurrentQuestion() def question_pick(self): # Check if Questions are left to play if self.played >= len(self.questions): # Game / Cat Finished! return False # Okay, Next One! self.question = self.questions[self.played] self.id = self.question.id A.text = self.question.response A.link = self.question.answer_link self.played += 1 return True def load_questions(self, cat_id): self._cat_id = cat_id self.played = 0 self.count_won = 0 self.count_lost = 0 q = 'SELECT text FROM categories WHERE id = %i' % cat_id res = __SERVICES__.db.query(q) Q.id = res[0][0] #get list of questions (by question_id) try: q = "SELECT question_id FROM quizlink WHERE quiz_id = %i" % cat_id questionlist = __SERVICES__.db.query(q) except: questionlist = [] #get actual questions self.questions = [] for questionid in questionlist: q = 'SELECT * from questions WHERE id = %i' % questionid[0] res = __SERVICES__.db.query(q) question = CurrentQuestion() question.id = res[0][0] question.prompt = res[0][1] question.response = res[0][2] question.imgfn = res[0][3] question.sndfn = res[0][4] question.map = res[0][5] question.answer_link=res[0][6] self.questions.append(question) #present questions randomly random.shuffle(self.questions) def show_answer(): A.display_answer() Q.in_question = True next_question() def finished(): sf.clear_text_items() sf.clear_question_frame(True) sf.refresh() sf.add_text_item("You have finished this category!", (180, 80)) sf.add_text_item("Won: %i" % Q.count_won, (220, 120)) sf.add_text_item("Lost: %i" % Q.count_lost, (220, 160)) pygame.display.update() # ask_category(130) def display_points(): format = TextFormat(None, 30) max_q = len(Q.questions) # sf.display_tooltip("%i" % (Q.count_won), (480, 14), format) # sf.display_tooltip("%i" % Q.count_lost, (480, 42), format) if max_q == 0: sf.display_line(0, True) sf.display_line(0, False) else: sf.display_line(100 * Q.count_won / max_q, True) sf.display_line(100 * Q.count_lost / max_q, False) sf.add_text_item("this is the score " + str(Q.count_won * 10), (600,0)) pygame.display.update() def self_test(): #show_answer() #show two clickable items: smiley face, sad face image_right, xy = sf.image_load("images/Emotes/face-grin.png") sf.display_surface(image_right, (750,100)) image_wrong, xy = sf.image_load("images/Emotes/face-devil-grin.png") sf.display_surface(image_wrong, (750,100)) pygame.display.update() #this should provide for click on image_right or image_wrong response = True return response def display_images(): global images candidate_images = [] for question in Q.questions: candidate_images.append(question.imgfn) random.shuffle(candidate_images) if Q.question.imgfn in candidate_images[:4]: images = candidate_images[:4] else: images = candidate_images[:3] images.append(Q.question.imgfn) random.shuffle(images) image_tl, xy = sf.image_load(images[0], path = IMAGEPATH) image_tr, xy = sf.image_load(images[1], path = IMAGEPATH) image_ll, xy = sf.image_load(images[2], path = IMAGEPATH) image_lr, xy = sf.image_load(images[3], path = IMAGEPATH) sf.display_surface(image_tl, (250, 150)) sf.display_surface(image_tr, (520, 150)) sf.display_surface(image_ll, (250, 420)) sf.display_surface(image_lr, (520, 420)) pygame.display.update def play_query_again(): global query if Q.in_question: #print 'play_query' query.play() while pygame.mixer.get_busy(): clock.tick(30) def play_sound(fn): global query query = sf.sound_load(Q.question.sndfn, path=SOUNDPATH) image_play, xy = sf.image_load("play_button.png", path = ICONPATH) sf.display_surface(image_play, (300,30)) pygame.display.update() query.play() while pygame.mixer.get_busy(): clock.tick(30) def picture_tl(): global images if Q.in_question: if images[0] == Q.question.imgfn: response = True else: response = False delete_reacts(response) def picture_tr(): global images if Q.in_question: if images[1] == Q.question.imgfn: response = True else: response = False delete_reacts(response) def picture_ll(): global images if Q.in_question: if images[2] == Q.question.imgfn: response = True else: response = False delete_reacts(response) def picture_lr(): global images if Q.in_question: if images[3] == Q.question.imgfn: response = True else: response = False delete_reacts(response) def delete_reacts(response): if response: correct_answer() else: wrong_answer() return True def play_correct_response_sound(): good = sf.sound_load(random.choice(CORRECT_SOUNDS)) good.play() while pygame.mixer.get_busy(): pygame.time.wait(30) def play_wrong_response_sound(): good = sf.sound_load(random.choice(WRONG_SOUNDS)) good.play() while pygame.mixer.get_busy(): pygame.time.wait(30) def update_leitner_attributes(): #update Leitner attributes #but need to get from db when loading questions #need to save in db before next_question #and update attributes actually in db #finally display 'boxes' and 'points' try: print 'update Leitner attributes' think = '%0.2f' % float( time.time() - Q.starttime) Q.time = think Q.count_found += 1 if Q.box < 5: Q.box += 1 Q.date = sys.date() except: print 'Leitner update failed' #now update database try: q = "UPDATE questions SET time = Q.time, count_found = Q.count_found, count_unfound = Q.count_unfound, box = Q.box, time = Q.time, date = Q.date WHERE id = Q.id" res = __SERVICES__.db.query(q) except: print 'Leitner db update failed' def correct_answer(next=True): if Q.in_question: A.display_icon("found") #A.play_sound(1) Q.in_question = False Q.count_won += 1 display_points() try: play_correct_response_sound() except: print 'play_correct_response_sound failed' update_leitner_attributes() if next: next_question() else: show_answer() def wrong_answer(next=True): if Q.in_question: A.display_icon("not-found") Q.in_question = False Q.count_lost += 1 display_points() play_wrong_response_sound() update_leitner_attributes() if next: next_question() else: show_answer() def getpossibles(teststr): #there may be multiple correct answers separated by a '/' lst = [] test = '' for i in range(len(teststr)): if teststr[i] != '/': test = test + teststr[i] else: lst.append(test) test = '' if len(test) > 0: lst.append(test) return lst def checkanswer(response): possibles = getpossibles(Q.question.response) return response.strip() in possibles def startgame(db_cat_id): Q.count_won = 0 A.reset_points() Q.load_questions(db_cat_id) display_points() sf.clear_text_items() next_question() def next_question(): #Q._cat_id #Q.questions global reacts if Q.question_pick(): clear_reacts() Q.in_question = True Q.starttime = time.time() if len(Q.question.map) > 0: #this is an imagequiz question iq = Imagequiz_question() iq.id = Q.question.id iq.imgfn = Q.question.imgfn iq.map = Q.question.map iq.cat = 0 iq.subcat = 0 iq.text = Q.question.prompt iq.answer = Q.question.response iq.answer_link = Q.question.answer_link __SERVICES__.frontend.question_display(iq) elif len(Q.question.imgfn) > 0 and len(Q.question.sndfn) > 0: #this is Renyi print 'Renyi', Q.question.imgfn, Q.question.sndfn #get four images (one is correct), shuffle them, put them in 'react' squares set_reacts() print 'display_images' display_images() print 'update pygame display' pygame.display.update() #load and play sound play_sound(Q.question.sndfn) elif len(Q.question.sndfn) > 0: #this in 'phrase' question #load and play sound print 'phrase question' play_sound(Q.question.sndfn) response = __SERVICES__.frontend.ask(Q.question.text) if len(response) == 0 or len(Q.question.answer) == 0: self_test() else: correct = checkanswer(response) y = 100 sf.add_text_item("ok", (750,y), next_question) if correct: correct_answer(False) else: wrong_answer(False) else: # this is flashcard print 'flashcard' print 'prompt', len(Q.question.prompt), Q.question.prompt print 'answer', len(Q.question.response), Q.question.response response = __SERVICES__.frontend.ask(Q.question.prompt) print 'response', len(response), response if len(response) == 0 or len(Q.question.response) == 0: correct = self_test() else: correct = checkanswer(response) delete_reacts(correct) next_question() Q.in_question = True else: # game / cat finished Q.in_question = False print "finished" Q._cat_id = -1 finished() def spot_found(): if Q.in_question: A.display_icon("found") play_correct_response_sound() #A.play_sound(1) Q.in_question = False Q.count_won += 1 display_points() show_answer() def spot_not_found(): if Q.in_question: A.display_icon("not-found") play_wrong_response_sound() #A.play_sound(0) Q.in_question = False Q.count_lost += 1 display_points() show_answer() def set_reacts(): global reacts reacts = [] reacts.append(sf.add_react(250, 150, 320, 240, picture_tl, stopsearch = True)) reacts.append(sf.add_react(520, 150, 320, 240, picture_tr, stopsearch = True)) reacts.append(sf.add_react(250, 420, 320, 240, picture_ll, stopsearch = True)) reacts.append(sf.add_react(520, 420, 320, 240, picture_lr, stopsearch = True)) reacts.append(sf.add_react(750,100,50,50, correct_answer, stopsearch = True)) reacts.append(sf.add_react(750,150,50,50, wrong_answer, stopsearch = True)) reacts.append(sf.add_react(300,30,100,100, play_query_again, stopsearch = True)) def clear_reacts(): global reacts for react in reacts: sf.del_react(react) def click_on_pick_category(): # Select Category cat_id = -1 sf.clear_text_items() sf.clear_question_frame(True) ask_category(cat_id) def ask_category(cat_id, offset_y = 0): i = 1 y = 110 + offset_y sf.add_text_item("Next Category:", (280,y)) #first we need to query for categories #might be nice to show them in alphabetical order #when y gets too big, we should change x if cat_id == -1: q = 'SELECT id, text FROM categories' res = __SERVICES__.db.query(q) for cat in res: cat_id = cat[0] category = cat[1] q = 'SELECT count(*) FROM quizlink WHERE quiz_id=%i' % cat_id res1 = __SERVICES__.db.query(q) count = res1[0][0] if count > 0: # this is a quiz, display in green y += 50 sf.add_text_item("%s (%s)" % (category, count), (300,y), startgame, cat_id, True) else: # this is a category - get number of children q = 'SELECT count(*) FROM catlink WHERE parent_id=%i' % cat_id res2 = __SERVICES__.db.query(q) count = res2[0][0] y += 50 sf.add_text_item("%s (%s)" % (category, count), (300,y), ask_category, cat_id, True) i += 1 else: #we need to pass a cat_id from the user's selection print 'second level selection not implemented - must select quiz' #initialization called by quiz.py def load(): global sf sf = __SERVICES__.frontend; global Q Q = Questions() global A A = Answer() global button1 global button2 global button3 global reacts reacts = [] sf.add_menu_item('/', 'Pick Category', click_on_pick_category, 0) sf.add_menu_dir('/options', "Options") button1 = sf.add_menu_item('/options', 'Display Answer [ On ]', click_options_answer) button2 = sf.add_menu_item('/options', 'Play Sound [ On ]', click_options_sound) button3 = sf.add_menu_item('/options', 'Draw Line [ On ]', click_options_line) __SERVICES__.add_service("next_question", next_question) __SERVICES__.add_service("spot_found", spot_found) __SERVICES__.add_service("spot_not_found", spot_not_found) #clean close def close(): pass # handle options tab on main menu def click_options_answer(): if A.display: A.display = False sf.change_menu_item("change_caption", button1, "Display Answer [ Off ]") else: A.display = True sf.change_menu_item("change_caption", button1, "Display Answer [ On ]") def click_options_line(): if A.display_line: A.display_line = False sf.change_menu_item("change_caption", button3, "Draw Line [ Off ]") else: A.display_line = True sf.change_menu_item("change_caption", button3, "Draw Line [ On ]") def click_options_sound(): if A.sound_enabled: A.sound_enabled = False sf.change_menu_item("change_caption", button2, "Play Sound [ Off ]") else: A.sound_enabled = True sf.change_menu_item("change_caption", button2, "Play Sound [ On ]")