Breaking the Game F5
Question:
Breaking the Game F5
F5 is sometimes the bane of programmers. Some would go a long way to dismantle this key from keyboards around the world. Unfortunately, this is not possible and our application also suffers from an F5 problem.
Choose the number of questions, the degree of difficulty, and the department. Start the game. And on the first question, do F5. And what happened? You got another question! You can finish the Quiz by pressing F5. Of course, the result will be that you didn’t answer any of the questions correctly, but that’s poor consolation. What is the reason for this error?
Let’s take a look at the view on_game:
def on_game(request):
quiz = Quiz.restore(request)
if not quiz:
return render(request, 'error.html')
answer = request.POST.get('answer')
try:
if answer:
quiz.check_answer(answer)
question = quiz.get_question()
quiz.save(request)
return render(request, 'game.html', vars(question))
except IndexError as x:
return redirect('/finish')
The moment you click "Play!", This view fires up. Since the quiz has just started, this is your first question, so no answer, so in Ifa:
if answer:
quiz.check_answer(answer)
won’t come in – makes sense, right? So the code will execute and ask the question. It’s a pity that pressing F5 will treat it as starting a Quiz! Because there will be no answer … And the code will continue.
Hint
You can add an additional field in the Quiz class named e.g. just_started, which will have the value True / False. Programmers call these variables flags because they are only used to identify a certain situation. Flagging whether it happened or not.
The situation is not as trivial as it seems, you may need to change the way the next question is returned from "get -> move -> return" to "move -> check". I mean the function get_question. Combine it with the aforementioned flag just_started. Try to understand the problem in depth before the fixes, otherwise you will bury yourself 🙂
@classmethod
def create_game(cls, number_of_questions, difficulty, category):
raw_questions = ApiClient.get_questions(difficulty, number_of_questions, category)
questions = list([Question(**raw_question) for raw_question in raw_questions])
return Quiz(number_of_questions, difficulty, questions, 0, 0)
def save(self, request):
request.session['saved_quiz'] = self
def stop(self, request):
del request.session['saved_quiz']
@classmethod
def restore(cls, request):
return request.session.get('saved_quiz')
def get_question(self):
question = self.questions[self.current_question]
self.current_question += 1
return question
def check_answer(self, answer):
if self.questions[self.current_question - 1].correct_answer == answer:
self.number_of_correct_answers += 1
def get_result(self):
return {
'correct_answers': self.number_of_correct_answers,
'all_questions': self.number_of_questions
}
Answers:
You need to separate the post and get methods. create new functions for these methods. Don’t listen to these hints because they are wrong.
File views.py
from django.shortcuts import render, redirect
from .api import ApiClient
from .game import Quiz
def index(request):
try:
quiz = ApiClient.get_quiz_options()
return render(request, 'index.html', quiz)
except ValueError:
return render(request, 'error.html')
def start_game(request):
number_of_questions = request.POST['quantity']
difficulty = request.POST['difficulty']
category = request.POST['category']
quiz = Quiz.create_game(number_of_questions, difficulty, category)
quiz.save(request)
return redirect('/on_game_get')
def on_game_get(request):
quiz = Quiz.restore(request)
if not quiz:
return render(request, 'error.html')
try:
question = quiz.get_question()
except IndexError:
return redirect('/finish')
return render(request, 'game.html', vars(question))
def on_game_post(request):
quiz = Quiz.restore(request)
answer = request.POST.get('answer')
if answer:
quiz.check_answer(answer)
quiz.next_question()
quiz.save(request)
return redirect('/on_game_get')
def finish(request):
quiz = Quiz.restore(request)
result = quiz.get_result()
return render(request, 'finish.html', result)
Breaking the Game F5
F5 is sometimes the bane of programmers. Some would go a long way to dismantle this key from keyboards around the world. Unfortunately, this is not possible and our application also suffers from an F5 problem.
Choose the number of questions, the degree of difficulty, and the department. Start the game. And on the first question, do F5. And what happened? You got another question! You can finish the Quiz by pressing F5. Of course, the result will be that you didn’t answer any of the questions correctly, but that’s poor consolation. What is the reason for this error?
Let’s take a look at the view on_game:
def on_game(request):
quiz = Quiz.restore(request)
if not quiz:
return render(request, 'error.html')
answer = request.POST.get('answer')
try:
if answer:
quiz.check_answer(answer)
question = quiz.get_question()
quiz.save(request)
return render(request, 'game.html', vars(question))
except IndexError as x:
return redirect('/finish')
The moment you click "Play!", This view fires up. Since the quiz has just started, this is your first question, so no answer, so in Ifa:
if answer:
quiz.check_answer(answer)
won’t come in – makes sense, right? So the code will execute and ask the question. It’s a pity that pressing F5 will treat it as starting a Quiz! Because there will be no answer … And the code will continue.
Hint
You can add an additional field in the Quiz class named e.g. just_started, which will have the value True / False. Programmers call these variables flags because they are only used to identify a certain situation. Flagging whether it happened or not.
The situation is not as trivial as it seems, you may need to change the way the next question is returned from "get -> move -> return" to "move -> check". I mean the function get_question. Combine it with the aforementioned flag just_started. Try to understand the problem in depth before the fixes, otherwise you will bury yourself 🙂
@classmethod
def create_game(cls, number_of_questions, difficulty, category):
raw_questions = ApiClient.get_questions(difficulty, number_of_questions, category)
questions = list([Question(**raw_question) for raw_question in raw_questions])
return Quiz(number_of_questions, difficulty, questions, 0, 0)
def save(self, request):
request.session['saved_quiz'] = self
def stop(self, request):
del request.session['saved_quiz']
@classmethod
def restore(cls, request):
return request.session.get('saved_quiz')
def get_question(self):
question = self.questions[self.current_question]
self.current_question += 1
return question
def check_answer(self, answer):
if self.questions[self.current_question - 1].correct_answer == answer:
self.number_of_correct_answers += 1
def get_result(self):
return {
'correct_answers': self.number_of_correct_answers,
'all_questions': self.number_of_questions
}
You need to separate the post and get methods. create new functions for these methods. Don’t listen to these hints because they are wrong.
File views.py
from django.shortcuts import render, redirect
from .api import ApiClient
from .game import Quiz
def index(request):
try:
quiz = ApiClient.get_quiz_options()
return render(request, 'index.html', quiz)
except ValueError:
return render(request, 'error.html')
def start_game(request):
number_of_questions = request.POST['quantity']
difficulty = request.POST['difficulty']
category = request.POST['category']
quiz = Quiz.create_game(number_of_questions, difficulty, category)
quiz.save(request)
return redirect('/on_game_get')
def on_game_get(request):
quiz = Quiz.restore(request)
if not quiz:
return render(request, 'error.html')
try:
question = quiz.get_question()
except IndexError:
return redirect('/finish')
return render(request, 'game.html', vars(question))
def on_game_post(request):
quiz = Quiz.restore(request)
answer = request.POST.get('answer')
if answer:
quiz.check_answer(answer)
quiz.next_question()
quiz.save(request)
return redirect('/on_game_get')
def finish(request):
quiz = Quiz.restore(request)
result = quiz.get_result()
return render(request, 'finish.html', result)