Page 1 of 1

WorDOSle -- Wordle for DOS made in QB!

Posted: Wed Feb 23, 2022 4:05 am
by Ham62
Hadn't touched QB in a few years, so I decided to dust off the old IDE and give this project a go.

Introducing WorDOSle!
This is a clone of the popular web game Wordle but written in QuickBASIC for DOS.
Now you can finally play Wordle on your Tandy 1000 or that old 286 sitting in the closet!

All in all, the game is just over 300 lines and was written in a single afternoon.
I made the game using QuickBASIC PDS 7.1, but it should run on QB45, or even QBasic if you comment out the COMMAND$ line.

Source available on my website:
http://grahamdowney.com/software/WorDOSle/WorDOSle.htm
WorDOSle Win.png
WorDOSle Win.png (8.02 KiB) Viewed 8539 times

Re: WorDOSle -- Wordle for DOS made in QB!

Posted: Wed Feb 23, 2022 10:17 am
by MikeHawk
Nice! Can I make three suggestions? They are trivial to implement, except for the last one.

In GetWord$, use the file size to obtain the number of words in the dictionary rather than using a hard-coded value (so words can be added or removed without modifying the program.)

Allow the player to give up by pressing escape (ASCII code 27.) It should only be a matter of converting GetGuess to a function (when it returns -1, leave the "for" loop as if all attempts were made, so we get to see the word and have the "play again" prompt.)

Now for the tricky part: Wordle only allows the user to input existing words, while your version allows completely random letter sequences. It is possible to test the input word against the dictionary with a binary search (it's super fast but requires the word list to be alphabetically ordered.) It would be preferable to store the whole dictionary in memory to make the search faster while it will obviously increase the initial loading time so I understand if you don't want that.

I did some legwork if you're interested...

Code: Select all

DEFINT A-Z

DECLARE FUNCTION openlist% (filename AS STRING)
DECLARE FUNCTION wordIndex% (myWord AS STRING)

REDIM SHARED wordLst(0) AS STRING * 5

DIM user AS STRING, id AS INTEGER


PRINT "Loading list..."
IF (NOT openlist%("words.dat")) THEN PRINT "Something went wrong": END

' picking a random word from the list...
PRINT "picking a random word: "; CHR$(34); wordLst(INT(RND * (UBOUND(wordLst) + 1))); CHR$(34)

' testing user input (only accept existing words)
DO
  INPUT "Guess a word:", user
  IF LEN(user) THEN
    id = wordIndex%(user)
    IF (id < 0) THEN
      PRINT "This word is not in the dictionary"
    ELSE
      PRINT "This word is index #"; id
    END IF
  ELSE
    EXIT DO
  END IF
LOOP

' Open word list. The list MUST be formatted and orderly; if not, the routine
' will handle that for you and update the file so it loads faster next time.
' Users can add (or remove) words from the list. In a perfect world, we should
' make sure there's enough memory available to store the dictionary and also
' verify data integrity (so only words with characters in range A-Z are ok'd.)
FUNCTION openlist% (filename AS STRING)
  DIM ff AS INTEGER, update AS INTEGER, tmp as string * 5
  DIM count AS INTEGER, offset AS INTEGER, max AS INTEGER, hiRef AS INTEGER
 
  ' open file
  ff = FREEFILE
  OPEN filename FOR INPUT AS #ff

  ' file size should be multiple of 7 (doesn't guarantee data integrity)
  IF (LOF(ff) MOD 7) THEN
    CLOSE #ff
    EXIT FUNCTION
  END IF

  ' resize word array
  REDIM wordLst(LOF(ff) \ 7 - 1) AS STRING * 5

  ' store all in uppercase, then close file
  FOR i% = 0 TO UBOUND(wordLst)
    INPUT #ff, tmp
    wordLst(i%) = ucase$(tmp)
  NEXT i%
  CLOSE #ff

  ' now make sure they are alphabetically sorted, this can take a while
  ' depending on the number of words and how disorderly the list is.
  count = UBOUND(wordLst) + 1
  offset = count \ 2
  DO WHILE offset
    max = count - offset - 1
    DO
      hiRef = 0
      FOR i% = 0 TO max
        IF (wordLst(i%) > wordLst(i% + offset)) THEN
          SWAP wordLst(i%), wordLst(i% + offset)
          update = -1
          hiRef = i%
        END IF
      NEXT i%
      max = hiRef - offset
    LOOP WHILE hiRef
    offset = offset \ 2
  LOOP

  ' list had to be sorted, re-save so it loads faster next time
  IF update THEN
    OPEN filename FOR OUTPUT AS #ff
    FOR i% = 0 TO UBOUND(wordLst)
      PRINT #ff, wordLst(i%)
    NEXT i%
    CLOSE #ff
  END IF

  ' all done
  openlist% = -1
