This commit is contained in:
2021-12-07 20:33:40 -08:00
parent 7117ddbdca
commit d76028b38c
11 changed files with 415 additions and 401 deletions

View File

@ -7,6 +7,8 @@
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# Set some global flags
add_compile_definitions(

View File

@ -19,7 +19,7 @@ target_link_libraries(${PROJECT_NAME}
target_sources(${PROJECT_NAME}
PRIVATE
glfwclient.c
glfwclient.cpp
)
# Includes

View File

@ -1,149 +0,0 @@
// Copyright (c) 2021 Dominic Msters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "glfwclient.h"
static game_t *GAME_STATE;
static GLFWwindow *window = NULL;
int32_t main() {
double time, newTime;
game_t *game;
input_t *input;
float fDelta;
// Attempt to init GLFW
if(!glfwInit()) return 1;
// Setup window hints
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, false);
// Create Window
window = glfwCreateWindow(
WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT, "", NULL, NULL
);
if(!window) {
glfwTerminate();
return 1;
}
// Load GLAD
glfwMakeContextCurrent(window);
glfwSwapInterval(0);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
// Setup window listeners
glfwSetWindowSizeCallback(window, &glfwOnResize);
glfwSetKeyCallback(window, &glfwOnKey);
glfwSetErrorCallback(&glfwOnError);
glfwSetCursorPosCallback(window, &glfwOnCursor);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// Prepare the game
game = malloc(sizeof(game_t));
GAME_STATE = game;
input = &game->engine.input;
printf("Game is %zu bytes.\n", sizeof(game_t));
// Init the render resolution
renderSetResolution(&game->engine.render,
WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT
);
// Init the game
if(gameInit(game)) {
// Bind initial keys
inputBind(input, INPUT_NULL, glfwGetInputSourceForKey(GLFW_KEY_ESCAPE));
inputBind(input, INPUT_UP, glfwGetInputSourceForKey(GLFW_KEY_UP));
inputBind(input, INPUT_DOWN, glfwGetInputSourceForKey(GLFW_KEY_DOWN));
inputBind(input, INPUT_LEFT, glfwGetInputSourceForKey(GLFW_KEY_LEFT));
inputBind(input, INPUT_RIGHT, glfwGetInputSourceForKey(GLFW_KEY_RIGHT));
inputBind(input, INPUT_UP, glfwGetInputSourceForKey(GLFW_KEY_W));
inputBind(input, INPUT_DOWN, glfwGetInputSourceForKey(GLFW_KEY_S));
inputBind(input, INPUT_LEFT, glfwGetInputSourceForKey(GLFW_KEY_A));
inputBind(input, INPUT_RIGHT, glfwGetInputSourceForKey(GLFW_KEY_D));
inputBind(input, INPUT_ACCEPT, glfwGetInputSourceForKey(GLFW_KEY_E));
inputBind(input, INPUT_ACCEPT, glfwGetInputSourceForKey(GLFW_KEY_ENTER));
inputBind(input, INPUT_ACCEPT, glfwGetInputSourceForKey(GLFW_KEY_SPACE));
// Bind the fake inputs
inputBind(input, INPUT_MOUSE_X, GLFW_PLATFORM_INPUT_MOUSE_X);
inputBind(input, INPUT_MOUSE_Y, GLFW_PLATFORM_INPUT_MOUSE_Y);
// Set up the client
game->engine.client.setTitle = &glfwClientSetTitle;
// Set up some GLFW stuff
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwSetWindowTitle(window, game->engine.name);
// Begin time.
time = 0;
// Main Render Loop
while(!glfwWindowShouldClose(window)) {
glfwPollEvents();
// Determine the delta.
newTime = glfwGetTime();
fDelta = (float)(newTime - time);
time = newTime;
// Tick the engine.
if(!gameUpdate(game, fDelta)) break;
glfwSwapBuffers(window);
sleep(0);//Fixes some weird high CPU bug, not actually necessary.
}
// Game has finished running, cleanup.
gameDispose(game);
}
free(game);
// Terminate the GLFW context.
glfwSetWindowSizeCallback(window, NULL);
glfwTerminate();
return 0;
}
void glfwOnResize(GLFWwindow *window, int32_t width, int32_t height) {
renderSetResolution(&GAME_STATE->engine.render, (float)width, (float)height);
}
void glfwOnKey(GLFWwindow *window,
int32_t key, int32_t scancode, int32_t action, int32_t mods
) {
input_t *input = &GAME_STATE->engine.input;
if(action == GLFW_PRESS) {
inputStateSet(input, glfwGetInputSourceForKey(key), 1.0f);
} else if(action == GLFW_RELEASE) {
inputStateSet(input, glfwGetInputSourceForKey(key), 0.0f);
}
}
void glfwOnError(int error, const char* description) {
fputs(description, stderr);
}
void glfwOnCursor(GLFWwindow *window, double x, double y) {
input_t *input = &GAME_STATE->engine.input;
inputStateSet(input, GLFW_PLATFORM_INPUT_MOUSE_X, (float)x);
inputStateSet(input, GLFW_PLATFORM_INPUT_MOUSE_Y, (float)y);
}
inputsource_t glfwGetInputSourceForKey(int32_t key) {
return (inputsource_t)((
key <= GLFW_KEY_GRAVE_ACCENT ? key - GLFW_KEY_SPACE :
key <= GLFW_KEY_MENU ? key - GLFW_KEY_ESCAPE + GLFW_KEY_GRAVE_ACCENT :
key
) % INPUT_SOURCE_COUNT);
}
void glfwClientSetTitle(char *name) {
glfwSetWindowTitle(window, name);
}

View File

@ -0,0 +1,157 @@
// Copyright (c) 2021 Dominic Msters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "glfwclient.hpp"
static GLFWEngine *engine;
GLFWEngine::GLFWEngine(char **args, int32_t argc) {
this->args = args;
this->argc = argc;
this->window = NULL;
}
int32_t GLFWEngine::init(void) {
if(!glfwInit()) return 1;
// Setup window hints
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, false);
// Create Window
this->window = glfwCreateWindow(
WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT, this->game.name, NULL, NULL
);
if(!this->window) {
glfwTerminate();
return 1;
}
// Load GLAD
glfwMakeContextCurrent(window);
glfwSwapInterval(0);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
// Init the game
this->game.init();
// Init the render resolution
this->game.render.setResolution(WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT);
// Bind initial keys
this->game.input.bind(glfwGetInputSourceForKey(GLFW_KEY_ESCAPE), INPUT_NULL);
// input = &this->game.input;
// inputBind(input, INPUT_NULL, glfwGetInputSourceForKey(GLFW_KEY_ESCAPE));
// inputBind(input, INPUT_UP, glfwGetInputSourceForKey(GLFW_KEY_UP));
// inputBind(input, INPUT_DOWN, glfwGetInputSourceForKey(GLFW_KEY_DOWN));
// inputBind(input, INPUT_LEFT, glfwGetInputSourceForKey(GLFW_KEY_LEFT));
// inputBind(input, INPUT_RIGHT, glfwGetInputSourceForKey(GLFW_KEY_RIGHT));
// inputBind(input, INPUT_UP, glfwGetInputSourceForKey(GLFW_KEY_W));
// inputBind(input, INPUT_DOWN, glfwGetInputSourceForKey(GLFW_KEY_S));
// inputBind(input, INPUT_LEFT, glfwGetInputSourceForKey(GLFW_KEY_A));
// inputBind(input, INPUT_RIGHT, glfwGetInputSourceForKey(GLFW_KEY_D));
// inputBind(input, INPUT_ACCEPT, glfwGetInputSourceForKey(GLFW_KEY_E));
// inputBind(input, INPUT_ACCEPT, glfwGetInputSourceForKey(GLFW_KEY_ENTER));
// inputBind(input, INPUT_ACCEPT, glfwGetInputSourceForKey(GLFW_KEY_SPACE));
// Bind the fake inputs
this->game.input.bind(GLFW_PLATFORM_INPUT_MOUSE_X, INPUT_MOUSE_X);
this->game.input.bind(GLFW_PLATFORM_INPUT_MOUSE_Y, INPUT_MOUSE_Y);
// Set up some GLFW callbacks
glfwSetWindowSizeCallback(window, &glfwOnResize);
glfwSetKeyCallback(window, &glfwOnKey);
glfwSetErrorCallback(&glfwOnError);
glfwSetCursorPosCallback(window, &glfwOnCursor);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
// glfwSetWindowTitle(window, game->engine.name);
return 0;
}
int32_t GLFWEngine::update(float delta) {
int32_t ret;
ret = this->game.update(delta);
if(ret != 0) return ret;
if(this->game.input.isPressed(INPUT_NULL)) return 1;
return 0;
}
GLFWEngine::~GLFWEngine(void) {
glfwSetWindowSizeCallback(window, NULL);
glfwTerminate();
}
int32_t main(int32_t argc, char **args) {
int32_t ret;
double time, newTime;
float fDelta;
// Init engine
engine = new GLFWEngine(args, argc);
ret = engine->init();
if(ret != 0) return ret;
printf("Game is %zu bytes.\n", sizeof(engine));
// Main Render Loop
while(!glfwWindowShouldClose(engine->window)) {
glfwPollEvents();
// Determine the delta.
newTime = glfwGetTime();
fDelta = (float)(newTime - time);
time = newTime;
ret = engine->update(fDelta);
// Tick the engine.
glfwSwapBuffers(engine->window);
sleep(0);
if(ret != 0) break;
}
// Cleanup the engine.
delete engine;
return 0;
}
void glfwOnResize(GLFWwindow *window, int32_t width, int32_t height) {
engine->game.render.setResolution((float)width, (float)height);
}
void glfwOnKey(GLFWwindow *window,
int32_t key, int32_t scancode, int32_t action, int32_t mods
) {
if(action == GLFW_PRESS) {
engine->game.input.setValue(glfwGetInputSourceForKey(key), 1.0f);
} else if(action == GLFW_RELEASE) {
engine->game.input.setValue(glfwGetInputSourceForKey(key), 0.0f);
}
}
void glfwOnError(int error, const char* description) {
fputs(description, stderr);
}
void glfwOnCursor(GLFWwindow *window, double x, double y) {
engine->game.input.setValue(GLFW_PLATFORM_INPUT_MOUSE_X, (float)x);
engine->game.input.setValue(GLFW_PLATFORM_INPUT_MOUSE_Y, (float)y);
}
inputsource_t glfwGetInputSourceForKey(int32_t key) {
return (inputsource_t)((
key <= GLFW_KEY_GRAVE_ACCENT ? key - GLFW_KEY_SPACE :
key <= GLFW_KEY_MENU ? key - GLFW_KEY_ESCAPE + GLFW_KEY_GRAVE_ACCENT :
key
) % 0xFF);
}

