--- title: "Memory Mat" id: 55198 type: "computer_media" slug: "memory-mat" url: "http://localhost/computer_media/memory-mat/" markdown_url: "http://localhost/computer_media/memory-mat.md" published_at: "2024-06-20T11:49:58+00:00" modified_at: "2026-03-31T18:28:43+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/06/memory-mat.png" excerpt: "Flip numbered tiles to uncover playing cards and match all 26 pairs in this concentration game that tracks your choices and rewards memory." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 55220 title: "Long Island Sinclair Timex (LIST) User Group Library Tape #7" type: "computer_media" url: "http://localhost/computer_media/long-island-sinclair-timex-list-user-group-library-tape-7/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Memory%20Mat%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/memory-mat.png" media_type_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 range | Purpose | | --- | --- | | 10 | Set screen attributes | | 20–30 | Define UDGs for H, C, D, S suit symbols | | 40–90 | Data setup, shuffle, and suit/value extraction | | 120–150 | Draw card-back grid using block graphics | | 160–380 | Main game loop: input, validation, reveal, scoring | | 500–540 | Subroutine: reveal a card face | | 600–630 | Subroutine: calculate screen row/column from card number | | 800–820 | Subroutine: 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. ## 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 ```