Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleplayer #11

Merged
merged 5 commits into from
Jan 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 25 additions & 30 deletions client/client.c
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "../source/board.h"
#include "../source/menu.h"
#include "../source/utils.h"
#include "../source/message.h"

#define SERVER_IP "127.0.0.1" // localhost
#include "client.h"

int initialize_client(int port)
{
Expand Down Expand Up @@ -56,8 +44,6 @@ void play_game(int sock)
// Initialize game boards
board_init(&b_own, 10);
board_init(&b_enemy, 10);

// Place ships
place_ships(&b_own);

// Game loop
Expand Down Expand Up @@ -118,11 +104,12 @@ void play_game(int sock)

if (my_turn) {
board_display(&b_own, &b_enemy);
char* shot = shoot(&b_enemy);
// TODO refactor?
int x = shot[0] - 'A';
int y = shot[1] - '0';
free(shot); // Add this to prevent memory leak
char* shot = calloc(3, sizeof(char));
shoot(shot, &b_enemy);
int x;
int y;
parse_input(shot, &x, &y, NULL);
free(shot);

// Send shot
Message msg = {
Expand All @@ -143,6 +130,9 @@ void play_game(int sock)
int main(int argc, char **argv)
{
int mode;
int port = DEFAULT_PORT;
if (argc >= 2)
port = atoi(argv[1]);

// Handle menu
mode = handle_menu();
Expand All @@ -151,24 +141,29 @@ int main(int argc, char **argv)

clear_screen();

if (mode == 1) // Computer game mode
if (mode == 1) // Computer singleplayer game mode
{
show_message("Computer mode not yet implemented");
sleep(3);
clear_screen();
// TODO Waffle: implement
show_message("Connecting to computer opponent...");
sleep(1);
int sock = initialize_client(port);

Message mode_msg = {.type = MSG_SINGLE_PLAYER};
send(sock, &mode_msg, sizeof(Message), 0);

play_game(sock);
close(sock);
return 0;
}

int port = 8536;
if (argc >= 2)
port = atoi(argv[1]);

if (mode == 2) // Human vs Human (network) mode
if (mode == 2) // Human vs Human multiplayer mode
{
show_message("Please wait, connecting...");
sleep(1);
int sock = initialize_client(port);

Message mode_msg = {.type = MSG_MULTI_PLAYER};
send(sock, &mode_msg, sizeof(Message), 0);

play_game(sock);
close(sock);
}
Expand Down
17 changes: 17 additions & 0 deletions client/client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include "../source/board.h"
#include "../source/menu.h"
#include "../source/utils.h"
#include "../source/message.h"

#define SERVER_IP "127.0.0.1" // localhost

int initialize_client(int port);

void play_game(int sock);
5 changes: 4 additions & 1 deletion server/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# Project: Server
add_executable(server server.c)
add_executable(server server.c bot_board.c)
target_include_directories(server PRIVATE ../source)
target_link_libraries(server PRIVATE board)
target_link_libraries(server PRIVATE utils)
42 changes: 42 additions & 0 deletions server/bot_board.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "bot_board.h"

void generate_board(board* b)
{
int placed = 0;
int size = 5;
char* position = calloc(3, sizeof(char));
while(placed < 5)
{
position[0] = 'A' + rand() % b->size_;
position[1] = '0' + rand() % b->size_;
position[2] = rand() % 2 ? 'd' : 'r';
if (validate_position(position, size, b))
{
finalise_placement(position, size, b);
placed++;
if (placed != 3)
{
size--;
}
}
}
free(position);
}

void generate_shot(char* shot, board* b_enemy)
{
while (true)
{
shot[0] = 'A' + rand() % b_enemy->size_;
shot[1] = '0' + rand() % b_enemy->size_;
shot[2] = '\0';
int x;
int y;
parse_input(shot, &x, &y, NULL);
if (b_enemy->board_[x][y] != NOT_HIT)
{
continue;
}
break;
}
}
5 changes: 5 additions & 0 deletions server/bot_board.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "../source/board.h"

void generate_board(board* b);

void generate_shot(char* shot, board* b_enemy);
138 changes: 101 additions & 37 deletions server/server.c
Original file line number Diff line number Diff line change
@@ -1,24 +1,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdbool.h>
#include "../source/message.h"

#define MAX_PLAYERS 2
#include "server.h"

atomic_int player_count = 0; // Atomic variable to track connected players

typedef struct {
int socket;
int opponent_socket;
bool has_opponent;
} Player;

Player players[MAX_PLAYERS];
pthread_mutex_t players_mutex = PTHREAD_MUTEX_INITIALIZER;

Expand Down Expand Up @@ -79,7 +62,13 @@ void *handle_client(void *arg)
//Dereference the pointer to get the socket
int client_socket = *(int *)arg;

// Register player
// Receive game mode from client
Message mode_msg;
if (recv(client_socket, &mode_msg, sizeof(Message), 0) <= 0) {
close(client_socket);
return NULL;
}

pthread_mutex_lock(&players_mutex);
int player_slot = find_empty_slot();
if(player_slot == -1) {
Expand All @@ -90,19 +79,39 @@ void *handle_client(void *arg)

players[player_slot].socket = client_socket;
players[player_slot].has_opponent = false;

//Try to find opponent, assign opponent socket and set has_opponent to true
for(int i = 0; i < MAX_PLAYERS; i++) {
if(i != player_slot && players[i].socket != 0 && !players[i].has_opponent) {
players[player_slot].opponent_socket = players[i].socket;
players[player_slot].player_type = PLAYER_TYPE_HUMAN;

if (mode_msg.type == MSG_SINGLE_PLAYER) {
// Create bot player
int bot_slot = find_empty_slot();
if(bot_slot != -1) {
players[bot_slot].socket = -1;
players[bot_slot].player_type = PLAYER_TYPE_BOT;
players[bot_slot].bot_state = malloc(sizeof(BotState));
board_init(&players[bot_slot].bot_state->b_own, 10);
board_init(&players[bot_slot].bot_state->b_enemy, 10);
generate_board(&players[bot_slot].bot_state->b_own);

// Connect bot and human player
players[player_slot].opponent_socket = -1;
players[player_slot].has_opponent = true;
players[i].opponent_socket = client_socket;
players[i].has_opponent = true;
break;
players[bot_slot].opponent_socket = client_socket;
players[bot_slot].has_opponent = true;
}
} else if (mode_msg.type == MSG_MULTI_PLAYER) {
//Try to find opponent, assign opponent socket and set has_opponent to true
for(int i = 0; i < MAX_PLAYERS; i++) {
if(i != player_slot && players[i].socket != 0 && !players[i].has_opponent) {
players[player_slot].opponent_socket = players[i].socket;
players[player_slot].has_opponent = true;
players[i].opponent_socket = client_socket;
players[i].has_opponent = true;
break;
}
}
}
pthread_mutex_unlock(&players_mutex);

// Wait for opponent
while(!players[player_slot].has_opponent) {
Message msg = {.type = MSG_WAIT_PLAYER};
Expand All @@ -121,10 +130,10 @@ void *handle_client(void *arg)
Message start_msg = {.type = MSG_START_GAME};
send(client_socket, &start_msg, sizeof(Message), 0);

// Wait a moment to ensure both players have received START_GAME
// Wait to ensure both players have received START_GAME
usleep(100000); // 100ms

// First player gets first turn, but only after both are ready
// First player gets first turn after both are ready
if(player_slot == 0) {
Message turn_msg = {.type = MSG_YOUR_TURN};
send(client_socket, &turn_msg, sizeof(Message), 0);
Expand All @@ -146,23 +155,72 @@ void *handle_client(void *arg)

switch(msg.type) {
case MSG_SHOT:
Message wait_msg = {.type = MSG_WAIT_PLAYER};
send(client_socket, &wait_msg, sizeof(Message), 0);
if (players[find_player_slot(opponent_socket)].player_type == PLAYER_TYPE_BOT)
{
// Handle bot response
int bot_slot = find_player_slot(opponent_socket);
BotState* bot_state = players[bot_slot].bot_state;

// Process player's shot
int hit = receive_shot(msg.x, msg.y, &bot_state->b_own);

// Send result back to player
Message result = {
.type = MSG_RESULT,
.x = msg.x,
.y = msg.y,
.hit = hit
};
send(client_socket, &result, sizeof(Message), 0);

// Forward shot to opponent
send(opponent_socket, &msg, sizeof(Message), 0);
if (hit == -1)
{
Message game_over = {.type = MSG_GAME_OVER};
send(client_socket, &game_over, sizeof(Message), 0);
break;
}
char shot[3];
generate_shot(shot, &bot_state->b_enemy);
int x;
int y;
parse_input(shot, &x, &y, NULL);
Message bot_shot = {
.type = MSG_SHOT,
.x = x,
.y = y
};
send(client_socket, &bot_shot, sizeof(Message), 0);
}
else
{
Message wait_msg = {.type = MSG_WAIT_PLAYER};
send(client_socket, &wait_msg, sizeof(Message), 0);
send(opponent_socket, &msg, sizeof(Message), 0);
}
break;

case MSG_RESULT:
// Forward result to shooter
send(opponent_socket, &msg, sizeof(Message), 0);
if (players[find_player_slot(opponent_socket)].player_type == PLAYER_TYPE_BOT)
{
int bot_slot = find_player_slot(opponent_socket);
BotState* bot_state = players[bot_slot].bot_state;
mark_hit(msg.x, msg.y, msg.hit, &bot_state->b_enemy);
}
else
{
send(opponent_socket, &msg, sizeof(Message), 0);
}
// Send turn message to the player who just got shot at
Message turn_msg = {.type = MSG_YOUR_TURN};
send(client_socket, &turn_msg, sizeof(Message), 0);
break;

case MSG_GAME_OVER:
send(opponent_socket, &msg, sizeof(Message), 0);
if (players[find_player_slot(opponent_socket)].player_type != PLAYER_TYPE_BOT)
{
send(opponent_socket, &msg, sizeof(Message), 0);
}
break;
}
}
Expand All @@ -185,6 +243,12 @@ void *handle_client(void *arg)
// destroy opponent's data
for(int i = 0; i < MAX_PLAYERS; i++) {
if(players[i].socket == opponent_socket) {
if (players[i].player_type == PLAYER_TYPE_BOT && players[i].bot_state != NULL) {
board_destroy(&players[i].bot_state->b_own);
board_destroy(&players[i].bot_state->b_enemy);
free(players[i].bot_state);
players[i].bot_state = NULL;
}
players[i].opponent_socket = 0;
players[i].has_opponent = false;
break;
Expand All @@ -202,7 +266,7 @@ int main(int argc, char** argv)
// Initialize players array with 0s
memset(players, 0, sizeof(players));

int port = 8536;
int port = DEFAULT_PORT;
if(argc >= 2)
port = atoi(argv[1]);

Expand Down
Loading