Commit 134af5e2 authored by Jostein Bratlie's avatar Jostein Bratlie

Refactoring of the engine/interface libraries.

parent 8967531d
......@@ -52,7 +52,6 @@ SET(MSVC_COMPILE_OPTIONS
# Include internal libraries to build
add_subdirectory(libs/interface)
add_subdirectory(libs/engine)
# Include qtclient to build
......
......@@ -40,10 +40,6 @@ set_target_properties(${PROJECT_NAME} PROPERTIES CXX_EXTENSIONS OFF)
### Include the engine interface library
target_link_libraries( ${PROJECT_NAME} PUBLIC engine_interface::engine_interface)
############################
# Target sources and friends
......@@ -55,8 +51,8 @@ target_include_directories( ${PROJECT_NAME} PUBLIC
# Source (translation units)
SET(SOURCE_PREFIX source)
target_sources( ${PROJECT_NAME} PRIVATE
${SOURCE_PREFIX}/mygameengine.cpp
${SOURCE_PREFIX}/orangemonkey_ai.cpp
${SOURCE_PREFIX}/gameengine.cpp
${SOURCE_PREFIX}/monkeyai.cpp
${SOURCE_PREFIX}/utility.cpp
)
......
......@@ -19,25 +19,31 @@ namespace gameengine
namespace detail
{
constexpr size_t computeBoardSize() { return 8 * 8; }
constexpr size_t invalidBoardPosition() { return computeBoardSize() + 1; }
constexpr size_t invalidBoardPosition() { return computeBoardSize(); }
} // namespace detail
struct BitPos {
constexpr BitPos() : m_bitpos{detail::invalidBoardPosition()} {}
constexpr explicit BitPos(size_t bitpos) : m_bitpos{bitpos} {}
constexpr explicit BitPos(int bitpos) : m_bitpos{size_t(bitpos)} {}
constexpr explicit BitPos(size_t bitpos) : m_bitpos{bitpos} {
if (bitpos >= detail::computeBoardSize())
throw std::out_of_range(
"BitPos input position out of range " + std::to_string(bitpos)
+ ". Must be between 0 and "
+ std::to_string(detail::computeBoardSize() - 1));
}
constexpr explicit BitPos(int bitpos) : m_bitpos{size_t(bitpos)} {
if (bitpos >= int(detail::computeBoardSize()) or bitpos < 0)
throw std::out_of_range(
"BitPos input position out of range " + std::to_string(bitpos)
+ ". Must be between 0 and "
+ std::to_string(detail::computeBoardSize() - 1));
}
BitPos(const BitPos&) = default;
BitPos& operator=(const BitPos& o)
{
m_bitpos = o.m_bitpos;
return *this;
}
static constexpr BitPos invalid()
{
return BitPos{detail::invalidBoardPosition()};
return BitPos();
}
constexpr size_t value() const { return m_bitpos; }
constexpr bool isValid() const
......@@ -86,7 +92,7 @@ namespace gameengine // resume namespace gameengine
using BitPieces = std::bitset<detail::computeBoardSize()>;
using BitBoard = std::array<BitPieces, 2>;
enum class PlayerId : size_t { One = 0, Two };
enum class PlayerId : size_t { One = 0, Two = 1 };
enum class PlayerType { Human, AI };
using PlayerIdSet = std::set<PlayerId>;
......
#ifndef GAMEENGINE_H
#define GAMEENGINE_H
#include "interface/gameengine_interface.h"
#include "utility.h"
namespace gameengine
{
namespace detail
{
struct PlayerStruct {
std::unique_ptr<PlayerInterface> obj;
PlayerType type;
};
} // namespace detail
class GameEngine : public GameEngineInterface {
public:
// Inherit constructors
using GameEngineInterface::GameEngineInterface;
// GameEngineInterface interface
public:
bool initNewGame() override;
void clearGame() override;
bool performMoveForCurrentHuman(const BitPos&) override;
bool legalMovesCheck();
void think(const std::chrono::seconds&) override;
PlayerId currentPlayerId() const override;
PlayerType currentPlayerType() const override;
BitPieces pieces(const PlayerId&) const override;
const BitBoard& board() const override;
template <typename Player_T, PlayerId PlayerId_T, typename... PlayerArg_Ts>
void initPlayerType(PlayerArg_Ts... player_args)
{
static_assert(
std::is_base_of<HumanPlayer, Player_T>::value
or std::is_base_of<AIInterface, Player_T>::value,
"Player class must be of either HumanPlayer or AIInterface base.");
const auto player_type = std::is_base_of<HumanPlayer, Player_T>::value
? PlayerType::Human
: PlayerType::AI;
if constexpr (PlayerId(PlayerId_T) == PlayerId::One) {
std::cout << "Hello player one!" << std::endl;
m_player_one.obj
= std::make_unique<Player_T>(PlayerId::One, player_args...);
m_player_one.type = player_type;
}
else {
std::cout << "Hello player two!" << std::endl;
m_player_two.obj
= std::make_unique<Player_T>(PlayerId::Two, player_args...);
m_player_two.type = player_type;
}
}
private:
BitBoard m_board; /*< BitBoard holding player piece positions */
detail::PlayerStruct m_player_one;
detail::PlayerStruct m_player_two;
};
} // namespace gameengine
#endif // GAMEENGINE_H
#ifndef GAMEENGINE_INTERFACE_H
#define GAMEENGINE_INTERFACE_H
#include "basic_types.h"
#include "../basic_types.h"
#include "player_interface.h"
// stl
......@@ -12,20 +12,10 @@
namespace gameengine
{
namespace detail
{
struct PlayerStruct {
std::unique_ptr<PlayerInterface> obj;
PlayerType type;
};
} // namespace detail
class GameEngineInterface {
public:
GameEngineInterface() = default;
virtual ~GameEngineInterface() = default;
/*! Init a new game for the set players */
virtual bool initNewGame() = 0;
......@@ -33,33 +23,6 @@ namespace gameengine
/*! End and reset the game object to a clean state */
virtual void clearGame() = 0;
template <typename Player_T, PlayerId PlayerId_T, typename... PlayerArg_Ts>
void initPlayerType(PlayerArg_Ts... player_args)
{
static_assert(
std::is_base_of<HumanPlayer, Player_T>::value
or std::is_base_of<AIInterface, Player_T>::value,
"Player class must be of either HumanPlayer or AIInterface base.");
const auto player_type = std::is_base_of<HumanPlayer, Player_T>::value
? PlayerType::Human
: PlayerType::AI;
if constexpr (PlayerId(PlayerId_T) == PlayerId::One) {
std::cout << "Hello player one!" << std::endl;
m_player_one.obj
= std::make_unique<Player_T>(PlayerId::One, player_args...);
m_player_one.type = player_type;
}
else {
std::cout << "Hello player two!" << std::endl;
m_player_two.obj
= std::make_unique<Player_T>(PlayerId::Two, player_args...);
m_player_two.type = player_type;
}
}
/*! Perform a move for the current HUMAN player
* \param[in] move What move to perform
* \return Returns true if a move was successfully made */
......@@ -83,12 +46,6 @@ namespace gameengine
/*! Query the complete bitboard piece set for the current game position. */
virtual const BitBoard& board() const = 0;
protected:
BitBoard m_board; /*< BitBoard holding player piece positions */
detail::PlayerStruct m_player_one;
detail::PlayerStruct m_player_two;
};
......
#ifndef PLAYER_INTERFACE_H
#define PLAYER_INTERFACE_H
#include "basic_types.h"
#include "../basic_types.h"
namespace gameengine
......
#ifndef ORANGEMONKEY_H
#define ORANGEMONKEY_H
#ifndef MONKEYAI_H
#define MONKEYAI_H
// engine interface
#include <engine/interface/player_interface.h>
#include "interface/player_interface.h"
// stl
#include <random>
namespace gameengine::monkey_ais
namespace gameengine
{
class OrangeMonkeyAI : public AIInterface {
class MonkeyAI : public AIInterface {
// Constructors
public:
OrangeMonkeyAI(const PlayerId& player_id);
MonkeyAI(const PlayerId& player_id);
// PlayerInterface interface
public:
......@@ -31,4 +30,4 @@ namespace gameengine::monkey_ais
};
} // namespace gameengine::monkey_ais
#endif // ORANGEMONKEY_H
#endif // MONKEYAI_H
#ifndef MYGAMEENGINE_H
#define MYGAMEENGINE_H
#include "utility.h"
// engine interface
#include <engine/interface/basic_types.h>
#include <engine/interface/gameengine_interface.h>
namespace gameengine
{
class MyGameEngine : public gameengine::GameEngineInterface {
// GameEngineInterface interface
public:
bool initNewGame() override;
void clearGame() override;
bool performMoveForCurrentHuman(const gameengine::BitPos&) override;
bool legalMovesCheck();
void think(const std::chrono::seconds&) override;
gameengine::PlayerId currentPlayerId() const override;
gameengine::PlayerType currentPlayerType() const override;
gameengine::BitPieces pieces(const gameengine::PlayerId&) const override;
const gameengine::BitBoard& board() const override;
};
} // namespace gameengine
#endif // MYGAMEENGINE_H
......@@ -2,11 +2,58 @@
#define UTILITY_H
// engine interface
#include <engine/interface/utility_interface.h>
#include "basic_types.h"
namespace gameengine::utility
{
// Board validation
/*! Query a a bitboard piece position set representing all players pieces
* for a given BitBoard */
BitPieces occupiedPositions(const BitBoard& board);
/*! Query whether a position on a bitboard piece position set is occupied
* \param[in] pieces A players pieces on the board
* \param[in] board_pos The board position
* \return Returns true if the queried position is occupied */
bool occupied(const BitPieces& pieces, const BitPos& board_pos);
/*! Query whether a position on a bitboard is occupied
* \param[in] board The board
* \param[in] board_pos The board position
* \return Returns true if the queried position is occupied */
bool occupied(const BitBoard& board, const BitPos& board_pos);
/*! Query the next board position given a direction
* \param[in] board_pos Board position of search start
* \param[in] dir Search direction
* \return Returns the next position */
BitPos nextPosition(const BitPos& board_pos, const MoveDirection& dir);
/*! Query a players legal moves
* \param[in] board The board
* \param[in] player_id Querying player's id
* \return Set of legal moves */
BitPosSet legalMoves(const BitBoard& board, const PlayerId& player_id);
/*! Query wherther a move for a given player is legal for a given bitboard
* \param[in] board The board
* \param[in] player_id The player id
* \param[in] board_pos The board position where the next piece is to be
* placed
* \return Returns true if the move is legal */
bool isLegalMove(const BitBoard& board, const PlayerId&,
const BitPos& board_pos);
/*! Perform a move on a given board for current player id and valid board
* position
* \param[in,out] board Bit board
* \param[in] player_id Current player
* \param[in board_pos Valid piece placement */
void move(BitBoard& board, const PlayerId& player_id,
const BitPos& board_pos);
} // namespace gameengine::utility
#endif // UTILITY_H
#include "engine/gameengine.h"
namespace gameengine
{
bool GameEngine::initNewGame() { return true; }
void GameEngine::clearGame() {}
bool GameEngine::performMoveForCurrentHuman(const BitPos& /*board_pos*/)
{
return true;
}
void GameEngine::think(const std::chrono::seconds& /*time_limit*/) {}
PlayerId GameEngine::currentPlayerId() const { return {}; }
PlayerType GameEngine::currentPlayerType() const { return {}; }
BitPieces GameEngine::pieces(const PlayerId& /*player_id*/) const
{
return {};
}
const BitBoard& GameEngine::board() const
{
static BitBoard bb;
return bb;
}
} // namespace gameengine
#include "engine/orangemonkey_ai.h"
#include "engine/monkeyai.h"
#include "engine/utility.h"
......@@ -6,17 +6,17 @@
// stl
#include <iostream>
namespace gameengine::monkey_ais
namespace gameengine
{
OrangeMonkeyAI::OrangeMonkeyAI(const PlayerId&) { m_engine.seed(m_rd()); }
MonkeyAI::MonkeyAI(const PlayerId&) { m_engine.seed(m_rd()); }
void OrangeMonkeyAI::think(const gameengine::BitBoard& board,
const gameengine::PlayerId& player_id,
const std::chrono::seconds& /*max_time*/)
void MonkeyAI::think(const gameengine::BitBoard& board,
const gameengine::PlayerId& player_id,
const std::chrono::seconds& /*max_time*/)
{
std::cout << "orange monkey thinking!!" << std::endl;
std::cout << "monkey thinking!!" << std::endl;
const auto legal_moves = utility::legalMoves(board, player_id);
std::uniform_int_distribution<size_t> dist(0, legal_moves.size() - 1);
......@@ -27,7 +27,7 @@ namespace gameengine::monkey_ais
m_best_move = *b_iter;
}
BitPos OrangeMonkeyAI::bestMove() const
BitPos MonkeyAI::bestMove() const
{
const auto best_move = BitPos(m_best_move.value());
......
#include "engine/mygameengine.h"
namespace gameengine
{
bool MyGameEngine::initNewGame() { return true; }
void MyGameEngine::clearGame() {}
bool MyGameEngine::performMoveForCurrentHuman(const BitPos& /*board_pos*/)
{
return true;
}
void MyGameEngine::think(const std::chrono::seconds& /*time_limit*/) {}
PlayerId MyGameEngine::currentPlayerId() const { return {}; }
PlayerType MyGameEngine::currentPlayerType() const { return {}; }
BitPieces MyGameEngine::pieces(const PlayerId& /*player_id*/) const
{
return {};
}
const BitBoard& MyGameEngine::board() const
{
static BitBoard bb;
return bb;
}
} // namespace gameengine
......@@ -6,14 +6,6 @@
namespace gameengine::utility
{
////////////////////
//
//
// Interface Utility
// Functions
//
//
////////////////////
BitPieces occupiedPositions(const BitBoard& /*board*/) { return {}; }
......
cmake_minimum_required(VERSION 3.8)
### Setup qtclient project
project(engine_interface VERSION 0.1 LANGUAGES CXX)
# interface part of engine library
add_library(${PROJECT_NAME} INTERFACE)
###########################
# Compiler spesific options
message("Clang compile options: ${CLANG_COMPILE_OPTIONS}")
message("GCC compile options: ${GCC_COMPILE_OPTIONS}")
message("MSVC compile options: ${MSVC_COMPILE_OPTIONS}")
# Turn on c++17 compile features -- minimum CMake Version 3.8 required
target_compile_features(${PROJECT_NAME}
INTERFACE $<$<CXX_COMPILER_ID:Clang>:cxx_std_17>
INTERFACE $<$<CXX_COMPILER_ID:GNU>:cxx_std_17>
INTERFACE $<$<CXX_COMPILER_ID:MSVC>:cxx_std_17>
)
# Compile definitions
target_compile_definitions( ${PROJECT_NAME}
INTERFACE $<$<CXX_COMPILER_ID:MSVC>: _USE_MATH_DEFINES>
)
# Comple options
target_compile_options(${PROJECT_NAME}
INTERFACE $<$<CXX_COMPILER_ID:Clang>: ${CLANG_COMPILE_OPTIONS}>
INTERFACE $<$<CXX_COMPILER_ID:GNU>: ${GCC_COMPILE_OPTIONS}>
INTERFACE $<$<CXX_COMPILER_ID:MSVC>: ${MSVC_COMPILE_OPTIONS}>
)
############################
# Target sources and friends
# Headers
target_include_directories( ${PROJECT_NAME} INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
)
##################################
# Export targets and configuration
export( TARGETS ${PROJECT_NAME}
NAMESPACE ${PROJECT_NAME}::
FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}-targets.cmake"
)
# Add alias gmlib2::${PROJECT_NAME} to ${PROJECT_NAME} such that we can reference this
# in the same way as for other consumer usages (external library)
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
#ifndef UTILITY_INTERFACE_H
#define UTILITY_INTERFACE_H
#include "basic_types.h"
namespace gameengine::utility
{
// Board validation
/*! Query a a bitboard piece position set representing all players pieces
* for a given BitBoard */
BitPieces occupiedPositions(const BitBoard& board);
/*! Query whether a position on a bitboard piece position set is occupied
* \param[in] pieces A players pieces on the board
* \param[in] board_pos The board position
* \return Returns true if the queried position is occupied */
bool occupied(const BitPieces& pieces, const BitPos& board_pos);
/*! Query whether a position on a bitboard is occupied
* \param[in] board The board
* \param[in] board_pos The board position
* \return Returns true if the queried position is occupied */
bool occupied(const BitBoard& board, const BitPos& board_pos);
/*! Query the next board position given a direction
* \param[in] board_pos Board position of search start
* \param[in] dir Search direction
* \return Returns the next position */
BitPos nextPosition(const BitPos& board_pos, const MoveDirection& dir);
/*! Query a players legal moves
* \param[in] board The board
* \param[in] player_id Querying player's id
* \return Set of legal moves */
BitPosSet legalMoves(const BitBoard& board, const PlayerId& player_id);
/*! Query wherther a move for a given player is legal for a given bitboard
* \param[in] board The board
* \param[in] player_id The player id
* \param[in] board_pos The board position where the next piece is to be
* placed
* \return Returns true if the move is legal */
bool isLegalMove(const BitBoard& board, const PlayerId&,
const BitPos& board_pos);
/*! Perform a move on a given board for current player id and valid board
* position
* \param[in,out] board Bit board
* \param[in] player_id Current player
* \param[in board_pos Valid piece placement */
void move(BitBoard& board, const PlayerId& player_id,
const BitPos& board_pos);
} // namespace gameengine::utility
#endif // UTILITY_INTERFACE_H
#include "gamemodel.h"
GameModel::GameModel(const gameengine::MyGameEngine& game_engine,
QObject* parent)
GameModel::GameModel(const gameengine::GameEngine& game_engine, QObject* parent)
: QAbstractListModel(parent), m_game_engine{game_engine}
{
}
......
......@@ -3,7 +3,7 @@
// engine
#include <engine/mygameengine.h>
#include <engine/gameengine.h>
// qt
#include <QAbstractListModel>
......@@ -27,8 +27,8 @@ public:
PieceNrRole
};
GameModel(const gameengine::MyGameEngine& game_engine,
QObject* parent = nullptr);
GameModel(const gameengine::GameEngine& game_engine,
QObject* parent =