Skip to content

Commit

Permalink
Merge pull request #11 from ArkadySK/singleplayer
Browse files Browse the repository at this point in the history
Singleplayer feature
  • Loading branch information
ArkadySK authored Jan 12, 2025
2 parents 3a0009b + 71ed9b2 commit c1f866e
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 103 deletions.
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 @@ -117,11 +103,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 @@ -142,6 +129,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 @@ -150,24 +140,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

0 comments on commit c1f866e

Please sign in to comment.