View File

@ -1,58 +0,0 @@
// Copyright (c) 2021 Dominic Msters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <libs.h>
#include "display/render.h"
#include "input/input.h"
#include "game/game.h"
#define WINDOW_WIDTH_DEFAULT 1280
#define WINDOW_HEIGHT_DEFAULT WINDOW_WIDTH_DEFAULT/16*9
#define GLFW_PLATFORM_INPUT_MOUSE_X (inputsource_t)0xFF
#define GLFW_PLATFORM_INPUT_MOUSE_Y (inputsource_t)0xFE
/**
* Entry of the program
* @return 0 if success, anything else for failure.
*/
int32_t main();
/**
* Resize callbacks.
*
* @param window Window that was resized.
* @param width New window width.
* @param height New window height.
*/
void glfwOnResize(GLFWwindow *window, int32_t width, int32_t height);
/**
* Keyboard Input callbacks.
*
* @param window Window that was resized.
*/
void glfwOnKey(GLFWwindow *window,
int32_t key, int32_t scancode, int32_t action, int32_t mods
);
void glfwOnError(int error, const char* description);
void glfwOnCursor(GLFWwindow *window, double x, double y);
/**
* Get the game engine specific input source for a given GLFW Key code.
*
* @param key Key to get the input source for.
* @return The input source.
*/
inputsource_t glfwGetInputSourceForKey(int32_t key);
/** GLFW Client Methods */
void glfwClientSetTitle(char *name);

