Friday, 15 December 2023

Matching Pairs\The Dyslexia Advantage

 It turns out there are some advantages with Dyslexia.

The Advantage

I recently came across this YouTube video.


It starts with how Dyslexic people can be better at identifying impossible shapes (like a Necker-Cube), but it turns out that there are a number of cases where dyslexic people are better at processing images as a whole; or containing scattered information; or interpreting information at the periphery of their vision.

And part of this is the trade-off from the relatively short period since humanity developed writing, compared with the relatively long period where a variety of visual abilities were more advantageous.

The interesting thing for me is that it made me think about why why my younger sister (who has some degree of Dyslexia) always used to beat me at card games, starting with Matching Pairs, even when we were really young. And this is perhaps a reason why.

Matching Pairs

I'd have a basic strategy for Matching Pairs, and I needed a strategy, because I couldn't remember the layout of the images when flipped over; nor the set of cards that had been flipped.

So, my strategy was that firstly, if I couldn't remember where two matching cards were (which was the normal case), then I'd randomly turn a card that I didn't think had been picked before (or recently). So, if it was familiar, I could guess at picking a matching card. However, if it wasn't familiar, I'd then try to pick another card that wasn't in the set of what I thought were recent cards, because then there's a better chance of picking a matching card.

The trouble was that even if the first card was familiar, I'd never know where the matching card was, I'd merely have a feeling about the general area. That meant I was wrong most of the time.

Reflecting on the way my memory works - which is visual, is somewhat like this:

Imagine in Matching pairs your whole visual field either contains a single card turned over, or there's a grid of unturned cards; or there are two turned cards and you can see how far one is away from the other, but not able to remember their absolute positions.

Then in the history of the game so far, you can flag up the areas where cards have been found, but each of those positions could contain any one of the cards seen so far. That's why it's challenging for me even though it's such a simple game.

For someone with Dyslexia, it could be an easier game, because they'd have a better sense of the whole playing area for the cards that have been turned over: rather like being able to visualise it in that state. Hence my sister was much better at playing the game than me.

ZX81 Version

Because it's such a simple game, it didn't take me long to imagine how I'd write a version in BASIC for a ZX81. Here's the listing for it. You can play it yourself by going to the ZX81 Javascript emulator and typing in the listing below:

Note: In line 5, the 16 graphics characters are: <space><graphic 1>..<graphic 7><graphic Q>..<graphic Y><graphic space>.



Playing The Game

The game works as a two-player game. A grid of 6 x 6 (i.e. 36) tiles are generated, each of which contains 18, unique pairs of random 4x4 pixel patterns. They're initially shown all face-down with the digits 0..9, then A..Z in the top left-hand corner of each tile.

Player 1 then types one of these characters (e.g. '0') and that tile is turned over. Then Player 1 types another character (e.g. 'J') and its tile is turned over. If the tiles match, Player 1's score is incremented otherwise the tiles remain turned over for 2 seconds to give the user a chance to memorise them before being turned back.

Player 2 always has the next go and they choose the codes for two overturned tiles in sequence as per Player 1.

Play returns to player 1 and both players repeat the process. Over time, more and more pairs are found.



