Last active
February 3, 2024 23:04
-
-
Save Jamesscn/af5e189aaf60e2afdf43b93b78a21690 to your computer and use it in GitHub Desktop.
Linux Tic Tac Toe Driver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/module.h> | |
#include <linux/cdev.h> | |
#include <linux/fs.h> | |
#define DRIVER_NAME "tictactoe" | |
#define MAJOR_NUMBER 168 | |
/* | |
Create the character device with the following commands: | |
$ sudo mknod /dev/tictactoe c 168 0 | |
$ sudo chmod 666 /dev/tictactoe | |
Then play with the cat and echo commands! | |
*/ | |
struct cdev tictactoe_cdev; | |
dev_t tictactoe_dev_t; | |
char command[8]; | |
int board[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; | |
int winner = 0; | |
int message_code = 0; | |
//checks the status of the game and determines if there is a winner | |
void check_board(void) { | |
if( //checks if the user has a line of three | |
(board[0] == 1 && board[1] == 1 && board[2] == 1) || | |
(board[3] == 1 && board[4] == 1 && board[5] == 1) || | |
(board[6] == 1 && board[7] == 1 && board[8] == 1) || | |
(board[0] == 1 && board[3] == 1 && board[6] == 1) || | |
(board[1] == 1 && board[4] == 1 && board[7] == 1) || | |
(board[2] == 1 && board[5] == 1 && board[8] == 1) || | |
(board[0] == 1 && board[4] == 1 && board[8] == 1) || | |
(board[2] == 1 && board[4] == 1 && board[6] == 1) | |
) { | |
winner = 1; | |
} else if ( //checks if the opponent has a line of three | |
(board[0] == -1 && board[1] == -1 && board[2] == -1) || | |
(board[3] == -1 && board[4] == -1 && board[5] == -1) || | |
(board[6] == -1 && board[7] == -1 && board[8] == -1) || | |
(board[0] == -1 && board[3] == -1 && board[6] == -1) || | |
(board[1] == -1 && board[4] == -1 && board[7] == -1) || | |
(board[2] == -1 && board[5] == -1 && board[8] == -1) || | |
(board[0] == -1 && board[4] == -1 && board[8] == -1) || | |
(board[2] == -1 && board[4] == -1 && board[6] == -1) | |
) { | |
winner = -1; | |
} else { //checks if there are any remaining spots | |
int board_full = 1; | |
for(int i = 0; i < 9; i++) { | |
if(board[i] == 0) { | |
board_full = 0; | |
break; | |
} | |
} | |
if(board_full) { | |
winner = -2; | |
} | |
} | |
} | |
//reads the output of the character driver and gives it to the user | |
ssize_t tictactoe_read(struct file *file_pointer, char __user *buffer, size_t length, loff_t *offset) { | |
char status_playing[] = "It's your turn...\n"; | |
char status_winner[] = "You won! Reset to play again.\n"; | |
char status_loser[] = "You lost! Reset to play again.\n"; | |
char status_draw[] = "It's a draw! Reset to play again.\n"; | |
char empty[] = ""; | |
char message_1[] = "Invalid command!\n"; | |
char message_2[] = "The tile you selected is already occupied!\n"; | |
char message_3[] = "Invalid tile number (Choose a tile from 1 to 9)!\n"; | |
char message_4[] = "Valid commands:\n- reset\n- tile <1-9>\n- help\n"; | |
char board_string[] = " | | \n-----\n | | \n-----\n | | \n"; | |
char* status = empty; | |
char* message = empty; | |
int indices[] = {0, 2, 4, 12, 14, 16, 24, 26, 28}; | |
int status_size = 0; | |
int message_size = 0; | |
int chars_printed = 0; | |
if(*offset > 0) { | |
return 0; | |
} | |
//prints the winner of the game | |
if(winner == 0) { | |
status = status_playing; | |
status_size = sizeof(status_playing); | |
} else if (winner == 1) { | |
status = status_winner; | |
status_size = sizeof(status_winner); | |
} else if (winner == -1) { | |
status = status_loser; | |
status_size = sizeof(status_loser); | |
} else if (winner == -2) { | |
status = status_draw; | |
status_size = sizeof(status_draw); | |
} | |
if(copy_to_user(buffer + chars_printed, status, status_size) != 0) { | |
return -EFAULT; | |
} | |
chars_printed += status_size; | |
*offset += status_size; | |
//prints a help or error message | |
if(message_code == 1) { | |
message = message_1; | |
message_size = sizeof(message_1); | |
} else if (message_code == 2) { | |
message = message_2; | |
message_size = sizeof(message_2); | |
} else if (message_code == 3) { | |
message = message_3; | |
message_size = sizeof(message_3); | |
} else if (message_code == 4) { | |
message = message_4; | |
message_size = sizeof(message_4); | |
} | |
if(copy_to_user(buffer + chars_printed, message, message_size) != 0) { | |
return -EFAULT; | |
} | |
chars_printed += message_size; | |
*offset += message_size; | |
//prints the board | |
for(int i = 0; i < 9; i++) { | |
char char_to_display = ' '; | |
if(board[i] == 1) { | |
char_to_display = 'X'; | |
} else if (board[i] == -1) { | |
char_to_display = 'O'; | |
} | |
board_string[indices[i]] = char_to_display; | |
} | |
if(copy_to_user(buffer + chars_printed, board_string, sizeof(board_string)) != 0) { | |
return -EFAULT; | |
} | |
chars_printed += sizeof(board_string); | |
*offset += sizeof(board_string); | |
return chars_printed; | |
} | |
//writes the input from the user and gives it to the character driver | |
ssize_t tictactoe_write(struct file *file_pointer, const char __user *buffer, size_t length, loff_t *offset) { | |
//reads the input into the command buffer | |
size_t original_length = length; | |
if(length > 8) { | |
length = 8; | |
} | |
if(copy_from_user(command, buffer, length) != 0) { | |
return -EFAULT; | |
} | |
command[7] = '\0'; | |
message_code = 0; | |
//checks which command is run | |
if(strncmp(command, "reset", 5) == 0) { //reset command | |
for(int i = 0; i < 9; i++) { | |
board[i] = 0; | |
} | |
winner = 0; | |
return original_length; | |
} else if (strncmp(command, "tile ", 5) == 0) { //tile <1-9> command | |
if(winner != 0) { | |
return original_length; | |
} | |
if(command[5] >= '1' && command[5] <= '9') { | |
int tile = command[5] - '1'; | |
if(board[tile] == 0) { | |
board[tile] = 1; | |
} else { | |
message_code = 2; | |
return original_length; | |
} | |
} else { | |
message_code = 3; | |
return original_length; | |
} | |
} else if (strncmp(command, "help", 4) == 0) { //help command | |
message_code = 4; | |
return original_length; | |
} else { //any other invalid command | |
message_code = 1; | |
return original_length; | |
} | |
check_board(); | |
//makes a random move for the opponent | |
if(winner == 0) { | |
int random; | |
int random_indices[] = {0, 1, 2, 3, 4, 5, 6, 7, 8}; | |
for(int i = 8; i > 0; i--) { | |
int tmp, j; | |
get_random_bytes(&random, sizeof(random)); | |
j = random % (i + 1); | |
tmp = random_indices[i]; | |
random_indices[i] = random_indices[j]; | |
random_indices[j] = tmp; | |
} | |
for(int i = 0; i < 9; i++) { | |
if(board[random_indices[i]] == 0) { | |
board[random_indices[i]] = -1; | |
break; | |
} | |
} | |
} | |
check_board(); | |
*offset += original_length; | |
return original_length; | |
} | |
const struct file_operations tictactoe_fops = { | |
.write = tictactoe_write, | |
.read = tictactoe_read, | |
.owner = THIS_MODULE | |
}; | |
//initializes the module | |
int __init tictactoe_init(void) { | |
int err; | |
tictactoe_dev_t = MKDEV(MAJOR_NUMBER, 0); | |
err = register_chrdev_region(tictactoe_dev_t, 1, DRIVER_NAME); | |
if(err != 0) { | |
return err; | |
} | |
cdev_init(&tictactoe_cdev, &tictactoe_fops); | |
cdev_add(&tictactoe_cdev, tictactoe_dev_t, 1); | |
return 0; | |
} | |
//unloads the module | |
void __exit tictactoe_exit(void) { | |
cdev_del(&tictactoe_cdev); | |
unregister_chrdev_region(tictactoe_dev_t, 1); | |
} | |
module_init(tictactoe_init); | |
module_exit(tictactoe_exit); | |
MODULE_LICENSE("GPL"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I decided to make a character device driver that allows you to play Tic Tac Toe, with the sole purpose of learning how to program kernel modules in Linux.
To play, first create a Makefile in the same directory as the module with the following contents:
Then compile and run the kernel module by running the following commands:
Next, create the device driver with mknod:
You should now be able to play Tic Tac Toe. As an example, try running the following commands: