Memory Mat

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

Memory Mat is a concentration-style card-matching game in which the player must find all 26 pairs among 52 face-down playing cards laid out in a 10×6 grid. The program defines four custom UDG characters for the card suit symbols (Hearts, Clubs, Diamonds, Spades) using POKE USR, then shuffles a 52-element array using a Fisher-Yates-style random swap loop. Cards are displayed as inverse-video numbered tiles; selecting a pair reveals the card value and suit, with red ink used for Hearts and Diamonds and black for Clubs and Spades. The game tracks both the number of choices made and pairs found, displaying them on screen throughout play, and uses BEEP tones to signal correct matches, mismatches, and game completion.


Program Analysis

Program Structure

The program is organized into a main flow (lines 10–380), two display subroutines (lines 500–540 and 800–820), a grid-position calculator (lines 600–630), and standard housekeeping at lines 9997–9998. Initialization occupies lines 10–90, the screen layout is drawn at lines 120–150, and the game loop runs from lines 200–380.

Line rangePurpose
10Set screen attributes
20–30Define UDGs for H, C, D, S suit symbols
40–90Data setup, shuffle, and suit/value extraction
120–150Draw card-back grid using block graphics
160–380Main game loop: input, validation, reveal, scoring
500–540Subroutine: reveal a card face
600–630Subroutine: calculate screen row/column from card number
800–820Subroutine: redraw a card face-down

UDG Suit Symbols

Lines 20–30 use a READ/POKE USR loop to load 8-byte bitmap data for four UDG characters. The suit letter ("H", "C", "D", "S") is read as a string, and USR x$ gives the address of its UDG bitmap in RAM. Line 60 converts these letters to their display character codes by adding 79 (CODE s$(i)+79), mapping the letters A–S range to UDG character codes 144–162, and stores them in the s() array for later use in card display.

Deck Shuffle

Lines 70–80 implement a shuffle: array c() is first filled sequentially 1–52, then each element is swapped with a randomly chosen element using INT(52*RND)+1. This is a variant of the Knuth (Fisher-Yates) shuffle, though it swaps with any position 1–52 rather than the canonical 1–i range; the result is still a reasonable randomization for gameplay purposes.

Suit and Value Extraction

Line 90 unpacks each shuffled card number into a suit index t(i) and a value index v(i) using integer division. t(i) = INT((c(i)-1)/13)+1 gives suits 1–4, and v(i) = c(i) - 13*(t(i)-1) gives values 1–13. The value string v$="A23456789TJQK" maps index positions to standard card face characters, with T representing the Ten.

Grid Layout and Position Calculation

The 52 cards are arranged in a 10-column by 6-row (with a partial last row) grid. The subroutine at lines 600–630 computes screen coordinates from a card number n: the tens digit determines the row (l1 = 1 + 3*x10) and the units digit determines the column (c1 = 3*x1 - 2). A special case at line 605 handles multiples of 10 by adjusting x10 and x1. Each card cell is 3 characters wide and 3 rows tall, giving a tightly packed but readable layout.

Card Face Display

Subroutine 500 reveals a card at position (l1, c1). Ink color is set conditionally: INK 2-(sn=2*INT(sn/2))*2 evaluates to INK 2 (red) when sn is even (suits 2 and 4, i.e. Clubs and Spades in this encoding) and INK 0 (black) otherwise, effectively coloring Hearts and Diamonds red. The value character is printed from v$(vn) and the suit symbol from CHR$ s(sn). A PLOT/DRAW sequence at line 530 draws a border rectangle around the revealed card cell in pixel coordinates.

Input Validation

The game validates each entered number with a chain of checks: non-integer or out-of-range values (lines 210, 250), already-paired cards where v(p)=0 (lines 220, 260), and identical pair entries (line 270). Each invalid case re-issues an appropriate INPUT prompt in a tight local loop rather than branching to a central error handler.

Card Concealment

Subroutine 800 redraws a card face-down by printing the card number in INVERSE 1 mode. Line 800 includes a conditional extra print of the block graphic \:: (a solid block) for single-digit numbers, ensuring the display is visually consistent regardless of whether the number occupies one or two character positions. Line 810 fills the second row of the card cell with a solid block graphic.

Matching Logic and Scoring

After both cards are revealed, line 300 compares v(p1) and v(p2). A match sets both to 0 (marking them as paired), increments pr, and plays a two-note ascending beep. A mismatch plays a low beep, pauses 150 frames (~6 seconds at 25 fps), then calls subroutine 800 twice to re-conceal both cards. The game ends when pr=26 (all 26 pairs found), reporting the total number of choices.

Notable Techniques

  • Using CODE s$(i)+79 to convert suit letter codes to UDG display codes avoids a lookup table.
  • The PLOT/DRAW card border in subroutine 500 uses the formula c1*8-1 and 178-8*l1 to convert PRINT AT coordinates to pixel coordinates.
  • Setting v(i)=0 to mark matched pairs reuses the value array as both a game-state flag and a value store, saving a separate matched/unmatched array.
  • The screen background is drawn using repeated \::\:: block graphic strings (solid blue squares on PAPER 4) to simulate face-down card backs before individual inverse numbers are overlaid.