View File

@ -0,0 +1,105 @@
// Copyright (c) 2021 Dominic Msters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
extern "C" {
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "libs.h"
}
#define WINDOW_WIDTH_DEFAULT 1280
#define WINDOW_HEIGHT_DEFAULT WINDOW_WIDTH_DEFAULT/16*9
#define GLFW_PLATFORM_INPUT_MOUSE_X (inputsource_t)0xFF
#define GLFW_PLATFORM_INPUT_MOUSE_Y (inputsource_t)0xFE
class GLFWEngine {
private:
char **args;
int32_t argc;
public:
GLFWwindow *window;
/**
* Construct a new GLFW Engine instance.
*
* @param argc Count of arguments.
* @param args Array of strings for the arguments provided to the engine.
*/
GLFWEngine(int32_t argc, char **args);
/**
* Initializes the GLFW engine.
*
* @return 0 if success, everything else is a failure.
*/
virtual int32_t init(void);
/**
* Tick the game engine by delta amount.
*
* @param delta
* @return 0 if success, everything else is a failure.
*/
int32_t update(float delta);
virtual ~GLFWEngine(void) override;
};
/**
* Entry of the program
*
* @param argc Count of args in the args array.
* @param args Args provided to us by the parent O.S.
* @return 0 if success, anything else for failure.
*/
int32_t main(int32_t argc, char **args);
/**
* Resize callbacks.
*
* @param window Window that was resized.
* @param width New window width.
* @param height New window height.
*/
void glfwOnResize(GLFWwindow *window, int32_t width, int32_t height);
/**
* Keyboard Input callbacks.
*
* @param window Window that was resized.
*/
void glfwOnKey(GLFWwindow *window,
int32_t key, int32_t scancode, int32_t action, int32_t mods
);
/**
* Callback for generic GLFW errors.
*
* @param error Error code/descriptor.
* @param description String representation of the error.
*/
void glfwOnError(int error, const char* description);
/**
* Event callback for mouse cursor movement.
*
* @param window
* @param x
* @param y
*/
void glfwOnCursor(GLFWwindow *window, double x, double y);
/**
* Get the game engine specific input source for a given GLFW Key code.
*
* @param key Key to get the input source for.
* @return The input source.
*/
inputsource_t glfwGetInputSourceForKey(int32_t key);

