Batchography – Programming the “Hangman game” using the Batch scripting language!

hangman-1In this blog post, I am going to share with you the high level steps needed to build the Hangman game using the Batch scripting language.

To learn more about how the Hangman is programmed using the Batch scripting language, please refer to Chapter 5 in the Batchography book.

flower separator
batchography-good-res
Get the book from Amazon:

  • the print editionbtn-buy-on-amazon
  • or the e-book editionbtn-buy-on-amazon

flower separator

 

What’s the Hangman game

Quoting Wikipedia:

hangman-2The word to guess is represented by a row of dashes, representing each letter of the word. In most variants, proper nouns, such as names, places, and brands, are not allowed. If the guessing player suggests a letter which occurs in the word, the other player writes it in all its correct positions. If the suggested letter or number does not occur in the word, the other player draws one element of a hanged man stick figure as a tally mark.

Game design

The Hangman game logic can be broken into the following steps:

  1. Using a dictionary file and randomly picking up a word to be guessed
  2. Drawing the initial game board
  3. Entering the guess letters loop and do one of the following on each turn:
    1. Reveal correct letter
    2. Draw the Hangman stick figure in steps
  4. Decide whether the game is over: Win or lose?
    1. Game over: when all the tries are exhausted and the correct word has not been guessed yet
    2. Player wins: when all the letters are revealed before the number of tries have been exhausted
  5. Prompt the user and ask if he/she wants to play again

Step 1 – Picking a word from the dictionary

For this step, we need a dictionary file. Any text file containing a list of words would do.

The following function (get-random-line) takes two arguments:

  1. The dictionary text file name
  2. The output variable that will contain the randomly picked word from the dictionary file
:: --------------------------------------------------------------------------
::
:: Get random line
:: 
:get-random-line <1=filename> <2=result-var>
  setlocal enabledelayedexpansion
  set MAXLINES=-1
  for /f "useback tokens=1 delims=:" %%a in (`findstr /N /B /C:"#" "%~1"`) DO set MAXLINES=%%a

  if %MAXLINES% EQU -1 (
    echo Fatal error: the words list file %1 does not end with the "#" marker!
    call :TermScript 2>nul
  )

  :: Get the random line number. Draw the random line a few times to increase randomness.
  set /a i=0
  :get-random-line-rnd
    SET /A RANDLINE="(%RANDOM% %% (MAXLINES-1))"
    set /a i+=1
    if %i% NEQ 3 goto get-random-line-rnd
  
  if %RANDLINE% EQU 0 (SET SKIPSTX=) ELSE (SET SKIPSTX=skip=%RANDLINE%)

  :: Skip some lines and return a single line (word)
  for /f "usebackq %SKIPSTX% delims=" %%w in ("%~1") DO (
    endlocal
    set %~2=%%w
    exit /b 0
  )

When this function is called like this:

::
:: Test the get random line
:: 
:test-get-random-line
  call :get-random-line words-sat300.txt w
  echo word=%w%
  exit /b 0

It will simply return a random line from the “words-sat300.txt” file.

Step 2 – The game board

The idea behind the game board is to:

  1. Display the stick figure: depending on how many tries have been exhausted so far, the stick figure may be:
    • Fully drawn
    • Not drawn yet
    • Or partially drawn
  2. Display the guess letters: also depending on what has been guessed so far, there could be:
    • No guessed letters
    • Some guessed letters
    • Or all letters are guessed

The stick figure is drawn using the draw-hangman function which takes the number of exhausted tries so far as its sole parameter:

::
:: Draw the hangman in progression
:: 
:draw-hangman <1=steps>

    if "%~1" EQU "0" (
        echo.
        echo.
        echo.
        echo.
        echo.
    )

    if "%~1" EQU "1" (
        echo      (o.o^)
        echo.
        echo.
        echo.
        echo.
    )

    if "%~1" EQU "2" (
        echo      (o.o^)
        echo.       ^|
        echo.    ___^| 
        echo.
        echo.
    )

    if "%~1" EQU "3" (
        echo      (o.o^)
        echo.       ^|
        echo.    ___^|___
        echo.
        echo.
    )

    if "%~1" EQU "4" (
        echo      (o.o^)
        echo.       ^|
        echo.    ___^|___
        echo.       ^|
        echo.      /
    )

    if "%~1" EQU "5" (
        echo      (o.o^)
        echo.       ^|
        echo.    ___^|___
        echo.       ^|
        echo.      / \
    )

    exit /b 0

To display the guessed words so far, we use the reveal-letters function. Essentially, this function displays underscore (“_”) characters that correspond to each letter of the picked word that has not been revealed yet by the player, or it will display the actual guessed letters.

