WORDLE (Atari 8 Bit) by D. Scott Williamson
A downloadable game
Wordle
By D. Scott Williamson
For the PUR-80 category of the 2025 BASIC 10 Liner contest
(Warning, spoilers below!)
Table of contents:
Included Files
Startup & Emulator Instructions
Description
Rules
How to Play
Cards
Gameplay Instructions
Strategy
Program features
Dictionary (Warning, spoilers)
Data Compression
Redundancy
Bit Packing
Representation in the program
Decompression (Lines 3,4)
Creation of the dictionary
Compression script
Encoding
Verification
Audio Visuals
Font & colors
Sound
Input method
Use of conditional expressions as integers (booleans)
Proof of 10 lines less than 256 columns
Program overview
Detailed program description
Included Files
README Wordle.txt This file
WORDLE.ATR Floppy disk image containing DOS, WORDLE.BAS, and WORDLE.LST
WORDLE.BAS BASIC file that can be loaded with LOAD
WORDLE.LST BASIC Listing that can be loaded with ENTER
WORDLE.LST.TXT BASIC Listing with line endings converted to LF (Unix style) for PC editors
WORDLE Listing.png Image of program listing
WORDLE1.png Screenshot of initial game screen
WORDLE2.png Screenshot during gameplay
WORDLE3.png Screenshot of winning game
Description
Rules
The objective of the game is to guess a secret 5 letter word.
The player takes up to 6 turns, each turn the player enters 5 letters.
At the end of each turn the color of each tile will change to show you how close the guess was.
* If the tile turns green, the letter is in the word, and it is in the correct spot.
* If the tile turns yellow, the letter is in the word, but it is not in the correct spot.
* If the tile remains gray, the letter is not in the word.
At the end of the game a win or loss sound is played, the secret word is displayed at the top of the screen for 1 turn, and a new game is started.
The secret word will not be a proper noun, a place name, a plural, or a past tense of a word.
How to play
This version of the popular WORDLE word game is true to the original and features a compressed 46 word dictionary in only 10 lines of 80 characters.
Which words? You'll have to play to find out!
You'll be able to play for a long time before likely to see a repeated word!
Press letter keys (A-Z) on the keyboard to input letters
Program features
Dictionary (Warning, spoilers)
Implementing a dictionary was the greatest challenge of this game. I made sure to use a source dictionary of 2310 legitimate Wordle words and worked to select and compress as many of those words as possible in ways that are easy to index and decompress in Atari BASIC. In earlier versions of the game I had as many as 71 words in the dictionary but had to reduce the word count to 46 in order to display the secret word at the end of the game.
Data Compression
Redundancy and bit packing are used to compress the dictionary to the point where each 5 letter word is packed into two bytes except the last word which requires (half of) one additional byte.
Redundancy: Game dictionary words were selected such that the first letter of each word is the same as the last letter of the previous so words overlap by one letter. I explored overlaps of 2,3, and even 4 letters but there were not enough overlapping 5 letter words to make a sizeable dictionary.
Bit Packing: Nibble packing or packing letters into 4 bits cuts character sizes in half storing two characters in a single byte or character in a string. The limitation is that only 16 letters may be used in all the words in the dictionary (61% of the letters in a 26 letter alphabet)
I explored three types of nibble packing, or packing characters into 4 bits:
Try to find words that shared any 16 unique letters - this requires a 16 byte character lookup which takes space from the dictionary
Try to find words that use any 16 contiguous letters in the alphabet - this requires a single hard coded offset to the first letter
Try to find words that use the first 16 contiguous letters in the alphabet - most of the dictionaries that use 16 contiguous letters used the first 16 due to the frequency of the letter "A" in words. This is the compression ultimately used.
Representation in the program
The dictionary is represented in the program as a string of characters (bytes) in Q$ and a word count in N.
There is only enough room on one 80 character line to represent 34 words but there was some room on subsequent lines so I start the game with 34 words and then I ran into an unpleasant problem at one point where the string contained Atari's carriage return character or ASCII line feed, the conversion between Atari format and ANSI would harm the dictionary. In the end I was able to select a dictionary that avoided those two bytes, but until I figured out what was going on it was a real head scratcher.
Decompression (Lines 3,4)
To index a word in the dictionary string multiply the word index by 2 and that is the offset of the compressed word in the dictionary string.
To decompress a word:
Loop through 3 bytes (6 letters)
Fetch a character
Divide by 16 and truncate using INT to get the high nibble
Subtract the high nibble*16 from the input character to get the low nibble
add the offset to "A" to each nibble and store them and you've decompressed two letters
repeat for 3 bytes or 6 letters
What's even better is that since the first and last letter of each word is the same, each word is effectively 4 characters long and since characters are compressed two to a byte, each word starts on a 2 byte boundary. If this was not done more work would be needed to decompress words starting on odd or even (low or high) nibbles.
Creation of the dictionary
Compression script
I wrote a python script to explore compression options and find dictionaries. The problem of finding sequences of words where each pair of words overlap by N letters, and all words use a subset of 16 characters from a 26 character set is an NP Hard problem. An exhaustive search for dictionaries would literally take forever in any practical sense. Fundamentally it's a graph coverage problem. I used recursion, the creation of a connectivity graph between all the words in the source dictionary, greedily selecting and sorting potential words in the sequence by their impact on the symbol table, providing early exits where solutions were not easily found, and ultimately finally providing a time limit to recursions I was able to extract sizeable dictionaries.
Encoding
I used this BASIC program to encode the dictionary string (Warning Spoiler, this contains the actual game dictionary in human readable text)
REM CREATE COMPRESSED DICTIONARY STRING
10 CLR:DIM S$(200)
20 S$="NINJABACKEBABADGEAGLEKINGAFFELFINEIGHEDGELIDEMAILABELADENICHEMBEDANCEMCEENEMABIDELOPEPOCHELLOAKENOMADECALAPELEACHIPPOFFALEGALIBELILACABALIMBOMEGABLEDENIMADAMAFIAGINGAMMAHEADINGOCEANIECEA"
30 ?"1Q$=";CHR$(34);:F.I=1 TO LEN(S$) STEP 2:?CHR$(27);CHR$((ASC(S$(I))-65)*16+ASC(S$(I+1))-65);:N.I:?
Verification
I used a line like this to print out and verify the dictionary in the game to make sure it was not malformed due to special characters, newlines, or string offset mistakes
10 FOR W=0 TO (N-1)*2 STEP 2:F.I=1TO3:J=ASC(Q$(W+I)):H=INT(J/S):W$(I*2-1)=CHR$(H+E):W$(I*2)=CHR$(J-H*S+E):N.I:?W$(1,5);" ";:N.W:?
Audio Visuals
Font & colors
Atari 8 bit computer graphics are usually are limited to 4 onscreen colors, 3 colors and a background. Additional colors are available using player missile graphics, display list interrupts (which require assembly language), GTIA fat pixel modes, or special character modes in graphics 1 or 2. I chose to use the big bold Graphics 2 because it filled the screen nicely for this game and it is able to display text in 4 colors (upper case, lower case, upper case inverted, and lower case inverted) on a fifth color background. The problem is that in graphics mode 2 characters do not have a background or outline that I could use to show the tile colors, only the color of the text itself may be changed. To allow multiple background colors I copied and inverted the Atari character set (line 2), that way all the character graphics are the black Atari background or overscan color on 4 different apparent tile background colors: white, grey, yellow, and green.
Sound
Even though the original Wordle has no sound I thought there should be a little more feedback at the completion of a game so I added dynamic sound controlled by the game state to the turn loop in line 9. The loop either loops once with a 0 value for regular turns or 31 times when the game is over. The sound statement uses the loop variable and the win/not win variable to configure the sound hardware to make different sounds.
Input method
There are too many options to use the joystick and it would have been unintuitive and using BASIC's INPUT requires pressing enter after every input and validating inputs so I decided to access the keyboard device to get blocking input on individual keypresses. The keyboard device is opened for input in line 0 O.#1,4,0,"K" (OPEN #1,4,0,"K") then keys are retrieved in input loop on line 5 GET#N1,K (GET #1,K). The GET statement waits for a key and returns the ATASCII value in K which is then tested to make sure the key pressed is valid, between "A" and "Z" inclusively V=(K>63)*(K<91).
Use of conditional expressions as integers (booleans)
The result of a comparison (=,<,<=,>,>=,<>) is 1 if true and 0 if false. This can be used to create conditional calculations in without using IF ... THEN. This technique is used extensively in this program to handle input, make conditional calculations in hand ranking, and in GOTO statements to vector to intended lines based on conditions.
10 lines less than 80 columns (also see included "WORDLE Listing.png")
00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000011111111112222222222333333333344444444445555555555666666666677777777778 12345678901234567890123456789012345678901234567890123456789012345678901234567890 0DI.Q$(200),W$(11):GR.18:SE.0,0,15:SE.1,14,12:SE.2,11,10:SE.3,0,10:O.#1,4,0,"K" 1N=34:Q$="ØÙ¤6@kJ`UKXÔ†t6KƒL°°4Ø'L0ÒL$MLƒKïOât»à¤ÞÀ4 °ô´xÿåP´`¸¸° ¸Á 2E=225:S=16:?#6;"WORDLE":F.I=0TO896:POK.10240+I,255-PEEK(57344+I):N.I:POK.756,40 3W$="":W=INT(RND(0)*N)*2:F.I=1TO3:J=ASC(Q$(W+I)):H=INT(J/S):W$(I*2-1)=CHR$(H+E) 4W$(I*2)=CHR$(J-H*S+E):N.I:F.Y=3TO8:POS.7,Y:?#6;"€€€€€":N.Y:R=3:C=6:Z=0 5GET#1,K:V=(K>63)*(K<91):K=V*(K+160)+(1-V)*128:W$(C)=CHR$(K):POS.C+1,R:?#6;W$(C) 6Y=0:G=0:F.I=1TO5:M=CHR$(K)=W$(I,I):Y=Y+(1-Y)*M:G=G+(I=C-5)*M:N.I:Z=Z+G 7W$(C)=CHR$(K-Y*128+G*96):C=C+V:O=((R=8)OR(Z=5))*2:Q$(71)="ìF´4ØÀ":G.5+3*(C=11) 8POS.7,1:?#6;" ":IF O THEN POS.7,1:?#6;W$(1,5):N=46:Q$(78)="0ÀX`Ì@8Öâ@ØB@ 9F.I=O*15TO0STEP-1:POS.7,R:?#6;W$(6):SO.0,9+I,10*(Z=5),I:C=6:N.I:Z=0:R=R+1:G.5-O Program Overview:
Line 0 Dimension strings, initialize graphics, initialize input device
Line 1 Initialize initial dictionary with first 34 words
Line 2 Initialize variables, print title, setup custom font
Line 3 Game loop, select random word from dictionary and begin decompression
Line 4 Finish decompressing word, clear letter playfield, set up game variables
Line 5 This is the main input loop, get key, validate input,
store and display character entered
Line 6 Evaluate input character to determine if it should be
yellow (in the word) or green (in the right place)
Line 7 Update character in the string to display correct color when printed later,
extend the dictionary string by 7 bytes, test for game over and loop
Line 8 At this point 5 characters have been entered and a line is complete,
display colored line, check for game over and display secret word,
add 16 bytes to the dictionary increasing size to 46 words
Line 9 Print the colored letter string for end of turn, make sound if the game is over,
take another turn if the game is not over, else start a new game
Detailed program description:
VARIABLES
Q$ String holding the compressed dictionary
N Number of words in the dictionary (starts with 37 and is expanded to 46 after the first turn)
W Word index in dictionary string, randomly selected on line 3
W$ String containing decompressed word, and letters input from the player
E Encoded index to first letter of decompressed word, inverse lower case
S Sixteen
K Key input
V Whether a key input is valid
I Loop variable
J Temporary variable used to hold nibble pair during word decompression
H High nibble of byte from dictionary
Y Vertical location on the screen in loop on line 4, and whether letter should be yellow on line 6 (in word in wrong place)
R Row on screen for printing letter inputs
C Character index in word (W$), also used to position cursor for printing characters
M Match, 1 if a character matches, 0 if it does not
G Whether character should be green on line 6
Z Number of Green characters in a word, if all 5 letters are in the correct place the player wins
O Whether the current game is over - either all letters are correct or 6 failed attempts
Expanded listing (one instruction per line, abbreviations expanded, functional description)
Line 0 Dimension strings, initialize graphics, initialize input device
DIM Q$(200),W$(11) Dimension dictionary and word strings
GRAPHICS 18 Graphics mode 2 large text +16 with no high resolution text window
SETCOLOR 0,0,15 Set background color to white
SETCOLOR 1,14,12 Set yellow color
SETCOLOR 2,11,10 Set green color
SETCOLOR 3,0,10 Set grey color
OPEN #1,4,0,"K" Open the keyboard device for reading keys with GET
Line 1 Initialize initial dictionary with first 34 words
N=34 Set number of words in the dictionary to 34
Q$="ØÙ¤6@kJ`UKXÔ†t6KƒL°°4Ø'L0ÒL$MLƒKïOât»à¤ÞÀ4 °ô´xÿåP´`¸¸° ¸Á
Initialize dictionary
(Closing quote not needed at end of line in Atari BASIC, full 80 character line used)
Line 2 Initialize variables, print title, setup custom font
E=225 Set the encode value for decompressing letters from the dictionary
S=16 Set sixteen variable
PRINT #6;"WORDLE" Print the title
FOR I=0 TO 896 Loop through 112 characters of font
POKE 10240+I,255-PEEK(57344+I) Copy characters from ROM to RAM and invert bits so backgrounds may be colored
NEXT I Loop
POKE 756,40 Set CHBASE to new font location in RAM (8192 = 32*256)
Line 3 Game loop, select random word from dictionary and begin decompression
W$="" Clear the word string
W=INT(RND(0)*N)*2 Select a random word start location in dictionary
FOR I=1 TO 3 Loop to decompress 6 letters from dictionary (only 5 are used)
J=ASC(Q$(W+I)) Fetch a byte from the string
H=INT(J/S) Isolate the high nibble by dividing by 16 and truncating with INT
W$(I*2-1)=CHR$(H+E) Add encoding offset to nibble, convert to character and place in word string
Line 4 Finish decompressing word, clear letter playfield, set up game variables
W$(I*2)=CHR$(J-H*S+E) Remove the high nibble from the byte, add encoding offset, and place in word string
NEXT I Loop
FOR Y=3 TO 8 Loop to draw/clear the grey word box for the turn
POSITION 7,Y Position the cursor
PRINT #6;"€€€€€" Print 5 inverted spaces as grey boxes
NEXT Y Loop
R=3 Set the row to 3, this is row on the screen during the turn, gameplay ranges from 3 to 8
C=6 Set the column on the screen during the turn, gameplay ranges from 6 to 10
Z=0 Set the win detect (green count) to zero
Line 5 This is the main input loop, get key, validate input, store and display character entered
GET #1,K Fetch keyboard input
V=(K>63)*(K<91) Calculate key validity, will be 1 if A-Z are pressed, 0 otherwise
K=V*(K+160)+(1-V)*128 Update the key to be either a valid encoded letter or space
W$(C)=CHR$(K) Set the character in the input part of the word string to validated encoded k
POSITION C+1,R Position the cursor
PRINT #6;W$(C) Print the key black on grey
Line 6 Evaluate input character to determine if it should be
yellow (in the word) or green (in the right place)
Y=0 Set number of yellows to 0
G=0 Set number of greens to 0
FOR I=1 TO 5 Loop through the characters in the word string
M=CHR$(K)=W$(I,I) Set the match variable if the last input character matches the character at index I
Y=Y+(1-Y)*M Yellow will be set to 1 if the character matches any position
G=G+(I=C-5)*M Green will be set to 1 only if the character matches the position where index I equals the current column
NEXT I Next loop
Z=Z+G
Line 7 Update character in the string to display correct color when printed later, test for game over and loop
W$(C)=CHR$(K-Y*128+G*96) Subtracting 128 will remove invert and cause background to be yellow, adding 96 will go to upper case causing the color to be green
C=C+V Advance to next column only if the input character was valid, if not just repeat in the current column
O=((R=8)OR(Z=5))*2 O is 2 if the game is over; if the current row is 8 or if all five letters are green
Q$(71)="ìF´4ØÀ" Add 7 bytes to dictionary
GOTO 5+3*(C=11) Input loop, if column is not 11 loop back to line 5 for more input, otherwise continue to next line 8
Line 8 At this point 5 characters have been entered and a line is complete, display colored line,
check for game over and display secret word, add 7 words to the dictionary increasing size to 46 words
POSITION 7,1 Position the cursor at the top of the screen
PRINT #6;" " Clear the previous word area of the screen
IF O THEN If the game is over...
POSITION 7,1 If the game is over position the cursor at the top of the screen
PRINT #6;W$(1,5) Print the secret word
N=46 Increase dictionary size to 46 words
Q$(78)="0ÀX`Ì@8Öâ@ØB@ Add 16 bytes to the dictionary string
(Closing quote not required at end of line in Atari BASIC, full 80 character line used)
Line 9 Print the colored letter string for end of turn, make sound if the game is over,
take another turn if the game is not over, else start a new game
FOR I=O*15 TO 0 STEP -1 Loop to make sound at end of game, either one iteration of I=0, or 31 iterations from 30 to 0
POSITION 7,R Position the cursor to print the color coded user input word (in the loop to slow down sound at end of game)
PRINT #6;W$(6) Print the color coded user input string (in the loop to slow down sound at end of game)
SOUND 0,9+I,10*(Z=5),I Make sound:
If the game is not over, there is only an I=0 loop setting a sound with volume 0
If the game is over I loops 30 to 0 which rises pitch (9+I) and decreases volume (I), the distortion is set to 0 for noise if the player didn't win, and pure tone if the player matched all 5 letters (10*Z=5)
C=6 Set the column (C) to 6 for the next turn (in the loop to slow down sound at end of game)
NEXT I Complete sound loop
Z=0 Set the count of letter matches (green letters) to 0 for next turn
R=R+1 Advance to next row on the screen
GOTO 5-O If the game is won GOTO line 3 to start new game, otherwise GOTO line 5 to take next turn
Download
Install instructions
Startup & Emulator Instructions
1. Run Atari800Win PLus
2. Set Machine Type to XL/XE
3. Set Video System to NTSC (not strictly necessary)
4. Make sure BASIC is enabled ("Disable BASIC when booting" is disabled in settings
4. Alt-D or drag and drop to attach "Wordle.atr" to the emulator
5. Shift-F5 to reset the emulator which will now be in BASIC at the READY prompt with the disk loaded in D1:
6. Type ENTER"D1:WORDLE.LST" and press enter to load the program (alternate LOAD"D1:WORDLE.BAS")
7. (optional) type "LIST" to see listing
8. type "RUN" to run the program
Comments
Log in with itch.io to leave a comment.
very good