2048 is a popular number puzzle game where the goal is to reach the 2048 tile by merging adjacent tiles with the same number. In this project, you'll learn how to create a simple 2048 game in C. We'll provide step-by-step instructions to build the game, from initializing the board to implementing game logic and running the game.
👀 Preview
🎯 Tasks
In this project, you will learn:
How to create the project files
How to define constants for the game
How to implement the main() function to run the game loop
How to initialize the game board
How to implement functions to check game state
How to create the logic for moving tiles
How to display the game board
How to compile and test the game
🏆 Achievements
After completing this project, you will be able to:
Create a C program for a game
Use arrays to represent the game board
Implement game logic for merging tiles
Display the game board
Handle player input
Check for game over and victory conditions
Skills Graph
%%%%{init: {'theme':'neutral'}}%%%%
flowchart RL
c(("`C`")) -.-> c/UserInteractionGroup(["`User Interaction`"])
c(("`C`")) -.-> c/BasicsGroup(["`Basics`"])
c(("`C`")) -.-> c/ControlFlowGroup(["`Control Flow`"])
c(("`C`")) -.-> c/PointersandMemoryGroup(["`Pointers and Memory`"])
c(("`C`")) -.-> c/FunctionsGroup(["`Functions`"])
c/UserInteractionGroup -.-> c/output("`Output`")
c/BasicsGroup -.-> c/comments("`Comments`")
c/BasicsGroup -.-> c/variables("`Variables`")
c/BasicsGroup -.-> c/data_types("`Data Types`")
c/BasicsGroup -.-> c/operators("`Operators`")
c/ControlFlowGroup -.-> c/if_else("`If...Else`")
c/ControlFlowGroup -.-> c/while_loop("`While Loop`")
c/ControlFlowGroup -.-> c/for_loop("`For Loop`")
c/ControlFlowGroup -.-> c/break_continue("`Break/Continue`")
c/UserInteractionGroup -.-> c/user_input("`User Input`")
c/PointersandMemoryGroup -.-> c/memory_address("`Memory Address`")
c/PointersandMemoryGroup -.-> c/pointers("`Pointers`")
c/FunctionsGroup -.-> c/function_parameters("`Function Parameters`")
c/FunctionsGroup -.-> c/function_declaration("`Function Declaration`")
c/FunctionsGroup -.-> c/recursion("`Recursion`")
subgraph Lab Skills
c/output -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/comments -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/variables -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/data_types -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/operators -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/if_else -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/while_loop -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/for_loop -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/break_continue -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/user_input -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/memory_address -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/pointers -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/function_parameters -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/function_declaration -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
c/recursion -.-> lab-298825{{"`Creating a 2048 Game in C`"}}
end
Create the Project Files
First, create a new file named 2048.c and open it in your preferred code editor.
cd ~/project
touch 2048.c
Define Constants
First, we need to write the C code. The first step is to include the header files:
Before writing the main() function, let's complete some basic tasks to define some constants:
#define SIZE 4
#define WIN_SCORE 2048
int board[SIZE][SIZE];
int score = 0;
main Function
int main() {
srand(time(NULL));
init_board();
print_board();
while (1) {
if (is_won()) {
printf("You won!\n");
break;
}
if (is_full() && !can_move()) {
printf("Game over!\n");
break;
}
int direction;
printf("Enter the move direction (0-Up, 1-Down, 2-Left, 3-Right): ");
scanf("%d", &direction);
if (move(direction)) {
// Print the board after the move
print_board();
}
}
return 0;
}
init board(): This function call initializes the game board, sets all cells of the game board to 0, and then generates two initial random numbers (2 or 4) at random locations.
print_board(): This function is used to display the current state of the game board, including the current score and the number on each cell.
while (1): This is an infinite loop that will keep running the game until the game ends condition is met.
The main flow of 2048 game is implemented here, including initializing the game board, moving the number blocks, judging the game victory or defeat, and waiting for player input to control the progress of the game.
Initialize the Game Board
To initialize the game board, we'll create a function init_board that sets up the board and generates two initial random numbers.
void init_board() {
// Initialize the board by setting all cells to 0
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
board[i][j] = 0;
}
}
// Generate two initial random numbers
for (int k = 0; k < 2; k++) {
int i = rand() % SIZE;
int j = rand() % SIZE;
int value = (rand() % 2 + 1) * 2; // Generate 2 or 4 randomly
board[i][j] = value;
}
}
What this function does is, at the beginning of the game, empty all the cells on the game board and generate two initial random blocks of numbers in random locations, providing the player with an initial game state.
Implement Functions to Check Game State
We need functions to check if the player has won, if the board is full, and if there are any valid moves left. Here are the functions:
int is_full() {
// Check if the board is full
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (board[i][j] == 0) {
return 0; // Board is not full
}
}
}
return 1; // Board is full
}
int is_won() {
// Check if the player has won
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (board[i][j] == WIN_SCORE) {
return 1; // Player has won
}
}
}
return 0; // Player has not won
}
int can_move() {
// Check if there are any valid moves left
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (board[i][j] == 0) {
return 1; // There are still empty cells to move
}
if (j > 0 && board[i][j] == board[i][j - 1]) {
return 1; // Can move left
}
if (j < SIZE - 1 && board[i][j] == board[i][j + 1]) {
return 1; // Can move right
}
if (i > 0 && board[i][j] == board[i - 1][j]) {
return 1; // Can move up
}
if (i < SIZE - 1 && board[i][j] == board[i + 1][j]) {
return 1; // Can move down
}
}
}
return 0; // No valid moves left
}
int is_full(): This function is used to check if the board is full, that is, all the cells are occupied.
int is_won(): This function checks if the player has won, that is, if there is a cell with the value of WIN_SCORE (usually 2048) for victory in the game.
int can_move(): This function is used to check if there are still valid move steps to ensure that the game can continue.
Create the Logic for Moving Tiles
Implement the logic for moving tiles in the move function. This function handles the player's move in four directions: up, down, left, and right. It also checks if the move is valid, updates the score, and generates a new random number.
int move(int dir) {
int moved = 0;
// Store the current board state
int prev_board[SIZE][SIZE];
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
prev_board[i][j] = board[i][j];
}
}
// Move upwards
if (dir == 0) {
for (int j = 0; j < SIZE; j++) {
for (int i = 1; i < SIZE; i++) {
if (board[i][j] != 0) {
int k = i;
while (k > 0 && board[k - 1][j] == 0) {
board[k - 1][j] = board[k][j];
board[k][j] = 0;
k--;
moved = 1;
}
if (k > 0 && board[k - 1][j] == board[k][j]) {
board[k - 1][j] *= 2;
score += board[k - 1][j];
board[k][j] = 0;
moved = 1;
}
}
}
}
}
// Move downwards
else if (dir == 1) {
for (int j = 0; j < SIZE; j++) {
for (int i = SIZE - 2; i >= 0; i--) {
if (board[i][j] != 0) {
int k = i;
while (k < SIZE - 1 && board[k + 1][j] == 0) {
board[k + 1][j] = board[k][j];
board[k][j] = 0;
k++;
moved = 1;
}
if (k < SIZE - 1 && board[k + 1][j] == board[k][j]) {
board[k + 1][j] *= 2;
score += board[k + 1][j];
board[k][j] = 0;
moved = 1;
}
}
}
}
}
// Move left
else if (dir == 2) {
for (int i = 0; i < SIZE; i++) {
for (int j = 1; j < SIZE; j++) {
if (board[i][j] != 0) {
int k = j;
while (k > 0 && board[i][k - 1] == 0) {
board[i][k - 1] = board[i][k];
board[i][k] = 0;
k--;
moved = 1;
}
if (k > 0 && board[i][k - 1] == board[i][k]) {
board[i][k - 1] *= 2;
score += board[i][k - 1];
board[i][k] = 0;
moved = 1;
}
}
}
}
}
// Move right
else if (dir == 3) {
for (int i = 0; i < SIZE; i++) {
for (int j = SIZE - 2; j >= 0; j--) {
if (board[i][j] != 0) {
int k = j;
while (k < SIZE - 1 && board[i][k + 1] == 0) {
board[i][k + 1] = board[i][k];
board[i][k] = 0;
k++;
moved = 1;
}
if (k < SIZE - 1 && board[i][k + 1] == board[i][k]) {
board[i][k + 1] *= 2;
score += board[i][k + 1];
board[i][k] = 0;
moved = 1;
}
}
}
}
}
// Check if the move was successful
if (moved) {
// Generate a new random number
int i = rand() % SIZE;
int j = rand() % SIZE;
while (board[i][j] != 0) {
i = rand() % SIZE;
j = rand() % SIZE;
}
board[i][j] = (rand() % 2 + 1) * 2; // Generate 2 or 4
// Print the board after the move
print_board();
}
// Check if the move was successful
if (moved) {
return 1;
} else {
// If the move failed, restore the previous board state
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
board[i][j] = prev_board[i][j];
}
}
return 0;
}
}
First define a variable moved to mark whether a move has occurred. The initial value is 0, indicating that no movement occurred. Create a temporary 2D array called prev_board to store the current state of the game board so that it can be restored to the previous state if the move fails.
Copy the current state of the game board to prev_board, saving the current state of the game board in case of a failed move.
According to the value of the dir parameter (0 for up, 1 for down, 2 for left, 3 for right), the corresponding move operation is performed.
If a move or merge occurs, the moved flag is set to 1, indicating that the game state has changed. If the move or merge operation is performed successfully, a new random number is generated, which is used to generate a new block of numbers in a blank spot on the game board. Finally, if a move occurs (moved is 1 ), the function returns 1, indicating that the move was successful. If the move fails (there is no move or merge operation), the game board is restored to its previous state, returning 0, indicating that the move failed.
Display the Game Board
To display the game board, we'll create a print_board function that clears the terminal and prints the current state of the board.
void print_board() {
// Clear the terminal
system("clear");
printf("Score: %d\n", score);
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
printf("%4d", board[i][j]);
}
printf("\n");
}
}
The main purpose here is to clear the terminal screen, then display the current score at the top of the screen, and then print the status of the game board on the screen so that the player can clearly see the number block on the game board and the score. This helps provide a user-friendly interface that lets players know the current state of the game.
Compile and Test
Enter the following command in the terminal to compile and run:
cd ~/project
gcc -o 2048 2048.c
./2048
Summary
In this project, you've learned how to create a basic 2048 game in C. You initialized the game board, implemented functions to check the game state, created logic for moving tiles, displayed the game board, and ran the game. Have fun playing and enhancing your 2048 game further!
We use cookies for a number of reasons, such as keeping the website reliable and secure, to improve your experience on our website and to see how you interact with it. By accepting, you agree to our use of such cookies. Privacy Policy