This tutorial is made for absolute beginners in programming. You can find whole code here: https://github.com/B-Matt/BalkanCoders/blob/master/01%20-%20Hangman/hangman.c
In this tutorial you will learn:
- Arrays of chars (strings)
- Loops
- Basic logic behind video games
What every video game has?
Besides great effects (both visual and sound), amusing gameplay, and very good marketing, every video game has a loop called game loop. That loop is iterating every few milliseconds and it has gameplay stuff like player movement, shooting, or any event inside a video game. But what exactly are loops?
Loops in the C programming language:
- for - Executes a block of statements repeatedly until a given condition is false. It is used when you KNOW repetition amount and with a counter variable.
- while - Executes a block of statements repeatedly until a given condition is false.
- do-while - Executes a block of statements repeatedly until a given condition is false. It will iterate at least once (because condition check is behind the block of statements).
People often mix conditional branching (with if, if-else, if-else if-else, and switch-case statements) with loops, but it is WRONG! You can visualize conditional branching as an intersection (you can go forward, left, or right based on your decision) and loops like roundabouts (you are inside a roundabout until you decide when you need to go out). Let's go back on the game loop... The game loop iterates every few milliseconds and renders game information (objects, events, effects, etc.) on the player screen. In this tutorial, we will use a game loop for rendering the hangman and the score on the player's screen. Every iteration of the game loop will clear the console and again show game information.
The picture was taken inside the Unity3D game engine from the Master Games game.
About the Hangman game
Every 90s kid at least once in his life played the Hangman game with his friends. But if you never played hangman the main goal of this game is to give a word that your opponent won't guess. And then your opponent repeatedly guesses the characters of your word. Every character that is not in the given word you draw part of the hangman's body. The game is stopped in two cases:
- Opponent hit the word
- Opponent didn't guess the word
How to devise an algorithm from the explanation of the task?
In this part of the tutorial, you will learn the difference between algorithms and programs and how to write algorithms from the given task. An algorithm is a set of step-by-step set of operations to be performed to solve a specific problem or a class of problems (eg. recipe for tasty cake). A computer program is a sequence of instructions that comply with the rules of a programming language, written to perform a specific task with a computer. To write a program you need to implement an algorithm in the specific programming language.
Let's start from the beginning! If the main goal of this game is to pick a given word what should you do? Well, you need to take input from player 1 for that word and save it into a variable. Okay, you saved that word in variable what next you should do? Draw the user interface. After that, you should create a game loop and in every iteration ask player 2 to input characters. After character input, you should write code to check if a given character is inside a saved word. If player 2 inputs the wrong character then you draw part of a hangman, but if player 2 inputs the correct character then you should draw that character on the user interface.
Basics of the C program
Every program written in the C programming language have 6 main sections:
- Documentation section - Consists of a set of comment lines that specifies code author, creation date and other details about program
- Include section - Section where programmer includes libraries using #include directive
- Definition section - Contains set of symbolic constants declared with #define directive
- Global declaration section - Contains global declaration of user defined functions and global variables
- Main function - Every C program must have main function
- Subprogram section - Contains all the user defined functions used to perform a specific task.
The picture taken from https://www.cybotech.xyz/2018/05/basic-structure-of-c-program.html
Something about strings
A string is a string of characters and they are in C programming language represented as an array of chars. In the picture defined down below, you can see the declaration and initialization of the strings.
When you are declaring a string you need to specify the size of an array. When you declare and initialize a string you don't need to specify the size of an array because the compiler will count characters and automatically specify the size of an array. Every character inside a char variable is represented with an integer value via the ASCII table (http://www.asciitable.com/). So you can manipulate a char variable with an integer or character value.
char testVar = 'a'; // Saved value is a
testVar = 67; // Saved value is C
To summarize everything written in this section. Variables are places inside memory where you save data. The creation of a variable is done in two parts: declaration (you declare variable type and name) and initialization (you set variable value). Char variable values are represented in memory as integers so you can manipulate it with integer or character value. A string is an array of characters.
Coding time!
There is one thing that I didn't tell you about declaring variables. You should ALWAYS give variable names so that any programmer understands the meaning of that variable. It is a bad habit to name your variables with one or two letters because only you know the meaning of that variable! Other programmers must search the whole code to find out the meaning of that variable. When you write EVERY program, you need to know what and where (in a local or a global variable) to save.
The first thing that you should do is to declare the variables used in this program. In an algorithm, we need to use one string for the word, but you need to think about other "complications". In this tutorial, I'm using three arrays because I'm writing it for absolute beginners, and the code looks much cleaner. Also, you can use one array to hold the word, but I didn’t want to complicate it furthermore.
char inputWord[8];
char helpWord[8];
char fullWord[8];
char inputLetter;
int playerHits = 0;
int playerErrors = 0;
int isGameOver = 0;
int isGameWon = 0;
In this version, the game DOESN'T show all characters that the player inputs. So if you input the word "test", and the player inputs the letter t. It will only show one letter t inside the word. For the practice, you should change this code so that it searches all characters inside a word for a given character.
After declaring all the variables we will write code for a string input which size is between 2 and 7 characters. For that behavior, we are using a do-while loop in which we put printing of info messages via printf() function and string input via scanf().In the do-while loop as a condition, you need to put a condition opposite from the condition that you want to achieve. Because do-while will iterate as long as the condition is not met.
do
{
printf("Input game word (max. 8 chars): ");
scanf("%s", inputWord);
}
while(strlen(inputWord) < 2 || strlen(inputWord) > 7);
printf() function is used for printing formatted text and the scanf() function is used for saving the user's input in the memory. The first argument in a scanf() function is a format of data that you want to save in memory. The second argument is a memory address where to save input. To get an address from the variable you need to use the address operator (&) like this &testVar and it will return the address in memory where the testVar is saved. But if you are using arrays you don't need an address operator but you only need to put variable name eg. testVar.
Table taken from https://alvinalexander.com/programming/printf-format-cheat-sheet
If you know the maximum number of iterations you can use for loop like me in the example code. We know that the maximum size of a string is 8 so I've used a for loop that goes from 0 to 8. You need to put an if statement before accessing data in memory and check if data is still available. By default when you declare a string it will be initialized with a null character (\0), which also marks the end of the string. We are adding # sign to the helpWord array (that word is rendered on the player's screen) and copying inputWord to the fullWord array.
for(int i = 0; i < 8; i++)
{
if(inputWord[i] != '\0')
{
helpWord[i] = '#';
fullWord[i] = inputWord[i];
}
}
You can also simplify the whole code by setting the for loop like this:
for(int i = 0; inputWord[i] != '\0'; i++)
Game Loop
For the game loop, we will use a do-while loop because we want to run the game at least one iteration and then check if the game is over. Inside the game loop we will:
- Render hangman tree
- Print the help word
- Ask for the input of a single character
- Search for that character inside the string array
- Decide if a player is picked good character or not
- Decide if the game is over or not and if the player is the winner or not
do
{
system("cls"); // system("clear") if you use Linux!
PrintHangmanTree(playerErrors);
printf("HELP: %s\n", helpWord);
printf("Input character: ");
inputLetter = getchar();
if(inputLetter == 10)
{
continue;
}
int letterIndex = SearchGameWord(inputLetter, inputWord, strlen(inputWord));
if(letterIndex != -1)
{
helpWord[letterIndex] = inputWord[letterIndex];
inputWord[letterIndex] = '#';
playerHits++;
if(playerHits >= strlen(fullWord))
{
isGameWon = 1;
isGameOver = 1;
}
}
else
{
playerErrors++;
if(playerErrors >= 6)
{
isGameOver = 1;
}
}
}
while(isGameOver == 0);
Besides the scanf() function, you can also use the getchar() function. getchar() function takes only ONE character from the users input. For searching the game word for the given letter we will write a new function named SearchGameWord(). That function should go through the whole string and check if a given letter is inside the game word. If the given character is inside the game word then the function should return the position of that character inside the string, otherwise returns -1 that signals that there is no that letter inside the game word. Usually zero or minus values are used to indicate that something did not end well.
int SearchGameWord(char inputChar, char gameWord[], int wordSize)
{
for(int i = 0; i < wordSize; i++)
{
if(gameWord[i] == inputChar)
{
return i;
}
}
return -1;
}
If SearchGameWord function returns a number bigger than -1 then the program changes '#' from the help word to the guessed letter. And then you should update two main variables isGameOver and isGameWon. isGameWon will be 1 (because in the ANSI C there are no booleans only in the newer versions of C programming language) only if the player guesses the whole word. isGameOver will be 1 only if the game ends with a win or with a loss. If SearchGameWord returns -1 then playerErrors variable will be incremented and the new parts of the hangman will be rendered. The Player has a maximum of 6 tries to check the correct letters (because the hangman has 6 parts (head, 2x arms, body, and 2x legs)). If the maximum is reached then the isGameOver variable will be 1 and the game loop ends.
int letterIndex = SearchGameWord(inputLetter, inputWord, strlen(inputWord));
if(letterIndex != -1)
{
helpWord[letterIndex] = inputWord[letterIndex];
inputWord[letterIndex] = '#';
playerHits++;
if(playerHits >= strlen(fullWord))
{
isGameWon = 1;
isGameOver = 1;
}
}
else
{
playerErrors++;
if(playerErrors >= 6)
{
isGameOver = 1;
}
}
Also every iteration we render hangman with a PrintHangman() function. That function will render hangman based on the error count. For that, we are using the switch-case branching so that we can easily render hangman on the player's screen. Remember that after every case you need to add a break statement! If you don't add it then that switch will show all the cases.
void PrintHangmanTree(int errors)
{
printf("__________________\n");
printf("|\t\t |\n");
printf("|\t\t |\n");
switch(errors)
{
case 0:
{
printf("|\n|\n|\n|\n|\n|\n");
break;
}
case 1:
{
printf("|\t\t O\n");
printf("|\n|\n|\n|\n|\n\n");
break;
}
case 2:
{
printf("|\t\t O\n");
printf("|\t\t/\n");
printf("|\n|\n|\n|\n");
break;
}
case 3:
{
printf("|\t\t O\n");
printf("|\t\t/X\n");
printf("|\n|\n|\n|\n");
break;
}
case 4:
{
printf("|\t\t O\n");
printf("|\t\t/X\\\n");
printf("|\n|\n|\n|\n");
break;
}
case 5:
{
printf("|\t\t O\n");
printf("|\t\t/X\\\n");
printf("|\t\t/\n");
printf("|\n|\n|\n");
break;
}
case 6:
{
printf("|\t\t O\n");
printf("|\t\t/X\\\n");
printf("|\t\t/ \\\n");
printf("|\n|\n|\n");
break;
}
}
}
When the game is ended then the console is cleared with a system() function and printed info message if the player is winning or lost his game.
system("cls");
if(isGameWon == 1)
{
printf("You won!\n");
}
PrintHangmanTree(playerErrors);
printf("WHOLE WORD: %s\n", fullWord);
And that's it! I hope you have learned something new today, if you have any questions ask them on the official profiles of the Master Games studio. As for the written code, don't hate it because it was written ten years ago when I was learning to program and when Master Games was a dream. I didn’t want to change much because it is a real example of how beginner code looks. It’s okay to feel "stupid" because you don’t understand how something works (at least I felt that way), but if you work hard regularly and learn new things you’ll get very far. Until the next reading!