View File

@ -6,5 +6,5 @@
# Sources
target_sources(${PROJECT_NAME}
PRIVATE
input.c
input.cpp
)

View File

@ -1,192 +0,0 @@
// Copyright (c) 2021 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "../libs.h"
#include "../util/list.h"
/** Debug Inputs */
#define INPUT_NULL (inputbind_t)0x00
/** Real Inputs (Starts at 32/0x20) */
#define INPUT_UP (inputbind_t)0x20
#define INPUT_DOWN (inputbind_t)0x21
#define INPUT_LEFT (inputbind_t)0x22
#define INPUT_RIGHT (inputbind_t)0x23
#define INPUT_ACCEPT (inputbind_t)0x24
/** Additional sources */
#define INPUT_MOUSE_X (inputsource_t)0x10
#define INPUT_MOUSE_Y (inputsource_t)0x11
#define INPUT_BIND_COUNT 0x40
#define INPUT_SOURCE_COUNT 0xFF
/**
* Input Bind, a specific action bind reference for the game engine to use.
* e.g. "Jump" or "Walk Forward".
*/
typedef uint8_t inputbind_t;
/**
* Input source identifier. It's up to the platform itself to decide what the
* hell this number refers to. For most platforms it will be an input, such as a
* keyboard scancode or a (pad number * button count) + button.
*/
typedef uint8_t inputsource_t;
/**
* Value that represents the state of an input. Defined as 0-1 where 0 is set
* to be completely off / netural state, and 1 is completely on / full state.
*/
typedef float inputval_t;
/**
* Structure for the entire input mapping.
*/
typedef struct {
/** Float of the input between 0 and 1. */
inputval_t inputsA[INPUT_BIND_COUNT];
/** Float of the input between 0 and 1. */
inputval_t inputsB[INPUT_BIND_COUNT];
/** Flippable state */
inputval_t *current, *previous;
/**
* Binding Map, Array of lists where index = binding and entry is a list of
* input sources.
*/
list_t *bindMap[INPUT_BIND_COUNT];
/**
* Input buffer array. Keeps track of raw values from the inputs.
* The engine will read from the buffer when necessary.
*/
inputval_t buffer[INPUT_SOURCE_COUNT];
/** Float of the GameTime that the input was actuated last. */
float times[INPUT_BIND_COUNT];
} input_t;
/**
* Initializes the input manager.
*
* @param input The input manager to initialize.
*/
void inputInit(input_t *input);
/**
* Tick the input manager.
*
* @param input The input manager to update.
*/
void inputUpdate(input_t *input);
/**
* Destroy the input manager and cleanup.
*
* @param input The input manager to dispose.
*/
void inputDispose(input_t *input);
/**
* Binds the given input binding to the input source. Essentially allowing any
* time we fetch the state of bind, we will read the value from source.
*
* @param input The input manager.
* @param bind The binding to bind against.
* @param source The source that is being bound.
*/
void inputBind(input_t *input, inputbind_t bind, inputsource_t source);
/**
* Unbind a previously bound input source from a binding. This method is costly.
*
* @param input The input manager.
* @param bind The binding to unbind from.
* @param source The source that is being unbound.
*/
void inputUnbind(input_t *input, inputbind_t bind, inputsource_t source);
/**
* Set the state of an input.
*
* @param input Input to set the state for.
* @param source Source to set.
* @param value Value to set.
*/
void inputStateSet(input_t *input, inputsource_t source, float value);
/**
* Is the current input "down", being pressed, being moved, not in a state
* of rest.
*
* @param input The input manager.
* @param binding The previously bound input binding.
* @return True if the input vector is non-zero.
*/
bool inputIsDown(input_t *input, inputbind_t binding);
/**
* Is the current input "up", in a state of rest, not being actioned, moved.
*
* @param input The input manager.
* @param binding The previously bound input binding.
* @return True if input vector is zero
*/
bool inputIsUp(input_t *input, inputbind_t binding);
/**
* Returns true on the first tick that an input was actioned/downed.
*
* @param input The input manager.
* @param binding The previously bound input binding.
* @return True if the input vector was non-zeroed this tick but not last.
*/
bool inputIsPressed(input_t *input, inputbind_t binding);
/**
* Returns true on the first tick that an input was released/upped.
*
* @param input The input manager.
* @param binding The previously bound input binding.
* @return True if the input vector was zeroed this tick but not last.
*/
bool inputIsReleased(input_t *input, inputbind_t binding);
/**
* Returns the raw input value as a float between 0 and 1. For digital (buttons)
* this will typicall be 0 or 1 only. Other analogue inputs will have anywhere
* within the range.
*
* @param input The input manager.
* @param binding The previously bound input binding.
* @return Input state of the axis.
*/
inputval_t inputGetAxis(input_t *input, inputbind_t binding);
/**
* Returns a raw input value between -1 and 1 between two axis. This would be
* indicitive of having an input with an axis that can be moved one direction
* for a positive input and another for a negative input, typically a game
* controller's analogue sticks.
*
* @param input The input manager.
* @param postitive The positive axis binding.
* @param negative The negative axis binding.
* @return A float between -1 and 1 representing the result of both.
*/
float inputGetFullAxis(input_t *input, inputbind_t positive, inputbind_t negative);
/**
* Returns the time that an input was actuated at. Actuate would count as a
* non-zero input for analogue inputs.
*
* @param input The input manager.
* @param binding The previously bound input binding.
* @return Game Engine time that an input was non-zeroed
*/
float inputGetAccuated(input_t *input, inputbind_t binding);

