Skeletons
From Future Skill
Revision as of 12:29, 12 January 2024 by Henrik Rostedt (talk | contribs)
This article goes through how to use the skeleton classes to write implementations. This is the preferred way of creating new exercises, challenges, etc.
The article contains both a broad overview of the material as well as deep dives, it is recommended to start reading the broad sections and come back to the sub-sections as questions arise.
Why and how to use
The skeletons are base classes that handles things that most implementations will need or things that are required. The specialized skeletons also provides a bunch of functionality that otherwise would take a lot of work to implement.
The provided skeletons form a hierarchy where the children inherit functionality from the parent skeletons:
BasicChallenge
- Provides the most common functionalityStageChallenge
- Provides a GUI structure and a couple other featuresGameChallenge
- Provides a bunch of features realated to making a turn-based game
To use a skeleton the Challenge
class should inherit from the skeleton class.
Each skeleton has a number of required methods that need to be implemented.
We provide a basic template for each skeleton under respective heading, which are good starting points for creating a challange.
BasicChallenge
This is the base of all the skeletons and provide the essential functionality, which will be used by all other skeletons as well.
However, keep in mind that some skeletons do implement some of the BasicChallenge
methods for you.
Template for BasicChallenge
:
""" This is the module containing the challenge implementation. """ from lib.exceptions import SolutionException from lib.skeletons import BasicChallenge, LevelInfo from lib.ui import Rectangle, Text class Challenge(BasicChallenge): """ This is a challenge implementation using the basic skeleton. """ def setup_level(self, level): """ This method returns required level information for the current level. The `level` parameter is the level index, starting at 0. """ return LevelInfo(name=f"Level {level + 1}", max_score=10) def setup_state(self): """ This method is where you should do all initial setup, except for graphics. """ # For example, setting up a variable which can be used later on self.my_variable = 5 # Or writing a message in the console self.console.log("Hi console!") def setup_canvas(self): """ This method is where you should create the initial graphics. """ # For example, creating a square self.my_square = Rectangle(w=20, h=20, x=20, y=20, color="green", parent=self.canvas) # And putting text inside it self.my_text = Text("N", font_size=10, color="white", parent=self.my_square) def update_state(self): """ This method is where you should call solutions and update the current state. It is called continuously until `self.finished` is set to `True`. """ # When calling a solution you need to handle any `SolutionException` try: solution = self.context.solutions[0] # TODO: call a solution method except SolutionException: # Code put here will run if the solution crashed pass # This is a good place to update the scores self.scores[0] = self.my_variable # You will want to set this conditionally if your challenge involves multiple steps self.finished = True def update_canvas(self): """ This method is where you should update the graphics based on the current state. """ # For example, changing the text inside the square self.my_text.text = str(self.my_variable)
Required methods
setup_level(self, level)
- Must return a
LevelInfo
object which includes the information needed for the framework to handle the level. - The
level
parameter is the index of the current level, starting at 0. - The
LevelInfo
object has the following attributes:name
- The name of the levelmax_score
- The maximum possible score for a solutionshow_canvas
- Whether the level uses the canvas, defaults toTrue
setup_state(self)
- Setup the challenge state here.
- Will be called once at startup, after
setup_level
. setup_canvas(self)
- Setup the canvas here.
- Will be called once at startup, after
setup_state
, unlessshow_canvas
isFalse
. - Note that this method is implemented by most of the other skeletons, so do not add your own when using those.
update_state(self)
- Call solutions and update the challenge state here.
- When calling solutions you must catch any
SolutionException
s that are raised. - Will continuously be called until
self.finished
is set toTrue
, so remember to do that. - Note that once that happens no other method is called, so remember to also update the scores.
update_canvas(self)
- Make any changes to the canvas here.
- Will be called immediately after
update_state
, unlessshow_canvas
isFalse
. - Note that this method is implemented by most of the other skeletons, so do not add your own when using those.
Features
The main feature of this skeleton is reduced boilerplate and some minor conveniences. Here is a list of class members and what they are used for:
finished
- A boolean that should be set to
True
when the challenge has ended. scores
- A list of scores for each solution, indices match the solution indices.
- Scores start at 0 and must be updated manually.
canvas
- Represents the canvas itself and is the top of the graphical hierarchy.
console
- Represents the console, and can be used to log messages.
context
- Used to access various features, but most commonly used to access
solutions
. session
- Used for interact mode, see subsection.
settings
- Used for interact mode, see subsection.
Interact mode
Interact mode is an advanced feature that is used to make challenges with interactive canvases. This skeleton has a couple of conveniences related to interact mode.
TODO: finish section
StageChallenge
This skeleton adds a GUI setup called the stage on top of the basic skeleton, and some related functionality. It acts as the base for other GUI focused skeletons, so it is likely you will want to use one of them instead.
Template for StageChallenge
:
""" This is the module containing the challenge implementation. """ from lib.exceptions import SolutionException from lib.skeletons import LevelInfo, StageChallenge from lib.ui import Rectangle, Text class Challenge(StageChallenge): """ This is a challenge implementation using the stage skeleton. """ def setup_level(self, level): """ This method returns required level information for the current level. The `level` parameter is the level index, starting at 0. """ return LevelInfo(name=f"Level {level + 1}", max_score=10) def setup_state(self): """ This method is where you should do all initial setup, except for graphics. """ # For example, setting up a variable which can be used later on self.my_variable = 5 # Or writing a message in the console self.console.log("Hi console!") # Or writing a messege in the stage log self.add_log_entry("Hi log!") def setup_view(self): """ This method returns the main view for the challenge, which can be any graphics element. """ # The view will automatically be attached, resized and positioned by the framework self.my_view = Rectangle(color="green") # Any elements put inside the view will be centered inside they view self.my_text = Text("N", font_size=50, color="white", parent=self.my_view) return self.my_view def update_state(self): """ This method is where you should call solutions and update the current state. It is called continuously until `self.finished` is set to `True`. """ # When calling a solution you need to handle any `SolutionException` try: solution = self.context.solutions[0] # TODO: call a solution method except SolutionException: # Code put here will run if the solution crashed pass # This is a good place to update the scores self.scores[0] = self.my_variable # You will want to set this conditionally if your challenge involves multiple steps self.finished = True def update_view(self): """ This method is where you should update the view based on the current state. """ # For example, changing the text inside the view self.my_text.text = str(self.my_variable)
Required methods
setup_level(self, level)
- See
BasicChallenge
. setup_state(self)
- See
BasicChallenge
. setup_view(self)
- Must return a graphics element which will comprise the main view of the challenge.
- It is automatically given a parent, size and position on the canvas.
- Will be called once at startup, after
setup_state
, unlessshow_canvas
isFalse
. update_state(self)
- See
BasicChallenge
. update_view(self)
- Make any changes to the view here.
- Will be called immediately after
update_state
, unlessshow_canvas
isFalse
.
Optional methods
setup_info_panel(self)
- Similar to
setup_view
but should return an element to put on the left-hand side of the main view. - There is no equivalent to
update_view
, so any needed updates should be put there. setup_description(self)
- Should return a text string that will be put in a "Description" tab.
- This text can contain html formatting, including images.
setup_settings(self)
- Used for interact mode, see subsection in the next section.
setup_center_buttons(self)
- Used for interact mode, see subsection in the next section.
setup_right_buttons(self)
- Used for interact mode, see subsection in the next section.
Features
The main feature of this skeleton is the GUI setup with a main view and various optional features. Many features are related to interact mode, and are covered in their own subsection.
Additional features include:
- Info panel on the left-hand side of the main view, see optional methods.
- A GUI log which can be written to using
self.add_log_entry
. - A description tab with support for html formatting, see optional methods.
- A GUI settings system, see interact mode subsection.
- A toolbar at the bottom of the canvas, see interact mode subsection.
Interact mode
This skeleton provides several useful features for interact mode, some that work on top of the basic skeleton features.
TODO: finish section
GameChallenge
This skeleton adds a bunch of nice features useful when creating games (or other challenges where solutions take turns). It makes extensive use of the stage skeleton features, so only implement methods also mentioned here. Like the stage skeleton, there are plenty of features specifically for interact mode.
Template for GameChallenge
:
""" This is the module containing the challenge implementation. """ from lib.exceptions import SolutionException from lib.skeletons import LevelInfo, GameChallenge, GameInfo, PlayerInfo from lib.ui import Rectangle, Text class Challenge(GameChallenge): """ This is a challenge implementation using the game skeleton. """ def setup_game(self): """ This method returns required game information for setting up the challenge. """ return GameInfo( title="My game", summary="My game summary", description="My game description.<br>Which can use <em>html formatting</em>!", ) def setup_players(self): """ This method returns a list with required information for each player. """ return [PlayerInfo( role="Player", name="Your solution", image="TODO", )] def setup_level(self, level): """ This method returns required level information for the current level. The `level` parameter is the level index, starting at 0. """ return LevelInfo(name=f"Level {level + 1}", max_score=10) def setup_state(self): """ This method is where you should do all initial setup, except for graphics. """ # For example, setting up a variable which can be used later on self.my_variable = 5 # Or writing a message in the console self.console.log("Hi console!") # Or writing a messege in the stage log self.add_log_entry("Hi log!") # Or writing a log message associated with a player (use player index starting at 0) self.add_player_log_entry(0, "Hi player!") def setup_view(self): """ This method returns the main view for the challenge, which can be any graphics element. """ # The view will automatically be attached, resized and positioned by the framework self.my_view = Rectangle(color="green") # Any elements put inside the view will be centered inside they view self.my_text = Text("N", font_size=50, color="white", parent=self.my_view) return self.my_view def update_state(self): """ This method is where you should call solutions and update the current state. It is called continuously until `self.finished` is set to `True`. """ # When calling a solution you need to handle any `SolutionException` try: solution = self.context.solutions[0] # TODO: call a solution method except SolutionException: # Code put here will run if the solution crashed pass # This is a good place to update the scores self.scores[0] = self.my_variable # You will want to set this conditionally if your challenge involves multiple steps self.finished = True def update_view(self): """ This method is where you should update the view based on the current state. """ # For example, changing the text inside the view self.my_text.text = str(self.my_variable)
Required methods
setup_game(self)
- Must return a
GameInfo
object which includes the information needed for the framework to handle the game. - It has the following attributes (all optional):
title
- The title of the gamesummary
- A short summary of the gamedescription
- A longer description of the game, which can include html formatting including imagesstep_mode
- See dedicated subsection of the features section.
setup_players(self)
- Must return a list of
PlayerInfo
objects including the information needed for the framework to handle players. - Typically the player list will correspond with the solution list, but there might be reasons to have more or fewer players in a challenge.
- The
PlayerInfo
object has the following attributes:role
- The role of the player, will be displayed above the namename
- The name of the playerimage
- The name of the image to use for the player, will be shown in the player list and the log
setup_level(self, level)
- See
BasicChallenge
. setup_state(self)
- See
BasicChallenge
. setup_view(self)
- See
StageChallenge
. update_state(self)
- See
BasicChallenge
. update_view(self)
- See
StageChallenge
.
Optional methods
setup_statistics(self)
- See dedicated subsection in the next section.
setup_settings(self)
- See
StageChallenge
. - Note that you need to include the settings from
super().setup_settings()
. setup_center_buttons(self)
- See
StageChallenge
. step_mode_check(self)
- See dedicated subsection in the next section.
resign(self)
- Used for interact mode, see subsection in next section.
Features
This skeleton adds a number of GUI features on top of the stage UI from the stage skeleton, as well as some core features for creating games. Most features are detailed in their own subsection.
Players
The main addition of this skeleton is the concept of players, which usually correspond to the solutions.
The information provided in setup_players
will be displayed in the new left-hand side information panel,
together with a game summary.
There might be reasons to have a different number of players than solutions, but normally only in interact mode.
Statistics
The skeleton includes a system for tracking statistics for each player during a game. These statistics will be shown in the player panel under the players name.
To setup a statistic you will include a StatisticInfo
object in setup_statistics
with the following attributes:
name
- The name of the statistic (required), should be a valid python identifiersuffix
- A suffix to append in the player panel (defaults to empty)type
- The type of the statistic (defaults tofloat
), useint
for integers orstr
for stringsdefault
- The initial value for the statistic (defaults to 0)
To get or set a statistic for a player you can use self.players[i].name
where i
is the player index and name
is the statistic.
For example, setting the "points" statistic for the first player: self.players[0].points = 42
.
Interact mode
This skeleton adds a complete turn-management system which greatly simplifies interaction for games.
TOOD: finish section