1

I'm building a multiple choice quiz game in pygame however there is an issue with my while loop.

I successfully built the game in python without the GUI and all the logic works fine, however, when I try and add the GUI to the logic it doesn't work in the same way.

In my working logic game the continuous while loop is paused at every question due to me using the 'raw_input()' function and waits for the user to answer. When I do the GUI version my questions are read in successfully but it reads them all in at once and there is no time to answer.

I've tried adding time.sleep() after reading each question but this means that the program doesn't respond to event handlers. If the event handlers did work then this would be the perfect solution, giving users a certain amount of time to answer.

This example code below won't compile as I have left out many classes and methods but wanted to show this is where my issue lies. I read dictionary keys and values in and then try and match the user input with the index of the correct answer which is always answerMain[0] before getting shuffled.

Has anyone had a similar issue or know of a possible solution?

        attemptMain = {'Q': 'Question Here', 'A': 'Answer1','B': 'Answer2','c': 'Answer3','D': 'Answer4',  }

        def normalQuestions(self, list):
             for i in list:
                questionMain = self.attemptMain.keys()[i - 1]
                answersMain = (self.attemptMain.values()[i - 1])
                correctAnswerMain = answersMain[0]
                shuffle(answersMain)
                answersMainIndex =  (1 + answersMain.index(correctAnswerMain) )
                self.layout.questionandAnswers(questionMain, answersMain[0], answersMain[1], answersMain[2], answersMain[3])
                time.sleep(10)
                x = self.layout.controls()
                if (x == answersMainIndex):
                    print("this is the correct answer")

            def controls(self):
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_1:
                        print("here" + str(1))
                        return '1'
                    elif event.key == pygame.K_2:
                        print("here" + str(2))
                        return '2'
                    elif event.key == pygame.K_3:
                        print("here" + str(3))
                        return '3'
                    elif event.key == pygame.K_4:
                        print("here" + str(4))
                        return '4'
odare
  • 19
  • 3
  • A [minimal, runnable example](https://stackoverflow.com/help/mcve) would be helpful. I can already tell you that you shouldn't use `time.sleep` in pygame, since the program will be unresponsive for that time. There are various ways to implement timers in pygame (search on Stack Overflow). – skrx Sep 27 '17 at 15:50
  • Ok thanks, I had a look at the time.() functions on tutorials point, will do a slightly more specific search on here then! – odare Sep 27 '17 at 19:36
  • Here are [some good answers](https://stackoverflow.com/questions/30720665/countdown-timer-in-pygame) (I mean the `pygame.time.get_ticks()`, `pygame.time.set_timer` and my `dt = clock.tick(30) / 1000` approach). – skrx Sep 27 '17 at 19:46
  • Ah I think I found that exact page before actually, it helped me get the countdown working, however my issue is within my for loop in the normalQuestion function, it iterates too fast when I want each iteration to take 10/15 seconds for example so the user has a chance to respond to the question – odare Sep 27 '17 at 20:23
  • Show me a minimal, runnable example and I'll check out where the problem lies. The code in the question is not complete enough to see how the program works. – skrx Sep 28 '17 at 09:35
  • I was trying to cut it down for you but then managed to think of a solution. I put a nested for loop from range(0,500) within my main loop. In the nested for loop I put a clock.tick(60). This now basically makes each iteration of the main for loop take around 15 seconds as it has to do 500 iterations. – odare Sep 30 '17 at 16:29
  • That doesn't sound like a good solution. `clock.tick` should be called once per frame to limit the frame rate, so that the game doesn't run with for example 500 fps. I'll just post an example to show you how I would implement a quiz game (I've already got a rudimentary prototype which I need to adjust a bit). – skrx Sep 30 '17 at 17:41

1 Answers1

0

Here's a quiz game example. I use the dt = clock.tick(fps) solution from the linked answer. You can just decrement a time variable by the dt and if it's below 0, switch to the next question. The user can input 1-4 which is then compared with the last element of the question tuple to check if the answer was correct.

import random
import pygame as pg


pg.init()
FONT = pg.font.Font(None, 34)
FONT_SMALL = pg.font.Font(None, 24)
BLUE = pg.Color('steelblue1')


def get_question(questions, question_index):
    """Create a surface with the question and the 4 answer choices."""
    question, *answers, _ = questions[question_index]

    # Blit the question and answers onto this transparent surface.
    question_surface = pg.Surface((500, 150), pg.SRCALPHA)
    question_surface.blit(FONT.render(str(question), True, BLUE), (0, 0))
    for y, answer in enumerate(answers, 1):
        txt = FONT_SMALL.render('{}:  {}'.format(y, answer), True, BLUE)
        question_surface.blit(txt, (0, 20*y+20))
    return question_surface


def main():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()
    dt = 0
    time_to_answer = 5  # User has 5 seconds to answer.
    time = time_to_answer

    # A list of tuples that contain the question, 4 multiple choice
    # answers and the last element is the correct answer.
    questions = [
        ('Is it A, B, C or D?', 'A', 'B', 'C', 'D', '3'),
        ("What's 2+3?", '23', '5', '3', '2', '2'),
        ]
    random.shuffle(questions)
    question_index = 0
    question_surface = get_question(questions, question_index)

    correct = 0
    incorrect = 0
    game_over = False
    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            elif event.type == pg.KEYDOWN:
                if not game_over:
                    user_input = event.unicode
                    correct_answer = questions[question_index][-1]
                    if user_input == correct_answer:
                        print('Correct')
                        correct += 1
                        time = time_to_answer
                    else:
                        incorrect += 1
                        time = time_to_answer

                    question_index += 1
                    if question_index >= len(questions):
                        game_over = True
                    else:
                        question_surface = get_question(questions, question_index)
                else:  # Restart.
                    game_over = False
                    correct = 0
                    incorrect = 0
                    random.shuffle(questions)
                    question_index = 0
                    question_surface = get_question(questions, question_index)

        if not game_over:
            time -= dt
            # If the timer is below 0, switch to the next question.
            if time <= 0:
                question_index += 1
                incorrect += 1
                if question_index >= len(questions):
                    game_over = True
                else:
                    time = time_to_answer
                    print('next')
                    question_surface = get_question(questions, question_index)

        screen.fill((30, 30, 30))
        if not game_over:
            screen.blit(question_surface, (20, 50))
            time_surface = FONT.render(str(round(time, 1)), True, BLUE)
            screen.blit(time_surface, (20, 20))
        correct_answer_surface = FONT_SMALL.render(
            'Correct {}, incorrect {}'.format(correct, incorrect),
            True, BLUE)
        screen.blit(correct_answer_surface, (20, 250))

        pg.display.flip()
        dt = clock.tick(30) / 1000


if __name__ == '__main__':
    main()
    pg.quit()
skrx
  • 19,980
  • 5
  • 34
  • 48