ENGS110 · Spring 2026 · C + Raylib
A graphical sea battle game with Player vs Player and Player vs AI modes.
4 weapon types: Basic, MissileV, MissileH, Bomb. Built in C using Raylib.
// source files ↓ download .zip
#include <stdlib.h>
#include <time.h>
#include "game.h"
#include "gui.h"
int main(void) {
srand((unsigned int)time(0));
Game game;
Game_init(&game);
Gui_run(&game);
Player_freeInventory(&game.player1);
Player_freeInventory(&game.player2);
return 0;
}#ifndef CELL_H
#define CELL_H
typedef struct {
int hasShip;
int isHit;
} Cell;
void Cell_init(Cell *c);
void Cell_placeShip(Cell *c);
int Cell_attack(Cell *c);
int Cell_isOccupied(const Cell *c);
int Cell_isHit(const Cell *c);
char Cell_toChar(const Cell *c, int reveal);
#endif#include "cell.h"
void Cell_init(Cell *c) {
c->hasShip = 0;
c->isHit = 0;
}
void Cell_placeShip(Cell *c) {
c->hasShip = 1;
}
int Cell_attack(Cell *c) {
if (c->isHit) return -1;
c->isHit = 1;
return c->hasShip;
}
int Cell_isOccupied(const Cell *c) { return c->hasShip; }
int Cell_isHit(const Cell *c) { return c->isHit; }
char Cell_toChar(const Cell *c, int reveal) {
if (c->isHit && c->hasShip) return 'X';
if (c->isHit) return 'o';
if (reveal && c->hasShip) return '#';
return '~';
}#ifndef BOARD_H
#define BOARD_H
#include "cell.h"
#define BOARD_SIZE 10
#define SHIPS_COUNT 10
typedef struct {
Cell cells[BOARD_SIZE][BOARD_SIZE];
} Board;
void Board_init(Board *b);
int Board_canPlaceShip(const Board *b, int row, int col);
int Board_placeShip(Board *b, int row, int col);
int Board_receiveAttack(Board *b, int row, int col);
Cell* Board_getCell(Board *b, int row, int col);
const Cell* Board_getCellConst(const Board *b, int row, int col);
void Board_print(const Board *b, int reveal);
#endif#include <stdio.h>
#include "board.h"
void Board_init(Board *b) {
for (int r = 0; r < BOARD_SIZE; r++)
for (int c = 0; c < BOARD_SIZE; c++)
Cell_init(&b->cells[r][c]);
}
int Board_canPlaceShip(const Board *b, int row, int col) {
if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) return 0;
if (b->cells[row][col].hasShip) return 0;
for (int dr = -1; dr <= 1; dr++) {
for (int dc = -1; dc <= 1; dc++) {
if (dr == 0 && dc == 0) continue;
int nr = row + dr;
int nc = col + dc;
if (nr >= 0 && nr < BOARD_SIZE && nc >= 0 && nc < BOARD_SIZE)
if (b->cells[nr][nc].hasShip) return 0;
}
}
return 1;
}
int Board_placeShip(Board *b, int row, int col) {
if (!Board_canPlaceShip(b, row, col)) return 0;
Cell_placeShip(&b->cells[row][col]);
return 1;
}
int Board_receiveAttack(Board *b, int row, int col) {
if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) return -1;
return Cell_attack(&b->cells[row][col]);
}
Cell* Board_getCell(Board *b, int row, int col) {
if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) return 0;
return &b->cells[row][col];
}
const Cell* Board_getCellConst(const Board *b, int row, int col) {
if (row < 0 || row >= BOARD_SIZE || col < 0 || col >= BOARD_SIZE) return 0;
return &b->cells[row][col];
}
void Board_print(const Board *b, int reveal) {
for (int r = 0; r < BOARD_SIZE; r++) {
for (int c = 0; c < BOARD_SIZE; c++) {
putchar(Cell_toChar(&b->cells[r][c], reveal));
putchar(' ');
}
putchar('\n');
}
}#ifndef WEAPON_H
#define WEAPON_H
typedef struct Player Player;
typedef enum {
WEAPON_BASIC,
WEAPON_MISSILE_V,
WEAPON_MISSILE_H,
WEAPON_BOMB
} WeaponType;
typedef struct Weapon Weapon;
struct Weapon {
WeaponType type;
void (*use)(Weapon *w, Player *attacker, Player *defender, int row, int col);
int price;
const char *displayName;
};
Weapon* Weapon_createBasic(void);
Weapon* Weapon_createMissile(int vertical);
Weapon* Weapon_createBomb(void);
const char* Weapon_typeName(WeaponType type);
#endif#include <stdlib.h>
#include "weapon.h"
#include "board.h"
#include "player.h"
static void basic_use(Weapon *w, Player *attacker, Player *defender, int row, int col) {
(void)w; (void)attacker;
int result = Board_receiveAttack(&defender->board, row, col);
if (result == 1) Player_reduceShips(defender);
}
static void missile_use(Weapon *w, Player *attacker, Player *defender, int row, int col) {
(void)attacker;
if (w->type == WEAPON_MISSILE_V) {
for (int r = 0; r < BOARD_SIZE; r++) {
const Cell *cell = Board_getCellConst(&defender->board, r, col);
if (cell && !cell->isHit) {
int result = Board_receiveAttack(&defender->board, r, col);
if (result == 1) Player_reduceShips(defender);
}
}
} else {
for (int c = 0; c < BOARD_SIZE; c++) {
const Cell *cell = Board_getCellConst(&defender->board, row, c);
if (cell && !cell->isHit) {
int result = Board_receiveAttack(&defender->board, row, c);
if (result == 1) Player_reduceShips(defender);
}
}
}
}
static void bomb_use(Weapon *w, Player *attacker, Player *defender, int row, int col) {
(void)w; (void)attacker;
int startR = row - 1 < 0 ? 0 : row - 1;
int endR = row + 1 >= BOARD_SIZE ? BOARD_SIZE - 1 : row + 1;
int startC = col - 1 < 0 ? 0 : col - 1;
int endC = col + 1 >= BOARD_SIZE ? BOARD_SIZE - 1 : col + 1;
for (int r = startR; r <= endR; r++) {
for (int c = startC; c <= endC; c++) {
const Cell *cell = Board_getCellConst(&defender->board, r, c);
if (cell && !cell->isHit) {
int result = Board_receiveAttack(&defender->board, r, c);
if (result == 1) Player_reduceShips(defender);
}
}
}
}
Weapon *Weapon_createBasic(void) {
Weapon *w = (Weapon *)malloc(sizeof(Weapon));
if (!w) return 0;
w->type = WEAPON_BASIC;
w->use = basic_use;
w->price = 0;
w->displayName = "Basic";
return w;
}
Weapon *Weapon_createMissile(int vertical) {
Weapon *w = (Weapon *)malloc(sizeof(Weapon));
if (!w) return 0;
w->type = vertical ? WEAPON_MISSILE_V : WEAPON_MISSILE_H;
w->use = missile_use;
w->price = 70;
w->displayName = vertical ? "MissileV" : "MissileH";
return w;
}
Weapon *Weapon_createBomb(void) {
Weapon *w = (Weapon *)malloc(sizeof(Weapon));
if (!w) return 0;
w->type = WEAPON_BOMB;
w->use = bomb_use;
w->price = 60;
w->displayName = "Bomb";
return w;
}
const char* Weapon_typeName(WeaponType type) {
switch (type) {
case WEAPON_BASIC: return "Basic";
case WEAPON_MISSILE_V: return "MissileV";
case WEAPON_MISSILE_H: return "MissileH";
case WEAPON_BOMB: return "Bomb";
default: return "Unknown";
}
}#ifndef PLAYER_H
#define PLAYER_H
#include "board.h"
#include "weapon.h"
#define MAX_INVENTORY 20
#define PLAYER_NAME_MAX 32
typedef struct Player Player;
struct Player {
char name[PLAYER_NAME_MAX];
Board board;
int budget;
int shipsCount;
Weapon *inventory[MAX_INVENTORY];
int inventorySize;
int isAI;
};
void Player_init(Player *p, const char *name, int budget, int isAI);
int Player_placeShip(Player *p, int row, int col);
int Player_buyWeapon(Player *p, const char *weaponName);
int Player_useWeapon(Player *p, const char *weaponName, Player *defender, int row, int col);
void Player_reduceShips(Player *p);
int Player_isDefeated(const Player *p);
void Player_freeInventory(Player *p);
#endif#include <string.h>
#include <stdlib.h>
#include "player.h"
void Player_init(Player *p, const char *name, int budget, int isAI) {
strncpy(p->name, name, PLAYER_NAME_MAX - 1);
p->name[PLAYER_NAME_MAX - 1] = '\0';
Board_init(&p->board);
p->budget = budget;
p->shipsCount = 0;
p->inventorySize = 0;
p->isAI = isAI;
for (int i = 0; i < MAX_INVENTORY; i++) p->inventory[i] = 0;
}
int Player_placeShip(Player *p, int row, int col) {
if (Board_placeShip(&p->board, row, col)) {
p->shipsCount++;
return 1;
}
return 0;
}
static Weapon *createWeaponByName(const char *name) {
if (strcmp(name, "MissileV") == 0) return Weapon_createMissile(1);
if (strcmp(name, "MissileH") == 0) return Weapon_createMissile(0);
if (strcmp(name, "Bomb") == 0) return Weapon_createBomb();
if (strcmp(name, "Basic") == 0) return Weapon_createBasic();
return 0;
}
int Player_buyWeapon(Player *p, const char *weaponName) {
Weapon *w = createWeaponByName(weaponName);
if (!w) return 0;
if (w->type == WEAPON_BASIC) { free(w); return 0; }
if (p->budget < w->price) { free(w); return 0; }
if (p->inventorySize >= MAX_INVENTORY) { free(w); return 0; }
p->budget -= w->price;
p->inventory[p->inventorySize++] = w;
return 1;
}
static int findWeaponIndex(Player *p, const char *name) {
for (int i = 0; i < p->inventorySize; i++)
if (strcmp(p->inventory[i]->displayName, name) == 0) return i;
return -1;
}
int Player_useWeapon(Player *p, const char *weaponName, Player *defender, int row, int col) {
Weapon *w = 0;
int index = findWeaponIndex(p, weaponName);
if (index >= 0) w = p->inventory[index];
else w = Weapon_createBasic();
if (!w) return 0;
w->use(w, p, defender, row, col);
if (index >= 0) {
free(w);
for (int j = index; j < p->inventorySize - 1; j++) p->inventory[j] = p->inventory[j + 1];
p->inventorySize--;
} else {
free(w);
}
return 1;
}
void Player_reduceShips(Player *p) {
if (p->shipsCount > 0) p->shipsCount--;
}
int Player_isDefeated(const Player *p) {
return p->shipsCount <= 0;
}
void Player_freeInventory(Player *p) {
for (int i = 0; i < p->inventorySize; i++) free(p->inventory[i]);
p->inventorySize = 0;
}#ifndef GAME_H
#define GAME_H
#include "player.h"
typedef enum { PHASE_MENU, PHASE_PLACING_P1, PHASE_PLACING_P2, PHASE_ATTACK, PHASE_GAMEOVER } GamePhase;
typedef enum { SELECT_BASIC, SELECT_MISSILE_V, SELECT_MISSILE_H, SELECT_BOMB } SelectedWeapon;
typedef struct {
Player player1;
Player player2;
Player *currentAttacker;
Player *currentDefender;
GamePhase phase;
int vsAI;
int shipsToPlace;
SelectedWeapon selectedWeapon;
char message[160];
} Game;
void Game_init(Game *g);
void Game_reset(Game *g, int vsAI);
void Game_updateClick(Game *g, int boardIndex, int row, int col);
const char* Game_selectedWeaponName(const Game *g);
void Game_setSelectedWeapon(Game *g, SelectedWeapon weapon);
#endif#include <stdio.h>
#include <string.h>
#include "game.h"
#include "ai.h"
static void Game_buyDefaultWeapons(Game *g) {
Player_buyWeapon(&g->player1, "MissileV");
Player_buyWeapon(&g->player1, "MissileH");
Player_buyWeapon(&g->player1, "Bomb");
if (g->vsAI) AI_buyWeapons(&g->player2);
else {
Player_buyWeapon(&g->player2, "MissileV");
Player_buyWeapon(&g->player2, "MissileH");
Player_buyWeapon(&g->player2, "Bomb");
}
}
void Game_init(Game *g) {
Game_reset(g, 0);
}
void Game_reset(Game *g, int vsAI) {
Player_init(&g->player1, "Player 1", 300, 0);
Player_init(&g->player2, vsAI ? "AI Player" : "Player 2", 300, vsAI);
g->currentAttacker = &g->player1;
g->currentDefender = &g->player2;
g->phase = PHASE_MENU;
g->vsAI = vsAI;
g->shipsToPlace = SHIPS_COUNT;
g->selectedWeapon = SELECT_BASIC;
strcpy(g->message, "Choose Two Players or Player vs AI.");
}
const char* Game_selectedWeaponName(const Game *g) {
switch (g->selectedWeapon) {
case SELECT_MISSILE_V: return "MissileV";
case SELECT_MISSILE_H: return "MissileH";
case SELECT_BOMB: return "Bomb";
default: return "Basic";
}
}
void Game_setSelectedWeapon(Game *g, SelectedWeapon weapon) {
g->selectedWeapon = weapon;
snprintf(g->message, sizeof(g->message), "Selected weapon: %s", Game_selectedWeaponName(g));
}
static void Game_nextTurn(Game *g) {
Player *tmp = g->currentAttacker;
g->currentAttacker = g->currentDefender;
g->currentDefender = tmp;
if (g->vsAI && g->currentAttacker == &g->player2) {
AI_attack(&g->player2, &g->player1);
if (Player_isDefeated(&g->player1)) {
g->phase = PHASE_GAMEOVER;
strcpy(g->message, "AI Player wins! Press R to restart.");
return;
}
g->currentAttacker = &g->player1;
g->currentDefender = &g->player2;
}
snprintf(g->message, sizeof(g->message), "%s turn. Select weapon and click enemy board.", g->currentAttacker->name);
}
void Game_updateClick(Game *g, int boardIndex, int row, int col) {
if (g->phase == PHASE_PLACING_P1) {
if (boardIndex != 0) { strcpy(g->message, "Player 1: click your own board."); return; }
if (Player_placeShip(&g->player1, row, col)) {
g->shipsToPlace--;
snprintf(g->message, sizeof(g->message), "Player 1 ships left to place: %d", g->shipsToPlace);
if (g->shipsToPlace == 0) {
if (g->vsAI) {
AI_placeShips(&g->player2);
Game_buyDefaultWeapons(g);
g->phase = PHASE_ATTACK;
g->currentAttacker = &g->player1;
g->currentDefender = &g->player2;
strcpy(g->message, "Attack phase. Click enemy board.");
} else {
g->phase = PHASE_PLACING_P2;
g->shipsToPlace = SHIPS_COUNT;
strcpy(g->message, "Player 2: place your ships.");
}
}
} else strcpy(g->message, "Invalid placement. Ships cannot touch.");
return;
}
if (g->phase == PHASE_PLACING_P2) {
if (boardIndex != 1) { strcpy(g->message, "Player 2: click your own board."); return; }
if (Player_placeShip(&g->player2, row, col)) {
g->shipsToPlace--;
snprintf(g->message, sizeof(g->message), "Player 2 ships left to place: %d", g->shipsToPlace);
if (g->shipsToPlace == 0) {
Game_buyDefaultWeapons(g);
g->phase = PHASE_ATTACK;
g->currentAttacker = &g->player1;
g->currentDefender = &g->player2;
strcpy(g->message, "Attack phase. Player 1 starts.");
}
} else strcpy(g->message, "Invalid placement. Ships cannot touch.");
return;
}
if (g->phase == PHASE_ATTACK) {
int enemyBoard = (g->currentDefender == &g->player1) ? 0 : 1;
if (boardIndex != enemyBoard) { strcpy(g->message, "Click the enemy board to attack."); return; }
Player_useWeapon(g->currentAttacker, Game_selectedWeaponName(g), g->currentDefender, row, col);
if (Player_isDefeated(g->currentDefender)) {
g->phase = PHASE_GAMEOVER;
snprintf(g->message, sizeof(g->message), "%s wins! Press R to restart.", g->currentAttacker->name);
return;
}
g->selectedWeapon = SELECT_BASIC;
Game_nextTurn(g);
}
}#ifndef AI_H #define AI_H #include "player.h" void AI_placeShips(Player *ai); void AI_buyWeapons(Player *ai); void AI_attack(Player *ai, Player *opponent); #endif
#include <stdlib.h>
#include "ai.h"
void AI_placeShips(Player *ai) {
while (ai->shipsCount < SHIPS_COUNT) {
int r = rand() % BOARD_SIZE;
int c = rand() % BOARD_SIZE;
Player_placeShip(ai, r, c);
}
}
void AI_buyWeapons(Player *ai) {
Player_buyWeapon(ai, "MissileV");
Player_buyWeapon(ai, "MissileH");
Player_buyWeapon(ai, "Bomb");
}
void AI_attack(Player *ai, Player *opponent) {
int row, col, safety = 0;
do {
row = rand() % BOARD_SIZE;
col = rand() % BOARD_SIZE;
safety++;
} while (Board_getCell(&opponent->board, row, col)->isHit && safety < 1000);
const char *weapon = "Basic";
if (ai->inventorySize > 0) weapon = ai->inventory[rand() % ai->inventorySize]->displayName;
Player_useWeapon(ai, weapon, opponent, row, col);
}#ifndef GUI_H #define GUI_H #include "game.h" void Gui_run(Game *game); #endif
#include "gui.h"
#include "raylib.h"
#include <stdio.h>
#define SCREEN_W 950
#define SCREEN_H 620
#define CELL_SIZE 35
#define P1_X 70
#define P2_X 560
#define BOARD_Y 150
static int pointInRect(int x, int y, Rectangle r) {
return x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height;
}
static void drawButton(Rectangle r, const char *text, int active) {
DrawRectangleRec(r, active ? SKYBLUE : LIGHTGRAY);
DrawRectangleLinesEx(r, 2, DARKGRAY);
DrawText(text, (int)r.x + 10, (int)r.y + 10, 18, BLACK);
}
static void drawBoard(Board *board, int x0, int y0, int reveal, const char *title) {
DrawText(title, x0, y0 - 30, 20, BLACK);
for (int r = 0; r < BOARD_SIZE; r++) {
for (int c = 0; c < BOARD_SIZE; c++) {
Cell *cell = Board_getCell(board, r, c);
Color color = BLUE;
if (cell->isHit && cell->hasShip) color = RED;
else if (cell->isHit) color = GRAY;
else if (reveal && cell->hasShip) color = GREEN;
Rectangle rec = { (float)(x0 + c * CELL_SIZE), (float)(y0 + r * CELL_SIZE), CELL_SIZE, CELL_SIZE };
DrawRectangleRec(rec, color);
DrawRectangleLinesEx(rec, 1, BLACK);
}
}
}
static int getClickedBoardCell(int mouseX, int mouseY, int *boardIndex, int *row, int *col) {
int starts[2] = {P1_X, P2_X};
for (int b = 0; b < 2; b++) {
int x0 = starts[b];
if (mouseX >= x0 && mouseX < x0 + BOARD_SIZE * CELL_SIZE &&
mouseY >= BOARD_Y && mouseY < BOARD_Y + BOARD_SIZE * CELL_SIZE) {
*boardIndex = b;
*row = (mouseY - BOARD_Y) / CELL_SIZE;
*col = (mouseX - x0) / CELL_SIZE;
return 1;
}
}
return 0;
}
void Gui_run(Game *game) {
InitWindow(SCREEN_W, SCREEN_H, "Sea Battle C - Raylib");
SetTargetFPS(60);
Rectangle twoPlayerBtn = {220, 80, 190, 45};
Rectangle aiBtn = {450, 80, 190, 45};
Rectangle basicBtn = {80, 540, 120, 40};
Rectangle missileVBtn = {230, 540, 130, 40};
Rectangle missileHBtn = {390, 540, 130, 40};
Rectangle bombBtn = {550, 540, 120, 40};
Rectangle restartBtn = {700, 540, 130, 40};
while (!WindowShouldClose()) {
if (IsKeyPressed(KEY_R)) Game_reset(game, game->vsAI);
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) {
Vector2 m = GetMousePosition();
int mx = (int)m.x, my = (int)m.y;
if (game->phase == PHASE_MENU) {
if (pointInRect(mx, my, twoPlayerBtn)) {
Game_reset(game, 0);
game->phase = PHASE_PLACING_P1;
game->shipsToPlace = SHIPS_COUNT;
snprintf(game->message, sizeof(game->message), "Player 1: place %d ships.", SHIPS_COUNT);
} else if (pointInRect(mx, my, aiBtn)) {
Game_reset(game, 1);
game->phase = PHASE_PLACING_P1;
game->shipsToPlace = SHIPS_COUNT;
snprintf(game->message, sizeof(game->message), "Player 1: place %d ships. AI will place automatically.", SHIPS_COUNT);
}
} else {
if (pointInRect(mx, my, basicBtn)) Game_setSelectedWeapon(game, SELECT_BASIC);
else if (pointInRect(mx, my, missileVBtn)) Game_setSelectedWeapon(game, SELECT_MISSILE_V);
else if (pointInRect(mx, my, missileHBtn)) Game_setSelectedWeapon(game, SELECT_MISSILE_H);
else if (pointInRect(mx, my, bombBtn)) Game_setSelectedWeapon(game, SELECT_BOMB);
else if (pointInRect(mx, my, restartBtn)) Game_reset(game, game->vsAI);
else {
int boardIndex, row, col;
if (getClickedBoardCell(mx, my, &boardIndex, &row, &col)) {
Game_updateClick(game, boardIndex, row, col);
}
}
}
}
BeginDrawing();
ClearBackground(RAYWHITE);
DrawText("SEA BATTLE", 385, 20, 30, DARKBLUE);
DrawText(game->message, 55, 55, 20, MAROON);
if (game->phase == PHASE_MENU) {
drawButton(twoPlayerBtn, "Two Players", 0);
drawButton(aiBtn, "Player vs AI", 0);
DrawText("Choose game mode.", 370, 155, 22, DARKGRAY);
} else {
int revealP1 = (game->phase == PHASE_PLACING_P1) || (game->phase == PHASE_GAMEOVER) || (game->currentAttacker == &game->player1);
int revealP2 = (game->phase == PHASE_PLACING_P2) || (game->phase == PHASE_GAMEOVER) || (!game->vsAI && game->currentAttacker == &game->player2);
drawBoard(&game->player1.board, P1_X, BOARD_Y, revealP1, game->player1.name);
drawBoard(&game->player2.board, P2_X, BOARD_Y, revealP2, game->player2.name);
drawButton(basicBtn, "Basic", game->selectedWeapon == SELECT_BASIC);
drawButton(missileVBtn, "MissileV", game->selectedWeapon == SELECT_MISSILE_V);
drawButton(missileHBtn, "MissileH", game->selectedWeapon == SELECT_MISSILE_H);
drawButton(bombBtn, "Bomb", game->selectedWeapon == SELECT_BOMB);
drawButton(restartBtn, "Restart", 0);
char info[120];
snprintf(info, sizeof(info), "%s attacking | Weapon: %s", game->currentAttacker->name, Game_selectedWeaponName(game));
DrawText(info, 70, 505, 20, BLACK);
}
EndDrawing();
}
CloseWindow();
}#ifndef FILE_LOGGER_H #define FILE_LOGGER_H void Log_message(const char *msg); #endif
#include "file_logger.h"
#include <stdio.h>
#ifdef _WIN32
#include <direct.h>
#else
#include <sys/stat.h>
#endif
void Log_message(const char *msg) {
#ifdef _WIN32
_mkdir("logs");
#else
mkdir("logs", 0755);
#endif
FILE *f = fopen("logs/log.txt", "a");
if (!f) return;
fprintf(f, "%s\n", msg);
fclose(f);
}// documentation
// readme ↓ download README.md
# SeaBattle_C_Raylib # SeaBattle — C + raylib A Battleships game written in C using [raylib](https://www.raylib.com/) for graphics. --- ## Requirements - Linux (Ubuntu/Debian or any distro with GCC and make) - GCC - make - raylib --- ## Setup Install the required Linux dependencies: ```bash sudo apt update sudo apt install build-essential git make gcc \ libgl1-mesa-dev libx11-dev \ libxrandr-dev libxinerama-dev \ libxcursor-dev libxi-dev ``` Then build and run the game: ```bash make run ``` If raylib is not installed on the system, the Makefile automatically: 1. Clones the official raylib repository 2. Builds raylib locally 3. Links the game against the local build No additional manual setup is required. --- ## Build commands | Command | Description | |--------------|------------------------------------| | `make` | Compile the project | | `make run` | Compile and launch the game | | `make clean` | Remove compiled objects and binary | --- ## Project structure ``` SeaBattle_C_Raylib/ ├── src/ # C source and header files ├── Makefile └── README.md ``` --- ## Libraries used | Library | Purpose | |------------|--------------------------------| | raylib | Window, input, and rendering | | libGL | OpenGL (via Mesa on Linux) | | libm | Math | | libpthread | Threads | | libdl | Dynamic linking | | librt | POSIX real-time extensions | | libX11 | X Window System |
ENGS110 · Spring 2026 · AUA