If the word length was 5 (for example the word “hello”), and only the letter “e” has been guessed so far, then we should display something like the following:

Guess: _e___
Enter a letter:

The reveal-letters function takes 4 parameters:

  1. The actual word
  2. An equal length (to the actual word) string that contains either “0” if the character at the same position in the actual word has not been revealed yet, or “1” if the character has been revealed.
  3. The letter to reveal
  4. A result / output variable to hold the result of the revelation

That’s how the reveal-letters function looks like:

::
:: Reveal letters in a word
::
:reveal-letters
:: Args: 
::      <1=Word-Val>              The value of the word
::      <2=WordBits-Var-InOut>    WordBit variable name
::      <3=Letter-Val>            The letter to reveal
::      <4=NewWord-Var-Out>       Variable to hold the revealed word
::  Returns: 
::      Number of newly revealed letters

    setlocal enabledelayedexpansion

    :: Get the input string length
    set Word=%~1
    call :strlen "%Word%"
    set /A WordLen=%errorlevel%-1

    :: Get the current revelation bits
    set Bits=!%~2!

    :: Reset internal variables
    set NewBits=
    set NewWord=
    set /A NbReveal=0

    :: Scan and reveal
    for /L %%i in (0, 1, %WordLen%) DO (
        
        :: Take the revealed letter
        set Word_I=!Word:~%%i,1!
        
        :: Take the current revelation bit
        set Bit_I=!Bits:~%%i,1!

        :: Not previously revealed?
        if !Bit_I! EQU 0 (
            :: Check whether to reveal
            if /I "%~3"=="!Word_I!" (
                set Bit_I=1
                set /A NbReveal += 1
            ) ELSE (
                set Word_I=_
            )
        )
        :: Form new revelation bits and word
        set NewBits=!NewBits!!Bit_I!
        set NewWord=!NewWord!!Word_I!
    )
    (
        endlocal
        set %~4=%NewWord%
        set %~2=%NewBits%

        exit /b %NbReveal%
    )

Steps 3 and 4 – The game loop

The game loop is actually called from the Game function which executed steps 3 and 4 described above:

  1. Initialize the game given the randomly picked word from the first step
  2. Prompt the user for a character to guess
  3. Draw the updated game board based on what the guessed character was and how many tries are remaining
  4. Repeat from step #2 until a win or a lose situation occurs
::
:: Game loop
:: 
:Game <1=WordToGuess>
    setlocal enabledelayedexpansion

    :: Reset the retries counter
    set /a MAX_TRIES=5
    set /a NbTries=0

    :: Initialize the word
    set Word=%~1

    :: Initialize the word bits
    call :init-word-bits "%Word%" WordBits

    :: Reveal nothing
    call :reveal-letters "%Word%" WordBits _ RevealedWord

    :Game-Letter-Loop
        cls
        call :draw-hangman %NbTries%

        :: Did we exhaust all the tries?
        if !NbTries! EQU !MAX_TRIES! (
            echo.
            echo Game over^^! The word was: '%Word%'^^!
            echo.
            exit /b 0
        )

        ECHO Guess: %RevealedWord%

        :: Did we reveal all the letters?
        if /I "!Word!"=="!RevealedWord!" goto Game-Finished

        :: Take a single letter
        SET /P Letter="Enter a letter: "
        SET Letter=!Letter:~0,1!

        set OldBits=!WordBits!
        call :reveal-letters "%Word%" WordBits !Letter! RevealedWord

        :: If nothing was revealed then decrease the number of tries
        if !OldBits!==!WordBits! SET /A NbTries+=1

        :: Repeat
        goto :Game-Letter-Loop

    :Game-Finished
    echo.
    echo Good job^^!
    echo.
    endlocal
    EXIT /b 1

Step 5 – Putting it altogether

In this last step, everything is put together:

  1. The game is started and a random word is picked
  2. The Game function is called and in turn it will invoke the Game-Loop function which will in turn reveal letters or draw more of the hangman stick figure
  3. After the Game function returns, the game may restart after prompting the player to play a new game or not
:PlayGame
    :PlayGame-Again
    call :get-random-line words-sat300.txt word
    call :Game %Word%
    choice /c:yn /m "Do you want to play more?"
    if "%ERRORLEVEL%" EQU "1" goto PlayGame-Again

    goto :eof

You may download the full Hangman’s source code from the Batchography’s book online source repository:

Download WifiPasswordReveal
[ Hangman.bat ]
flower separatorYou might also like:

Leave a Reply