3. The file webapp/__init__.py¶
This section explains the purpose of each web app function recognised by the CTGames framework. The overall idea is to make the web app more engaging for children. Each function is optional; the web app will run without each one, but if any of the functions are defined, the CTGames framework will notice and execute it at the appropriate time during the game round. Each function can also be used in a number of ways, as explained below.
The subsections in this section are
See also
Animation of web apps is not covered in this section. For this advanced topic, see Chapter “Animations for web app games”.
3.1. The function format_problem_instance()¶
(defined in webapp/__init__.py)
See also
This function returns a TextImageSeq object. Read about this class here.
This function is used to display a graphical version of the problem instance.
The developer can use the text from the command-line version of the game (passed as the argument default_text) as a starting point to creating the graphical version, or they can choose to ignore the command-line text and just use the data that they previously stored in game_state.custom.
See also
Recall, the way to change the command-line 'problem_instance' string was explained previously in section “Modify the function construct_cl_formatted_strings()”.
This function works by creating a namedtuple of type TextImageSeq and returning it to the CTGames framework.
The main fields in the namedtuple are text_seq for a sequence of text strings, im_seq for a sequence of image filenames, and dom_element_seq for a sequence of DOMNode objects (such as images).
The CTGames framework interlaces/interleaves the two sequences in an appropriate DIV in the DOM.
See also
Wondering which to use, im_seq or dom_element_seq, to display the images in your game?
See section “Which to use, im_seq or dom_element_seq?” to help you understand the use cases for each.
3.1.1. Examples¶
In file justaddition/webapp/__init__.py, the function format_problem_instance() takes the string from the command-line version of the game and replaces selected characters with image filenames. It replaces them and collects them into a list in one line by using a list comprehension fnames = [IM_PATH.format(symbol) for symbol in symbols]. It returns a TextImageSeq object using attributes text_seq for text and im_seq for images. Because it uses im_seq, it must specify values for attributes im_height and im_width.
def format_problem_instance(default_text: str) -> TextImageSeq:
"""Docstring hidden for this example."""
# Use the command-line string that was formatted in
# `construct_cl_formatted_strings`. We used a character ":" to
# separate the text from the sum.
text, symbols = default_text.split(':')
# Remove all spaces so only symbols remain
symbols = symbols.replace(' ', '')
# Replace each symbol with an appropriate image filename
fnames = [IM_PATH.format(symbol) for symbol in symbols]
text_im_seq = TextImageSeq(
text_seq=[text + ': '],
im_seq=fnames,
im_height=IM_HEIGHT,
im_width=IM_WIDTH,
)
return text_im_seq
In file justreverse/webapp/__init__.py, the function format_problem_instance() ignores the string from the command-line version of the game and instead uses a value stored in game_state.custom to specify the sequence of images to be displayed in the DOM. It specifies the sequence by using a list comprehension of the form blocks = [create_image(letter) for letter in word]. It creates a SVG instance using svg_instance = create_svg_instance(...), and inserts the sequence into the SVG using svg_instance <= blocks. It returns the SVG instance as part of a TextImageSeq object:
def format_problem_instance(default_text: str) -> TextImageSeq:
"""Docstring hidden for this example."""
word, animation = game_state.custom
parent_width = IM_BLOCK_SIDE * len(word)
svg_instance = create_svg_instance(
id='svg_instance', width=parent_width, height=IM_BLOCK_SIDE * 2
)
blocks = [
create_image_from_file(
# Parameters hidden for this example
)
for letter in word
]
svg_instance <= blocks
# Animation code hidden for this example
return TextImageSeq(dom_element_seq=[svg_instance])
In file password/webapp/__init__.py, the function format_problem_instance() (just like the justreverse/ example above) creates a SVG instance and inserts into it a list of SVGs. However, it also inserts another object (a pet pig character in multiple parts), and calculates appropriate values for attributes x and y to align it to the rightmost end of the list of SVGs.
def format_problem_instance(default_text: str) -> TextImageSeq:
"""Docstring hidden for this example."""
SHAPE_H_PITCH = 130
"""The horizontal pitch of the shapes in the seq of shapes."""
SHAPE_DIM = 100
"""The side length of each shape."""
PET_DIM = 200
"""The dimensions of the pet."""
V_GAP = 20
"""The vertical gap between the pet/box and the seq of shapes."""
(encoded_password,) = game_state.custom
svg_instance = create_svg_instance(
# Code hidden for this example
)
shapes = [
create_image_from_file(
id=f'shape_{index}',
x=index * SHAPE_H_PITCH,
y=PET_DIM + V_GAP,
width=SHAPE_DIM,
height=SHAPE_DIM,
href=IM_PATH.format(shape),
)
for index, shape in enumerate(encoded_password)
]
svg_instance <= shapes
pet_parts = ['pig_body', 'pig_face']
x = SHAPE_H_PITCH * len(encoded_password) - PET_DIM
pet_svgs = [
create_image_from_file(
id=pet_part,
x=x,
y=0,
width=PET_DIM,
height=PET_DIM,
href=IM_PATH.format(pet_part),
)
for pet_part in pet_parts
]
svg_instance <= pet_svgs
return TextImageSeq(dom_element_seq=[svg_instance])
3.2. The function format_player_answer_area()¶
(defined in webapp/__init__.py)
This function is used if we wish the player to input their answer using graphical elements different from the default (a SELECT box, as shown in Just Addition, or any game newly created from a template). For example, Password uses radio buttons, Just Odd uses check boxes, and Just Reverse uses buttons to specify an answer that consists of a string of characters.
See also
This function returns a namedtuple InputDOMElements object. Read about this class here.
3.2.1. Examples¶
In file password/webapp/__init__.py, the function format_player_answer_area() specifies that radio buttons should be used because there is only one correct answer. The option InputDOMElementOptions.VerticalList specifies that the multiple answers should be arranged vertically (each one added directly below the previous ones):
def format_player_answer_area() -> InputDOMElements:
"""Create what is necessary to allow the player to input their answer."""
mcq = zip(MCQ_ANSWER_LABELS, game_state.multiple_answers)
input_dom_elements = InputDOMElements(
dom_elem_type=InputDOMElementType.RADIOBUTTONS,
dom_elem_options=[InputDOMElementOptions.VerticalList],
options=mcq,
)
return input_dom_elements
In file justodd/webapp/__init__.py, the function format_player_answer_area() specifies that checkboxes should be used because multiple answers are correct:
def format_player_answer_area() -> InputDOMElements:
"""Create what is necessary to allow the player to input their answer."""
# This game has multiple correct answers so we replace the default
# answer layout with something that allows multiple selection:
# checkboxes.
options = [(a, a) for a in game_state.custom.seq]
input_dom_elements = InputDOMElements(
dom_elem_type=InputDOMElementType.CHECKBOXES,
options=options,
)
return input_dom_elements
In file justreverse/webapp/__init__.py, the function format_player_answer_area() specifies that two buttons should be used to allow the player to input a binary word, and that each button should have an image label rather than a text label:
def format_player_answer_area() -> InputDOMElements:
"""Create what is necessary to allow the player to input their answer."""
# Specify the text string (in this case ``None``), graphic URL, and
# DOM object (in this case nothing) to be put on each button.
a = (None, IM_PATH.format('a'))
b = (None, IM_PATH.format('b'))
# Pair each answer with an appropriate MCQ label
options = zip(MCQ_ANSWER_LABELS, [a, b])
input_dom_elements = InputDOMElements(
dom_elem_type=InputDOMElementType.IMG_BUTTONS,
options=options,
im_height=IM_BTN_BLOCK_SIDE,
)
return input_dom_elements
3.3. The function format_correct_answer()¶
(defined in webapp/__init__.py)
In the event that a player submits the wrong answer for a game round, the web app will print the same default text string as used in the command-line version of the game to inform the player of what was the correct answer. This function can used by game developers to change how the correct answer is presented to the player, either by changing text itself, or by adding images, or both.
See also
This function returns a TextImageSeq object. Read about this class here.
Tip
This function is the web app equivalent of changing the key 'correct_answer' in dict formatted_strings in a game’s cl.py file. If one has made such a change for the command-line version of a game, one should make an equivalent change for the web app version as described here; the changes made to formatted_strings['correct_answer'] will not automatically appear in the web app version of the game.
The only argument to this function is a string preamble that is what the framework suggests will be the lead-in text, such as “The correct answer was: “, to ensure uniformity between games. The programmer of this function can choose to use it or ignore it.
3.3.1. Examples¶
In file password/webapp/__init__.py, the function format_correct_answer() specifies that, rather than print the MCQ label (A, B, C, …) associated with the correct multiple choice answer, that the correct answer itself should be printed:
def format_correct_answer(preamble: str) -> TextImageSeq:
"""Docstring hidden for this example."""
# Obtain the correct answer using the appropriate MCQ label (e.g. A,
# B, C, ...). There is only one correct answer for this game.
correct_answer: str = game_state.multiple_answers[
MCQ_ANSWER_LABELS.index(game_state.targets[0])
]
return TextImageSeq(text_seq=[preamble, correct_answer])
In file dungeonescape/webapp/__init__.py, the function format_correct_answer() specifies that a graphic should be used in place of the text of the correct answer. As with other games, the function first uses the label of the correct answer (one of the MCQ labels A, B, C, …) to obtain the correct answer itself:
def format_correct_answer(preamble: str) -> TextImageSeq:
"""Docstring hidden for this example."""
# Show the correct answer at a smaller scale to the rest of the images
element_side = int(IM_SIDE * 0.6)
# Obtain the correct answer using the appropriate MCQ label (e.g. A,
# B, C, ...). There is only one correct answer for this game.
correct_answer: str = game_state.multiple_answers[
MCQ_ANSWER_LABELS.index(game_state.targets[0])
]
# Create a single DOMNode object consisting of a sequence of images
instance = _create_key_door_svg(
id='correct_answer_svg',
string=correct_answer,
width=element_side * len(correct_answer),
height=element_side,
element_side=element_side,
)
return TextImageSeq(text_seq=[preamble], dom_element_seq=[instance])
3.4. The function modify_answered_problem_instance()¶
(defined in webapp/__init__.py)
This… modify the graphical problem instance after the player’s answer.
This piece of functionality is only relevant where the game has defined modifications to the existing content, for example changing a single graphical element whilst leaving all other graphical elements as they are.
This function will be called immediately after the player has given their answer to the problem instance. The graphical instance of this answered problem only is updated, not any future problem instance.
3.4.1. Examples¶
In file password/webapp/__init__.py, the function modify_answered_problem_instance() specifies that when a player submits their answer the character’s ears should change colour and that the head should rotate (clockwise if the player was correct, counter-clockwise otherwise):
def modify_answered_problem_instance() -> None:
"""Docstring hidden for this example."""
# Modify selected parts of the character SVG. These parts of the SVG
# will have been manually given ids using Inkscape.
document['pig_ear_left'].style.fill = '#ff9999'
document['pig_ear_right'].style.fill = '#ff9999'
pig_head = document['pig_head']
if game_state.player_correct:
# Rotate -10 deg. (i.e. 10 deg. clockwise) about centre of element
pig_head.setAttribute(
'transform',
'matrix(0.98480775,0.16612754,-0.18150928,'
'0.98480775,7.2962881,73.884641)',
)
else:
# Rotate 10 deg. about centre of element
pig_head.setAttribute(
'transform',
'matrix(0.98480775,-0.16612754,0.18150928,'
'0.98480775,-20.596189,-71.550053)',
)
3.6. The function format_rules_text()¶
(defined in webapp/__init__.py)
See also
This function returns a TextImageSeq object. Read about this class here.
This… “””Graphically display the rules of the current round to the player.
Appropriately colour the names of the runners, and add suitable graphics.
3.6.1. Returns¶
- TextImageSeq
A namedtuple referring to sequences of text, image filenames, and DOMNode elements to be displayed in an interlaced/ interleaved arrangement in an appropriate DIV in the DOM.
“””
3.6.2. Examples¶
In file crosscountry/webapp/__init__.py, the function format_rules_text() specifies that the rule are to be formatted as a table containing text and images (the values in parameters text_table and im_table are interleaved/interlaced into a single table in the DOM). In this example, a table caption is provided. A common height for each image in the table is specified by passing a single number to im_height rather than a list of numbers. The table is styled using various parameters such as cell_border, cell_padding, and table_style.
def format_rules_text():
"""Docstring hidden for this example."""
text_table = [ *hidden* ]
im_table = [ *hidden* ]
text_image_seq = new_text_image_seq()._replace(
text_table=text_table,
im_table=im_table,
im_height=IMG_RULES_HEIGHT,
cell_border=1,
cell_padding=5,
caption='Overtakers',
table_style={'text-align': 'right'},
)
return text_image_seq
3.7. The function format_question()¶
(defined in webapp/__init__.py)
See also
This function returns a TextImageSeq object. Read about this class here.
By default, each web app will display a purely text-based question, identical to that used for the command-line version of the game. Such a text-based question would have been specified in name GAME_QUESTION_CHILDREN in file text_constants.py and possibly modified by function construct_cl_formatted_strings() in file cl.py.
Note
Any modifications to the command-line question string in function construct_cl_formatted_strings() appear in the web app version of the game.
The function format_question() allows one to display a graphically-rich version of the question for the player. The question is specified in the form of sequences of text and images.
3.7.1. Parameters¶
- default_textstr
A string that the framework suggests will be the default text (the string from the command line version of the game). The programmer of this function can choose to use it or ignore it.
3.7.2. Returns¶
- TextImageSeq
A namedtuple referring to sequences of text and images (actually, image filenames) to be displayed in an interlaced/interleaved arrangement in an appropriate DIV in the DOM.
3.7.3. Examples¶
In file countfromzero/webapp/__init__.py, the function format_question() specifies that the question should contain some characters of text, followed by an image, followed by more text. In this simple example, the same image is used each time:
def format_question(default_text):
"""Docstring hidden for this example."""
# Make a list containing two strings, one string for the text before
# the image of the prize, and one for after.
text_seq = default_text.split(PRIZE)
text_im_seq = new_text_image_seq()._replace(
text_seq=text_seq, im_seq=[_G_PRIZE_HREF],
im_height=30,
)
return text_im_seq
3.8. The function format_additional_feedback()¶
(defined in webapp/__init__.py)
See also
This function returns a TextImageSeq object. Read about this class here.
By default, each web app will give feedback after each round consisting of whether the answer was correct or not, the points gained, and what to click next. In addition, a graphical indicator (a treasure chest, for example) is displayed.
The function format_additional_feedback() allows one to modify this feedback (append something extra or replace it completely). For example, this would be useful for games with staged rounds.
3.8.1. Examples¶
This function is not used by any games yet. The code below shows an example that returns exactly the same as if the function was not defined in the game. It shows how to correctly format the function:
def format_additional_feedback(
main_feedback_text: str, dom_elem_seq: Iterable[DOMNode]
) -> TextImageSeq:
"""Docstring hidden for this example."""
return TextImageSeq(
text_seq=[main_feedback_text], dom_element_seq=dom_elem_seq
)
3.9. The function custom_player_stats()¶
(defined in webapp/__init__.py)
If a game has some additional information that should be included in the scoreboard/player statistics area, this function can be used to specify it.
3.9.1. Examples¶
In file crosscountry/webapp/__init__.py, the function custom_player_stats() returns some game-specific information to the player statistics area:
def custom_player_stats() -> str:
"""Docstring hidden for this example."""
# Indicate whether team mode is on or off
if game_state.custom.teams:
return 'Teams mode: ON!'
else:
return 'Teams mode: off'