In the introduction to the game, I described what the Atoms board is like and how to play the game. In this tutorial, I’ll start with a skeletal framework in C and explain all the block that make up this program. Here’s what the program does,
- Lets you input the X and Y coordinates (each 1-8) . Or you can press q or Q or escape to exit the game.
- Add one to the board to show.
- Draws the board and shows all player and computer moves. Note this version doesn’t do the computers move.
The program is just over 90 lines long with perhaps 10 comments.
You can download the full listing from GitHub; it’s called atoms.c. However I’ll list it in several parts here and explain what each part does. Here’s the first part
#include <stdio.h>
#include <conio.h>
#include <memory.h>
// Game definitions
#define BOARDSIZE 8
// Game variables
int board[BOARDSIZE][BOARDSIZE];
int playerCell[BOARDSIZE][BOARDSIZE]; // 1 means player piece 0 means empty or computer owned
// Fast way to set all elements of board to 0
void ClearBoard() {
memset(board,0,sizeof(board));
memset(playerCell, 0, sizeof(playerCell));
}
The first three lines are all #includes. This tells the C compiler which libraries you are going to use. C comes with several libraries. stdio.h is the most popular. The conio.h library is needed because we call a function to read a key from the keyboard. The memory.h is needed because we call a function memset (seen in the function ClearBoard).
The lines that start with a // are comment lines. They do nothing but are a useful way to add notes to your code.
The #define tells the compiler that wherever you find the defined text (in this case BOARDSIZE), it substitutes the 8. You’ll see this is used in the game variables.
You could have just put
int board[8][8];
<code class="language-c">int playerCell[8][8]; // 1 means player piece 0 means empty or computer owned
But that wouldn’t explain what the 8 actually means. It’s all the same to the C compiler whether you use BOARDSIZE or 8 in your declaration, but at least you know what BOARDSIZE means
These two lines declare an 8 x8 array of ints each. An int is a variable that holds an integer. In C an integer is a 32-bit number in the range -2 billion to 2 billion.. So board declares 64 of these in one go and playerCell declares another 64. When the program runs, the operating system is told to allocate two blocks of 256 bytes (= 64 x 4).
What is a function?
The line void ClearBoard() declares a function. It’s a way of bundling up some code with a name that can be called one or more times. The function memset is library code (it’s in memory.h) that sets a block of memory to a value. It’s the fastest way to do this. When you declare variables in a program, they aren’t initialized with any value and its up to us to do that.
This program only plays one game at a time but in the real world its likely we might want to replay a game. So we’ll call ClearBoard at the start of each game.
Drawing the game board
When the game is being played, you make a move and the computer does and the program then draws the board.
Here’s what it looks like after one move:
Your move X: 1-8 or Q/esc to exit:2 Your move Y: 1-8 or Q/esc to exit:2 1 2 3 4 5 6 7 8 1........................ 1 2... 1P.................. 2 3........................ 3 4........................ 4 5........................ 5 6........................ 6 7........................ 7 8........................ 8 1 2 3 4 5 6 7 8
I draw three characters for every board piece. For empty spaces its … and for the player it’s a dot then the count followed by a P e.g. .1P. If the cell has a computer piece it’ll shows as .1C This is the code that draws the board.
// Draw the board with 3 spaces for every point
void DrawBoard() {
printf(" 1 2 3 4 5 6 7 8\n");
for (int y = 0; y < 8; y++) {
printf("%3d",y+1);
for (int x = 0; x < 8; x++) {
if (board[x][y] == 0) {
printf("...");
}
else
{
printf("%3d", board[x][y]);
if (playerCell[x][y] == 1)
printf("P");
else
printf("C");
}
}
printf("%3d\n", y+1);
}
printf(" 1 2 3 4 5 6 7 8\n");
}
The first and last line in the function draws the numbers 1-8 at the top and bottom of the board. All drawing is done by calling a function called printf. This comes from the stdio.h library. When you call printf you supply it with a text string (in quotes like “%2d” and this tells the C compiler that you want formatted output. The d means it is an int variable. Accompanying each format string like %2d is an actual in variable like printf("%3d",y+1);
Why the + 1 in y + 1?
The for statements sets up a loop. The for loop is controlled by the definition at the start of the loop. int y = 0; y < 8; y++
. The int y declares a loop variable. The y =0 assigns an initial value, the y < 8 tells it to keep doing the loop as long as y is less than 8 and y++ adds one to y. So it runs this loop 8 times with y going from 0 to 7. However the start and end of each line shows the values 1-8 not 0-7. This is because of the y + 1.
C has a bit of a peculiarity., When you have declared an array [8][8] (or [BOARDSIZE][BOARDSIZE] you access the elements using indexes 0-7 not 1-8. That’s why the two for loops go from 0 to 7. The outer loop (y) is for the eight rows of the board and the inner loop (x) is for the eight columns on each row.
The code in the inner loop prints out … if the board [x][y] is equal to 0. This is the line if (board[x][y] == 0) followed by printf(“…”) if true. If it isn’t true ie there is a non-zero value then it falls though and does the else branch. That printes out the non-zero value using printf(“%2d”, board[x][y]); and if the playerCell[x][y] ==1 then it prints out P for player else it prints out C for computer.
Note that printf(“%2d”, value) always prints a blank space before the number so it always has a width of three characters.
Statements in C
The if statement has two different syntaxes
if (non-zero) statement; and if (non-zero) statement1; else
statement2;
And a statement can be a single line of code or multiple lines inside curly brackets.
So you’ll see this
if (playerCell[x][y] == 1)
printf("P");
else
printf("C");
Where printf is a single statement. Or
if (board[x][y] == 0) {
printf("...");
}
else
{
printf("%2d", board[x][y]);
if (playerCell[x][y] == 1)
printf("P");
else
printf("C");
}
Where the else statement is a block (multiples lines inside curly braces { }) consisting of a printf and an if statement.
The Player’s Move
This function is called once the player has entered the two coordinates of the piece.
// If move ok set it on the board and return 0. If a bad move return 1
int PlayMove(int x, int y) {
x--; // array is 0..7,0..7 but we passed in 1..8 so subtract 1
y--;
if (playerCell[x][y] == 0 && board[x][y] > 0) {
printf("You cannot play on (%d,%d) as it's computer owned", x, y);
return 1;
}
board[x][y]++;
playerCell[x][y] = 1;
return 0;
}
PlayMove is a function but it does a lot more than say ClearBoard. We call it with two int parameters x and y. These each have to be in the range 1-8. The first thing we do is convert them to the range 0-7 by subtracting one from each. x– subtracts one from x and y– does the same for y.
This is followed by a check to see if the cell you are trying to play on is a computer owned cell. The if statement does two checks. The first playerCell[x][y] == 0 is a check that the cell is either empty or computer owned. But we only want to disallow it if the cell (board[x][y]) also y has a non-zero value.
If both checks are true then it’s an invalid move and it prints out the error message and does a return 1. This function was defined with an int before the function name (PlayMove). This means it always returns with an int value. If the move isn’t allowed then it returns a 1. If it is allowed then it returns 0.
If the move is allowed then we add one to the cell and set the playerCell[x][y] to 1 showing its a player piece. At this point we should be checking to see if the cell has a value of 4. If it does then it should explode but we’ll leave the implementation of that to a future tutorial.
Reading the Keyboard
The function ReadKbd() waits for you to press a key then checks that it is in the range ‘1’-‘8’, or is one of escape, Q or q. If you press any other key, it will just ignore it.
// Read a single key from the keyboard
int ReadKbd() {
int c,IsOk;
do {
c = _getch();
IsOk = (c >= '1' && c <= '8') || (c == 27) || (c == 'q') || (c=='Q');
}
while (IsOk == 0);
return c;
}
The _getch() function that it calls comes from the conio.h library. That’s why there was a #include <conio.h> at the start.
The variable IsOk is set to a non-zero value if all the conditions in the IsOk = line are true. The || means logical Or. So in plain English what this lines does is
If (c is greater than or equal to ‘1’ and C is less than or equal to 8) or C equals the escape character (ASCII 27) or c equals ‘q’ or C equals ‘Q’ then set IsOk to 1 else set IsOk to 0.
The do statement while(expression) is a loop where it does the body of the loop (the statement) so long as expression is true. Expression in this case is IsOk == 0.
Finally, if c has one of the accepted values the function returns it.
The main function
All c programs have a main function. When you run the program, it always starts executing the main statement first.
// Main game code
int main(int arcg, char* args) {
int moveOk;
ClearBoard();
while (1) {
printf("Your move X: 1-8 or Q/esc to exit:");
int x = ReadKbd();
if (x == 27 || x == 'q' || x == 'Q') {
break;
}
printf("%c\nYour move Y: 1-8 or Q/esc to exit:",x);
int y = ReadKbd();
if (y == 27 || y == 'q' || y == 'Q') {
break;
}
printf("%c\n",y);
x -= '0'; // Convert '0' to 0, '1' to 1
y -= '0';
do {
moveOk=PlayMove(x, y); // 0 = ok, 1 = not ok
} while (moveOk == 1);
// PlayComputerMove(); To be coded
DrawBoard();
}
printf("\n");
return 0;
}
This starts by declaring a variable moveOk. This will hold the result returned from PlayMove. The main function then calls the ClearBoard() function to clear the board and playerCell arrays. Clear just means set each cell to 0.
While(1) is an infinite loop. It will carry on until the heat death of the universe in this loop unless there is someway to break out of it. That occurs twice when the X and Y values are returned from calling the function ReadKbd. If either is one of escape(27), ‘q’ or ‘Q’ then it does a break statement. This jumps to the first statement after the while loop, in this case printf(“\n”);
What is a void function?
Functions such as ClearBoard and DrawBoard do not return a value. These are called void functions and are declared that way like for example DrawBoard() below.
void DrawBoard() {
In the function PlayMove() is an example of returning early with return 1. You can also do the same in a void function but use a return by itself.
Char or Int?
When you press 1-8 on the keyboard the ReadKbd function returns the values ‘1’..’8′. These are ASCII characters with a numerical value in the range 48 to 55. We need to convert them to ints with the value 0-8. C lets you do this by subtracting ‘0’ (aka 48) from the value. That’s what the two lines x -= ‘0’; and y -= ‘0’; does. But before this we want to show what key you pressed. If we used printf(“%d\n”,x) it would print out 48 etc rather than 0..7. That’s why we use the printf format %c to print out the character ‘1’.
Next is a do while loop until a valid move is received. At that point the DrawBoard function is called and it loops round.
In the next tutorial, I’ll look at doing explosions and how the computer can make moves.
LInk to next tutorial (TBD)