133
src/input/input.hpp Normal file
View File

@ -0,0 +1,133 @@
// Copyright (c) 2021 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
extern "C" {
#include "../util/math.h"
}
#include "../libs.hpp"
/**
* Input source identifier. It's up to the platform itself to decide what the
* hell this number refers to. For most platforms it will be an input, such as a
* keyboard scancode or a (pad number * button count) + button.
*/
typedef uint8_t inputsource_t;
/**
* Input Bind, a specific action bind reference for the game engine to use.
* e.g. "Jump" or "Walk Forward".
*/
typedef uint8_t inputbind_t;
/**
* Value that represents the state of an input. Defined as 0-1 where 0 is set
* to be completely off / netural state, and 1 is completely on / full state.
*/
typedef float inputval_t;
/** Debug Inputs */
#define INPUT_NULL (inputbind_t)0x00
/** Additional sources */
#define INPUT_MOUSE_X (inputbind_t)0x10
#define INPUT_MOUSE_Y (inputsource_t)0x11
namespace Dawn {
class Input {
private:
std::map<inputbind_t, std::vector<inputsource_t>> binds;
std::map<inputbind_t, inputval_t> valsLeft;
std::map<inputbind_t, inputval_t> valsRight;
std::map<inputsource_t, inputval_t> buffer;
bool left;
public:
/**
* Initializes the input manager.
*/
Input(void);
/**
* Tick the input manager.
*/
void update(void);
/**
* Binds the given input binding to the input source. Essentially allowing any
* time we fetch the state of bind, we will read the value from source.
*
* @param source The source that is being bound.
* @param bind The binding to bind against.
*/
void bind(inputsource_t source, inputbind_t bind);
/**
* Unbind a previously bound input source from a binding.
*
* @param source The source that is being unbound.
* @param bind The binding to unbind from.
*/
void unbind(inputsource_t source, inputbind_t bind);
/**
* Set the state of an input.
*
* @param source Source to set.
* @param value Value to set.
*/
void setValue(inputsource_t source, float value);
/**
* Returns the raw input value as a float between 0 and 1. For digital
* (buttons) this will typicall be 0 or 1 only. Other analogue inputs will
* have anywhere within the range.
*
* @param binding The previously bound input binding.
* @return Input state of the axis.
*/
inputval_t getAxis(inputbind_t bind);
/**
* Is the current input "down", being pressed, being moved, not in a state
* of rest.
*
* @param binding The previously bound input binding.
* @return True if the input vector is non-zero.
*/
bool isDown(inputbind_t binding);
/**
* Is the current input "up", in a state of rest, not being actioned,
* moved.
*
* @param binding The previously bound input binding.
* @return True if input vector is zero
*/
bool isUp(inputbind_t binding);
/**
* Returns true on the first tick that an input was actioned/downed.
*
* @param binding The previously bound input binding.
* @return True if the input vector was non-zeroed this tick but not last.
*/
bool isPressed(inputbind_t binding);
/**
* Returns true on the first tick that an input was released/upped.
*
* @param binding The previously bound input binding.
* @return True if the input vector was zeroed this tick but not last.
*/
bool isReleased(inputbind_t binding);
/**
* Destroy the input manager and cleanup.
*/
~Input(void);
};
}

16
src/libs.hpp Normal file
View File

@ -0,0 +1,16 @@
// Copyright (c) 2021 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
extern "C" {
#include "libs.h"
}
#include <vector>
#include <iostream>
#include <map>
#include <iterator>
#include <algorithm>