END FUNCTION

' Binary search. It's much faster than a linear search (one entry after the
' other) but it requires the array to be alphabetically ordered. This function
' returns the index of the word within the wordLst() array. If the word cannot
' be found, this function returns -1.
FUNCTION wordIndex% (myWord AS STRING)
  DIM search AS STRING, cueLo AS INTEGER, cueHi AS INTEGER, cueMid AS INTEGER

  search = UCASE$(myWord) ' make sure the input is formatted
  cueHi = UBOUND(wordLst) ' upper boundary
  wordIndex% = -1         ' assume the word doesn't exist

  DO WHILE (cueLo <= cueHi)
    cueMid = (cueLo + cueHi) \ 2 ' test central word
    SELECT CASE wordLst(cueMid)
    CASE IS < search
      cueLo = cueMid + 1 ' word must be in the 2nd half
    CASE IS > search
      cueHi = cueMid - 1 ' word must be in the 1st half
    CASE ELSE
      wordIndex% = cueMid ' found it!
      EXIT FUNCTION
    END SELECT
  LOOP
END FUNCTION

Re: WorDOSle -- Wordle for DOS made in QB!

Posted: Thu Feb 24, 2022 3:39 am
by Ham62
Hey Mike, thanks for the suggestions! Those shouldn't be too much hassle to add in the next update.

The only thing I was concerned with the word lookup would be how fast it's gonna run on an 8088 (that was my initial benchmark for getting this to run). If I do add it I'll probably just pre-sort the whole text file that way it won't rely on having to sort it when it loads initially.

I'll also need to check how the load time for that is off the 360k diskette drive, but I'm thinking worst case scenario I could probably make some changes to the .dat file so I could just load the whole thing in a single GET # command.

Fun fact: the word list I'm using was actually the original Wordle word-set from before New York Times changed the dictionary! I originally considered adding the day counter like they had so that I could sync up with past words from there, but the random word selection seemed more fun in the end.

Re: WorDOSle -- Wordle for DOS made in QB!

Posted: Thu Feb 24, 2022 9:43 am
by MikeHawk
Great! I'm fairly confident a binary search is fast enough to return the index in the array, but you're right about the ordered list: it takes time... I gave a try with Quick sort, and even though it's 4x faster than Shell sort, it also takes 2x the time to parse an already sorted list. Storing the formatted and sorted strings with BSAVE and reload with BLOAD would definitely be faster. That would also shave about 4 Kbs off the word list too.

Re: WorDOSle -- Wordle for DOS made in QB!

Posted: Sat Feb 26, 2022 9:33 pm
by Ham62
Was pretty busy the last few days, but managed to get together an update!

Here's everything that I changed in version 1.1:
- Only accept guess if it's in the dictionary
- Don't register key used to close menu as game input
- Can give up round by pressing [Esc]
- Optimized dictionary (~4.5kb smaller)
- Improved monochrome display mode

Updated binaries and source are both available on the website.

Re: WorDOSle -- Wordle for DOS made in QB!

Posted: Thu Mar 03, 2022 4:15 pm
by Erik
Nice job! I had a lot of fun with this game so far. I've never really played the original WORDLE game so this was new to me. :)

I loaded it up on my IBM XT and ran it off a 360Kb floppy and then off the CF hard drive (MFM drive is sticking so I wasn't able to try that today... need to go in and manually spin it a few times to get it going again).

Off a 360Kb floppy, it took around 7-8 seconds to start. Off the CF card, it started in just under 2 seconds. Those times are with using the compiled EXE file.

Here's a pic of it running on my XT using the /mono flag. The picture doesn't really do the screen justice. It looks much better in person.
Image

Re: WorDOSle -- Wordle for DOS made in QB!

Posted: Sun Mar 06, 2022 4:02 pm
by MikeHawk
Damn, that's really cool!