I was challenged to write Hangman. It started out with a simple enough idea. Have the computer pick a word from a wordlist, and present the hangman structure, and proceed through the game. As is often the case, while the idea is simple, it took me a bit to figure it out. See my Github Repo for the full code.
Subroutines
I created the following subroutines for this program:
readInDictionary
readInDictionary looks for a file called dict.txt, opens it, puts it into an array, and returns a reference to the array.
sub readInDictionary
{
#Opens the dictionary file
open FILE , " dict.txt " or die $! ;
#Saves the file to an array
my @dict = < FILE > ;
#Returns a pointer to the array
return \ @dict ;
}
chooseWord
chooseWord takes in the array reference for the dictionary, and selects a random word. It then turns that word into an array, and returns the array reference for the word array.
sub chooseWord
{
#Gets the dictionary array reference from pass in
my $dict = shift ;
my @dict = @$dict ;
#Removes extra whitespace and uppercases the word.
chomp ( my $word = uc ( $dict [ rand @dict ]));
#Splits the string to an array
my @theWord = split ('', $word );
#Returns the reference of the array
return \ @theWord ;
}
initGuessArray
initGuessArray takes in the reference to the Word array, and uses that to create the array which is used to capture the correct guesses. The fillIn Array, is poplulated with _ unless a non letter is found. It then returns the reference to the initalized array.
sub initGuessArray
{
#Initalize variables
#Split the string into an array
my $theWord = shift ;
my @theWord = @$theWord ;
#Make an array to fill in
my @fillIn ;
#Counter to use to populate the fill-in array.
my $counter = 0 ;
#For each character in the @theWord Array, fill the fillIn array with an _ if its a letter, otherwise fill it with the character.from the
foreach ( @theWord )
{
#If its a letter populate an underscore
if ( $_ =~ /[a-zA-Z]/ )
{
$fillIn [ $counter ] = " _ ";
}
#If its not a letter populate the character
else
{
$fillIn [ $counter ] = $theWord [ $counter ];
}
#Increment the counter for the space in the arrays
$counter ++ ;
}
#Returns an array reference where the letters are represented by underscores.
return \ @fillIn ;
}
checkLetter
checkLetter checks the guess for any instances of it in the word. If they are it changes the fillInArray values for those elements to the letter. It also adds letters that are not in the word to the wrong guess array.
sub initGuessArray
{
#Initalize variables
#Split the string into an array
my $theWord = shift ;
my @theWord = @$theWord ;
#Make an array to fill in
my @fillIn ;
#Counter to use to populate the fill-in array.
my $counter = 0 ;
#For each character in the @theWord Array, fill the fillIn array with an _ if its a letter, otherwise fill it with the character.from the
foreach ( @theWord )
{
#If its a letter populate an underscore
if ( $_ =~ /[a-zA-Z]/ )
{
$fillIn [ $counter ] = " _ ";
}
#If its not a letter populate the character
else
{
$fillIn [ $counter ] = $theWord [ $counter ];
}
#Increment the counter for the space in the arrays
$counter ++ ;
}
#Returns an array reference where the letters are represented by underscores.
return \ @fillIn ;
}
printArray
printArray prints the contents of arrays. I implemented “Color Schemes” so that I can pass to the array what color scheme to use.
sub printArray
{
#Get in the array
my $arrayRef = shift ;
my $colorScheme = shift ;
my $counter = 0 ;
#Prints each letter
foreach ( @$arrayRef )
{
given ( $colorScheme )
{
when ( 1 )
{
if ( @$arrayRef [ $counter ] eq ' _ ')
{
print colored [' red '], @$arrayRef [ $counter ];
}
else
{
print colored [' cyan '], @$arrayRef [ $counter ];
}
break ;
}
when ( 2 )
{
print colored [' yellow '], @$arrayRef [ $counter ];
break ;
}
when ( 3 )
{
print colored [' red '], @$arrayRef [ $counter ];
break ;
}
}
$counter ++ ;
}
}
drawHang
This is where I defined the various stages of hangman, and draw it all out.
sub drawHang
{
my $turn = shift ;
given ( $turn )
{
when ( 0 )
{
print colored [' bright_green ']," ┌────────┐ \n ";
print colored [' bright_green ']," │ │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 1 )
{
print colored [' bright_green ']," ┌────────┐ \n ";
print colored [' bright_green ']," │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 2 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 3 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " /| \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 4 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " /| \\\n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 5 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " /| \\\n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " / \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 6 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " /| \\\n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " / \\\n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 7 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/| \\\n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " / \\\n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 8 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/| \\ _ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " / \\\n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 9 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/| \\ _ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/ \\\n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
when ( 10 )
{
print colored [' bright_green '], " ┌────────┐ \n ";
print colored [' bright_green '], " │ │ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/| \\ _ " . " \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | \n ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/ \\ _ " . " \n ";
print colored [' bright_green '], " │ \n ";
print colored [' bright_blue '], " ██████████ \n ";
print colored [' bright_blue '], " ████████████ \n ";
print colored [' bright_blue '], " ██████████████ \n ";
break ;
}
}
}
takeInputAndValidate takes in input, verifies that it is only a capital letter, and that it has not been used before.
sub takeInputAndValidate
{
#Initalize wrongLetter Array
my $wrongLetter = shift ;
#Make a Hash of the letters
my %wrongLetters = map { $_ => 1 } @$wrongLetter ;
#Initalize the letter with invalid content
my $letter = " AAA112 ";
#Create error Flag
my $errorFlag = 0 ;
#Take Input while the length of the letter is greater than 1, while it does not only contain letters, and if it has already been guessed.
while ( length $letter > 1 || $letter =~ m/[^A-Z]/ || exists ( $wrongLetters { $letter }))
{
if ( $errorFlag > 0 )
{
print " You entered a character, which is not a letter, or multiple letters. \n ";
}
print " Please Enter a letter. \n ## ";
#Take Input and Uppercase it.
chomp ( $letter = uc ( < STDIN > ));
$errorFlag ++ ;
}
return $letter ;
}
gameLogic
gameLogic holds the majority of the actual hangman game. It returns the value of the win flag to the subroutine that calls it.
sub gameLogic
{
#Initalizes variables
my $letter = "";
my $winFlag = 0 ;
my $theWord = shift ;
my $fillIn = shift ;
my $wrongLetter = shift ;
my $wrongGuesses = 0 ;
#Run the game through 10 errors. The equivalant of head, body, arms, legs, hands, feet
for ( my $i = 0 ; $i < 11 ;)
{
system (" clear ");
drawHang ( $i );
print " \n ";
#Print FillIn Array
printArray ( $fillIn , 1 );
print " \n ";
#Print wrong letter array
printArray ( $wrongLetter , 2 );
print " \n ";
#Ask for Input
$letter = takeInputAndValidate ( $wrongLetter );
#Run Checkletter to check if its in the word
checkLetter ( $theWord , $fillIn , $wrongLetter , $letter );
#Make the counter equal to the length of the wrong letter array to count how many errors have been made.
$i = scalar @$wrongLetter ;
#Make the arrays strings so they can be compared
my $word = join ("", @$theWord );
my $fillItIn = join ("", @$fillIn );
#Compare the strings. If they match Set win condition
if ( $word eq $fillItIn )
{
$winFlag = 1 ;
last ;
}
}
#Print messages for win and loose conditions
given ( $winFlag )
{
when ( 0 )
{
print " You Loose! The word was ";
&printArray ( $theWord , 3 );
print " . \n ";
break ;
}
when ( 1 )
{
print " You Win! The Word was ";
printArray ( $theWord , 2 );
print " . \n ";
break ;
}
}
pressAKey ();
print " \n\n ";
return $winFlag ;
}
pressAKey
pressAKey just waits for a key to be pressed.
sub pressAKey
{
print " Press a Key to Continue \n ";
my $waitForIt = < STDIN > ;
}
titlePage
titlePage displays a title page for the game.
sub titlePage
{
system (" clear ");
#Create a 80x60 character title screen
print colored [' bright_blue ']," ╔══════════════════════════════════════════════════════════════════════════════╗ \n ";
print colored [' bright_blue ']," ║ ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' red '], " ██░ ██ ▄▄▄ ███▄ █ ▄████ ███▄ ▄███▓ ▄▄▄ ███▄ █ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ▓██░ ██▒▒████▄ ██ ▀█ █ ██▒ ▀█▒▓██▒▀█▀ ██▒▒████▄ ██ ▀█ █ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ▒██▀▀██░▒██ ▀█▄ ▓██ ▀█ ██▒▒██░▄▄▄░▓██ ▓██░▒██ ▀█▄ ▓██ ▀█ ██▒ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ░▓█ ░██ ░██▄▄▄▄██ ▓██▒ ▐▌██▒░▓█ ██▓▒██ ▒██ ░██▄▄▄▄██ ▓██▒ ▐▌██▒ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ░▓█▒░██▓ ▓█ ▓██▒▒██░ ▓██░░▒▓███▀▒▒██▒ ░██▒ ▓█ ▓██▒▒██░ ▓██░ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ▒ ░░▒░▒ ▒▒ ▓▒█░░ ▒░ ▒ ▒ ░▒ ▒ ░ ▒░ ░ ░ ▒▒ ▓▒█░░ ▒░ ▒ ▒ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ▒ ░▒░ ░ ▒ ▒▒ ░░ ░░ ░ ▒░ ░ ░ ░ ░ ░ ▒ ▒▒ ░░ ░░ ░ ▒░ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ░ ░░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░ ░ ▒ ░ ░ ░ ";
print colored [' bright_blue ']," ║ \n ║ ";
print colored [' red '], " ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ║ \n ";
print colored [' bright_blue ']," ║ ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_green '], " ┌────────┐ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_green '], " │ │ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_green '], " │ ";
print colored [' red '], " @ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/| \\ _ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_green '], " │ ";
print colored [' red '], " | ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_green '], " │ ";
print colored [' red '], " _/ \\ _ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_green '], " │ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_blue '], " ██████████ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_blue '], " ████████████ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' bright_blue '], " ██████████████ ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ║ \n ";
print colored [' bright_blue ']," ║ ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' cyan ']," By: Jordan McGilvray ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ║ \n ";
print colored [' bright_blue ']," ║ ";
print colored [' cyan ']," Licenced under the GNU General Public Licence (GPL) v. 333.0 ";
print colored [' bright_blue ']," ║ \n ";
print colored [' bright_blue ']," ║ ║ \n ";
print colored [' bright_blue ']," ╚══════════════════════════════════════════════════════════════════════════════╝ \n ";
print " \n ";
pressAKey ();
}
mainMenu
mainMenu takes in a y, n, yes, or no. It capitalizes the input, so it only evaluates on Y, N, YES, or NO. It displays a Main Menu text, and quits if N or NO are entered. It also captures how many games have been won. and how many have been played.
sub mainMenu
{
my $gamesPlayed = 0 ;
my $gamesWon = 0 ;
for ( $gamesPlayed = 0 ;; $gamesPlayed ++ )
{
if ( $gamesPlayed > 0 )
{
system (" clear ");
print colored [' green ']," # # # # \n ";
print colored [' green ']," ## ## ## # # # ## ## ###### # # # # \n ";
print colored [' green ']," # # # # # # # ## # # # # # # ## # # # \n ";
print colored [' green ']," # # # # # # # # # # # # ##### # # # # # \n ";
print colored [' green ']," # # ###### # # # # # # # # # # # # \n ";
print colored [' green ']," # # # # # # ## # # # # ## # # \n ";
print colored [' green ']," # # # # # # # # # ###### # # #### \n ";
print " \n\n ";
my $errorFlag = 0 ;
my $playGame = ' asdf ';
while ( $playGame =~ /[^Y|YES|N|NO]/ )
{
if ( $errorFlag > 0 )
{
print " Please Enter Y, YES, N, or NO \n ";
}
print " Do you want to play a game of Hangman? \n Enter (Y)ES to play or (N)O to Quit. \n ## ";
chomp ( $playGame = uc ( < STDIN > ));
$errorFlag ++ ;
}
given ( $playGame )
{
when ( $playGame =~ /Y|YES/ )
{
$gamesWon = $gamesWon + mainRoutine ();
break ;
}
when ( $playGame =~ /N|NO/ )
{
system (" clear ");
print " Goodbye \n ";
last ;
}
}
}
else
{
$gamesWon = $gamesWon + mainRoutine ();
}
}
print " You won $gamesWon games of $gamesPlayed games played. \n ";
}
mainRoutine
mainRoutine initalizes all of the game variables, and runs the other subroutines for the game to work.
sub mainRoutine
{
#Initialize the arrays
my @wrongLetter = ();
my @theWord = ();
my @fillIn = ();
my $theWord = chooseWord ( readInDictionary ());
@theWord = @$theWord ;
my $fillIn = initGuessArray ( \ @theWord );
@fillIn = @$fillIn ;
#Run the Game
my $win = gameLogic ( \ @theWord , \ @fillIn , \ @wrongLetter );
return $win ;
}
main
main just runs titlePage, and mainMenu.
sub main
{
titlePage ();
mainMenu ();
}
That is how I built hangman in Perl.
dict.pl
I also wrote the dict.pl script to get larger words from a list of all the words in English.
#! /usr/bin/perl
use warnings ;
use strict ;
sub readInDictionary
{
#Opens the dictionary file
open FILE , " dict.new " or die $! ;
#Saves the file to an array
my @dict = < FILE > ;
#Returns a pointer to the array
return \ @dict ;
}
sub getWords
{
my $words = shift ;
my @keep ;
foreach ( @$words )
{
if ( length $_ > 5 )
{
push ( @keep , $_ );
}
}
return \ @keep ;
}
sub writeToFile
{
my $InputArray = shift ;
open FILE , " >> dict.txt ";
print FILE @$InputArray ;
close ( FILE );
}
sub main
{
my $dictionary = readInDictionary ();
my @dict = @$dictionary ;
my $dictionary1 = getWords ( $dictionary );
print $dictionary1 . " \n ";
my @dict1 = @$dictionary1 ;
writeToFile ( $dictionary1 );
}
main ();