3. The function create_game_round()¶
(found in logic.py)
This section tells you all you need to know about the function create_game_round(). You will have already read the first subsection below (subsection “Basics”) from Chapter “Template game explained” that explained how to get started programming a command-line CTGames game (specifically, subsection “Modify create_game_round()”). It is reproduced here for completeness.
The full list of subsections in this section is
subsection “Basics” everything you need to get started with this function
subsection “Answer style options (key answer_type)” explains the full set of possibilities for the style of answer for one’s game (values for key
answer_typereturned fromcreate_game_round())
3.1. Basics¶
Function create_game_round() creates a single round of one’s game dynamically.
It acts as the “main” method of the game for the game developer.
There is no actual logic in this function. Instead, it is where data structures returned from the game logic are used to populate a dict to be returned to the CTGames framework.
Note
The CTGames framework calls this function each time a new game round is to be created.
The framework passes a namedtuple of type SublevelBehaviour.
It either chooses the appropriate namedtuple from file behaviour.py or, if it is a custom round, from the developer through the command line or from a teacher through the web app).
The game developer does not need to understand how the framework chooses which SublevelBehaviour object to pass as round_behaviour, they just need to be able to write this function so that it responds appropriately whatever values are passed.
Tip
The game developer can be confident that the only values that will be passed in parameter round_behaviour will be those allowed by ROUND_BEHAVIOUR_RANGES (see section “Modify ROUND_BEHAVIOUR_RANGES”).
Therefore, it is unlikely that there will be a need for the developer to check for invalid values.
Note
The dict returned from this function is used by the CTGames framework to update the game state that it maintains throughout the game round.
The dict should contain everything that the CTGames framework needs for a new round of your game.
There are five aspects of this function to update, each explained in detail in the subsequent sections:
Decide on an answer type
Use the game logic to decide on a game instance
(Only for MCQ-type games) Decide on a suitable list of multiple choice answers
Populate a new
GameStateCustomobjectConstruct a
dictto update the game state
3.1.1. Decide on an answer type¶
You must decide what is the style of answer expected from the player for your game, e.g AnswerType.Int if the game expects an integer, AnswerType.MCQ if it is an MCQ game, and so on.
Note
The choice made here affects what the player will be presented with during a game round (e.g. MCQ answers, a text box, an integer spin button, and so on) and how the player’s answer will be pre-processed (all values entered on the command line are strings; if you specify AnswerType.Int for your game, the CTGames framework will ensure those strings can be converted into integers before passing them to your code).
The full list of possibilities for the style of answer is given in subsection “Answer style options (key answer_type)”.
When you decide on the style of answer for your game, you will enter your decision into the dict that updates the game state (explained later in this section).
3.1.2. Use the game logic to decide on a game instance¶
A call must be made to the function _decide_on_problem_instance() passing a value for round_behaviour to it.
This function (which you can treat as a black box just for now – we’ll fill in the code for it later) has all of the game logic for the game.
It should return
whatever data should be stored in
GameStateCustom, which is whatever information is unique to this game round (and should have the same name(s) as the field(s) inGameStateCustom), andthe correct answer (or sequence of correct answers, if there are multiple equally correct ways the player can answer).
In Game Template Int, this line looks like
custom_field1, target = _decide_on_problem_instance(round_behaviour)
In Game Template MCQ, this line looks like
custom_field1, target_long = _decide_on_problem_instance(round_behaviour)
Note
There is a slight difference between the two names we use for the correct answer: in MCQ-type games we use the name target_long, and for all other types of game we use target.
This is a convention to acknowledge that there are two concepts of correct answer in MCQ-type games.
Firstly, there is the logical answer to the game round (i.e. the correct answer to the logical problem, which might be an
intsuch as5, aboolsuch asTrue, or astrsuch as'0011'), andsecondly, there is the answer that we want the player to select in a MCQ-type game, which is always one of the multiple-choice question labels (
'A','B','C', and so on).
In MCQ-type games, we always use target_long for the logical answer and target for the correct multiple-choice question label.
Further, in MCQ-type games as well as all other games, target will refer to the exact string that the player is supposed to enter.
3.1.3. Decide on a suitable list of multiple choice answers¶
This is only relevant for MCQ-type games. This function takes as parameters whatever is needed for that particular game to return
a randomised-order list of possible answers, one of which must be name
target_long(the logical answer to the game round), andthe multiple-choice question label (‘A’, ‘B’, ‘C’, and so on) that represents the location of :py:data:``target_long` in the list.
In Game Template MCQ, what is needed as parameters are the logical answer to the game round and the list of values given in the question, and this looks like:
multiple_answers, targets = _decide_on_answers(target_long, custom_field1)
3.1.4. Populate a new GameStateCustom object¶
The custom information returned from _decide_on_problem_instance() needs to be put in the correct format (a GameStateCustom object).
In the template, this looks like:
custom = GameStateCustom(custom_field1)
3.1.5. Construct a dict to update the game state¶
The following three keys, at least, must be be returned to the CTGames framework from this function
answer_typeUse whatever was decided in the previous step, e.g
AnswerType.Int,AnswerType.MCQ, and so on.targetsThis is the list of correct/expected answers. It is always in the form of a list, even if there is only one answer. Each answer is generally the int or string expected (e.g. 3, ‘Apple’, etc.), except in the cases of
AnswerType.MCQandAnswerType.ImgMCQ, where it is the multiple-choice label corresponding to the correct answer, e.g. ‘A’, ‘B’, etc.customThis is the name of the
GameStateCustomobject created a couple of steps before.
For MCQ-type games, the following key must also be returned
multiple_answersThe list of possible answers.
In Game Template Int, this dict looks like
updates = {
'answer_type': AnswerType.Int,
'targets': [target], # `targets` is a list of correct answers
'custom': custom,
}
In Game Template MCQ, this dict looks like
updates = {
'answer_type': AnswerType.MCQ,
'multiple_answers': multiple_answers,
'targets': targets, # `targets` is a list of correct answers
'custom': custom,
}
3.2. Answer style options (key answer_type)¶
The game developer has several possibilities for the style of answer they wish for their game. The choice is made in function create_game_round() in file logic.py.
Note
When scoring a game round, usually the player gets full marks for a correct answer and (for example for MCQ-type games) a player can lose some marks for an incorrect answer. However, there is also the concept of a partial mark in games. In such a case, a player may get some marks for a good attempt even if their answer is incorrect. In the descriptions below, it is mentioned for which game styles the concept of a partial mark is supported.
The full list of possibilities is:
AnswerType.StringThe answer to the game round is a string. No marks will be given for a partially correct answer (i.e. there is no partial mark).
AnswerType.StringCaseIThe answer to the game round is a string. No marks will be given for a partially correct answer (i.e. there is no partial mark). The comparison is case insensitive.
AnswerType.PartialThe answer to the game round is a string. The partial mark will be the length of the correct prefix. (Default.)
AnswerType.PartialCaseIThe answer to the game round is a string. The partial mark will be the length of the correct prefix. The comparison is case insensitive.
AnswerType.MCQThe answer to the game round is a label for one of the multiple answers presented to the player (it was a multiple-choice question). Negative marking is used for the partial mark.
AnswerType.ImgMCQThe same as
MCQbut allows images to be displayed instead of text in the Brython version of the game.AnswerType.IntThe answer to the game round is an int. No marks will be given for an incorrect answer.
AnswerType.NaturalMinThe answer to the game round is a non-negative int. The player’s answer will be used as the partial mark. An answer greater than or equal to the target is deemed correct. If there is a goal, and that goal is achieved, the partial mark will be doubled.
AnswerType.NaturalMaxThe answer to the game round is a non-negative int. An answer less than or equal to the target is deemed correct. If correct, the target minus the player’s answer will be used as the partial mark. If there is a goal, and that goal is achieved, the partial mark will be doubled.
AnswerType.BagThe answer to the game round is a bag (an unordered list, or, equivalently, a set that can contain multiple elements). The number of elements in the set will be used as the partial mark. No marks will be given for an incorrect answer. It is assumed that the bag contains strings (as that is what comes naturally from the command line) and that each string contains no spaces and commas.
AnswerType.BagCaseIAs with
Bagabove, except the strings are compared case-insensitively.
3.2.1. Examples¶
In file countfromzero/logic.py, the function create_game_round() specifies that the answer type should be an integer, with
updates = {
'answer_type': AnswerType.Int,
'targets': [target], # `targets` is a list of correct answers
'custom': custom,
}
return updates
In file justaddition/logic.py, the function create_game_round() specifies that the answer type should be a multiple-choice question label (e.g. ‘A’, ‘B’, ‘C’, …) with
updates = {
'answer_type': AnswerType.MCQ,
'multiple_answers': multiple_answers,
'targets': targets, # `targets` is a list of correct answers
'rules_text': mcq_rules_text(multiple_answers),
'custom': custom,
}
return updates
In file justreverse/logic.py, the function create_game_round() specifies that the answer type should be a case-insensitive string where the player gets a partial mark depending on the position of the first incorrect character in the string (full marks if there is no incorrect character) with
updates = {
'answer_type': AnswerType.PartialCaseI,
'targets': [target], # `targets` is a list of correct answers
'target_item_name': _TARGET_ITEM_NAME,
'custom': custom,
}
return updates
In file justodd/logic.py, the function create_game_round() specifies that the answer type should be a set of strings, with
updates = {
'answer_type': AnswerType.Bag,
'targets': [target], # `targets` is a list of correct answers
'custom': custom,
}
return updates