From 0f9e9f2ccc8626a7d35c62f9b33095c5d0741b63 Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Sun, 26 Feb 2023 14:01:45 -0800 Subject: [PATCH] Tic tac toe basically done --- src/dawn/CMakeLists.txt | 1 + src/dawn/games/CMakeLists.txt | 7 +++ src/dawn/games/tictactoe/CMakeLists.txt | 10 ++++ src/dawn/games/tictactoe/TicTacToeLogic.cpp | 45 +++++++++++++++ src/dawn/games/tictactoe/TicTacToeLogic.hpp | 20 +++++++ src/dawnglfw/host/DawnGLFWHost.cpp | 10 ++++ src/dawnglfw/host/DawnGLFWHost.hpp | 3 +- src/dawnglfw/input/InputManager.hpp | 1 + src/dawntictactoe/CMakeLists.txt | 2 +- .../components/TicTacToeGame.cpp | 56 ++++++++++++++++--- .../components/TicTacToeGame.hpp | 4 +- .../components/TicTacToeTile.cpp | 8 +++ .../components/TicTacToeTile.hpp | 15 ++--- src/dawntictactoe/input/InputBinds.hpp | 4 +- src/dawntictactoe/scenes/TicTacToeScene.hpp | 11 ++-- 15 files changed, 171 insertions(+), 26 deletions(-) create mode 100644 src/dawn/games/CMakeLists.txt create mode 100644 src/dawn/games/tictactoe/CMakeLists.txt create mode 100644 src/dawn/games/tictactoe/TicTacToeLogic.cpp create mode 100644 src/dawn/games/tictactoe/TicTacToeLogic.hpp diff --git a/src/dawn/CMakeLists.txt b/src/dawn/CMakeLists.txt index 5d3fa62e..7157d936 100644 --- a/src/dawn/CMakeLists.txt +++ b/src/dawn/CMakeLists.txt @@ -26,6 +26,7 @@ target_sources(${DAWN_TARGET_NAME} # Subdirs add_subdirectory(asset) add_subdirectory(display) +add_subdirectory(games) add_subdirectory(input) add_subdirectory(locale) add_subdirectory(physics) diff --git a/src/dawn/games/CMakeLists.txt b/src/dawn/games/CMakeLists.txt new file mode 100644 index 00000000..05d350a9 --- /dev/null +++ b/src/dawn/games/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Subdirs +add_subdirectory(tictactoe) \ No newline at end of file diff --git a/src/dawn/games/tictactoe/CMakeLists.txt b/src/dawn/games/tictactoe/CMakeLists.txt new file mode 100644 index 00000000..cb13d496 --- /dev/null +++ b/src/dawn/games/tictactoe/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2023 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DAWN_TARGET_NAME} + PRIVATE + TicTacToeLogic.cpp +) \ No newline at end of file diff --git a/src/dawn/games/tictactoe/TicTacToeLogic.cpp b/src/dawn/games/tictactoe/TicTacToeLogic.cpp new file mode 100644 index 00000000..e8d36b27 --- /dev/null +++ b/src/dawn/games/tictactoe/TicTacToeLogic.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#include "TicTacToeLogic.hpp" + +using namespace Dawn; + +enum TicTacToeTileState Dawn::ticTacToeDetermineWinner( + const std::map board, + std::vector *winningCombo +) { + uint8_t i; + assertNotNull(winningCombo); + + // Check rows + for(i = 0; i < 9; i += 3) { + if(board.at(i) == board.at(i + 1) && board.at(i) == board.at(i + 2) && board.at(i) != 0) { + *winningCombo = { i, (uint8_t)i + 1, (uint8_t)i + 2 }; + return board.at(i); + } + } + + // Check columns + for(i = 0; i < 3; i++) { + if(board.at(i) == board.at(i + 3) && board.at(i) == board.at(i + 6) && board.at(i) != 0) { + *winningCombo = { i, (uint8_t)i + 3, (uint8_t)i + 6 }; + return board.at(i); + } + } + + // Check diagonals + if(board.at(0) == board.at(4) && board.at(0) == board.at(8) && board.at(0) != 0) { + *winningCombo = {0, 4, 8}; + return board.at(0); + } + + if(board.at(2) == board.at(4) && board.at(2) == board.at(6) && board.at(2) != 0) { + *winningCombo = { 2, 4, 6 }; + return board.at(2); + } + + return TIC_TAC_TOE_EMPTY; +} \ No newline at end of file diff --git a/src/dawn/games/tictactoe/TicTacToeLogic.hpp b/src/dawn/games/tictactoe/TicTacToeLogic.hpp new file mode 100644 index 00000000..4ebfe200 --- /dev/null +++ b/src/dawn/games/tictactoe/TicTacToeLogic.hpp @@ -0,0 +1,20 @@ +// Copyright (c) 2023 Dominic Masters +// +// This software is released under the MIT License. +// https://opensource.org/licenses/MIT + +#pragma once +#include "assert/assert.hpp" + +namespace Dawn { + enum TicTacToeTileState { + TIC_TAC_TOE_EMPTY, + TIC_TAC_TOE_NOUGHT, + TIC_TAC_TOE_CROSS + }; + + enum TicTacToeTileState ticTacToeDetermineWinner( + const std::map board, + std::vector *winningCombo + ); +} \ No newline at end of file diff --git a/src/dawnglfw/host/DawnGLFWHost.cpp b/src/dawnglfw/host/DawnGLFWHost.cpp index 02898f0f..f3e56122 100644 --- a/src/dawnglfw/host/DawnGLFWHost.cpp +++ b/src/dawnglfw/host/DawnGLFWHost.cpp @@ -66,6 +66,7 @@ int32_t DawnHost::init(DawnGame *game) { game->inputManager.bind(INPUT_BIND_NEGATIVE_Y, GLFW_KEY_S); game->inputManager.bind(INPUT_BIND_POSITIVE_Y, GLFW_KEY_W); + game->inputManager.bind(INPUT_BIND_MOUSE_CLICK, INPUT_MANAGER_AXIS_MOUSE_0); game->inputManager.bind(INPUT_BIND_MOUSE_X, INPUT_MANAGER_AXIS_MOUSE_X); game->inputManager.bind(INPUT_BIND_MOUSE_Y, INPUT_MANAGER_AXIS_MOUSE_Y); @@ -85,6 +86,7 @@ int32_t DawnHost::init(DawnGame *game) { glfwSetFramebufferSizeCallback(this->data->window, &glfwOnResize); glfwSetKeyCallback(this->data->window, &glfwOnKey); glfwSetCursorPosCallback(this->data->window, &glfwOnCursor); + glfwSetMouseButtonCallback(this->data->window, &glfwOnMouseButton); return DAWN_HOST_INIT_RESULT_SUCCESS; } @@ -195,4 +197,12 @@ void glfwOnCursor(GLFWwindow* window, double xpos, double ypos) { (float_t)(ypos / DAWN_HOST->game->renderManager.backBuffer.getHeight()), 0, 1 ); +} + +void glfwOnMouseButton(GLFWwindow* window, int button, int action, int mods) { + if(DAWN_HOST == nullptr) return; + assertTrue(window == DAWN_HOST->data->window); + + float_t value = action == GLFW_PRESS ? 1 : 0; + DAWN_HOST->game->inputManager.rawInputValues[INPUT_MANAGER_AXIS_MOUSE_0 + button] = value; } \ No newline at end of file diff --git a/src/dawnglfw/host/DawnGLFWHost.hpp b/src/dawnglfw/host/DawnGLFWHost.hpp index 1b3ea616..73ce46df 100644 --- a/src/dawnglfw/host/DawnGLFWHost.hpp +++ b/src/dawnglfw/host/DawnGLFWHost.hpp @@ -31,4 +31,5 @@ void glfwOnKey( GLFWwindow *window, int32_t key, int32_t scancode, int32_t action, int32_t mods ); -void glfwOnCursor(GLFWwindow* window, double xpos, double ypos); \ No newline at end of file +void glfwOnCursor(GLFWwindow* window, double xpos, double ypos); +void glfwOnMouseButton(GLFWwindow* window, int button, int action, int mods); \ No newline at end of file diff --git a/src/dawnglfw/input/InputManager.hpp b/src/dawnglfw/input/InputManager.hpp index 784d5b4e..c9d48e0f 100644 --- a/src/dawnglfw/input/InputManager.hpp +++ b/src/dawnglfw/input/InputManager.hpp @@ -8,6 +8,7 @@ #define INPUT_MANAGER_AXIS_MOUSE_X -580000 #define INPUT_MANAGER_AXIS_MOUSE_Y -580001 +#define INPUT_MANAGER_AXIS_MOUSE_0 -590000 namespace Dawn { class InputManager : public IInputManager { diff --git a/src/dawntictactoe/CMakeLists.txt b/src/dawntictactoe/CMakeLists.txt index d23fdf6f..aba4bdf0 100644 --- a/src/dawntictactoe/CMakeLists.txt +++ b/src/dawntictactoe/CMakeLists.txt @@ -24,4 +24,4 @@ add_subdirectory(save) set(DIR_GAME_ASSETS games/tictactoe) tool_language(locale_en ${DIR_GAME_ASSETS}/locale/en.xml) -tool_tileset(tileset_xo texture_xo ${DIR_GAME_ASSETS}/xo.png 1 3) \ No newline at end of file +tool_tileset(tileset_xo texture_xo ${DIR_GAME_ASSETS}/xo.png 1 4) \ No newline at end of file diff --git a/src/dawntictactoe/components/TicTacToeGame.cpp b/src/dawntictactoe/components/TicTacToeGame.cpp index c7b4b56f..d061a66d 100644 --- a/src/dawntictactoe/components/TicTacToeGame.cpp +++ b/src/dawntictactoe/components/TicTacToeGame.cpp @@ -13,11 +13,22 @@ using namespace Dawn; TicTacToeGame::TicTacToeGame(SceneItem *item) : SceneItemComponent(item) {} void TicTacToeGame::onStart() { - this->tiles = getScene()->findComponents(); + // Map tiles by tile number = tile + auto tiles = getScene()->findComponents(); + auto itTiles = tiles.begin(); + while(itTiles != tiles.end()) { + this->tiles[(*itTiles)->tile] = *itTiles; + ++itTiles; + } + // Listen getScene()->eventSceneUpdate.addListener(this, &TicTacToeGame::onSceneUpdate); } +void TicTacToeGame::onDispose() { + getScene()->eventSceneUpdate.removeListener(this, &TicTacToeGame::onSceneUpdate); +} + void TicTacToeGame::onSceneUpdate() { // Get mouse in screen space. auto mouse = getGame()->inputManager.getAxis2D(INPUT_BIND_MOUSE_X, INPUT_BIND_MOUSE_Y); @@ -30,17 +41,46 @@ void TicTacToeGame::onSceneUpdate() { struct Ray3D ray; ray.origin = camera->transform->getWorldPosition(); ray.direction = camera->getRayDirectionFromScreenSpace(mouse); - std::cout << "World " << ray.origin.x << ", " << ray.origin.y << ", " << ray.origin.z << std::endl; + + // Find the hovered tile (if any) + TicTacToeTile *hovered = nullptr; auto results = getPhysics()->raycast3DAll(ray); auto itResult = results.begin(); while(itResult != results.end()) { auto result = *itResult; - getScene()->debugRay((struct SceneDebugRay){ - .start = result.point, - .direction = result.normal, - .color = COLOR_BLUE - }); - ++itResult; + auto tile = result.collider->item->getComponent(); + if(tile == nullptr) { + ++itResult; + continue; + } + + hovered = tile; + break; } + + // Now update the state of each tile, also get the state while we are at it + std::map tileMap; + + auto itTiles = tiles.begin(); + while(itTiles != tiles.end()) { + auto t = itTiles->second; + + if(t == hovered) { + if(t->state == TIC_TAC_TOE_EMPTY && getGame()->inputManager.isPressed(INPUT_BIND_MOUSE_CLICK)) { + t->setState(nextMove); + nextMove = nextMove == TIC_TAC_TOE_NOUGHT ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT; + } else if(t->state == TIC_TAC_TOE_EMPTY) { + t->hovered = true; + } + } else { + t->hovered = false; + } + tileMap[itTiles->second->tile] = itTiles->second->getState(); + ++itTiles; + } + + // Determine winner + std::vector winningCombo; + auto winner = ticTacToeDetermineWinner(tileMap, &winningCombo); } \ No newline at end of file diff --git a/src/dawntictactoe/components/TicTacToeGame.hpp b/src/dawntictactoe/components/TicTacToeGame.hpp index e1fc50fd..819ea95e 100644 --- a/src/dawntictactoe/components/TicTacToeGame.hpp +++ b/src/dawntictactoe/components/TicTacToeGame.hpp @@ -11,11 +11,13 @@ namespace Dawn { class TicTacToeGame : public SceneItemComponent { public: - std::vector tiles; + std::map tiles; + enum TicTacToeTileState nextMove = TIC_TAC_TOE_NOUGHT; TicTacToeGame(SceneItem *item); void onStart() override; + void onDispose() override; void onSceneUpdate(); }; diff --git a/src/dawntictactoe/components/TicTacToeTile.cpp b/src/dawntictactoe/components/TicTacToeTile.cpp index df7cc20e..c0245048 100644 --- a/src/dawntictactoe/components/TicTacToeTile.cpp +++ b/src/dawntictactoe/components/TicTacToeTile.cpp @@ -11,6 +11,14 @@ using namespace Dawn; TicTacToeTile::TicTacToeTile(SceneItem *item) : SceneItemComponent(item) { } +void TicTacToeTile::onStart() { + this->setState(TIC_TAC_TOE_EMPTY); +} + +enum TicTacToeTileState TicTacToeTile::getState() { + return this->state; +} + void TicTacToeTile::setState(enum TicTacToeTileState state) { auto ts = this->item->getComponent(); diff --git a/src/dawntictactoe/components/TicTacToeTile.hpp b/src/dawntictactoe/components/TicTacToeTile.hpp index 09efbe03..bd1ccd4f 100644 --- a/src/dawntictactoe/components/TicTacToeTile.hpp +++ b/src/dawntictactoe/components/TicTacToeTile.hpp @@ -6,20 +6,21 @@ #pragma once #include "scene/SceneItemComponent.hpp" #include "scene/components/display/TiledSprite.hpp" +#include "games/tictactoe/TicTacToeLogic.hpp" namespace Dawn { - enum TicTacToeTileState { - TIC_TAC_TOE_BLANK, - TIC_TAC_TOE_NOUGHT, - TIC_TAC_TOE_CROSS - }; - class TicTacToeTile : public SceneItemComponent { public: - enum TicTacToeTileState state = TIC_TAC_TOE_BLANK; + enum TicTacToeTileState state = TIC_TAC_TOE_EMPTY; + bool_t hovered = false; + uint8_t tile; TicTacToeTile(SceneItem *item); + void onStart() override; + + enum TicTacToeTileState getState(); + void setState(enum TicTacToeTileState state); }; } \ No newline at end of file diff --git a/src/dawntictactoe/input/InputBinds.hpp b/src/dawntictactoe/input/InputBinds.hpp index eb47ba8f..5a6dbe40 100644 --- a/src/dawntictactoe/input/InputBinds.hpp +++ b/src/dawntictactoe/input/InputBinds.hpp @@ -7,10 +7,12 @@ #include "input/InputManager.hpp" #define INPUT_BIND(n) ((inputbind_t)n) + #define INPUT_BIND_ACCEPT INPUT_BIND(1) #define INPUT_BIND_NEGATIVE_X INPUT_BIND(2) #define INPUT_BIND_POSITIVE_X INPUT_BIND(3) #define INPUT_BIND_NEGATIVE_Y INPUT_BIND(4) #define INPUT_BIND_POSITIVE_Y INPUT_BIND(5) #define INPUT_BIND_MOUSE_X INPUT_BIND(6) -#define INPUT_BIND_MOUSE_Y INPUT_BIND(7) \ No newline at end of file +#define INPUT_BIND_MOUSE_Y INPUT_BIND(7) +#define INPUT_BIND_MOUSE_CLICK INPUT_BIND(8) \ No newline at end of file diff --git a/src/dawntictactoe/scenes/TicTacToeScene.hpp b/src/dawntictactoe/scenes/TicTacToeScene.hpp index 8a193ab5..976c8145 100644 --- a/src/dawntictactoe/scenes/TicTacToeScene.hpp +++ b/src/dawntictactoe/scenes/TicTacToeScene.hpp @@ -17,10 +17,7 @@ namespace Dawn { void stage() override { camera = Camera::create(this); - // camera->transform->lookAt(glm::vec3(0, 0, 5), glm::vec3(0, 0, 0)); - // camera->type = CAMERA_TYPE_ORTHONOGRAPHIC; - // camera->transform->lookAt(glm::vec3(1, 2, 3), glm::vec3(0, 0, 0)); - camera->transform->lookAt(glm::vec3(0, 0, 3), glm::vec3(0, 0, 0)); + camera->transform->lookAt(glm::vec3(0, 0, 8), glm::vec3(0, 0, 0)); float_t s = 2.0f; camera->orthoTop = s; @@ -33,12 +30,12 @@ namespace Dawn { auto gameItem = this->createSceneItem(); auto game = gameItem->addComponent(); - int32_t i = 0; + uint8_t i = 0; for(int32_t x = -1; x <= 1; x++) { for(int32_t y = -1; y <= 1; y++) { auto tile = TicTacToeTilePrefab::create(this); - tile->transform.setLocalPosition(glm::vec3(x * 1.25f, y * 1.25f, 0)); - // tile->ticTacToe->setState(i % 2 == 0 ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT); + tile->transform.setLocalPosition(glm::vec3(x * 1, y * 1, 0)); + tile->ticTacToe->tile = i++; } } }