The code for the Memory game is thoroughly commented with explanations of what each line or section are for. This page will provide a higher level summary of the major components.
Hardware Principals
This game is designed around two primary hardware peripherals: the HSTX connector with a DVI breakout for the display, and a basic USB mouse for the player control input.
HSTX Display
First the code will attempt to use the built-in display from supervisor.runtime.display
. If that doesn't work, then it will initialize the display using the built-in core modules picodvi
, and framebufferio
. These modules support a few different resolutions and color depths. This project is made for the 320x240 resolution with 16 bit color depth. The pixels are automatically doubled before being pushed to the display, so it will come out as 640x480, depending on your monitor or TV, it may further upscale it to fit the screen.
If you haven't already, be sure to add CIRCUITPY_DISPLAY_WIDTH=320
to your settings.toml file to ensure the correct configuration.
USB Mouse
The USB mouse setup, data reading, and cursor movement are all documented on the Mouse Input guide page. See that page for more details.
Helper Functions
The code contains two helper functions: random_selection()
, and update_score_text()
.
-
random_selection()
Accepts list and count arguments. It returns a list of items randomly selected from the given list with a length ofcount
. This is used to select a number of cards at random out of the pool of all possible cards. -
update_score_text()
Is a display helper function that will update the visual score text on the display of both players to reflect their current score.
State Machine
The code is based upon a state machine. There are 3 states that it can be in:
- Title state - shows the title screen and waits for a click of the mouse anywhere on the screen. When click occurs the state is changed to playing.
- Playing state - The primary gameplay occurs during this state. Players take turns flipping cards. Once there are no more cards to flip, the state is changed to gameover.
- Gameover state - The gameover message is shown along with buttons that can be clicked to play again or quit.
Player Specific Variables
There are a number of player specific variables stored in lists indexed with the current_player_index
values as described on the multi-player turns mechanic page.
-
score_lbls
list will contain a Label instance for each player which will get added tomain_group
to be shown on the display. Each player'sscore_lbl
will show their score during the gameplay. -
colors
list will contain a hex color code for each player. By default these are pink0xff00ff
for player index0
, and green0x00ff00
for player index1
. Feel free to change the colors to suit your own style. The mouse cursor and text indicator at the top of the screen will be set to these colors to indicate which player's turn it is. -
player_scores
list will contain the numerical score of each player.
Initializing Cards
First the code builds up pool
, a list of indexes within the spritesheet, each index representing one of the possible cards. Initially the list is created with two copies of each possible card index. Two of each are desired so that there are guaranteed to be matches for every card used.
There are only 8 different cards, which makes 16 total cards once they are each paired up with a match. The grid is 6x4 which is 24 cards, so an additional 8 cards or 4 pairs are needed in order to fill out the rest of the grid. 4 card indexes are chosen at random using the random_selection()
function, then those 4 plus a duplicate of each are added to the pool
list to bring it up to a total of 24 card indexes.
The cards are arranged visually using a GridLayout
which takes a height
and width
in pixels, as well as a grid_size
in cells and automatically arranges the content added to it in a grid. A nested for loop of 4 rows by 6 columns is used to create a TileGrid
for each card and add it to the grid at the appropriate cell. All TileGrid
s are created with default_tile=10
which is the face down card tile index within the spritesheet.
Within the nested loop the code also chooses and removes a random card index from the pool
and adds it to the card_locations
list. card_locations
serves as the "key" to the location of the cards. It keeps track of which card is where on the board so that when a card is clicked we can change to the appropriate sprite to reveal which card it was.
Flipping Cards
A list named cards_flipped_this_turn
stores the TileGrid
indexes of the cards that have been flipped over this turn. When the first card is clicked it's index within the grid is added to this list, when the second card is clicked its index is added as well, then the code compares the sprite indexes of the TileGrid
s at the grid indexes stored within cards_flipped_this_turn
. If they match, a point is awarded to the current player and the cards are removed. If they don't match, there is a small delay before they are flipped back over by changing their TileGrid
sprite index back to 10
, the face down card sprite. The WAIT_UNTIL
variable is used to store the timestamp of time in the future when the cards will get flipped back down. The delay gives the players time to see the cards and try to commit their location to memory.
Check If the Game Is Over
To determine whether the game is over, the code will see if the sum of both players score is equal to the total size of the grid divided by two. For the default board of 6x4, this means the code is looking for the sum of scores to be 12
. When it is, the code knows that the game is over because twelve pairs have been found which accounts for all 24 cards we used.
The code checks if one score is higher than the other and sets the gameover
message accordingly, including a message for tie game if the scores are the same.
Page last edited April 03, 2025
Text editor powered by tinymce.