Potential Issues

  • The color assignment logic in line 500 (INK 2-(sn=2*INT(sn/2))*2) colors even-numbered suits red and odd-numbered suits black. Whether this correctly assigns red to Hearts and Diamonds depends on the suit ordering established by the string "HCDS" (H=1 odd=black, C=2 even=red, D=3 odd=black, S=4 even=red), which reverses the conventional red/black mapping. Hearts and Spades would appear black; Clubs and Diamonds red.
  • The shuffle at line 80 iterates i from 1 to 52 but swaps with any random index 1–52, meaning position i can be revisited and un-shuffled in a later pass. This is a known slight bias but does not affect playability.
  • Lines 120–130 use PAPER 4 for the blue card-back grid, but line 140 switches to PAPER 7 only for the border drawing and does not restore PAPER 4, so subsequent attribute changes depend on the current state at that point.

Content

Appears On

The biggest LIST tape yet — play poker accompanied by chip-tune renditions of classic songs, diagnose illnesses with a Bayesian expert system, master Z80 opcodes with a quiz, or unscramble anagrams before the cauldron boils over. Four custom fonts included.

Related Products

Related Articles

Related Content

Image Gallery

Memory Mat

Source Code

   10 PAPER 7: INK 0: FLASH 0: BORDER 7
   20 FOR i=1 TO 4: READ x$: FOR j=0 TO 7: READ x: POKE USR x$+j,x: NEXT j: NEXT i
   30 DATA "H",34,119,127,127,62,62,28,8,"C",28,28,8,107,127,107,8,8,"D",8,28,62,127,62,28,8,0,"S",8,28,62,127,127,127,107,8
   40 LET s$="HCDS": LET v$="A23456789TJQK"
   50 DIM c(52): DIM t(52): DIM v(52): DIM s(4)
   60 FOR i=1 TO 4: LET s(i)=CODE s$(i)+79: NEXT i
   70 FOR i=1 TO 52: LET c(i)=i: NEXT i
   80 FOR i=1 TO 52: LET r=INT (52*RND)+1: LET z=c(i): LET c(i)=c(r): LET c(r)=z: NEXT i
   90 FOR i=1 TO 52: LET t(i)=INT ((c(i)-1)/13)+1: LET v(i)=c(i)-13*(t(i)-1): NEXT i
  120 PAPER 4: PRINT TAB 31: FOR i=1 TO 5: FOR j=1 TO 2: PRINT " \::\:: \::\:: \::\:: \::\:: \::\:: \::\:: \::\:: \::\:: \::\:: \::\:: ": NEXT j: PRINT TAB 31: NEXT i
  130 FOR j=1 TO 2: PRINT " \::\:: \::\:: ": NEXT j: PRINT TAB 7
  140 PAPER 7: PLOT 0,175: DRAW 0,-151: DRAW 55,0: DRAW 0,24: DRAW 192,0: DRAW 0,127: DRAW -247,0
  150 FOR n=1 TO 52: GO SUB 600: PRINT AT l1,c1; INVERSE 1;n: NEXT n
  160 LET ch=0: LET pr=0: PRINT AT 16,8;"CHOICES: 0   PAIRS: 0"
  200 PRINT AT 18,8;"               ": INPUT "Enter pair: (1)=";p1
  210 IF p1<>INT p1 OR p1>52 OR p1<1 THEN INPUT "Invalid - reenter (1)=";p1: GO TO 210
  220 IF v(p1)=0 THEN INPUT "Already paired - reenter (1)=";p1: GO TO 210
  230 LET n=p1: GO SUB 600: GO SUB 500
  240 INPUT "             (2)=";p2
  250 IF p2<>INT p2 OR p2>52 OR p2<1 THEN INPUT "Invalid - reenter (2)=";p2: GO TO 250
  260 IF v(p2)=0 THEN INPUT "Already paired - reenter (2)=";p2: GO TO 250
  270 IF p1=p2 THEN INPUT "Pair must be separate - reenter (2)";p2: GO TO 250
  280 LET n=p2: GO SUB 600: GO SUB 500
  290 LET ch=ch+1: PRINT AT 16,17;ch
  300 IF v(p1)<>v(p2) THEN GO TO 340
  310 PRINT AT 18,8; INK 7;"Correct choice!": LET v(p1)=0: LET v(p2)=0: LET pr=pr+1: PRINT AT 16,28;pr: BEEP .5,25: BEEP .5,15
  320 IF pr=26 THEN PRINT AT 20,0;"You have finished after ";ch;" turns": BEEP .5,20: BEEP .5,25: BEEP .5,20: GO TO 360
  330 GO TO 350
  340 PRINT AT 18,8;"Do not match ": BEEP 1,-15: PAUSE 150: GO SUB 800: LET n=p1: GO SUB 600: GO SUB 800
  350 INPUT "Press ENTER for next go"; LINE z$: GO TO 200
  360 PAUSE 150: INPUT "Press 1 to repeat or 0 to stop"; LINE z$
  370 IF z$="1" THEN RUN 
  380 STOP 
  500 PAPER 7: LET vn=v(n): LET sn=t(n): INK 2-(sn=2*INT (sn/2))*2
  510 PRINT AT l1,c1;v$(vn);" ";AT l1+1,c1;" ";CHR$ s(sn)
  520 INK 0
  530 PLOT c1*8-1,178-8*l1: DRAW 17,0: DRAW 0,-17: DRAW -17,0: DRAW 0,17
  540 BEEP 1,5: RETURN 
  600 LET x10=INT (n/10): LET x1=n-10*x10
  605 IF x1=0 THEN LET x10=x10-1: LET x1=10
  610 LET l1=1+3*x10
  620 LET c1=3*x1-2
  630 RETURN 
  800 PRINT AT l1,c1; INVERSE 1;n:  IF n<10 THEN PRINT "\::"
  810 PRINT AT l1+1,c1;"\::\::"
  820 RETURN 
 9997 STOP 
 9998 SAVE "MEMORY MAT" LINE 1

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top