The game finishes when all the tiles have been matched. The player with the most overturned tiles wins (or if both have 9 pairs each, it's a draw).

This version of the game is good for exploring players' relative strengths. In particular, one of the differences between this game and a classical Matching Pairs game is that the patterns on each tile are abstract, because they're just a random arrangement of 4x4 pixels rather than familiar images. This makes it easier for people who can process abstract images more easily.

The Automatic Game

It's fairly easy to change the game so that the computer plays itself. In this version, the program selects a random character in the right range, as though it was typed and then the simulation plays out just as it would with human players. We don't need to pause at the end of every go where there's no match, but we do need to make sure that the computer doesn't try to pick the same tile twice, nor overturn tiles that are already matched: so we set matched pairs to '<space>' characters and build in an extra rule.


There are three things I really noted about the automatic game. Firstly, the ZX81 is terribly slow. Even though its effective reaction times are much faster than a human, I found it would take over 20 minutes to solve a game, about 3 or 4 times slower than a pair of humans.

Secondly, even though I consider myself to have a terrible memory for this kind of game, the automatic version shows that I do in fact remember the patterns much better than my personal algorithm described above implies: I would end up remembering the positions of several pairs while the computer was repeatedly choosing the wrong second tile for one I already knew. Some of this came from the sheer repetition of wrong moves by the computer, but it still demonstrated that I was in fact memorising locations even though I thought I didn't.

Thirdly, watching it was quite pleasantly calming and after several minutes I struggled to stay awake! Yay for boring programs as a cure for insomnia!

Program Analysis

I originally wanted the game to fit in 1kB, but that turned out not to be possible - at least I don't yet know how to squeeze it down. The two-player code itself is about 1124 bytes long if I remove the REM'd statements in lines 83 and 86, but include the extra automatic game checking in lines 125, 130, 203 and 206. To do this it has all the normal short-cuts like NOT PI (0), SGN PI (1), CODE "X" for some values in the range 10..255 and VAL "X" for others.

Generating Patterns

The first challenge with the game was to create the random set of patterns. I didn't store any fixed pictures, because the ZX81 has such terrible graphics anyway it hardly seemed worthwhile and also, it would take up memory I didn't want to waste.

So, instead I created 4x4 block patterns. The first issue then is how to create random patterns without duplicating them - the ZX81 is slow, so if I was checking each new pattern against all the previous ones, it would take O(n²) time, up to about 1000 checks. Instead what I did was simply generate a random number and then use the ZX81's 16-bit System Variable: SEED to generate the pattern directly, since every time it executes RND, SEED is updated, but it goes through all the 65536 binary values before repeating itself: hence I didn't need to check for repeats. I stored each 16-bit random pattern in a pair of 8-bit characters; so I needed an array of 18, 2-byte strings (M$) to do this.

The second challenge was to make sure I created proper pairs of values in a random order. To do this I first split the card generation into two levels: the 18 card patterns, which were 16-bits, and the cards themselves, which are indexes to those patterns and only need to be 8-bits.

For those, I adapted my card-shuffling algorithm. I essentially created a string containing a list of pairs of cards: AABB..RR (the first to 18th letter). Then I picked & removed a random card from the 'remaining' set of sorted cards; and placed it at the beginning of the string after the previous random cards. Then defined the remaining set as the set of following cards (which is just the n+1th card onwards after n cards have been picked).

This card shuffling algorithm guarantees that all the cards get shuffled, whereas a more literal algorithm would leave some pairs of cards still in order, if they didn't happen to be picked to be shuffled.

Displaying Tiles

I wanted to be able to display back-facing tiles without using any graphics characters used by tiles themselves and without spaces between, so I used ':'s for the main background and
 graphic characters between them. The routine for displaying tiles is a bit convoluted as it involves a loop for each row, but it saves on 3 different AT calculations. Perhaps, a single Print AT would have been better (and faster than the loop).

For the front side I didn't need to consider the display of the  separators, because they've been displayed earlier. All I needed to do was display each row, which was fairly easy as each row is 8-pixels. So, I obtained the pattern from the tile in P$ indexed by the position (1..36). This gave me a two character M$ string, one character per row. Then I extracted each nibble and indexed that into the graphics patterns in C$.

The Y, X print AT calculations were common to both front and back facing tiles, so I factored that. None of this is fast, but it's fast enough for playing interactively.

A 1K Version

It always seems that I should be able to squeeze a ZX81 program into 1K, but perhaps the only way to do this here is to resort to machine code. The screen takes up about 20 chars per row x 21 rows + 4 = 424 bytes. The patterns and tiles take up 36 x 2 bytes = 72 bytes and the graphics take another 16 = 88 bytes.

This means the total space in BASIC is about 1124+424+88 = 1636, which means it ought to fit in a Timex Sinclair 1000 as it has 2kB instead of 1kB of RAM.

It might be shorter to compute patterns from scratch by resetting SEED each time. The ZX81 has about 800 or something bytes free, so this leaves about 300 bytes for the program - pretty tight, but at least it wouldn't take too long to write ;-) . And I could use things like the printer buffer for some extra space if needed.