From 647dc399200a88ac299decc9397727c9808f1b88 Mon Sep 17 00:00:00 2001 From: Tobias Berger Date: Mon, 22 Nov 2021 16:40:49 +0100 Subject: [PATCH] Tic-Tac-Toe --- .vscode/c_cpp_properties.json | 17 +++ .vscode/tasks.json | 40 +++--- src/main.c | 260 ++++++++++++++++++++++++---------- 3 files changed, 222 insertions(+), 95 deletions(-) create mode 100644 .vscode/c_cpp_properties.json diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..7438a8a --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,17 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/raylib/src/extras" + ], + "windowsSdkVersion": "10.0.19041.0", + "compilerPath": "C:\\dev\\tcc\\tcc.exe", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "windows-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index de8193c..b485fe3 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -8,25 +8,26 @@ "type": "shell", "command": "tcc", "args": [ - { "value": "src/**.c", "quoting": "weak" }, - { "value": "-ooutput.exe", "quoting": "strong" }, - { "value": "-bench", "quoting": "strong" }, - { "value": "-v", "quoting": "strong" }, - { "value": "-b", "quoting": "strong" }, - { "value": "-Iinclude", "quoting": "strong" }, - { "value": "-Iraylib/src", "quoting": "strong" }, - { "value": "-Llib", "quoting": "strong" }, - { "value": "-stdc99", "quoting": "strong" }, - { "value": "-Wall", "quoting": "strong" }, - { "value": "-Werror", "quoting": "strong" }, - { "value": "-Wwrite-strings", "quoting": "strong" }, - { "value": "-lraylib", "quoting": "strong" }, - { "value": "-lopengl32", "quoting": "strong" }, - { "value": "-lgdi32", "quoting": "strong" }, - { "value": "-luser32", "quoting": "strong" }, - { "value": "-lshell32", "quoting": "strong" }, - { "value": "-lwinmm", "quoting": "strong" }, - { "value": "-D__DEBUG", "quoting": "strong" }, + { "value": "src/**.c", "quoting": "weak" }, + { "value": "-ooutput.exe", "quoting": "strong" }, + { "value": "-bench", "quoting": "strong" }, + { "value": "-v", "quoting": "strong" }, + { "value": "-b", "quoting": "strong" }, + { "value": "-Iinclude", "quoting": "strong" }, + { "value": "-Iraylib/src", "quoting": "strong" }, + { "value": "-Iraylib/src/extras", "quoting": "strong" }, + { "value": "-Llib", "quoting": "strong" }, + { "value": "-stdc99", "quoting": "strong" }, + { "value": "-Wall", "quoting": "strong" }, + { "value": "-Werror", "quoting": "strong" }, + { "value": "-Wwrite-strings", "quoting": "strong" }, + { "value": "-lraylib", "quoting": "strong" }, + { "value": "-lopengl32", "quoting": "strong" }, + { "value": "-lgdi32", "quoting": "strong" }, + { "value": "-luser32", "quoting": "strong" }, + { "value": "-lshell32", "quoting": "strong" }, + { "value": "-lwinmm", "quoting": "strong" }, + { "value": "-D__DEBUG", "quoting": "strong" }, ], "group": { "kind": "build", @@ -44,6 +45,7 @@ { "value": "-v", "quoting": "strong" }, { "value": "-Iinclude", "quoting": "strong" }, { "value": "-Iraylib/src", "quoting": "strong" }, + { "value": "-Iraylib/src/extras", "quoting": "strong" }, { "value": "-Llib", "quoting": "strong" }, { "value": "-stdc99", "quoting": "strong" }, { "value": "-Wall", "quoting": "strong" }, diff --git a/src/main.c b/src/main.c index 3c36e92..93e983e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,116 +1,224 @@ -/******************************************************************************************* -* -* raylib [models] example - Heightmap loading and drawing -* -* This example has been created using raylib 1.8 (www.raylib.com) -* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) -* -* Copyright (c) 2015 Ramon Santamaria (@raysan5) -* -********************************************************************************************/ - #include "raylib.h" +#include "raymath.h" -#define FNL_IMPL -#include "FastNoiseLite.h" +// Size of a gameboard pixel +#define SCREEN_SCALE 256 -#define HEIGHT_MAP_SIZE 128 +#define TOP_OFFSET 40 + +#define SCREEN_SIZE (SCREEN_SCALE * 3) + +#define VIRTUAL_SCREEN_SIZE SCREEN_SIZE / SCREEN_SCALE + +typedef enum Player +{ + NONE, + RED_PLAYER, + BLUE_PLAYER, +} Player; + +Player checkBoard(Player board[9]) +{ + // horizontal, vertical, and diagonal checks from first cell + if (board[0] != NONE && ((board[0] == board[1] && board[0] == board[2]) || (board[0] == board[3] && board[0] == board[6]) || (board[0] == board[4] && board[0] == board[8]))) + return board[0]; + + // vertical check from second cell + if (board[1] != NONE && (board[1] == board[4] && board[1] == board[7])) + return board[1]; + + // vertical and diagonal checks from third cell + if (board[2] != NONE && ((board[2] == board[5] && board[2] == board[8]) || (board[2] == board[4] && board[2] == board[6]))) + return board[2]; + + // horizontal check from fourth cell + if (board[3] != NONE && (board[3] == board[4] && board[3] == board[5])) + return board[3]; + + // horizontal check from seventh cell + if (board[6] != NONE && (board[6] == board[7] && board[6] == board[8])) + return board[6]; + + return NONE; +} + +void resetBoard(Player board[9]) +{ + for (unsigned char i = 0; i < 9; i++) + { + board[i] = NONE; + } +} int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; +#if defined(__DEBUG) + SetTraceLogLevel(LOG_ALL); +#else + SetTraceLogLevel(LOG_NONE); +#endif - fnl_state fnlState = fnlCreateState(); - fnlState.seed = 0; - fnlState.noise_type = FNL_NOISE_CELLULAR; - fnlState.fractal_type = FNL_FRACTAL_PINGPONG; - fnlState.octaves = 3; - fnlState.frequency = 1.0f / 50.0f; + SetConfigFlags(0); + InitWindow(SCREEN_SIZE, SCREEN_SIZE + TOP_OFFSET, "Tic Tac Toe"); - InitWindow(screenWidth, screenHeight, "raylib [models] example - heightmap loading and drawing"); + /* For drawing */ + RenderTexture2D target = LoadRenderTexture(VIRTUAL_SCREEN_SIZE, VIRTUAL_SCREEN_SIZE); - // Define our custom camera to look into our 3d world - Camera camera = {{18.0f, 18.0f, 18.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, 45.0f, 0}; + Rectangle sourceRec = {0.0f, + 0.0f, + (float)VIRTUAL_SCREEN_SIZE, + -(float)VIRTUAL_SCREEN_SIZE}; + Rectangle destRec = {0.0f, + (float)(TOP_OFFSET), + SCREEN_SIZE, + SCREEN_SIZE}; - Color pixels[HEIGHT_MAP_SIZE * HEIGHT_MAP_SIZE] = {0}; + /* For game logic */ + Player board[9] = {0}; + resetBoard(board); + Player currentPlayer = RED_PLAYER; - for (int x = 0; x < HEIGHT_MAP_SIZE; x++) - { - for (int y = 0; y < HEIGHT_MAP_SIZE; y++) - { - float raw_noise = fnlGetNoise2D(&fnlState, (float)x, (float)y); - float noise = ((raw_noise + 1.0f) / 2.0f) * 255.0f; - unsigned char intensity = (unsigned char)noise; - pixels[x + y * HEIGHT_MAP_SIZE] = (Color){ - intensity, - intensity, - intensity, - 255, - }; - } - } - - Image image = { - .data = pixels, - .width = HEIGHT_MAP_SIZE, - .height = HEIGHT_MAP_SIZE, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1, - }; // Load heightmap image (RAM) - Texture2D texture = LoadTextureFromImage(image); // Convert image to texture (VRAM) - - Mesh mesh = GenMeshHeightmap(image, (Vector3){16, 8, 16}); // Generate heightmap mesh (RAM and VRAM) - Model model = LoadModelFromMesh(mesh); // Load model from generated mesh - - model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; // Set map diffuse texture - Vector3 mapPosition = {-8.0f, 0.0f, -8.0f}; // Define model position - - UnloadImage(image); // Unload heightmap image from RAM, already uploaded to VRAM - - SetCameraMode(camera, CAMERA_ORBITAL); // Set an orbital camera mode - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- + unsigned char points[3] = {0, 0, 0}; + unsigned char turn = 0; // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { + //---------------------------------------------------------------------------------- // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); // Update camera - //---------------------------------------------------------------------------------- + // Toggle borderless + if (IsKeyPressed(KEY_F1)) + { + if (IsWindowState(FLAG_WINDOW_UNDECORATED)) + { + ClearWindowState(FLAG_WINDOW_UNDECORATED); + } + else + { + SetWindowState(FLAG_WINDOW_UNDECORATED); + } + } + + Vector2 mousePos = GetMousePosition(); + // Floor instead of trunc to check negative values for out-of-bounds check for cursor + char hovered = (char)floorf(mousePos.x / (float)SCREEN_SCALE) + 3 * (char)floorf((mousePos.y - ((float)TOP_OFFSET)) / (float)SCREEN_SCALE); + + if (hovered < 0 || hovered >= 9 || board[hovered] != NONE) + { + SetMouseCursor(MOUSE_CURSOR_DEFAULT); + } + else if (board[hovered] == NONE) + { + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + board[hovered] = currentPlayer; + currentPlayer = (currentPlayer == RED_PLAYER) ? BLUE_PLAYER : RED_PLAYER; + // Cursor doesn't really matter in this frame + + Player winner = checkBoard(board); + + // Game is over + if (winner != NONE || (++turn) >= 9) + { + TraceLog(LOG_INFO, TextFormat("Winner: %d", winner)); + points[(int)winner]++; + resetBoard(board); + turn = 0; + } + } + else + { + SetMouseCursor(MOUSE_CURSOR_POINTING_HAND); + } + } + + //---------------------------------------------------------------------------------- // Draw //---------------------------------------------------------------------------------- + + BeginTextureMode(target); + { + ClearBackground(WHITE); + + for (unsigned char i = 0; i < 9; i++) + { + Color color = {0}; + + switch (board[i]) + { + case RED_PLAYER: + color = RED; + break; + case BLUE_PLAYER: + color = BLUE; + break; + default: + color = WHITE; + break; + } + + const unsigned char x = i % 3; + const unsigned char y = i / 3; + DrawPixel(x, y, color); + + if (i == hovered) + { + DrawPixel(x, y, (Color){255, 255, 255, 128}); + } + } + } + EndTextureMode(); + BeginDrawing(); + { + ClearBackground(BLACK); - ClearBackground(RAYWHITE); + // Draw rendered game texture + DrawTexturePro(target.texture, sourceRec, destRec, (Vector2){0.0f, 0.0f}, 0.0f, WHITE); - BeginMode3D(camera); +#if __DEBUG + DrawFPS(0, TOP_OFFSET); +#endif - DrawModel(model, mapPosition, 1.0f, RED); + // Draw Top Bar - DrawGrid(20, 1.0f); + // Current player in top-left corner + switch (currentPlayer) + { + case RED_PLAYER: + DrawRectangle(0, 0, TOP_OFFSET, TOP_OFFSET, RED); + break; + case BLUE_PLAYER: + DrawRectangle(0, 0, TOP_OFFSET, TOP_OFFSET, BLUE); + break; + } - EndMode3D(); + // Scores in top-left, labelled with colored squares + const char *blueScore = TextFormat("%03u", points[2]); + const int blueScoreWidth = MeasureText(blueScore, TOP_OFFSET); - DrawTexture(texture, screenWidth - texture.width - 20, 20, WHITE); - DrawRectangleLines(screenWidth - texture.width - 20, 20, texture.width, texture.height, GREEN); + const char *redScore = TextFormat("%03u %s", points[1], blueScore); + const int redScoreWidth = MeasureText(redScore, TOP_OFFSET); - DrawFPS(10, 10); + const char *scoreboard = TextFormat("%03u %s", points[0], redScore); + const int scoreboardWidth = MeasureText(scoreboard, TOP_OFFSET); + DrawText(scoreboard, SCREEN_SIZE - scoreboardWidth, 0, TOP_OFFSET, WHITE); + + DrawRectangle(SCREEN_SIZE - TOP_OFFSET - blueScoreWidth - 2, 0, TOP_OFFSET, TOP_OFFSET, BLUE); + DrawRectangle(SCREEN_SIZE - TOP_OFFSET - redScoreWidth - 2, 0, TOP_OFFSET, TOP_OFFSET, RED); + DrawRectangle(SCREEN_SIZE - TOP_OFFSET - scoreboardWidth - 2, 0, TOP_OFFSET, TOP_OFFSET, WHITE); + } EndDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- - UnloadTexture(texture); // Unload texture - UnloadModel(model); // Unload model - CloseWindow(); // Close window and OpenGL context //--------------------------------------------------------------------------------------