main.c
/*
INSTALLATION:
sudo apt-get install libsdl2-dev
COMPILING:
cc main.c lib/glad/glad.c -ldl -lSDL2 -Ilib
GET GLAD FROM HERE:
https://glad.dav1d.de/
- select version 4.6 from the "gl" list
- hit generate
- extract the package in the "lib" folder
PROJECT STRUCTURE:
.
├── a.out
├── build.sh
├── lib
│ ├── glad
│ │ ├── glad.c
│ │ └── glad.h
│ └── KHR
│ └── khrplatform.h
└── main.c
*/
#include <SDL2/SDL.h>
#include <glad/glad.h>
#include <GL/gl.h>
#include <stdint.h>
#include <time.h>
typedef uint32_t u32;
typedef int32_t i32;
// globals, for simplicity
SDL_Window *window;
u32 client_width = 600;
u32 client_height = 600;
u32 tile_size = 200;
u32 x_tiles = 3;
u32 y_tiles = 3;
u32 available_tiles;
typedef enum {
TURN_NONE,
TURN_PLAYER,
TURN_COMPUTER
} Turn;
Turn turn = TURN_PLAYER;
u32 board[] = {
0, 0, 0,
0, 0, 0,
0, 0, 0
};
// ratio for converting pixels
// [client_width, client_height] to [-1,1] range
float ratio_x;
float ratio_y;
unsigned int shader_program;
unsigned int VAO, VBO;
const char *vertex_shader_source = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"uniform mat4 world;\n"
"void main()\n"
"{\n"
" gl_Position = world * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragment_shader_source = "#version 330 core\n"
"out vec4 FragColor;\n"
"uniform vec3 color;"
"void main()\n"
"{\n"
" FragColor = vec4(color, 1.0f);\n"
"}\n\0";
void draw_rectangle(u32 x, u32 y, float color[]);
void check_win_condition(Turn t) {
/*
000
000
000
*/
u32 combinations[24] = {
0, 1, 2, 3, 4, 5, 6, 7, 8,
0, 3, 6, 1, 4, 7, 2, 5, 8,
0, 4, 8, 2, 4, 6
};
for(u32 i = 0; i < 24; i+=3) {
if(
board[combinations[i]] == t &&
board[combinations[i+1]] == t &&
board[combinations[i+2]] == t
)
{
board[combinations[i]] = 3;
board[combinations[i+1]] = 3;
board[combinations[i+2]] = 3;
if(t == TURN_PLAYER) {
printf("Player wins.\n");
} else {
printf("Computer wins.\n");
}
turn = TURN_NONE;
break;
}
}
}
// identity matrix
float matrix[] = {
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
};
void update() {
int r = rand() % (x_tiles * y_tiles);
while(board[r] != 0) {
r = rand() % (x_tiles * y_tiles);
}
board[r] = TURN_COMPUTER;
--available_tiles;
turn = TURN_PLAYER;
check_win_condition(TURN_COMPUTER);
}
void draw_board() {
for(u32 y = 0; y < y_tiles; ++y) {
for(u32 x = 0; x < x_tiles; ++x) {
float color[] = {0.0f, 0.0f, 0.0f};
if(board[y * x_tiles + x] == 1) { color[2] = 1.0f; }
if(board[y * x_tiles + x] == 2) { color[1] = 1.0f; }
if(board[y * x_tiles + x] == 3) { color[0] = 1.0f; }
draw_rectangle(x * tile_size,
y * tile_size, color);
}
}
}
// draws a tile_size sized rectangle
void draw_rectangle(u32 x, u32 y, float color[]) {
// x and y: rectangle left top corner in pixels
// client area left top corner is (0,0)
glUseProgram(shader_program);
unsigned int uniform_loc =
glGetUniformLocation(shader_program,"world");
// matrix[12] and matrix[13] transforms the rectangle along the x and y axis
// 1. with identity matrix the rectangle center is in the center of the client area
// 2. -1.0f moves the rectangle center to the left edge of the client area
// 3. (tile_size / 2) * ratio_x moves the rectangle to the right, so that its left edge aligns with the left side of the client area
// 4. (float)x * ratio_x moves the rectangle x pixels to the right, to its x position
matrix[12] =
-1.0f + (tile_size / 2) * ratio_x + (float)x * ratio_x;
matrix[13] =
1.0f - (tile_size / 2) * ratio_y - (float)y * ratio_y;
glUniformMatrix4fv(
uniform_loc, 1, GL_FALSE, matrix);
unsigned int color_uniform_loc =
glGetUniformLocation(shader_program, "color");
glUniform3fv(color_uniform_loc, 1, color);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void init() {
available_tiles = x_tiles * y_tiles;
srand(time(NULL));
ratio_x = 2.0f / (float)client_width;
ratio_y = 2.0f / (float)client_height;
SDL_Init(SDL_INIT_VIDEO);
window =
SDL_CreateWindow(
"Game",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
client_width,
client_height,
SDL_WINDOW_OPENGL);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GLContext main_context = SDL_GL_CreateContext(window);
gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress);
glViewport(0, 0, client_width, client_height);
SDL_GL_SetSwapInterval(1);
// shaders
unsigned int vertex_shader =
glCreateShader(GL_VERTEX_SHADER);
glShaderSource(
vertex_shader, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader);
int success;
glGetShaderiv(
vertex_shader, GL_COMPILE_STATUS, &success);
unsigned int fragment_shader =
glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(
fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);
shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
// geometry
// vertices array describes a tile_size sized rectangle
// o (offset) is the distance of each corner from the rectangle center
float o = (float)tile_size / 2 * ratio_x;
float vertices[] = {
-o, -o, 0.0f,
o, o, 0.0f,
-o, o, 0.0f,
-o, -o, 0.0f,
o, o, 0.0f,
o, -o, 0.0f,
};
// vao & vbo
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(
GL_ARRAY_BUFFER, sizeof(vertices),
vertices, GL_STATIC_DRAW);
glVertexAttribPointer(
0, 3, GL_FLOAT, GL_FALSE,
3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
int main(int argc, char **argv)
{
init();
int running = 1;
SDL_Event event;
while(running)
{
while(SDL_PollEvent(&event))
{
switch(event.type) {
case SDL_QUIT: { running = 0; } break;
case SDL_MOUSEBUTTONDOWN: {
if(turn == TURN_NONE) break;
printf("%d, %d\n", event.button.x, event.button.y);
u32 x = event.button.x / tile_size;
u32 y = event.button.y / tile_size;
if(board[y * x_tiles + x] == 0) {
board[y * x_tiles + x] = 1;
turn = TURN_COMPUTER;
check_win_condition(TURN_PLAYER);
--available_tiles;
if(available_tiles <= 0) {
turn = TURN_NONE;
}
}
} break;
case SDL_KEYDOWN: {
switch(event.key.keysym.sym) {
case SDLK_o: { running = 0; } break;
}
}
}
}
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
draw_board();
if(turn == TURN_COMPUTER) update();
// draw_rectangle(64, 64, (float[]){1.0f, 0.0f, 0.0f});
SDL_GL_SwapWindow(window);
}
printf("%s\n", glGetString(GL_VERSION));
// apparently, with some platforms, in some circumstances, the original resolution of the display is not restored if you don't call SDL_Quit(): https://discourse.libsdl.org/t/does-sdl2-sdl-quit-require-ttf-quit-sdl-destroywindow-etc/25182/3
SDL_Quit();
return 0;
}
Leave a comment