├── .doxygen ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── CONTRIBUTING.md ├── README.md ├── config └── chesspp │ ├── board.json │ └── resources.json ├── docs └── Doxyfile ├── res ├── font │ └── OpenSans │ │ ├── LICENSE.txt │ │ ├── OpenSans-Bold.ttf │ │ ├── OpenSans-BoldItalic.ttf │ │ ├── OpenSans-ExtraBold.ttf │ │ ├── OpenSans-ExtraBoldItalic.ttf │ │ ├── OpenSans-Italic.ttf │ │ ├── OpenSans-Light.ttf │ │ ├── OpenSans-LightItalic.ttf │ │ ├── OpenSans-Regular.ttf │ │ ├── OpenSans-Semibold.ttf │ │ └── OpenSans-SemiboldItalic.ttf └── img │ ├── chessboard_640x640.png │ ├── enemy_capture.png │ ├── enemy_move.png │ ├── missing.png │ ├── pieces │ ├── black │ │ ├── bishop.png │ │ ├── king.png │ │ ├── knight.png │ │ ├── pawn.png │ │ ├── queen.png │ │ └── rook.png │ └── white │ │ ├── bishop.png │ │ ├── king.png │ │ ├── knight.png │ │ ├── pawn.png │ │ ├── queen.png │ │ └── rook.png │ ├── title.png │ ├── valid_capture.png │ └── valid_move.png └── src ├── CMakeLists.txt ├── Debug.cpp ├── Debug.hpp ├── Exception.hpp ├── Main.cpp ├── SFML.hpp ├── SfmlEventHandler.hpp ├── app ├── AppState.hpp ├── Application.cpp ├── Application.hpp ├── Button.cpp ├── Button.hpp ├── ButtonManager.cpp ├── ButtonManager.hpp ├── CMakeLists.txt ├── ChessPlusPlusState.cpp ├── ChessPlusPlusState.hpp ├── StartMenuState.cpp └── StartMenuState.hpp ├── board ├── Board.cpp ├── Board.hpp └── CMakeLists.txt ├── config ├── BoardConfig.cpp ├── BoardConfig.hpp ├── CMakeLists.txt ├── Configuration.cpp ├── Configuration.hpp └── ResourcesConfig.hpp ├── gfx ├── CMakeLists.txt ├── GraphicsHandler.cpp └── GraphicsHandler.hpp ├── piece ├── Archer.cpp ├── Archer.hpp ├── Bishop.cpp ├── Bishop.hpp ├── CMakeLists.txt ├── King.cpp ├── King.hpp ├── Knight.cpp ├── Knight.hpp ├── Pawn.cpp ├── Pawn.hpp ├── Piece.cpp ├── Piece.hpp ├── Queen.cpp ├── Queen.hpp ├── Rook.cpp └── Rook.hpp ├── res ├── CMakeLists.txt ├── ResourceManager.hpp └── SfmlFileResource.hpp └── util ├── CMakeLists.txt ├── Direction.cpp ├── Direction.hpp ├── JsonReader.cpp ├── JsonReader.hpp ├── Position.hpp └── Utilities.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | #http://www.kernel.org/pub/software/scm/git/docs/gitignore.html 2 | 3 | *.sdf 4 | *.sln 5 | *.suo 6 | *.vcxproj 7 | *.filters 8 | *.opensdf 9 | *.exe 10 | *.bat 11 | *.pdb 12 | *.ilk 13 | *.DS_Store 14 | *.dll 15 | *.log 16 | *.bin 17 | *.cmake 18 | *~ 19 | 20 | /chesspp 21 | debug.txt 22 | build/ 23 | /docs/ 24 | !/docs/Doxyfile 25 | Makefile 26 | CMakeCache.txt 27 | ChessPlusPlus/ 28 | Debug/ 29 | Release/ 30 | lib/ 31 | bin/ 32 | obj/ 33 | deps.debug/ 34 | objs.debug/ 35 | CMakeFiles/ 36 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "deps/SFML"] 2 | path = deps/SFML 3 | url = https://github.com/SFML/SFML.git 4 | [submodule "deps/json-parser"] 5 | path = deps/json-parser 6 | url = https://github.com/udp/json-parser.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | sudo: false 3 | matrix: 4 | include: 5 | - os: linux 6 | compiler: clang 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | - llvm-toolchain-precise 12 | - llvm-toolchain-precise-3.7 13 | packages: 14 | - clang-3.8 15 | - g++-5 16 | - libglew-dev 17 | - libsndfile1-dev 18 | - libopenal-dev 19 | - libfreetype6-dev 20 | - libx11-xcb-dev 21 | - udev 22 | - libudev-dev 23 | - os: linux 24 | compiler: gcc 25 | addons: 26 | apt: 27 | sources: 28 | - ubuntu-toolchain-r-test 29 | packages: 30 | - g++-5 31 | - libglew-dev 32 | - libsndfile1-dev 33 | - libopenal-dev 34 | - libfreetype6-dev 35 | - libx11-xcb-dev 36 | - udev 37 | - libudev-dev 38 | cache: 39 | apt: true 40 | directories: 41 | - "$HOME/.travis/cmake/build/install" 42 | - "deps/SFML/build/install" 43 | - "deps/SFML/build-static/install" 44 | - "deps/boost-install" 45 | 46 | before_install: 47 | - if [ "$CC" == "gcc" ]; then export CC=gcc-5; fi 48 | - if [ "$CXX" == "g++" ]; then export CXX=g++-5; fi 49 | - if [ "$CC" == "clang" ]; then export CC=clang-3.8; fi 50 | - if [ "$CXX" == "clang++" ]; then export CXX=clang++-3.8; fi 51 | - pushd . && cd $HOME 52 | - git clone https://github.com/LB--/travis.git travis 53 | - source "./travis/update-cmake.sh" 54 | - popd 55 | 56 | install: 57 | - pushd . 58 | # SFML 2.3 has new dependencies that Travis doesn't have 59 | - cd deps/SFML 60 | - git checkout 2.2 61 | - popd && pushd . 62 | # Build SFML (dynamic) 63 | - if [ ! -d "deps/SFML/build/install/lib" ]; then export REBUILD_SFML="yes"; else export REBUILD_SFML="no"; fi 64 | - if [ "$REBUILD_SFML" == "yes" ]; then cd deps/SFML && rm -rf build && mkdir build && cd build; fi 65 | - if [ "$REBUILD_SFML" == "yes" ]; then cmake .. -DCMAKE_INSTALL_PREFIX="$(pwd)/install" && cmake --build . && cmake --build . --target install; fi 66 | - if [ "$REBUILD_SFML" == "yes" ]; then cd install/lib; fi 67 | - if [ "$REBUILD_SFML" == "no" ]; then cd deps/SFML/build/install/lib; fi 68 | - export PATH="$(pwd):$PATH" && cd .. && export SFML_ROOT="$(pwd)" 69 | - popd && pushd . 70 | # Build SFML (static) 71 | - if [ ! -d "deps/SFML/build-static/install/lib" ]; then export REBUILD_SFML_STATIC="yes"; else export REBUILD_SFML_STATIC="no"; fi 72 | - if [ "$REBUILD_SFML_STATIC" == "yes" ]; then cd deps/SFML && rm -rf build-static && mkdir build-static && cd build-static; fi 73 | - if [ "$REBUILD_SFML_STATIC" == "yes" ]; then cmake .. -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DBUILD_SHARED_LIBS=OFF && cmake --build . && cmake --build . --target install; fi 74 | - if [ "$REBUILD_SFML_STATIC" == "yes" ]; then cd install; fi 75 | - if [ "$REBUILD_SFML_STATIC" == "no" ]; then cd deps/SFML/build-static/install; fi 76 | - export SFML_STATIC_ROOT="$(pwd)" 77 | - popd && pushd . 78 | # Build Boost 79 | - if [ ! -d "deps/boost-install/lib" ]; then export REBUILD_BOOST="yes"; else export REBUILD_BOOST="no"; fi 80 | - cd deps 81 | - if [ "$REBUILD_BOOST" == "yes" ]; then rm -rf boost && rm -rf boost-install; fi 82 | - if [ "$REBUILD_BOOST" == "yes" ]; then git clone --quiet --recursive https://github.com/boostorg/boost.git boost; fi 83 | - if [ "$REBUILD_BOOST" == "yes" ]; then cd boost && chmod +x bootstrap.sh; fi 84 | - if [ "$REBUILD_BOOST" == "yes" ]; then ./bootstrap.sh --prefix="$(pwd)/../boost-install"; fi 85 | - if [ "$REBUILD_BOOST" == "yes" ]; then ./b2 headers; fi 86 | - if [ "$REBUILD_BOOST" == "yes" ]; then ./b2 toolset=gcc-5 cxxflags="-std=c++14" variant=release link=static --with-filesystem --with-system install -d0; fi 87 | - if [ "$REBUILD_BOOST" == "no" ]; then mkdir boost && cd boost; fi 88 | - cd ../boost-install && export BOOST_ROOT="$(pwd)" 89 | - popd 90 | 91 | script: 92 | - mkdir build && cd build 93 | - cmake .. -DSFML_ROOT="$SFML_ROOT" -DBOOST_ROOT="$BOOST_ROOT" 94 | - cmake --build . 95 | - ctest -VV 96 | - cd .. && mkdir build-static && cd build-static 97 | - cmake .. -DSFML_ROOT="$SFML_STATIC_ROOT" -DSFML_STATIC_LIBRARIES=ON -DBOOST_ROOT="$BOOST_ROOT" 98 | - cmake --build . 99 | - ctest -VV 100 | 101 | notifications: 102 | email: false 103 | irc: 104 | channels: 105 | - "irc.freenode.net#ChessPlusPlus" 106 | template: 107 | - "Build #%{build_number} at %{repository_slug}/%{branch} took %{duration}" 108 | - "%{author} %{commit} \"%{commit_message}\" %{compare_url}" 109 | - "%{message} %{build_url}" 110 | webhooks: 111 | urls: 112 | - https://webhooks.gitter.im/e/23283bd266397b30488c 113 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.3) 2 | 3 | project(ChessPlusPlus) 4 | 5 | # json-parser 6 | add_library(json-parser 7 | "deps/json-parser/json.c" 8 | ) 9 | set_property(TARGET json-parser PROPERTY C_STANDARD 99) 10 | target_include_directories(json-parser 11 | PUBLIC "deps/json-parser/" 12 | ) 13 | target_compile_definitions(json-parser 14 | PUBLIC JSON_TRACK_SOURCE 15 | ) 16 | 17 | # Find Boost 18 | find_package(Boost REQUIRED COMPONENTS filesystem system) 19 | 20 | # Find SFML 21 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/deps/SFML/cmake/Modules" ${CMAKE_MODULE_PATH}) 22 | if(WIN32) 23 | find_package(SFML 2 REQUIRED COMPONENTS graphics window system audio main) 24 | else() 25 | find_package(SFML 2 REQUIRED COMPONENTS graphics window system audio) 26 | endif() 27 | 28 | # ChessPlusPlus 29 | if(APPLE) # Application bundle if on an apple machine 30 | # Optionally build application bundle 31 | set(BUILD_APPBUNDLE FALSE CACHE BOOL "Build into OS x Application Bundle.") 32 | if(BUILD_APPBUNDLE) 33 | # Set bundle properties 34 | set(MACOSX_BUNDLE_BUNDLE_NAME ChessPlusPlus) 35 | set(MACOSX_BUNDLE_INFO_STRING ChessPlusPlus) 36 | set(MACOSX_BUNDLE_SHORT_VERSION_STRING 0.0.1) 37 | set(MACOSX_BUNDLE_BUNDLE_VERSION 0.0.1) 38 | set(MACOSX_BUNDLE_GUI_IDENTIFIER com.cplusplus.chesspp) 39 | 40 | # Throw all the resource paths into a variable 41 | file(GLOB_RECURSE CHESSPP_RESOURCES 42 | ${PROJECT_SOURCE_DIR}/res/* 43 | ${PROJECT_SOURCE_DIR}/config/* 44 | ) 45 | 46 | # Make sure each resource file gets put in the right directory 47 | # in the application bundle 48 | FOREACH(file ${CHESSPP_RESOURCES}) 49 | file(RELATIVE_PATH relPath ${PROJECT_SOURCE_DIR} ${file}) 50 | string(FIND ${relPath} "/" inSubDirectory REVERSE) 51 | if(${inSubDirectory} GREATER 0) 52 | string(SUBSTRING ${relPath} 0 ${inSubDirectory} relDir) 53 | set(PACKAGE_LOCATION Resources/${relDir}) 54 | else() 55 | set(PACKAGE_LOCATION Resources) 56 | endif() 57 | set_source_files_properties(${file} 58 | PROPERTIES 59 | MACOSX_PACKAGE_LOCATION ${PACKAGE_LOCATION} 60 | ) 61 | ENDFOREACH() 62 | add_executable(ChessPlusPlus MACOSX_BUNDLE 63 | "src/Main.cpp" 64 | ) 65 | endif() 66 | endif() 67 | if(NOT APPLE OR NOT BUILD_APPBUNDLE) 68 | # Copy resources to build directory if build directory is different from source directory. 69 | if(NOT ${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${PROJECT_SOURCE_DIR}) 70 | file(COPY config/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/config/) 71 | file(COPY res/ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/res/) 72 | endif() 73 | if(WIN32) 74 | add_executable(ChessPlusPlus WIN32 75 | "src/Main.cpp" 76 | ) 77 | else() 78 | add_executable(ChessPlusPlus 79 | "src/Main.cpp" 80 | ) 81 | endif() 82 | endif() 83 | set_property(TARGET ChessPlusPlus PROPERTY CXX_STANDARD 14) 84 | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 85 | if(APPLE) 86 | # Building with Clang (on OS x at least) requires -stdlib=libc++ flag 87 | set(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") 88 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") 89 | endif() 90 | endif() 91 | target_include_directories(ChessPlusPlus PUBLIC 92 | "src/" 93 | ${SFML_INCLUDE_DIR} 94 | ${Boost_INCLUDE_DIRS} 95 | ) 96 | target_link_libraries(ChessPlusPlus json-parser ${SFML_LIBRARIES} ${SFML_DEPENDENCIES} ${Boost_LIBRARIES}) 97 | if(SFML_STATIC_LIBRARIES) 98 | target_link_libraries(ChessPlusPlus z bz2) 99 | endif() 100 | 101 | # options 102 | if(WIN32) 103 | set(CHESSPP_REDIRECT_OUTPUT_DEFAULT ON) 104 | else() 105 | set(CHESSPP_REDIRECT_OUTPUT_DEFAULT OFF) 106 | endif() 107 | option(CHESSPP_REDIRECT_OUTPUT "Redirects output to files instead of stdout and stderr" ${CHESSPP_REDIRECT_OUTPUT_DEFAULT}) 108 | if(CHESSPP_REDIRECT_OUTPUT) 109 | target_compile_definitions(ChessPlusPlus 110 | PRIVATE CHESSPP_REDIRECT_OUTPUT 111 | ) 112 | endif() 113 | include(CMakeDependentOption) 114 | CMAKE_DEPENDENT_OPTION(CHESSPP_TRUNC_LOGS "Truncate the log files at the start of the program" ON 115 | "CHESSPP_REDIRECT_OUTPUT" ON 116 | ) 117 | if(CHESSPP_TRUNC_LOGS) 118 | target_compile_definitions(ChessPlusPlus 119 | PRIVATE CHESSPP_TRUNC_LOGS 120 | ) 121 | endif() 122 | 123 | # Divvy out work to subdirectories 124 | add_subdirectory("src/") 125 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Style Guide & Coding Conventions 2 | ================================ 3 | 4 | ## General 5 | - Indentation is to be consistently four spaces, not tabs or a mixture of spaces and tabs. 6 | - All files are to have a single blank line at their end, if possible. 7 | - Prefer C++14 features to C++03 features when possible, and avoid deprecated language features. 8 | - When using C++14 features, try to use those which are supported by at least both clang++ and gcc 9 | - Prefer to use `` rather than `<____.h>` for C standard library headers 10 | - Headers should be included in this order to reduce pollution: first ChessPlusPlus headers, then library headers, then C++ standard headers. 11 | - Prefer use of `class` to `struct` for declaring classes (this means you need to explicitly specify public inheritance). 12 | - Prefer to use file extensions .cpp and .hpp (using .h may cause the file to be detected as C or Objective-C rather than C++) 13 | - Prefer `lowercase` for parameter and local variable names. 14 | - Prefer `UpperCamelCase` for class and type names 15 | - Prefer `lowerCamelCase` for member function names 16 | - Prefer `lower_case_underscore` for private member names 17 | - Prefer `lwrabbrcase` for namespace names 18 | - Braces should be on their own lines, even for empty definitions 19 | - Namespaces cause indentation to occur, inclusion guards do not 20 | - Avoid using raw pointers when possible 21 | - Prefer creating `using` declarations to using raw primitive types 22 | - Avoid use of exceptions when possible 23 | - Avoid ambiguous or non-obvious public interfaces that would otherwise require documentation 24 | - Prefer code that makes less assumptions and allows more flexibility to client code 25 | - Prefer using e.g. functions defined in `` to using common idioms - this allows refactoring to be more obvious and/or straightforward 26 | - First design abstract/high-level/object-oriented code, and optimize later as needed 27 | - Use nameless scopes in functions to emphasize the lifetime of RAII objects 28 | - Place source file helper functions/classes in anonymous namespaces AND declare them static, so as to avoid name/symbol conflicts across irrelevant source files 29 | - Keep clear the separation between client and server behaviors and responsibilities 30 | - Prefer inlining functions, ctors, and dtors, so long as they do not increase header file coupling. 31 | - Try to make as many functions `noexcept` as possible, or at least use the conditional `noexcept` syntax. 32 | - If a function is known for throwing exceptions, write `noexcept(false)` as a visual indicator. 33 | - Destructors are `noexcept(true)` by default, do not put the `noexcept` specifier on a dtor unless it is conditional or `noexcept(false)`. 34 | - Try to avoid excess or repetitive "access:" specifiers. 35 | - Avoid strange spaces before open parens and open braces and in other places unless you are lining up code with other code for readability and emphasis of relation. 36 | - There is no line character limit, but that is because you shouldn't have exceedingly long lines (unless they just have exceedingly long identifiers). Instead, avoid deeply nested compound expressions - they reduce readability and hinder understanding of what code is for and/or doing. 37 | - Include guards should mirror the primary namespace + class of the file, e.g. `chesspp::piece::Piece` maps to `chesspp_piece_Piece_HeaderPlusPlus` 38 | 39 | ## Specifics 40 | One-liners should be **one line**, otherwise they should be in braces: 41 | ```cpp 42 | if(condition) one(liner); 43 | //or 44 | if(condition) 45 | { 46 | really(long(one, liner)); 47 | } 48 | ``` 49 | 50 | Constructor initializer lists and class inheritance lists should have each element on their own line and have the same indentation level as the opening brace of the ctor/class: 51 | ```cpp 52 | class OurClass 53 | : TheirClassA 54 | , TheirClassB 55 | { 56 | OurClass() 57 | : first(element) 58 | , second(element) 59 | , nth(element) 60 | { 61 | //... 62 | } 63 | }; 64 | ``` 65 | 66 | The `const` keyword is to appear on the right side of types, to be consistent with other uses of the `const` keyword: 67 | ```cpp 68 | char const *identifier; 69 | ``` 70 | It should also _not_ snuggle next to * and & 71 | 72 | \* and & should sunggle next to identifiers: 73 | ```cpp 74 | void func(ns::ClassName *classname, ns::OtherClass const &otherclass) const 75 | { 76 | //... 77 | } 78 | ``` 79 | 80 | To suggest changes to this guide, fork the project, make amendments, submit the pull request, and partake in discussion of the changes. 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [ChessPlusPlus](https://github.com/cpluspluscom/ChessPlusPlus) [![Build Status](https://travis-ci.org/cpluspluscom/ChessPlusPlus.png?branch=master)](https://travis-ci.org/cpluspluscom/ChessPlusPlus) [![Join the chat at https://gitter.im/cpluspluscom/ChessPlusPlus](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cpluspluscom/ChessPlusPlus?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | ChessPlusPlus is a project by the cplusplus.com commuity aiming to support any and all variants of Chess in modern C++. We want to show the simplicity and elegance of modern C++ as well as demonstrate use of the latest features of the language and the more modern design practices. 4 | 5 | Currently we use C++14, so make sure your compiler has good support for it. Known good compilers are GCC 5.2, Clang 3.8, and Visual Studio 2015. Build with CMake 3.3 or later. 6 | 7 | ## Interested in participating? 8 | Be sure to read `CONTRIBUTING.md` before submitting pull requests. If you want to join developer conversation, join the Gitter channel via the badge above. If you're an active community member on [cplusplus.com](http://www.cplusplus.com), let us know and we can add you to the community GitHub organization. 9 | 10 | ## Documentation 11 | General information is available on the [wiki](https://github.com/cpluspluscom/ChessPlusPlus/wiki), including build instructions for those unfamiliar with CMake. 12 | 13 | Source code documentation is [hosted on the `gh-pages` branch](https://CPlusPlusCom.GitHub.IO/ChessPlusPlus/) and is built with Doxygen. 14 | 15 | ## Credits 16 | Chess piece images by @Lowest0ne with unspecified licensing. 17 | 18 | Open Sans font by Steve Matteson, used under the Apache License 2.0. 19 | -------------------------------------------------------------------------------- /config/chesspp/board.json: -------------------------------------------------------------------------------- 1 | { 2 | "board": 3 | { 4 | "width": 8, 5 | "height": 8, 6 | "pieces": 7 | [ 8 | ["Rook", "Knight", "Bishop", "Queen", "King", "Bishop", "Knight", "Rook"], 9 | ["Pawn", "Pawn" , "Pawn" , "Pawn" , "Pawn", "Pawn" , "Pawn" , "Pawn"], 10 | [null , null , null , null , null , null , null , null ], 11 | [null , null , null , null , null , null , null , null ], 12 | [null , null , null , null , null , null , null , null ], 13 | [null , null , null , null , null , null , null , null ], 14 | ["Pawn", "Pawn" , "Pawn" , "Pawn" , "Pawn", "Pawn" , "Pawn" , "Pawn"], 15 | ["Rook", "Knight", "Bishop", "Queen", "King", "Bishop", "Knight", "Rook"] 16 | ], 17 | "suits": 18 | [ 19 | ["Black", "Black", "Black", "Black", "Black", "Black", "Black", "Black"], 20 | ["Black", "Black", "Black", "Black", "Black", "Black", "Black", "Black"], 21 | [null , null , null , null , null , null , null , null ], 22 | [null , null , null , null , null , null , null , null ], 23 | [null , null , null , null , null , null , null , null ], 24 | [null , null , null , null , null , null , null , null ], 25 | ["White", "White", "White", "White", "White", "White", "White", "White"], 26 | ["White", "White", "White", "White", "White", "White", "White", "White"] 27 | ], 28 | "cell width": 80, 29 | "cell height": 80, 30 | "metadata": 31 | { 32 | "pawn facing": 33 | [ 34 | [null , null , null , null , null , null , null , null ], 35 | ["South", "South", "South", "South", "South", "South", "South", "South"], 36 | [null , null , null , null , null , null , null , null ], 37 | [null , null , null , null , null , null , null , null ], 38 | [null , null , null , null , null , null , null , null ], 39 | [null , null , null , null , null , null , null , null ], 40 | ["North", "North", "North", "North", "North", "North", "North", "North"], 41 | [null , null , null , null , null , null , null , null ] 42 | ], 43 | "first turn": "White" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /config/chesspp/resources.json: -------------------------------------------------------------------------------- 1 | { 2 | "": "res/img/missing.png", 3 | "menu": 4 | { 5 | "title": "res/img/title.png", 6 | "background": "res/img/chessboard_640x640.png", 7 | "font": "res/font/OpenSans/OpenSans-Semibold.ttf" 8 | }, 9 | "board": 10 | { 11 | "board": "res/img/chessboard_640x640.png", 12 | "valid move": "res/img/valid_move.png", 13 | "enemy move": "res/img/enemy_move.png", 14 | "valid capture": "res/img/valid_capture.png", 15 | "enemy capture": "res/img/enemy_capture.png", 16 | "pieces": 17 | { 18 | "Black": 19 | { 20 | "Pawn": "res/img/pieces/black/pawn.png", 21 | "Rook": "res/img/pieces/black/rook.png", 22 | "Knight": "res/img/pieces/black/knight.png", 23 | "Bishop": "res/img/pieces/black/bishop.png", 24 | "Queen": "res/img/pieces/black/queen.png", 25 | "King": "res/img/pieces/black/king.png", 26 | "": "res/img/missing.png" 27 | }, 28 | "White": 29 | { 30 | "Pawn": "res/img/pieces/white/pawn.png", 31 | "Rook": "res/img/pieces/white/rook.png", 32 | "Knight": "res/img/pieces/white/knight.png", 33 | "Bishop": "res/img/pieces/white/bishop.png", 34 | "Queen": "res/img/pieces/white/queen.png", 35 | "King": "res/img/pieces/white/king.png", 36 | "": "res/img/missing.png" 37 | }, 38 | "": "res/img/missing.png" 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /res/font/OpenSans/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright Steve Matteson 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-Light.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-Semibold.ttf -------------------------------------------------------------------------------- /res/font/OpenSans/OpenSans-SemiboldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/font/OpenSans/OpenSans-SemiboldItalic.ttf -------------------------------------------------------------------------------- /res/img/chessboard_640x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/chessboard_640x640.png -------------------------------------------------------------------------------- /res/img/enemy_capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/enemy_capture.png -------------------------------------------------------------------------------- /res/img/enemy_move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/enemy_move.png -------------------------------------------------------------------------------- /res/img/missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/missing.png -------------------------------------------------------------------------------- /res/img/pieces/black/bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/black/bishop.png -------------------------------------------------------------------------------- /res/img/pieces/black/king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/black/king.png -------------------------------------------------------------------------------- /res/img/pieces/black/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/black/knight.png -------------------------------------------------------------------------------- /res/img/pieces/black/pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/black/pawn.png -------------------------------------------------------------------------------- /res/img/pieces/black/queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/black/queen.png -------------------------------------------------------------------------------- /res/img/pieces/black/rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/black/rook.png -------------------------------------------------------------------------------- /res/img/pieces/white/bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/white/bishop.png -------------------------------------------------------------------------------- /res/img/pieces/white/king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/white/king.png -------------------------------------------------------------------------------- /res/img/pieces/white/knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/white/knight.png -------------------------------------------------------------------------------- /res/img/pieces/white/pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/white/pawn.png -------------------------------------------------------------------------------- /res/img/pieces/white/queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/white/queen.png -------------------------------------------------------------------------------- /res/img/pieces/white/rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/pieces/white/rook.png -------------------------------------------------------------------------------- /res/img/title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/title.png -------------------------------------------------------------------------------- /res/img/valid_capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/valid_capture.png -------------------------------------------------------------------------------- /res/img/valid_move.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/res/img/valid_move.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add sources 2 | target_sources(ChessPlusPlus PUBLIC 3 | "${CMAKE_CURRENT_LIST_DIR}/Debug.cpp" 4 | ) 5 | 6 | # Divvy out work to subdirectories 7 | add_subdirectory("app/") 8 | add_subdirectory("board/") 9 | add_subdirectory("config/") 10 | add_subdirectory("gfx/") 11 | add_subdirectory("piece/") 12 | add_subdirectory("res/") 13 | add_subdirectory("util/") 14 | -------------------------------------------------------------------------------- /src/Debug.cpp: -------------------------------------------------------------------------------- 1 | #include "Debug.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /** 14 | * \def USE_STD_PUT_TIME 15 | * \brief 16 | * Controls whether or not to use std::put_time() or boost::posix_time::to_simple_string() 17 | * 18 | * If not defined, the decision will be made automatically - it is known that GCC 4.9.2 and 19 | * below do not support std::put_time() and it is assumed that GCC 5.x will (maybe) support it. 20 | * 21 | * You can force one or the other by defining as 1 to favor std::put_time() or 0 to favor boost. 22 | */ 23 | #if !defined(USE_STD_PUT_TIME) 24 | //GCC 4.9.2 doesn't support std::put_time yet 25 | #if !defined(__GNUC__) || (__GNUC__ > 4) 26 | #define USE_STD_PUT_TIME 1 //next version of GCC probably does 27 | #else 28 | #define USE_STD_PUT_TIME 0 //use boost alternative 29 | #include 30 | #endif 31 | #endif 32 | 33 | /** 34 | * \def CHESSPP_TRUNC_LOGS 35 | * \brief 36 | * Controls whether logs are truncated on each run, or appended to. 37 | * 38 | * To append to logs on each run, do not define. 39 | * To truncate lots on each run, define via compiler command line. 40 | */ 41 | #ifdef CHESSPP_TRUNC_LOGS 42 | #define CHESSPP_LOG_FILE_OPEN_MODE std::ios::trunc 43 | #else 44 | #define CHESSPP_LOG_FILE_OPEN_MODE std::ios::app 45 | #endif 46 | 47 | namespace chesspp 48 | { 49 | namespace 50 | { 51 | /** 52 | * \brief 53 | * Replaces std::clog, std::cerr, std::cout with file streams. 54 | * 55 | * Calling enableRedirection() will permanently redirect std::clog, std::cerr, and std::cout to 56 | * individual files. 57 | */ 58 | class LogUtil 59 | { 60 | class LogUtil_buffer 61 | : public std::streambuf 62 | { 63 | bool timestamp_on_next_text = true; 64 | std::ostream &sink; 65 | std::vector buffer; 66 | 67 | public: 68 | explicit LogUtil_buffer(std::ostream &sink_, std::size_t buff_sz = 256) 69 | : sink(sink_) //can't use {} 70 | , buffer(buff_sz + 1) //don't use {} 71 | { 72 | sink.clear(); 73 | char *base = &buffer.front(); 74 | setp(base, base + buffer.size()-1); //reserve an extra char for calls to overflow 75 | } 76 | 77 | private: 78 | //Copying disabled 79 | LogUtil_buffer(LogUtil_buffer const &) = delete; 80 | LogUtil_buffer &operator=(LogUtil_buffer const &) = delete; 81 | 82 | //Applies timestamps and flushes buffer 83 | bool timestamp_and_flush() 84 | { 85 | std::stringstream out; 86 | for(char *p = pbase(), *e = pptr(); p != e; ++p) 87 | { 88 | if(*p == '\n') timestamp_on_next_text = true; 89 | else if(timestamp_on_next_text) 90 | { 91 | if(!std::isspace(*p)) out << timestamp(); 92 | timestamp_on_next_text = false; 93 | } 94 | out << *p; 95 | } 96 | 97 | std::ptrdiff_t n = pptr() - pbase(); 98 | pbump(-n); 99 | 100 | sink << out.str(); 101 | sink.flush(); 102 | return (!sink.fail() && !sink.bad()); 103 | } 104 | 105 | //Overridden streambuf functions 106 | int_type overflow(int_type ch) override 107 | { 108 | if(sink && ch != traits_type::eof()) 109 | { 110 | assert(std::less_equal()(pptr(), epptr())); 111 | *pptr() = ch; 112 | pbump(1); 113 | if(timestamp_and_flush()) 114 | return ch; 115 | } 116 | return traits_type::eof(); 117 | } 118 | int sync() override 119 | { 120 | return timestamp_and_flush() ? 0 : -1; 121 | } 122 | 123 | //returns std::string containing current system time. 124 | //should eventually be updated to use std::put_time 125 | static std::string timestamp() 126 | { 127 | std::time_t curr_time_raw = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 128 | std::tm *lt = std::localtime(&curr_time_raw); 129 | 130 | std::stringstream time; 131 | #if USE_STD_PUT_TIME 132 | time << "[" << std::put_time(lt, "%H:%M:%S") << "] "; 133 | #else 134 | time << "[" << boost::posix_time::to_simple_string(boost::posix_time::time_duration(lt->tm_hour, lt->tm_min, lt->tm_sec)) << "] "; 135 | #endif 136 | return time.str(); 137 | } 138 | }; 139 | 140 | std::ofstream log {"debug_log.log", std::ios::out|CHESSPP_LOG_FILE_OPEN_MODE} 141 | , err {"debug_err.log", std::ios::out|CHESSPP_LOG_FILE_OPEN_MODE} 142 | , out {"debug_out.log", std::ios::out|CHESSPP_LOG_FILE_OPEN_MODE}; 143 | LogUtil_buffer logbuf {log} 144 | , errbuf {err} 145 | , outbuf {out}; 146 | std::streambuf *clogbuf {log ? std::clog.rdbuf(&logbuf) : std::clog.rdbuf()} 147 | , *cerrbuf {err ? std::cerr.rdbuf(&errbuf) : std::cerr.rdbuf()} 148 | , *coutbuf {out ? std::cout.rdbuf(&outbuf) : std::cout.rdbuf()}; 149 | 150 | public: 151 | LogUtil() noexcept 152 | { 153 | //Force file output to appear instantly so crashes don't swallow buffered content 154 | logbuf.pubsetbuf(0, 0); 155 | errbuf.pubsetbuf(0, 0); 156 | outbuf.pubsetbuf(0, 0); 157 | } 158 | LogUtil(LogUtil const &) = delete; 159 | LogUtil(LogUtil &&) = delete; 160 | LogUtil &operator=(LogUtil const &) = delete; 161 | LogUtil &operator=(LogUtil &&) = delete; 162 | ~LogUtil() 163 | { 164 | std::clog.rdbuf(clogbuf), clogbuf = nullptr; 165 | std::cerr.rdbuf(cerrbuf), cerrbuf = nullptr; 166 | std::cout.rdbuf(coutbuf), coutbuf = nullptr; 167 | } 168 | }; 169 | } 170 | void enableRedirection() noexcept 171 | { 172 | static LogUtil lu; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/Debug.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_Debug_HeaderPlusPlus 2 | #define chesspp_Debug_HeaderPlusPlus 3 | 4 | namespace chesspp 5 | { 6 | /** 7 | * \brief 8 | * Redirects std::clog, std::cerr, and std::cout to individual files, permanently. 9 | * 10 | * Calling this more than once is harmless and has no effect. 11 | */ 12 | void enableRedirection() noexcept; 13 | } 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/Exception.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_Exception_HeaderPlusPlus 2 | #define chesspp_Exception_HeaderPlusPlus 3 | 4 | #include "util/Utilities.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace chesspp 10 | { 11 | /** 12 | * \brief 13 | * The base exception class for all ChessPlusPlus exceptions. 14 | */ 15 | class Exception 16 | : public virtual std::exception 17 | { 18 | std::string e; //message 19 | 20 | public: 21 | /** 22 | * \brief 23 | * Construct an exception with an optional message. 24 | * 25 | * \param e_ optional message of exception. 26 | */ 27 | Exception(std::string const &e_ = "") noexcept(noexcept(std::string(std::string("")))) 28 | : e{e_} 29 | { 30 | } 31 | Exception(Exception const &) = default; 32 | Exception(Exception &&) = default; 33 | Exception &operator=(Exception const &) = default; 34 | Exception &operator=(Exception &&) = default; 35 | virtual ~Exception() = default; 36 | 37 | /** 38 | * \brief 39 | * Compares two exceptions by their memory address. 40 | */ 41 | virtual bool operator==(std::exception const &other) const noexcept 42 | { 43 | return &other == this; 44 | } 45 | /** 46 | * \brief 47 | * Compares two exceptions by their memory address. 48 | */ 49 | friend bool operator==(std::exception const &e1, Exception const &e2) noexcept 50 | { 51 | return e2 == e1; //calls above operator== 52 | } 53 | 54 | /** 55 | * \brief 56 | * Returns the message of the exception. 57 | * 58 | * \return The message of the exception. 59 | */ 60 | virtual const char *what() const noexcept override 61 | { 62 | return e.c_str(); 63 | } 64 | /** 65 | * \brief 66 | * Returns the message of the exception. 67 | * 68 | * \return The message of the exception. 69 | */ 70 | virtual operator std::string() const noexcept 71 | { 72 | return e; 73 | } 74 | }; 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "app/Application.hpp" 2 | #include "app/StartMenuState.hpp" 3 | #include "Debug.hpp" 4 | #include "Exception.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /** 11 | * \brief 12 | * Entry point of the application. 13 | * 14 | * Sets up the application window and then passes control to chesspp::app::StartMenuState 15 | * 16 | * \param nargs argument count, not used. 17 | * \param args arguments, not used. 18 | */ 19 | int main(int nargs, char const *const *args) 20 | { 21 | #ifdef CHESSPP_REDIRECT_OUTPUT 22 | chesspp::enableRedirection(); 23 | #endif 24 | 25 | try 26 | { 27 | sf::RenderWindow disp 28 | { 29 | sf::VideoMode(640, 640), 30 | "ChessPlusPlus", 31 | sf::Style::Close 32 | }; 33 | chesspp::app::Application app {disp}; 34 | app.changeState(std::ref(app), std::ref(disp)); 35 | return app.execute(); 36 | } 37 | catch(std::exception &e) 38 | { 39 | std::clog << typeid(e).name() << " caught in main: " << e.what() << std::endl; 40 | return EXIT_FAILURE; 41 | } 42 | std::clog << "Exiting normally from main" << std::endl; 43 | } 44 | -------------------------------------------------------------------------------- /src/SFML.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef chesspp_SFML_HeaderPlusPlus 3 | #define chesspp_SFML_HeaderPlusPlus 4 | 5 | #include 6 | #include 7 | //#include 8 | #include 9 | #include 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/SfmlEventHandler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_SfmlEventHandler_HeaderPlusPlus 2 | #define chesspp_SfmlEventHandler_HeaderPlusPlus 3 | 4 | #include "SFML.hpp" 5 | 6 | namespace chesspp 7 | { 8 | /** 9 | * \brief 10 | * Base class for all classes wishing to process SFML events. 11 | * 12 | * All of the member functions in this class are virtual and have a no-op default 13 | * implementation. Deriving classes need only override the member functions corresponding to 14 | * the events they are interested in. 15 | */ 16 | class SfmlEventHandler 17 | { 18 | public: 19 | using uint = unsigned int; 20 | virtual void onClosed () {} 21 | virtual void onResized (uint w, uint h) {} 22 | virtual void onLostFocus () {} 23 | virtual void onGainedFocus () {} 24 | virtual void onTextEntered (sf::Uint32 unicode) {} 25 | virtual void onKeyPressed (sf::Keyboard::Key key, bool alt, bool control, bool shift, bool system){} 26 | virtual void onKeyReleased (sf::Keyboard::Key key, bool alt, bool control, bool shift, bool system){} 27 | virtual void onMouseWheelMoved (int delta, int x, int y) {} 28 | virtual void onLButtonPressed (int x, int y) {} 29 | virtual void onLButtonReleased (int x, int y) {} 30 | virtual void onRButtonPressed (int x, int y) {} 31 | virtual void onRButtonReleased (int x, int y) {} 32 | virtual void onMButtonPressed (int x, int y) {} 33 | virtual void onMButtonReleased (int x, int y) {} 34 | virtual void onMouseButtonPressed (sf::Mouse::Button button, int x, int y) {} 35 | virtual void onMouseButtonReleased (sf::Mouse::Button button, int x, int y) {} 36 | virtual void onMouseMoved (int x, int y) {} 37 | virtual void onMouseEnteredWindow () {} 38 | virtual void onMouseLeftWindow () {} 39 | virtual void onJoystickButtonPressed (uint joystickID, uint button) {} 40 | virtual void onJoystickButtonReleased(uint joystickID, uint button) {} 41 | virtual void onJoystickMoved (uint joystickID, sf::Joystick::Axis axis, float position) {} 42 | virtual void onJoystickConnected (uint joystickID) {} 43 | virtual void onJoystickDisconnected (uint joystickID) {} 44 | 45 | virtual ~SfmlEventHandler() = default; 46 | }; 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/app/AppState.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_app_AppState_HeaderPlusPlus 2 | #define chesspp_app_AppState_HeaderPlusPlus 3 | 4 | #include "SFML.hpp" 5 | #include "SfmlEventHandler.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace app 10 | { 11 | /** 12 | * \brief 13 | * Base class for application states. 14 | * 15 | * See also, chesspp::app::Application 16 | */ 17 | class AppState 18 | : public virtual SfmlEventHandler 19 | { 20 | public: 21 | /** 22 | * \brief 23 | * Construct bound to an sf::RenderWindow 24 | * 25 | * \param disp The sf::RenderWindow which must outlive this object. 26 | */ 27 | AppState(sf::RenderWindow &disp) 28 | : display(disp) //cannot use {} 29 | { 30 | } 31 | 32 | /** 33 | * \brief 34 | * Called to render the current state. 35 | * 36 | * Rendering should be done to the protected display member variable. 37 | */ 38 | virtual void onRender() = 0; 39 | 40 | protected: 41 | /** 42 | * \brief 43 | * The sf::RenderWindow bound at construction. 44 | */ 45 | sf::RenderWindow &display; 46 | }; 47 | } 48 | } 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/app/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.hpp" 2 | 3 | namespace chesspp 4 | { 5 | namespace app 6 | { 7 | int Application::execute() 8 | { 9 | running = true; 10 | sf::Event event; 11 | while(running) 12 | { 13 | while(display.pollEvent(event)) 14 | { 15 | onEvent(event); 16 | } 17 | 18 | state->onRender(); 19 | display.display(); 20 | } 21 | 22 | return 0; 23 | } 24 | 25 | void Application::stop() noexcept 26 | { 27 | running = false; 28 | } 29 | 30 | void Application::onEvent(sf::Event &e) 31 | { 32 | switch(e.type) 33 | { 34 | case sf::Event::Closed: 35 | { 36 | state->onClosed(); 37 | running = false; 38 | break; 39 | } 40 | case sf::Event::Resized: 41 | { 42 | state->onResized(e.size.width, e.size.height); 43 | break; 44 | } 45 | case sf::Event::LostFocus: 46 | { 47 | state->onLostFocus(); 48 | break; 49 | } 50 | case sf::Event::GainedFocus: 51 | { 52 | state->onGainedFocus(); 53 | break; 54 | } 55 | case sf::Event::TextEntered: 56 | { 57 | state->onTextEntered(e.text.unicode); 58 | break; 59 | } 60 | case sf::Event::KeyPressed: 61 | { 62 | state->onKeyPressed(e.key.code, e.key.alt, e.key.control, e.key.shift, e.key.system); 63 | break; 64 | } 65 | case sf::Event::KeyReleased: 66 | { 67 | state->onKeyReleased(e.key.code, e.key.alt, e.key.control, e.key.shift, e.key.system); 68 | break; 69 | } 70 | case sf::Event::MouseWheelMoved: 71 | { 72 | state->onMouseWheelMoved(e.mouseWheel.delta, e.mouseWheel.x, e.mouseWheel.y); 73 | break; 74 | } 75 | case sf::Event::MouseButtonPressed: 76 | { 77 | switch(e.mouseButton.button) 78 | { 79 | case sf::Mouse::Left: 80 | { 81 | state->onLButtonPressed(e.mouseButton.x, e.mouseButton.y); 82 | break; 83 | } 84 | case sf::Mouse::Right: 85 | { 86 | state->onRButtonPressed(e.mouseButton.x, e.mouseButton.y); 87 | break; 88 | } 89 | case sf::Mouse::Middle: 90 | { 91 | state->onMButtonPressed(e.mouseButton.x, e.mouseButton.y); 92 | break; 93 | } 94 | default: 95 | { 96 | state->onMouseButtonPressed(e.mouseButton.button, e.mouseButton.x, e.mouseButton.y); 97 | break; 98 | } 99 | } 100 | break; 101 | } 102 | case sf::Event::MouseButtonReleased: 103 | { 104 | switch(e.mouseButton.button) 105 | { 106 | case sf::Mouse::Left: 107 | { 108 | state->onLButtonReleased(e.mouseButton.x, e.mouseButton.y); 109 | break; 110 | } 111 | case sf::Mouse::Right: 112 | { 113 | state->onRButtonReleased(e.mouseButton.x, e.mouseButton.y); 114 | break; 115 | } 116 | case sf::Mouse::Middle: 117 | { 118 | state->onMButtonReleased(e.mouseButton.x, e.mouseButton.y); 119 | break; 120 | } 121 | default: 122 | { 123 | state->onMouseButtonPressed(e.mouseButton.button, e.mouseButton.x, e.mouseButton.y); 124 | break; 125 | } 126 | } 127 | break; 128 | } 129 | case sf::Event::MouseMoved: 130 | { 131 | state->onMouseMoved(e.mouseMove.x, e.mouseMove.y); 132 | break; 133 | } 134 | case sf::Event::MouseEntered: 135 | { 136 | state->onMouseEnteredWindow(); 137 | break; 138 | } 139 | case sf::Event::MouseLeft: 140 | { 141 | state->onMouseLeftWindow(); 142 | break; 143 | } 144 | case sf::Event::JoystickButtonPressed: 145 | { 146 | state->onJoystickButtonPressed(e.joystickButton.joystickId, e.joystickButton.button); 147 | break; 148 | } 149 | case sf::Event::JoystickButtonReleased: 150 | { 151 | state->onJoystickButtonReleased(e.joystickButton.joystickId, e.joystickButton.button); 152 | break; 153 | } 154 | case sf::Event::JoystickMoved: 155 | { 156 | state->onJoystickMoved(e.joystickMove.joystickId, e.joystickMove.axis, e.joystickMove.position); 157 | break; 158 | } 159 | case sf::Event::JoystickConnected: 160 | { 161 | state->onJoystickConnected(e.joystickConnect.joystickId); 162 | break; 163 | } 164 | case sf::Event::JoystickDisconnected: 165 | { 166 | state->onJoystickDisconnected(e.joystickConnect.joystickId); 167 | break; 168 | } 169 | default: break; 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/app/Application.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_app_Application_HeaderPlusPlus 2 | #define chesspp_app_Application_HeaderPlusPlus 3 | 4 | #include "AppState.hpp" 5 | #include "config/ResourcesConfig.hpp" 6 | 7 | #include 8 | #include 9 | 10 | namespace chesspp 11 | { 12 | namespace app 13 | { 14 | /** 15 | * \brief 16 | * Represents the application and holds the current AppState 17 | */ 18 | class Application 19 | { 20 | config::ResourcesConfig res_config; 21 | sf::RenderWindow &display; 22 | bool running = false; 23 | std::unique_ptr state; 24 | 25 | void onEvent(sf::Event &e); 26 | 27 | public: 28 | /** 29 | * \brief 30 | * Construct from an sf::RenderWindow 31 | * 32 | * Also calls `sf::RenderWindow::setVerticalSyncEnabled(true)`. 33 | * 34 | * \param disp The sf::RenderWindow that will be drawn to. Must outlive this object. 35 | */ 36 | Application(sf::RenderWindow &disp) 37 | : display(disp) //can't use {} 38 | { 39 | display.setVerticalSyncEnabled(true); 40 | } 41 | 42 | /** 43 | * \brief 44 | * Changes the current AppState by constructing a new one in place and destructing 45 | * the old one. 46 | * 47 | * The old AppState instance is destructed within this function, so it must not be 48 | * referenced anymore. 49 | * 50 | * \tparam NewState The type of AppState to construct. 51 | * \tparam Args deuced by the compiler from the parameter list. 52 | * \param args Arguments to pass to the constructor of NewState 53 | */ 54 | template 55 | void changeState(Args &&... args) 56 | { 57 | //make sure we can create the new state first 58 | std::unique_ptr temp {new NewState(std::forward(args)...)}; 59 | //then swap ownership 60 | state.swap(temp); 61 | //temp properly deletes the old state at the end of its scope 62 | } 63 | 64 | /** 65 | * \brief 66 | * Begins running the application via the current AppState 67 | * 68 | * This function blocks while the application is running. Application::stop() can be 69 | * called to cause this function to return. 70 | * 71 | * \return An appropriate return value for returning from main() 72 | */ 73 | int execute(); 74 | /** 75 | * \brief 76 | * Causes execute() to stop running and return. 77 | */ 78 | void stop() noexcept; 79 | 80 | /** 81 | * \brief 82 | * Gives access to the config::ResourcesConfig used by this Application and its 83 | * AppState 84 | * 85 | * \return The config::ResourcesConfig in use. 86 | */ 87 | config::ResourcesConfig &resourcesConfig() noexcept 88 | { 89 | return res_config; 90 | } 91 | }; 92 | } 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /src/app/Button.cpp: -------------------------------------------------------------------------------- 1 | #include "Button.hpp" 2 | 3 | namespace chesspp 4 | { 5 | namespace app 6 | { 7 | Button::Button(const std::string &text, unsigned font_size) 8 | { 9 | button.setCharacterSize(font_size); 10 | button.setString(text); 11 | button.setStyle(sf::Text::Bold); 12 | button.setColor(sf::Color::Black); 13 | recenter(); 14 | } 15 | 16 | void Button::draw(sf::RenderTarget& target, sf::RenderStates states) const 17 | { 18 | target.draw(button, states); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/Button.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_app_Button_HeaderPlusPlus 2 | #define chesspp_app_Button_HeaderPlusPlus 3 | 4 | #include "SFML.hpp" 5 | 6 | #include 7 | namespace chesspp 8 | { 9 | namespace app 10 | { 11 | /** 12 | * \brief 13 | * Simple text-based GUI buttons 14 | */ 15 | class Button 16 | : public sf::Drawable 17 | { 18 | sf::Text button; 19 | sf::Vector2f center; 20 | /** 21 | * \brief 22 | * Overriden sf::Drawable::draw method to provide to custom rendering behavior 23 | * 24 | * \param target Render target to draw to 25 | * \param states Current render states 26 | */ 27 | virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override; 28 | 29 | void recenter() noexcept 30 | { 31 | button.setPosition(center.x - (button.getLocalBounds().width/2), center.y - (button.getLocalBounds().height/2)); 32 | } 33 | 34 | public: 35 | /** 36 | * \brief 37 | * Construct from text and a font size 38 | * 39 | * \param text Button text 40 | * \param font_size Font size of the button text 41 | */ 42 | Button(const std::string &text, unsigned font_size); 43 | 44 | /** 45 | * \brief 46 | * Compares two Buttons for equality 47 | * 48 | * \param other Other button to test for comparison 49 | * \return True if and only if other is this 50 | */ 51 | bool operator==(const Button& other) const 52 | { 53 | return this == &other; 54 | } 55 | 56 | /** 57 | * \brief 58 | * Sets abolute position of button 59 | * 60 | * \param x The x coordinate of the new position 61 | * \param y The y coordinate of the new position 62 | */ 63 | void setPosition(int x, int y) 64 | { 65 | center = sf::Vector2f{sf::Vector2i{x, y}}; 66 | recenter(); 67 | } 68 | 69 | /** 70 | * \brief 71 | * Sets absolute position of button 72 | * 73 | * \param pos New position 74 | */ 75 | void setPosition(sf::Vector2f &pos) 76 | { 77 | center = pos; 78 | recenter(); 79 | } 80 | 81 | /** 82 | * \brief 83 | * Sets the button's text 84 | * 85 | * \param text Button's text 86 | */ 87 | void setText(const std::string &text) 88 | { 89 | button.setString(text); 90 | recenter(); 91 | } 92 | 93 | /** 94 | * \brief 95 | * Sets the button's text color 96 | * 97 | * \param color Color of button text 98 | */ 99 | void setColor(const sf::Color &color) 100 | { 101 | button.setColor(color); 102 | } 103 | 104 | /** 105 | * \brief 106 | * Sets button's text font 107 | * 108 | * \param font Font of button text 109 | */ 110 | void setFont(const sf::Font &font) 111 | { 112 | button.setFont(font); 113 | recenter(); 114 | } 115 | 116 | /** 117 | * \brief 118 | * Sets the selected status of the button 119 | * 120 | * /param selected Selected status 121 | */ 122 | void setSelected(bool selected) 123 | { 124 | if(selected) setColor(sf::Color::Blue); 125 | else setColor(sf::Color::Black); 126 | } 127 | 128 | /** 129 | * \brief 130 | * Returns position of button 131 | * 132 | * \return Button position 133 | */ 134 | sf::Vector2f getPosition() 135 | { 136 | return center; 137 | } 138 | 139 | /** 140 | * \brief 141 | * Returns button's text 142 | * 143 | * \return Text of button 144 | */ 145 | const sf::Text& getText() 146 | { 147 | return button; 148 | } 149 | 150 | /** 151 | * \brief 152 | * Check if point is within the rectangle area of the text 153 | * 154 | * \return True if (x,y) is within text area 155 | */ 156 | bool contains(int x, int y) 157 | { 158 | recenter(); 159 | return button.getGlobalBounds().contains(x,y); 160 | } 161 | 162 | }; 163 | } 164 | } 165 | #endif 166 | -------------------------------------------------------------------------------- /src/app/ButtonManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ButtonManager.hpp" 2 | 3 | namespace chesspp 4 | { 5 | namespace app 6 | { 7 | ButtonManager::ButtonManager() 8 | { 9 | 10 | } 11 | 12 | void ButtonManager::registerButton(Button &button) 13 | { 14 | buttons.push_back(std::ref(button)); 15 | } 16 | 17 | bool ButtonManager::deregisterButton(Button &button) 18 | { 19 | for(std::size_t i = 0; i < buttons.size(); ++i) 20 | { 21 | //Surely there's a better way to do this 22 | if(&(buttons[i].get()) == &button) 23 | { 24 | buttons.erase(buttons.begin() + (i - 1)); 25 | return true; 26 | } 27 | } 28 | return false; 29 | } 30 | 31 | bool ButtonManager::setSelected(Button &button) 32 | { 33 | bool isPresent = false; 34 | //Checks to see if button is present 35 | for(std::size_t i = 0; i < buttons.size(); ++i) 36 | { 37 | if(&(buttons[i].get()) == &button) 38 | { 39 | isPresent = true; 40 | break; 41 | } 42 | } 43 | if(!isPresent) return false; 44 | for(std::size_t i = 0; i < buttons.size(); ++i) 45 | { 46 | if(&(buttons[i].get()) == &button) 47 | { 48 | //Need the .get() because of std::reference_wrapper 49 | buttons[i].get().setSelected(true); 50 | selected_button = &(buttons[i].get()); 51 | } 52 | else 53 | { 54 | buttons[i].get().setSelected(false); 55 | } 56 | } 57 | 58 | return true; 59 | } 60 | 61 | void ButtonManager::cycleSelectedNext() 62 | { 63 | for(std::size_t i = 0; i < buttons.size(); ++i) 64 | { 65 | if(&(buttons[i].get()) == selected_button) 66 | { 67 | setSelected(buttons[((i + 1) % buttons.size())].get()); 68 | return; 69 | } 70 | } 71 | } 72 | 73 | void ButtonManager::cycleSelectedPrevious() 74 | { 75 | for(std::size_t i = 0; i < buttons.size(); ++i) 76 | { 77 | if(&(buttons[i].get()) == selected_button) 78 | { 79 | setSelected(buttons[(((i + buttons.size()) - 1) % buttons.size())].get()); 80 | return; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/app/ButtonManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_app_ButtonManager_HeaderPlusPlus 2 | #define chesspp_app_ButtonManager_HeaderPlusPlus 3 | 4 | #include "Button.hpp" 5 | #include "SFML.hpp" 6 | 7 | #include 8 | #include 9 | 10 | /** 11 | * ButtonManager is a class which handles button selection of menus 12 | * Inspiration for this comes from ButtonGroups in Java's Swing 13 | * To use, just create your Buttons inside your menu class, then register 14 | * via registerButton(Button &button). Setting a button as selected will automatically 15 | * 'deselect' all other buttons. User can then call getSelected() to get the 16 | * currently selected button. 17 | * 18 | * Should this class handle placement also? 19 | * This should be renamed to ButtonGroup or something similar 20 | */ 21 | namespace chesspp 22 | { 23 | namespace app 24 | { 25 | class ButtonManager 26 | { 27 | //std::map be any better maybe? 28 | using Buttons_t = std::vector>; 29 | Buttons_t buttons; 30 | Button *selected_button; 31 | 32 | //Noncopyable 33 | ButtonManager(const ButtonManager&) = delete; 34 | ButtonManager& operator=(const ButtonManager&) = delete; 35 | 36 | public: 37 | /** 38 | * \brief 39 | * Default constructor 40 | */ 41 | ButtonManager(); 42 | /** 43 | * \brief 44 | * Registers a new button with the ButtonManager 45 | * 46 | * \param button New button to be registered 47 | */ 48 | void registerButton(Button &button); 49 | /** 50 | * \brief 51 | * Deregistered a button from the ButtonManager 52 | * 53 | * \param Button to be deregistered 54 | * \return True if succeeded to deregister 55 | */ 56 | bool deregisterButton(Button &button); 57 | /** 58 | * \brief 59 | * Sets selected status of a button to true 60 | * 61 | * \param button Button to be set as selected 62 | * \return True if succeeded to set selected status 63 | */ 64 | bool setSelected(Button &button); 65 | /** 66 | * \brief 67 | * Deselects all registered buttons 68 | */ 69 | void deselectAll(); 70 | /** 71 | * \brief 72 | * Sets selected status of next (according to time registered) button to true 73 | */ 74 | void cycleSelectedNext(); 75 | /** 76 | * \brief 77 | * Sets selected status of previous (according to time registered) button to true 78 | */ 79 | void cycleSelectedPrevious(); 80 | /** 81 | * \brief 82 | * Returns selected button 83 | * 84 | * \return Currently selected button 85 | */ 86 | const Button& getSelected() 87 | { 88 | return *selected_button; 89 | } 90 | }; 91 | } 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /src/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add sources 2 | target_sources(ChessPlusPlus PUBLIC 3 | "${CMAKE_CURRENT_LIST_DIR}/Application.cpp" 4 | "${CMAKE_CURRENT_LIST_DIR}/Button.cpp" 5 | "${CMAKE_CURRENT_LIST_DIR}/ButtonManager.cpp" 6 | "${CMAKE_CURRENT_LIST_DIR}/ChessPlusPlusState.cpp" 7 | "${CMAKE_CURRENT_LIST_DIR}/StartMenuState.cpp" 8 | ) 9 | -------------------------------------------------------------------------------- /src/app/ChessPlusPlusState.cpp: -------------------------------------------------------------------------------- 1 | #include "ChessPlusPlusState.hpp" 2 | 3 | #include "StartMenuState.hpp" 4 | #include "util/Utilities.hpp" 5 | 6 | #include 7 | #include 8 | 9 | namespace chesspp 10 | { 11 | namespace app 12 | { 13 | ChessPlusPlusState::ChessPlusPlusState(Application &app_, sf::RenderWindow &disp) 14 | : AppState(disp) //can't use {} 15 | , app(app_) //can't use {} 16 | , res_config(app.resourcesConfig()) //can't use {} 17 | , board_config{res_config} 18 | , graphics{display, res_config, board_config} 19 | , board{board_config} 20 | , players{util::KeyIter 21 | (board_config.texturePaths().cbegin()), 22 | util::KeyIter 23 | (board_config.texturePaths().cend())} 24 | , turn{players.find(board_config.metadata("first turn"))} 25 | { 26 | std::clog << "Number of players: " << players.size() << std::endl; 27 | if(turn == players.end()) 28 | { 29 | turn = players.begin(); 30 | } 31 | } 32 | 33 | void ChessPlusPlusState::nextTurn() 34 | { 35 | if(++turn == players.end()) 36 | { 37 | turn = players.begin(); 38 | } 39 | } 40 | 41 | board::Board::Pieces_t::iterator ChessPlusPlusState::find(board::Board::Position_t const &pos) const 42 | { 43 | return std::find_if(board.begin(), board.end(), 44 | [&](std::unique_ptr const &up) -> bool 45 | { 46 | return up->pos == pos; 47 | }); 48 | } 49 | 50 | void ChessPlusPlusState::onRender() 51 | { 52 | graphics.drawBoard(board); 53 | if(selected != board.end()) 54 | { 55 | graphics.drawTrajectory(**selected); 56 | } 57 | if(board.valid(p)) 58 | { 59 | auto piece = find(p); 60 | if(piece != board.end()) 61 | { 62 | graphics.drawTrajectory(**piece, (*piece)->suit != *turn); 63 | } 64 | } 65 | } 66 | 67 | void ChessPlusPlusState::onKeyPressed(sf::Keyboard::Key key, bool /*alt*/, bool /*control*/, bool /*shift*/, bool /*system*/) 68 | { 69 | if(key == sf::Keyboard::Escape) 70 | { //Exit to menu screen 71 | return app.changeState(std::ref(app), std::ref(display)); 72 | } 73 | } 74 | void ChessPlusPlusState::onMouseMoved(int x, int y) 75 | { 76 | p.x = static_cast(x/board.config.cellWidth()); 77 | p.y = static_cast(y/board.config.cellHeight()); 78 | } 79 | void ChessPlusPlusState::onLButtonPressed(int x, int y) 80 | { 81 | } 82 | void ChessPlusPlusState::onLButtonReleased(int x, int y) 83 | { 84 | if(!board.valid(p)) return; 85 | if(selected == board.end()) 86 | { 87 | selected = find(p); //doesn't matter if board.end(), selected won't change then 88 | if(selected != board.end() && (*selected)->suit != *turn) 89 | { 90 | selected = board.end(); //can't select enemy pieces 91 | } 92 | } 93 | else 94 | { 95 | if(find(p) == board.end() || (*find(p))->suit != (*selected)->suit)[&] 96 | { 97 | { 98 | auto it = std::find_if(board.pieceCapturings().begin(), 99 | board.pieceCapturings().end(), 100 | [&](board::Board::Movements_t::value_type const &m) 101 | { 102 | return m.first == selected && m.second == p; 103 | }); 104 | if(it != board.pieceCapturings().end()) 105 | { 106 | for(auto jt = board.pieceCapturables().begin(); jt != board.pieceCapturables().end(); ++jt) 107 | { 108 | if(jt->second == p) 109 | { 110 | if(board.capture(selected, it, jt)) 111 | { 112 | nextTurn(); 113 | return; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | { 120 | auto it = std::find_if(board.pieceTrajectories().begin(), 121 | board.pieceTrajectories().end(), 122 | [&](board::Board::Movements_t::value_type const &m) 123 | { 124 | return m.first == selected && m.second == p; 125 | }); 126 | if(it != board.pieceTrajectories().end()) 127 | { 128 | if(board.move(selected, it)) 129 | { 130 | nextTurn(); 131 | } 132 | } 133 | } 134 | }(); 135 | selected = board.end(); //deselect 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/app/ChessPlusPlusState.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_app_ChessPlusPlusState_HeaderPlusPlus 2 | #define chesspp_app_ChessPlusPlusState_HeaderPlusPlus 3 | 4 | #include "gfx/GraphicsHandler.hpp" 5 | #include "board/Board.hpp" 6 | 7 | #include "AppState.hpp" 8 | #include "Application.hpp" 9 | 10 | #include 11 | 12 | namespace chesspp 13 | { 14 | namespace app 15 | { 16 | /** 17 | * \brief 18 | * The AppState class for the state of the chess gameplay. 19 | * 20 | * This class handles user-interaction by manipulating the chesspp::board::Board 21 | * directly. 22 | */ 23 | class ChessPlusPlusState 24 | : public AppState 25 | { 26 | Application &app; 27 | config::ResourcesConfig &res_config; 28 | config::BoardConfig board_config; 29 | gfx::GraphicsHandler graphics; 30 | board::Board board; 31 | 32 | board::Board::Pieces_t::iterator selected = board.end(); 33 | board::Board::Position_t p; 34 | using Players_t = std::set; 35 | Players_t players; 36 | Players_t::const_iterator turn; 37 | void nextTurn(); 38 | board::Board::Pieces_t::iterator find(board::Board::Position_t const &pos) const; 39 | 40 | public: 41 | /** 42 | * \brief 43 | * Construct from the Application and sf::RenderWindow to be used. 44 | * 45 | * \param app The Application owning this instance, must outlive this instance. 46 | * \param display The sf::RenderWindow use during onRender(), must outlive this 47 | * instance. 48 | */ 49 | ChessPlusPlusState(Application &app, sf::RenderWindow &display); 50 | 51 | /** 52 | * \brief 53 | * Renders the chessboard, all chess pieces, and any highlighted moves. 54 | */ 55 | virtual void onRender() override; 56 | 57 | /** 58 | * \brief 59 | * Allows the player to exit to the main menu by pressing escape. 60 | */ 61 | virtual void onKeyPressed(sf::Keyboard::Key key, bool alt, bool control, bool shift, bool system) override; 62 | /** 63 | * \brief 64 | * Updates which highlighted moves are visible. 65 | */ 66 | virtual void onMouseMoved(int x, int y) override; 67 | /** 68 | * \brief 69 | * Currently does nothing. 70 | */ 71 | virtual void onLButtonPressed(int x, int y) override; 72 | /** 73 | * \brief 74 | * Selects or deselects pieces, or moves a selected piece to the clicked position. 75 | */ 76 | virtual void onLButtonReleased(int x, int y) override; 77 | }; 78 | } 79 | } 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/app/StartMenuState.cpp: -------------------------------------------------------------------------------- 1 | #include "StartMenuState.hpp" 2 | 3 | #include "ChessPlusPlusState.hpp" 4 | #include "res/SfmlFileResource.hpp" 5 | 6 | #include 7 | 8 | namespace chesspp 9 | { 10 | namespace app 11 | { 12 | using Font_res = res::SfmlFileResource; 13 | using Texture_res = res::SfmlFileResource; 14 | StartMenuState::StartMenuState(Application &app_, sf::RenderWindow &display_) 15 | : AppState(display_) //can't use {} 16 | , app(app_) //can't use {} 17 | , menu_background{app.resourcesConfig().resources().from_config("menu", "background")} 18 | , logo {app.resourcesConfig().resources().from_config("menu", "title") } 19 | , font (app.resourcesConfig().resources().from_config ("menu", "font") ) //can't use {} 20 | , start_text{"Start", 75} 21 | , quit_text {"Quit", 75} 22 | { 23 | //Sets position at centered horizontally, down 10% vertically 24 | logo.setPosition((display.getSize().x/2) - (logo.getLocalBounds().width/2), display.getSize().y*0.10); 25 | 26 | //Set up text 27 | start_text.setPosition(display.getSize().x/2, display.getSize().y*0.35); 28 | start_text.setFont(font); 29 | 30 | quit_text.setPosition (display.getSize().x/2, display.getSize().y*0.47); 31 | quit_text.setFont(font); 32 | 33 | //Register buttons with button_manager 34 | button_manager.registerButton(start_text); 35 | button_manager.registerButton(quit_text); 36 | button_manager.setSelected(start_text); 37 | } 38 | 39 | void StartMenuState::onRender() 40 | { 41 | display.clear(); 42 | display.draw(menu_background); 43 | display.draw(logo); 44 | display.draw(start_text); 45 | display.draw(quit_text); 46 | } 47 | 48 | void StartMenuState::onLButtonReleased(int x, int y) 49 | { 50 | //If clicked on Start button 51 | if(start_text.contains(x,y)) 52 | { 53 | std::clog << "State Change: StartMenuState -> ChessPlusPlusState" << std::endl; 54 | return app.changeState(std::ref(app), std::ref(display)); 55 | } 56 | 57 | //If clicked on Exit button 58 | if(quit_text.contains(x,y)) 59 | { 60 | std::clog << "Exiting from StartMenuState" << std::endl; 61 | return app.stop(); 62 | } 63 | } 64 | 65 | void StartMenuState::onMouseMoved(int x, int y) 66 | { 67 | //If moused over Start button 68 | if(start_text.contains(x,y)) 69 | { 70 | button_manager.setSelected(start_text); 71 | } 72 | else if(quit_text.contains(x,y)) 73 | { 74 | button_manager.setSelected(quit_text); 75 | } 76 | } 77 | 78 | void StartMenuState::onKeyPressed(sf::Keyboard::Key key, bool alt, bool control, bool shift, bool system) 79 | { 80 | //If Enter (Return) key is pressed, perform action for currently selected button 81 | if(key == sf::Keyboard::Key::Return) 82 | { 83 | if(button_manager.getSelected() == start_text) 84 | { 85 | std::clog << "State changing to ChessPlusPlus" << std::endl; 86 | return app.changeState(std::ref(app), std::ref(display)); 87 | } 88 | if(button_manager.getSelected() == quit_text) 89 | { 90 | std::clog << "Exiting from StartMenuState" << std::endl; 91 | return app.stop(); 92 | } 93 | } 94 | 95 | //Allowed to 'cycle' through menu items with arrow keys 96 | if(key == sf::Keyboard::Key::Up) 97 | { 98 | button_manager.cycleSelectedNext(); 99 | } 100 | if(key == sf::Keyboard::Key::Down) 101 | { 102 | button_manager.cycleSelectedPrevious(); 103 | } 104 | 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/app/StartMenuState.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_app_StartMenuState_HeaderPlusPlus 2 | #define chesspp_app_StartMenuState_HeaderPlusPlus 3 | 4 | #include "SFML.hpp" 5 | 6 | #include "AppState.hpp" 7 | #include "Application.hpp" 8 | #include "Button.hpp" 9 | #include "ButtonManager.hpp" 10 | 11 | namespace chesspp 12 | { 13 | namespace app 14 | { 15 | /** 16 | * \brief 17 | * Handles the start screen/main menu for the game. 18 | */ 19 | class StartMenuState 20 | : public AppState 21 | { 22 | Application &app; 23 | 24 | sf::Sprite menu_background; 25 | sf::Sprite logo; 26 | 27 | sf::Font font; 28 | 29 | Button start_text; 30 | Button quit_text; 31 | 32 | ButtonManager button_manager; 33 | 34 | public: 35 | /** 36 | * \brief 37 | * Construct from the Application and sf::RenderWindow to be used. 38 | * 39 | * \param app The Application owning this instance, must outlive this instance. 40 | * \param display The sf::RenderWindow use during onRender(), must outlive this 41 | * instance. 42 | */ 43 | StartMenuState(Application &app, sf::RenderWindow &display); 44 | 45 | /** 46 | * \brief 47 | * Renders the menu title and menu buttons. 48 | */ 49 | virtual void onRender() override; 50 | 51 | /** 52 | * \brief 53 | * Handles clicking on the menu buttons. 54 | */ 55 | virtual void onLButtonReleased(int x, int y) override; 56 | virtual void onMouseMoved(int x, int y) override; 57 | virtual void onKeyPressed(sf::Keyboard::Key key, bool alt, bool control, bool shift, bool system) override; 58 | }; 59 | } 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/board/Board.cpp: -------------------------------------------------------------------------------- 1 | #include "Board.hpp" 2 | 3 | #include "piece/Piece.hpp" 4 | 5 | #include 6 | 7 | namespace chesspp 8 | { 9 | namespace board 10 | { 11 | Board::Board(config::BoardConfig const &conf) 12 | : config(conf) //can't use {} 13 | { 14 | for(auto const &slot : conf.initialLayout()) 15 | { 16 | pieces.emplace(factory().at(slot.second.first)(*this, slot.first, slot.second.second)); 17 | } 18 | 19 | for(auto const &p : pieces) 20 | { 21 | p->makeTrajectory(); 22 | } 23 | } 24 | 25 | bool Board::occupied(Position_t const &pos) const noexcept 26 | { 27 | for(auto const &p : *this) 28 | { 29 | if(p->pos == pos) 30 | { 31 | return true; 32 | } 33 | } 34 | return false; 35 | } 36 | auto Board::find(piece::Piece const &p) const noexcept 37 | -> Pieces_t::const_iterator 38 | { 39 | return std::find_if 40 | ( 41 | std::begin(pieces), 42 | std::end(pieces), 43 | [&](Pieces_t::value_type const &v) 44 | { 45 | return v.get() == std::addressof(p); 46 | } 47 | ); 48 | } 49 | 50 | void Board::Movements::add(piece::Piece const &p, Position_t const &tile) 51 | { 52 | if(b.valid(tile)) 53 | { 54 | auto it = b.find(p); 55 | if(it != b.end()) 56 | { 57 | m.insert(Movements_t::value_type(it, tile)); 58 | } 59 | } 60 | } 61 | void Board::Movements::remove(piece::Piece const &p, Position_t const &tile) 62 | { 63 | auto it = b.find(p); 64 | if(it != b.end()) 65 | { 66 | auto range = m.equal_range(it); 67 | for(auto jt = range.first; jt != range.second; ) 68 | { 69 | if(jt->second == tile) 70 | { 71 | jt = m.erase(jt); 72 | } 73 | else ++jt; 74 | } 75 | } 76 | } 77 | 78 | auto Board::pieceTrajectory(piece::Piece const &p) noexcept 79 | -> MovementsRange 80 | { 81 | auto range = trajectories.equal_range(find(p)); 82 | return {{range.first, range.second}}; 83 | } 84 | auto Board::pieceCapturing(piece::Piece const &p) noexcept 85 | -> MovementsRange 86 | { 87 | auto range = capturings.equal_range(find(p)); 88 | return {{range.first, range.second}}; 89 | } 90 | auto Board::pieceCapturable(piece::Piece const &p) noexcept 91 | -> MovementsRange 92 | { 93 | auto range = capturables.equal_range(find(p)); 94 | return {{range.first, range.second}}; 95 | } 96 | 97 | void Board::update(Position_t const &pos) 98 | { 99 | trajectories.clear(); 100 | capturings.clear(); 101 | capturables.clear(); 102 | for(auto &p : pieces) 103 | { 104 | p->tick(pos); 105 | p->makeTrajectory(); 106 | } 107 | } 108 | 109 | bool Board::capture(Pieces_t::iterator source, Movements_t::const_iterator target, Movements_t::const_iterator capturable) 110 | { 111 | if(source == pieces.end()) 112 | { 113 | std::cerr << "source iterator of piece to capture with is invalid" << std::endl; 114 | return false; 115 | } 116 | if(source != target->first) 117 | { 118 | std::cerr << "target iterator does not match source iterator, source{" << **source << "}, target {" << **(target->first) << "}" << std::endl; 119 | return false; 120 | } 121 | if(capturable->second != target->second) 122 | { 123 | std::cerr << "capturable may not be captured at target" << std::endl; 124 | } 125 | 126 | pieces.erase(capturable->first); 127 | std::clog << "Capture: "; 128 | return move(source, target); //re-use existing code 129 | } 130 | bool Board::move(Pieces_t::iterator source, Movements_t::const_iterator target) 131 | { 132 | if(source == pieces.end()) 133 | { 134 | std::cerr << "source iterator of piece to move is invalid" << std::endl; 135 | return false; 136 | } 137 | if(target == trajectories.end() && target == capturings.end()) 138 | { 139 | std::cerr << "target iterator of piece to move to is invalid" << std::endl; 140 | return false; 141 | } 142 | if(source != target->first) 143 | { 144 | std::cerr << "target iterator does not match source iterator, source{" << **source << "}, target {" << **(target->first) << "}" << std::endl; 145 | return false; 146 | } 147 | if(occupied(target->second)) 148 | { 149 | std::cerr << "target iterator to move to is occupied:" << std::endl; 150 | for(auto &p : pieces) 151 | { 152 | if(p->pos == target->second) 153 | { 154 | std::cerr << "\t" << *p << std::endl; 155 | } 156 | } 157 | return false; 158 | } 159 | 160 | std::clog << "Moved piece at " << (*source)->pos << std::flush; 161 | auto t = target->second; 162 | (*source)->move(t); 163 | update(t); 164 | std::clog << " to " << t << std::endl; 165 | return true; 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/board/Board.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_board_Board_HeaderPlusPlus 2 | #define chesspp_board_Board_HeaderPlusPlus 3 | 4 | #include "config/BoardConfig.hpp" 5 | #include "util/Utilities.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace chesspp 15 | { 16 | namespace piece 17 | { 18 | class Piece; 19 | } 20 | namespace board 21 | { 22 | /** 23 | * \brief 24 | * Manages pieces on a chess board. 25 | * 26 | * Can be iterated over with a range-based for-loop. 27 | */ 28 | class Board 29 | { 30 | public: 31 | using BoardSize_t = config::BoardConfig::BoardSize_t; 32 | using Position_t = config::BoardConfig::Position_t; 33 | using Suit = config::BoardConfig::SuitClass_t; 34 | using Pieces_t = std::set>; 35 | private: 36 | struct Pieces_t_const_iterator_compare 37 | { 38 | bool operator()(Pieces_t::const_iterator const &a, Pieces_t::const_iterator const &b) const 39 | { 40 | return *a < *b; 41 | } 42 | }; 43 | public: 44 | using Movements_t = std::multimap; 45 | using Factory_t = std::map>; //Used to create new pieces 46 | 47 | /** 48 | * \brief 49 | * The chesspp::config::BoardConfig currently in use. 50 | */ 51 | config::BoardConfig const &config; 52 | private: 53 | Pieces_t pieces; 54 | Movements_t trajectories; //where pieces can go 55 | Movements_t capturings; //where pieces can capture 56 | Movements_t capturables; //where pieces can be captured 57 | static Factory_t &factory() 58 | { 59 | static Factory_t f; 60 | return f; 61 | } 62 | 63 | public: 64 | /** 65 | * \brief 66 | * Constructs the Board from the given chesspp::config::BoardConfig 67 | * 68 | * \param conf The BoardConfig, must outlive this instance. 69 | */ 70 | Board(config::BoardConfig const &conf); 71 | 72 | /** 73 | * \brief 74 | * Register a new type of chess piece. 75 | * 76 | * This should be called via assignment to a global variable in the source file of 77 | * the chess piece implementation. 78 | * 79 | * \param type The unique type of the chess piece. 80 | * \param ctor The function used to construct new instances of the chess piece. 81 | * \return The registration receipt, to be stored in the global variable. 82 | */ 83 | static auto registerPieceClass(Factory_t::key_type const &type, Factory_t::mapped_type ctor) 84 | -> Factory_t::iterator 85 | { 86 | return factory().insert({type, ctor}).first; 87 | } 88 | 89 | /** 90 | * \brief 91 | * Check if a location on the board is occupied by at least one piece. 92 | * 93 | * \param pos The position to check. 94 | * \return true if at least one piece occupies the position, false otherwise. 95 | */ 96 | bool occupied(Position_t const &pos) const noexcept; 97 | /** 98 | * \brief 99 | * Given a Piece, obtain its iterator. 100 | * 101 | * \param p The Piece to search for. 102 | * \return The iterator to the piece, or the end iterator. 103 | */ 104 | auto find(piece::Piece const &p) const noexcept -> Pieces_t::const_iterator; 105 | 106 | /** 107 | * \brief 108 | * Get the beginning iterator of the board pieces. 109 | */ 110 | auto begin() const noexcept 111 | -> Pieces_t::const_iterator 112 | { 113 | return pieces.cbegin(); 114 | } 115 | /** 116 | * \brief 117 | * Get the end iterator of the board pieces. 118 | */ 119 | auto end() const noexcept 120 | -> Pieces_t::const_iterator 121 | { 122 | return pieces.cend(); 123 | } 124 | 125 | /** 126 | * \brief 127 | * Holds the valid movements for all pieces on the board. 128 | * 129 | * There are three different kinds of movements: 130 | * - Trajectory - the locations a piece can move to 131 | * - Capturing - the locations a piece can capture other pieces 132 | * - Capturable - the locations a piece can be captured by other pieces 133 | * 134 | * This class provides a consistent interface for all three types. 135 | * 136 | * Can be iterated over with a range-based for-loop. 137 | */ 138 | class Movements 139 | { 140 | Board &b; 141 | Movements_t &m; 142 | Movements(Board &b_, Movements_t Board::*m_) 143 | : b(b_) //can't use {} 144 | , m(b_.*m_) //can't use {} 145 | { 146 | } 147 | Movements(Movements const &) = delete; 148 | Movements(Movements &&) = delete; 149 | Movements &operator=(Movements const &) = delete; 150 | Movements &operator=(Movements &&) = delete; 151 | friend class ::chesspp::board::Board; 152 | 153 | public: 154 | /** 155 | * \brief 156 | * Get the beginning iterator of the valid movements. 157 | */ 158 | Movements_t::const_iterator begin() const 159 | { 160 | return m.cbegin(); 161 | } 162 | /** 163 | * \brief 164 | * Get the end iterator of the valid movements. 165 | */ 166 | Movements_t::const_iterator end() const 167 | { 168 | return m.cend(); 169 | } 170 | 171 | /** 172 | * \brief 173 | * Adds a valid movement. 174 | */ 175 | void add(piece::Piece const &p, Position_t const &tile); 176 | /** 177 | * \brief 178 | * Removes a valid movement. 179 | */ 180 | void remove(piece::Piece const &p, Position_t const &tile); 181 | }; 182 | private: 183 | Movements trajs {*this, &Board::trajectories}; 184 | Movements captings {*this, &Board::capturings }; 185 | Movements captables {*this, &Board::capturables }; 186 | public: 187 | Movements const &pieceTrajectories() const noexcept { return trajs; } 188 | Movements &pieceTrajectories() noexcept { return trajs; } 189 | Movements const &pieceCapturings() const noexcept { return captings; } 190 | Movements &pieceCapturings() noexcept { return captings; } 191 | Movements const &pieceCapturables() const noexcept { return captables; } 192 | Movements &pieceCapturables() noexcept { return captables; } 193 | using MovementsRange = util::Range; 194 | MovementsRange pieceTrajectory(piece::Piece const &p) noexcept; 195 | MovementsRange pieceCapturing(piece::Piece const &p) noexcept; 196 | MovementsRange pieceCapturable(piece::Piece const &p) noexcept; 197 | 198 | private: 199 | void update(Position_t const &pos); 200 | public: 201 | /** 202 | * \brief 203 | * Capture a capturable piece. 204 | * 205 | * \param source The piece performing the capture. 206 | * \param target The new location of the source piece. 207 | * \param capturable The location being captured. 208 | * \return true if the capture was successful, false otherwise. 209 | */ 210 | bool capture(Pieces_t::iterator source, Movements_t::const_iterator target, Movements_t::const_iterator capturable); 211 | /** 212 | * \brief 213 | * Move a piece without capturing. 214 | * 215 | * \param source The piece being moved. 216 | * \param target The new location of the source piece. 217 | * \return true if the capture was successful, false otherwise. 218 | */ 219 | bool move(Pieces_t::iterator source, Movements_t::const_iterator target); 220 | 221 | /** 222 | * \brief 223 | * Check if a position is a valid position that exists on the board. 224 | * 225 | * \param pos The position to check for validity. 226 | * \return true if the position is valid, false otherwise. 227 | */ 228 | bool valid(Position_t const &pos) const noexcept 229 | { 230 | return pos.isWithin(Position_t::Origin(), {config.boardWidth(), config.boardHeight()}); 231 | } 232 | }; 233 | } 234 | } 235 | 236 | #endif 237 | -------------------------------------------------------------------------------- /src/board/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add sources 2 | target_sources(ChessPlusPlus PUBLIC 3 | "${CMAKE_CURRENT_LIST_DIR}/Board.cpp" 4 | ) 5 | -------------------------------------------------------------------------------- /src/config/BoardConfig.cpp: -------------------------------------------------------------------------------- 1 | #include "BoardConfig.hpp" 2 | 3 | namespace chesspp 4 | { 5 | namespace config 6 | { 7 | BoardConfig::BoardConfig(ResourcesConfig &res) 8 | : Configuration{"config/chesspp/board.json"} 9 | , board_width {reader()["board"]["width"] } 10 | , board_height {reader()["board"]["height"] } 11 | , cell_width {reader()["board"]["cell width"] } 12 | , cell_height {reader()["board"]["cell height"]} 13 | { 14 | auto pieces = reader()["board"]["pieces"]; 15 | auto suits = reader()["board"]["suits"]; 16 | for(BoardSize_t r = 0; r < board_height; ++r) 17 | { 18 | for(BoardSize_t c = 0; c < board_width; ++c) 19 | { 20 | auto piece = pieces[r][c]; 21 | auto suit = suits [r][c]; 22 | if(piece.type() != util::JsonReader::Type::Null) //it is OK if suit is null 23 | { 24 | layout[{c, r}] = std::make_pair(piece, suit); 25 | } 26 | } 27 | } 28 | 29 | auto const &tex = res.setting("board", "pieces"); 30 | for(auto const &suit : tex.object()) 31 | { 32 | for(auto const &piece : suit.second.object()) 33 | { 34 | textures[suit.first][piece.first] = std::string(Textures_t::mapped_type::mapped_type(piece.second)); 35 | } 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/config/BoardConfig.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_config_BoardConfig_HeaderPlusPlus 2 | #define chesspp_config_BoardConfig_HeaderPlusPlus 3 | 4 | #include "Configuration.hpp" 5 | #include "ResourcesConfig.hpp" 6 | #include "util/Position.hpp" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace chesspp 14 | { 15 | namespace config 16 | { 17 | /** 18 | * \brief 19 | * Holds configuration values for the board (layout, metadata, etc) 20 | */ 21 | class BoardConfig 22 | : public Configuration 23 | { 24 | public: 25 | using BoardSize_t = std::uint8_t; 26 | using CellSize_t = std::uint16_t; 27 | using Position_t = util::Position; //Position type is based on Board Size type 28 | using PieceClass_t = std::string; 29 | using SuitClass_t = std::string; 30 | using Layout_t = std::map>; 31 | using Textures_t = std::map>; 32 | private: 33 | BoardSize_t board_width, board_height; 34 | CellSize_t cell_width, cell_height; 35 | Layout_t layout; 36 | Textures_t textures; 37 | 38 | public: 39 | /** 40 | * \brief 41 | * Loads board configuration from "config/chesspp/board.json" 42 | * 43 | * \param The ResourcesConfig to use for resource configuration. 44 | */ 45 | BoardConfig(ResourcesConfig &res); 46 | virtual ~BoardConfig() = default; 47 | 48 | BoardSize_t boardWidth () const noexcept { return board_width; } 49 | BoardSize_t boardHeight () const noexcept { return board_height; } 50 | Layout_t const &initialLayout() const noexcept { return layout; } 51 | CellSize_t cellWidth () const noexcept { return cell_width; } 52 | CellSize_t cellHeight () const noexcept { return cell_height; } 53 | Textures_t const &texturePaths () const noexcept { return textures; } 54 | 55 | /** 56 | * \brief 57 | * Get metadata for the board pieces. 58 | * 59 | * Equivalent to calling `setting("board", "metadata", ...)` 60 | * \see setting() 61 | */ 62 | template 63 | util::JsonReader::NestedValue metadata(Args const &... path) const 64 | { 65 | return reader.navigate("board", "metadata", path...); 66 | } 67 | }; 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /src/config/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add sources 2 | target_sources(ChessPlusPlus PUBLIC 3 | "${CMAKE_CURRENT_LIST_DIR}/BoardConfig.cpp" 4 | "${CMAKE_CURRENT_LIST_DIR}/Configuration.cpp" 5 | ) 6 | -------------------------------------------------------------------------------- /src/config/Configuration.cpp: -------------------------------------------------------------------------------- 1 | #include "Configuration.hpp" 2 | 3 | #include "Exception.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #if defined(__linux__) 13 | #include 14 | #elif defined(_WIN32) 15 | #include 16 | #elif defined(__APPLE__) 17 | #include 18 | #endif 19 | 20 | namespace chesspp 21 | { 22 | namespace config 23 | { 24 | //Linux and Windows, resource path is defined as the absolute path the folder where the application executable is stored. 25 | ///res/img/... should be where resources are stored. 26 | //OS x, resource path is defined as the absolute path to the Resources folder of the .app structure. 27 | //<.app>/Contents/Resources/res/img... should be where resources are stored. 28 | std::string Configuration::executablePath() 29 | { 30 | char buf[1024]; 31 | std::uint32_t size = sizeof(buf); 32 | memset(buf, 0, size); 33 | std::string ret; 34 | #if defined(__linux__) 35 | if(readlink("/proc/self/exe", buf, size) == -1) 36 | throw Exception("Unable to determine executable path on Linux."); 37 | ret = buf; 38 | ret = ret.substr(0, ret.find_last_of('/')+1); 39 | 40 | #elif defined(_WIN32) 41 | if(GetModuleFileNameA(NULL, buf, size) == 0) 42 | throw Exception("Unable to determine executable path on Windows."); 43 | ret = buf; 44 | boost::replace_all(ret, "\\", "/"); 45 | ret = ret.substr(0, ret.find_last_of('/')+1); 46 | 47 | #elif defined(__APPLE__) 48 | if (_NSGetExecutablePath(buf, &size) != 0) 49 | throw Exception("Unable to determine executable path on OS x. (Buffer size too small?)"); 50 | ret = buf; 51 | ret = ret.substr(0, ret.find_last_of('/')+1) + "../Resources/"; 52 | //Need to go up one directory because the exe is stored in <.app>/Contents/MacOS/, 53 | //And we need <.app>/Contents/Resources 54 | 55 | #else 56 | throw Exception("Unknown OS. Unable to determine executable path."); 57 | #endif 58 | 59 | std::clog << "Executable path = \"" << ret << '"' << std::endl; 60 | std::clog << "Working directory = \"" << boost::filesystem::current_path().string() << '"' << std::endl; 61 | 62 | return ret; 63 | } 64 | std::string Configuration::validateConfigFile(std::string const &configFile) 65 | { 66 | static std::string exe_path = executablePath(); 67 | 68 | if(boost::filesystem::extension(configFile) != ".json") 69 | { 70 | throw Exception("Configuration cannot read non-json config files."); 71 | } 72 | 73 | if(boost::filesystem::exists(configFile)) 74 | { 75 | res_path = ""; 76 | } 77 | else 78 | { 79 | res_path = exe_path; 80 | } 81 | return res_path + configFile; 82 | } 83 | Configuration::Configuration(std::string const &configFile) noexcept(false) 84 | : reader{std::ifstream(validateConfigFile(configFile))} 85 | { 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/config/Configuration.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_config_Configuration_HeaderPlusPlus 2 | #define chesspp_config_Configuration_HeaderPlusPlus 3 | 4 | #include "util/JsonReader.hpp" 5 | 6 | #include 7 | 8 | namespace chesspp 9 | { 10 | namespace config 11 | { 12 | /** 13 | * \brief 14 | * Holds configuration settings that affect the behavior of the game. 15 | * 16 | * Configuration files are in JSON format - http://www.json.org/ 17 | */ 18 | class Configuration 19 | { 20 | protected: 21 | /** 22 | * \brief 23 | * Get the path to the directory the executable is in. 24 | * 25 | * \return Path to the directory the executable is in. 26 | */ 27 | static std::string executablePath(); 28 | 29 | /** 30 | * \brief 31 | * Stores the path to the directory where configuration paths are relative to. 32 | * 33 | * This may not be the same as the directory containing the executable. 34 | */ 35 | std::string res_path; 36 | /** 37 | * \brief 38 | * The chessp::util::JsonReader for reading the configuration values. 39 | */ 40 | util::JsonReader reader; 41 | 42 | private: 43 | std::string validateConfigFile(std::string const &configFile); 44 | 45 | public: 46 | /** 47 | * \brief 48 | * Loads the configuration from the given file path. 49 | * 50 | * \param configFile the relative path to the configuration file. Must end in .json 51 | * \throw chesspp::Exception if the path does not end in .json 52 | */ 53 | Configuration(std::string const &configFile) noexcept(false); 54 | virtual ~Configuration() = default; 55 | 56 | /** 57 | * \brief 58 | * Access values in the configuration. 59 | * 60 | * \param path The path to the value as multiple parameters, e.g. `"a", 1, "c"` 61 | * \return The requested chesspp::util::JsonReader::NestedValue 62 | */ 63 | template 64 | auto setting(Path const &... path) 65 | -> decltype(reader.navigate(path...)) 66 | { 67 | return reader.navigate(path...); 68 | } 69 | }; 70 | } 71 | } 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/config/ResourcesConfig.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_config_ResourcesConfig_HeaderPlusPlus 2 | #define chesspp_config_ResourcesConfig_HeaderPlusPlus 3 | 4 | #include "Configuration.hpp" 5 | #include "res/ResourceManager.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace config 10 | { 11 | /** 12 | * \brief 13 | * Holds configuration for resources (graphics, fonts, etc) 14 | */ 15 | class ResourcesConfig 16 | : public Configuration 17 | { 18 | res::ResourceManager res; 19 | 20 | public: 21 | /** 22 | * \brief 23 | * Loads resource configuration from "config/chesspp/resources.json" 24 | */ 25 | ResourcesConfig() 26 | : Configuration{"config/chesspp/resources.json"} 27 | , res{*this} 28 | { 29 | } 30 | 31 | /** 32 | * \brief 33 | * Get the chesspp::res::ResourceManager 34 | * 35 | * \return chesspp::res::ResourceManager 36 | */ 37 | res::ResourceManager &resources() noexcept 38 | { 39 | return res; 40 | } 41 | }; 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/gfx/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add sources 2 | target_sources(ChessPlusPlus PUBLIC 3 | "${CMAKE_CURRENT_LIST_DIR}/GraphicsHandler.cpp" 4 | ) 5 | -------------------------------------------------------------------------------- /src/gfx/GraphicsHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "GraphicsHandler.hpp" 2 | 3 | #include "res/SfmlFileResource.hpp" 4 | #include "config/Configuration.hpp" 5 | 6 | #include 7 | 8 | namespace chesspp 9 | { 10 | namespace gfx 11 | { 12 | using Texture_res = res::SfmlFileResource; 13 | GraphicsHandler::GraphicsHandler(sf::RenderWindow &disp, config::ResourcesConfig &resc, config::BoardConfig &bc) 14 | : display(disp) //can't use {} 15 | , res_config(resc) //can't use {} 16 | , board_config(bc) //can't use {} 17 | , res(resc.resources()) //can't use {} 18 | , board {res.from_config("board", "board" )} 19 | , valid_move {res.from_config("board", "valid move" )} 20 | , enemy_move {res.from_config("board", "enemy move" )} 21 | , valid_capture{res.from_config("board", "valid capture")} 22 | , enemy_capture{res.from_config("board", "enemy capture")} 23 | { 24 | } 25 | 26 | void GraphicsHandler::drawBackground() 27 | { 28 | display.draw(board); 29 | } 30 | void GraphicsHandler::drawSpriteAtCell(sf::Sprite &s, std::size_t x, std::size_t y) 31 | { 32 | s.setPosition(x*board_config.cellWidth(), y*board_config.cellHeight()); 33 | display.draw(s); 34 | } 35 | void GraphicsHandler::drawPiece(piece::Piece const &p) 36 | { 37 | sf::Sprite piece {res.from_config("board", "pieces", p.suit, p.pclass)}; 38 | drawSpriteAtCell(piece, p.pos.x, p.pos.y); 39 | } 40 | void GraphicsHandler::drawPieceAt(piece::Piece const &p, sf::Vector2i const &pos) 41 | { 42 | sf::Sprite piece {res.from_config("board", "pieces", p.suit, p.pclass)}; 43 | piece.setPosition(pos.x - (board_config.cellWidth()/2), pos.y - (board_config.cellHeight()/2)); 44 | display.draw(piece); 45 | } 46 | void GraphicsHandler::drawTrajectory(piece::Piece const &p, bool enemy) 47 | { 48 | { 49 | auto &sprite = (enemy? enemy_move : valid_move); 50 | for(auto const &it : p.board.pieceTrajectory(p)) 51 | { 52 | if(!p.board.occupied(it.second)) 53 | { 54 | if(std::find_if(p.board.pieceCapturables().begin(), 55 | p.board.pieceCapturables().end(), 56 | [&](board::Board::Movements_t::value_type const &m) 57 | { 58 | return m.second == it.second && (*m.first)->suit != p.suit; 59 | }) == p.board.pieceCapturables().end()) 60 | { 61 | drawSpriteAtCell(sprite, it.second.x, it.second.y); 62 | } 63 | } 64 | } 65 | } 66 | { 67 | auto &sprite = (enemy? enemy_capture : valid_capture); 68 | for(auto const &it : p.board.pieceCapturing(p)) 69 | { 70 | for(auto const &c : p.board.pieceCapturables()) 71 | { 72 | if(c.second == it.second && (*c.first)->suit != p.suit) 73 | { 74 | drawSpriteAtCell(sprite, it.second.x, it.second.y); 75 | auto jt = std::find_if(p.board.pieceTrajectories().begin(), 76 | p.board.pieceTrajectories().end(), 77 | [&](board::Board::Movements_t::value_type const &m) 78 | { 79 | return (*m.first)->pos == it.second; 80 | }); 81 | if(jt != p.board.pieceTrajectories().end()) 82 | { 83 | drawPiece(**(jt->first)); //redraw 84 | } 85 | break; 86 | } 87 | } 88 | } 89 | } 90 | } 91 | void GraphicsHandler::drawBoard(board::Board const &b) 92 | { 93 | drawBackground(); 94 | 95 | for(auto const &pp : b) 96 | { 97 | drawPiece(*pp); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/gfx/GraphicsHandler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_gfx_GraphicsHandler_HeaderPlusPlus 2 | #define chesspp_gfx_GraphicsHandler_HeaderPlusPlus 3 | 4 | #include "SFML.hpp" 5 | #include "config/ResourcesConfig.hpp" 6 | #include "config/BoardConfig.hpp" 7 | #include "board/Board.hpp" 8 | #include "piece/Piece.hpp" 9 | 10 | #include 11 | 12 | namespace chesspp 13 | { 14 | namespace gfx 15 | { 16 | /** 17 | * \brief 18 | * Handles drawing graphics, such as pieces, trajectories, and boards. 19 | */ 20 | class GraphicsHandler 21 | { 22 | sf::RenderWindow &display; 23 | config::ResourcesConfig &res_config; 24 | config::BoardConfig &board_config; 25 | res::ResourceManager &res; 26 | 27 | sf::Sprite board 28 | , valid_move 29 | , enemy_move 30 | , valid_capture 31 | , enemy_capture; 32 | 33 | public: 34 | /** 35 | * \brief 36 | * Constructs for a specific sf::RenderWindow given the resource and board 37 | * configurations. 38 | * 39 | * \param display The sf::RenderWindow, must outlive this instance. 40 | * \param resc The chesspp::config::ResourcesConfig, must outlive this instance. 41 | * \param bc The chesspp::config::BoardConfig, must outlive this instance. 42 | */ 43 | GraphicsHandler(sf::RenderWindow &display, config::ResourcesConfig &resc, config::BoardConfig &bc); 44 | 45 | /** 46 | * \brief 47 | * Draws the board background. 48 | */ 49 | void drawBackground(); 50 | 51 | /** 52 | * \brief 53 | * Draws any sprite in the center of cell at (x, y). 54 | * 55 | * \param s The sprite to draw, centered on (x, y). Changes the sprite's position. 56 | */ 57 | void drawSpriteAtCell(sf::Sprite &s, std::size_t x, std::size_t y); 58 | 59 | /** 60 | * \brief 61 | * Draws a piece at its real location. 62 | * 63 | * \param p The piece to draw. 64 | */ 65 | void drawPiece(piece::Piece const &p); 66 | 67 | /** 68 | * \brief 69 | * Draws a piece at a different location than it actually is. 70 | * 71 | * \param p The piece to draw. 72 | * \param pos The location to draw the piece at. 73 | */ 74 | void drawPieceAt(piece::Piece const &p, sf::Vector2i const &pos); 75 | 76 | /** 77 | * \brief 78 | * Draws a piece's trajectory moves and capturing moves. 79 | * 80 | * \param p The piece for which to draw trajectory and capturings. 81 | * \param enemy true if the piece is an enemy piece, false otherwise. 82 | */ 83 | void drawTrajectory(piece::Piece const &p, bool enemy = false); 84 | 85 | /** 86 | * \brief 87 | * Draws a board and its pieces. 88 | * 89 | * \param b The board to draw. 90 | */ 91 | void drawBoard(board::Board const &b); 92 | }; 93 | } 94 | } 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/piece/Archer.cpp: -------------------------------------------------------------------------------- 1 | #include "Archer.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace piece 8 | { 9 | static auto ArcherRegistration = board::Board::registerPieceClass 10 | ( 11 | "Archer", 12 | [](board::Board &b, board::Board::Position_t const &p, board::Board::Suit const &s) 13 | -> board::Board::Pieces_t::value_type 14 | { 15 | return board::Board::Pieces_t::value_type(new Archer(b, p, s, "Archer")); 16 | } 17 | ); 18 | 19 | Archer::Archer(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc) 20 | : Piece{b, pos_, s_, pc} 21 | { 22 | } 23 | 24 | void Archer::calcTrajectory() 25 | { 26 | //Archers can move one space in four directions 27 | using Dir = util::Direction; 28 | for(Dir d : {Dir::NorthEast 29 | ,Dir::SouthEast 30 | ,Dir::SouthWest 31 | ,Dir::NorthWest}) 32 | { 33 | Position_t t = Position_t(pos).move(d); 34 | addTrajectory(t); 35 | } 36 | //Archers can be captured at four spots around them 37 | using Dir = util::Direction; 38 | for(Dir d : {Dir::North 39 | ,Dir::East 40 | ,Dir::South 41 | ,Dir::West}) 42 | { 43 | Position_t t = Position_t(pos).move(d); 44 | addCapturable(t); 45 | } 46 | //Archers can only capture at a circle around them 47 | for(Position_t m : {Position_t( 1, -2) 48 | ,Position_t( 2, -1) 49 | ,Position_t( 2, 1) 50 | ,Position_t( 1, 2) 51 | ,Position_t(-1, 2) 52 | ,Position_t(-2, 1) 53 | ,Position_t(-2, -1) 54 | ,Position_t(-1, -2) 55 | ,Position_t( 0, 2) 56 | ,Position_t(-2, 0) 57 | ,Position_t( 2, 0) 58 | ,Position_t( 0, -2)}) 59 | { 60 | Position_t t = Position_t(pos).move(m.x, m.y); 61 | addCapturing(t); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/piece/Archer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_Archer_HeaderPlusPlus 2 | #define chesspp_piece_Archer_HeaderPlusPlus 3 | 4 | #include "board/Board.hpp" 5 | #include "piece/Piece.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace piece 10 | { 11 | class Archer 12 | : public virtual Piece 13 | { 14 | public: 15 | Archer(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc); 16 | 17 | protected: 18 | virtual void calcTrajectory() override; 19 | }; 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/piece/Bishop.cpp: -------------------------------------------------------------------------------- 1 | #include "Bishop.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace piece 8 | { 9 | static auto BishopRegistration = board::Board::registerPieceClass 10 | ( 11 | "Bishop", 12 | [](board::Board &b, board::Board::Position_t const &p, board::Board::Suit const &s) 13 | -> board::Board::Pieces_t::value_type 14 | { 15 | return board::Board::Pieces_t::value_type(new Bishop(b, p, s, "Bishop")); 16 | } 17 | ); 18 | 19 | Bishop::Bishop(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc) 20 | : Piece{b, pos_, s_, pc} 21 | { 22 | } 23 | 24 | void Bishop::calcTrajectory() 25 | { 26 | //Bishops can move infinitely in the four diagonal directions 27 | using Dir = util::Direction; 28 | for(Dir d : {Dir::NorthEast 29 | ,Dir::SouthEast 30 | ,Dir::SouthWest 31 | ,Dir::NorthWest}) 32 | { 33 | Position_t t; 34 | for(signed i = 1; board.valid(t = Position_t(pos).move(d, i)); ++i) 35 | { 36 | addCapturing(t); 37 | if(!board.occupied(t)) 38 | { 39 | addTrajectory(t); 40 | } 41 | else break; //can't jump over pieces 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/piece/Bishop.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_Bishop_HeaderPlusPlus 2 | #define chesspp_piece_Bishop_HeaderPlusPlus 3 | 4 | #include "board/Board.hpp" 5 | #include "piece/Piece.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace piece 10 | { 11 | class Bishop 12 | : public virtual Piece 13 | { 14 | public: 15 | Bishop(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc); 16 | 17 | protected: 18 | virtual void calcTrajectory() override; 19 | }; 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/piece/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add sources 2 | target_sources(ChessPlusPlus PUBLIC 3 | "${CMAKE_CURRENT_LIST_DIR}/Archer.cpp" 4 | "${CMAKE_CURRENT_LIST_DIR}/Bishop.cpp" 5 | "${CMAKE_CURRENT_LIST_DIR}/King.cpp" 6 | "${CMAKE_CURRENT_LIST_DIR}/Knight.cpp" 7 | "${CMAKE_CURRENT_LIST_DIR}/Pawn.cpp" 8 | "${CMAKE_CURRENT_LIST_DIR}/Piece.cpp" 9 | "${CMAKE_CURRENT_LIST_DIR}/Queen.cpp" 10 | "${CMAKE_CURRENT_LIST_DIR}/Rook.cpp" 11 | ) 12 | -------------------------------------------------------------------------------- /src/piece/King.cpp: -------------------------------------------------------------------------------- 1 | #include "King.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace piece 8 | { 9 | static auto KingRegistration = board::Board::registerPieceClass 10 | ( 11 | "King", 12 | [](board::Board &b, board::Board::Position_t const &p, board::Board::Suit const &s) 13 | -> board::Board::Pieces_t::value_type 14 | { 15 | return board::Board::Pieces_t::value_type(new King(b, p, s, "King")); 16 | } 17 | ); 18 | 19 | King::King(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc) 20 | : Piece{b, pos_, s_, pc} 21 | { 22 | } 23 | 24 | void King::calcTrajectory() 25 | { 26 | //Kings can move one space in all eight directions 27 | using Dir = util::Direction; 28 | for(Dir d : {Dir::North 29 | ,Dir::NorthEast 30 | ,Dir::East 31 | ,Dir::SouthEast 32 | ,Dir::South 33 | ,Dir::SouthWest 34 | ,Dir::West 35 | ,Dir::NorthWest}) 36 | { 37 | Position_t t = Position_t(pos).move(d); 38 | addTrajectory(t); 39 | addCapturing(t); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/piece/King.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_King_HeaderPlusPlus 2 | #define chesspp_piece_King_HeaderPlusPlus 3 | 4 | #include "board/Board.hpp" 5 | #include "piece/Piece.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace piece 10 | { 11 | class King 12 | : public virtual Piece 13 | { 14 | public: 15 | King(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc); 16 | 17 | protected: 18 | virtual void calcTrajectory() override; 19 | }; 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/piece/Knight.cpp: -------------------------------------------------------------------------------- 1 | #include "Knight.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace piece 8 | { 9 | static auto KnightRegistration = board::Board::registerPieceClass 10 | ( 11 | "Knight", 12 | [](board::Board &b, board::Board::Position_t const &p, board::Board::Suit const &s) 13 | -> board::Board::Pieces_t::value_type 14 | { 15 | return board::Board::Pieces_t::value_type(new Knight(b, p, s, "Knight")); 16 | } 17 | ); 18 | 19 | Knight::Knight(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc) 20 | : Piece{b, pos_, s_, pc} 21 | { 22 | } 23 | 24 | void Knight::calcTrajectory() 25 | { 26 | //Knights can only move in 3-long 2-short L shapes 27 | 28 | for(Position_t m : {Position_t( 1, -2) 29 | ,Position_t( 2, -1) 30 | ,Position_t( 2, 1) 31 | ,Position_t( 1, 2) 32 | ,Position_t(-1, 2) 33 | ,Position_t(-2, 1) 34 | ,Position_t(-2, -1) 35 | ,Position_t(-1, -2)}) 36 | { 37 | Position_t t = Position_t(pos).move(m.x, m.y); 38 | addTrajectory(t); 39 | addCapturing(t); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/piece/Knight.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_Knight_HeaderPlusPlus 2 | #define chesspp_piece_Knight_HeaderPlusPlus 3 | 4 | #include "board/Board.hpp" 5 | #include "piece/Piece.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace piece 10 | { 11 | class Knight 12 | : public virtual Piece 13 | { 14 | public: 15 | Knight(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc); 16 | 17 | protected: 18 | virtual void calcTrajectory() override; 19 | }; 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/piece/Pawn.cpp: -------------------------------------------------------------------------------- 1 | #include "Pawn.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace piece 8 | { 9 | static auto PawnRegistration = board::Board::registerPieceClass 10 | ( 11 | "Pawn", 12 | [](board::Board &b, board::Board::Position_t const &p, board::Board::Suit const &s) 13 | -> board::Board::Pieces_t::value_type 14 | { 15 | auto d = util::Direction::None; 16 | std::istringstream {std::string(b.config.metadata("pawn facing", p.y, p.x))} >> d; 17 | return board::Board::Pieces_t::value_type(new Pawn(b, p, s, "Pawn", d)); 18 | } 19 | ); 20 | 21 | Pawn::Pawn(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc, util::Direction const &face) 22 | : Piece{b, pos_, s_, pc} 23 | , facing{face} 24 | { 25 | } 26 | 27 | void Pawn::tick(Position_t const &m) 28 | { 29 | if(moves == 1 && m != pos) 30 | { //moved just happened, en passant no longer allowed 31 | en_passant = false; 32 | } 33 | } 34 | 35 | void Pawn::calcTrajectory() 36 | { 37 | //Pawns can move 1 or 2 spaces forward on their first turn, 38 | //or only 1 space forward on any other turn. 39 | //On any turn they can move diagonally forward to capture, 40 | //but may not capture when moving straight forward. 41 | //They may be captured via the space behind them 42 | //if they just moved forward two spaces (en passant). 43 | 44 | addTrajectory(Position_t(pos).move(facing)); 45 | if(moves == 0) //first move 46 | { 47 | if(!board.occupied(Position_t(pos).move(facing))) //can't jump over pieces 48 | { 49 | addTrajectory(Position_t(pos).move(facing, 2)); 50 | } 51 | } 52 | else if(moves == 1 && en_passant) //just moved 2 spaces forward 53 | { 54 | addCapturable(Position_t(pos).move(facing, -1)); //enable en passant 55 | } 56 | 57 | Position_t diagr = Position_t(pos).move(Rotate(facing, +1)); 58 | if(board.valid(diagr)) //can capture diagonally forward-right 59 | { 60 | addCapturing(diagr); 61 | } 62 | Position_t diagl = Position_t(pos).move(Rotate(facing, -1)); 63 | if(board.valid(diagl)) //can capture diagonally forward-left 64 | { 65 | addCapturing(diagl); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/piece/Pawn.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_Pawn_HeaderPlusPlus 2 | #define chesspp_piece_Pawn_HeaderPlusPlus 3 | 4 | #include "board/Board.hpp" 5 | #include "piece/Piece.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace piece 10 | { 11 | class Pawn 12 | : public virtual Piece 13 | { 14 | bool en_passant = true; 15 | util::Direction facing; 16 | 17 | public: 18 | Pawn(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc, util::Direction const &face); 19 | 20 | virtual void tick(Position_t const &p) override; 21 | 22 | protected: 23 | virtual void calcTrajectory() override; 24 | }; 25 | } 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/piece/Piece.cpp: -------------------------------------------------------------------------------- 1 | #include "Piece.hpp" 2 | 3 | #include "board/Board.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace chesspp 9 | { 10 | namespace piece 11 | { 12 | Piece::Piece(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc) 13 | : board(b) //can't use {} 14 | , p{pos_} 15 | , s{s_} 16 | , c{pc} 17 | { 18 | std::clog << "Creation of " << *this << std::endl; 19 | } 20 | 21 | void Piece::addTrajectory(Position_t const &tile) 22 | { 23 | board.pieceTrajectories().add(*this, tile); 24 | } 25 | void Piece::removeTrajectory(Position_t const &tile) 26 | { 27 | board.pieceTrajectories().remove(*this, tile); 28 | } 29 | 30 | void Piece::addCapturing(Position_t const &tile) 31 | { 32 | board.pieceCapturings().add(*this, tile); 33 | } 34 | void Piece::removeCapturing(Position_t const &tile) 35 | { 36 | board.pieceCapturings().remove(*this, tile); 37 | } 38 | 39 | void Piece::addCapturable(Position_t const &tile) 40 | { 41 | board.pieceCapturables().add(*this, tile); 42 | } 43 | void Piece::removeCapturable(Position_t const &tile) 44 | { 45 | board.pieceCapturables().remove(*this, tile); 46 | } 47 | 48 | std::ostream &operator<<(std::ostream &os, Piece const &p) 49 | { 50 | return os << "Piece (" << typeid(p).name() << ") \"" << p.suit << "\" \"" << p.pclass << "\" at " << p.pos << " having made " << p.moves << " moves"; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/piece/Piece.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_Piece_HeaderPlusPlus 2 | #define chesspp_piece_Piece_HeaderPlusPlus 3 | 4 | #include "config/BoardConfig.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace chesspp 12 | { 13 | namespace board 14 | { 15 | class Board; 16 | } 17 | namespace piece 18 | { 19 | /** 20 | * \brief 21 | * A chess piece. 22 | */ 23 | class Piece 24 | { 25 | friend class ::chesspp::board::Board; 26 | 27 | public: 28 | using Position_t = config::BoardConfig::Position_t; 29 | using Suit_t = config::BoardConfig::SuitClass_t; 30 | using Class_t = config::BoardConfig::PieceClass_t; 31 | 32 | /** 33 | * \brief 34 | * The chesspp::board::Board in which the Piece exists. 35 | */ 36 | board::Board &board; 37 | private: 38 | Position_t p; 39 | Suit_t s; 40 | Class_t c; 41 | std::size_t m = 0; 42 | public: 43 | /** 44 | * \brief 45 | * The position of this piece. 46 | */ 47 | Position_t const &pos = p; 48 | /** 49 | * \brief 50 | * The display suit of this piece (e.g. Black or White). 51 | */ 52 | Suit_t const &suit = s; 53 | /** 54 | * \brief 55 | * The display class of this piece (e.g. Pawn, Rook, etc). 56 | * 57 | * \note 58 | * This cannot be used to determine what kind of piece this is, as this value is 59 | * only for display purposes and may not match the actual type. 60 | */ 61 | Class_t const &pclass = c; 62 | /** 63 | * \brief 64 | * The number of moves this piece has made. 65 | */ 66 | std::size_t const &moves = m; 67 | 68 | /** 69 | * \brief 70 | * Construct onto a board at the given position with the given suit and class. 71 | * 72 | * \param b The chesspp::board::Board on which this piece is to live, must outlive 73 | * this instance. 74 | * \param pos The initial position of this piece. 75 | * \param s The display suit of the piece. 76 | * \param pc The display class of the piece. 77 | */ 78 | Piece(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc); 79 | virtual ~Piece() = default; 80 | 81 | /** 82 | * \brief 83 | * Update movement information. 84 | * 85 | * \note 86 | * Should not be overridden or shadowed by deriving classes. 87 | */ 88 | void makeTrajectory() 89 | { 90 | addCapturable(pos); 91 | calcTrajectory(); 92 | } 93 | 94 | protected: 95 | /** 96 | * \brief 97 | * Called by `makeTrajectory()` to calculate movement information. 98 | * 99 | * Implementations should call `addTrajectory()`, `addCapturing()`, and 100 | * `addCapturable()`, as well as their `remove` counterparts if necessary. By 101 | * default, `addCapturable()` is called with the current position of the piece 102 | * before this member function is called. 103 | */ 104 | virtual void calcTrajectory() = 0; 105 | 106 | /** 107 | * \brief 108 | * Mark a position as a valid position to move to without capturing. 109 | * 110 | * \param tile The position to mark. 111 | */ 112 | void addTrajectory(Position_t const &tile); 113 | /** 114 | * \brief 115 | * Unmark a position as a valid position to move to without capturing. 116 | * 117 | * \param tile The position to unmark. 118 | */ 119 | void removeTrajectory(Position_t const &tile); 120 | 121 | /** 122 | * \brief 123 | * Mark a position as a valid position to move to when capturing. 124 | * 125 | * \param tile The position to mark. 126 | */ 127 | void addCapturing(Position_t const &tile); 128 | /** 129 | * \brief 130 | * Unmark a position as a valid position to move to when capturing. 131 | * 132 | * \param tile The position to unmark. 133 | */ 134 | void removeCapturing(Position_t const &tile); 135 | 136 | /** 137 | * \brief 138 | * Mark a position as a valid position to capture this piece from. 139 | * 140 | * \param tile The position to mark. 141 | */ 142 | void addCapturable(Position_t const &tile); 143 | /** 144 | * \brief 145 | * Unmark a position as a valid position to capture this piece from. 146 | * 147 | * \param tile The position to mark. 148 | */ 149 | void removeCapturable(Position_t const &tile); 150 | 151 | private: 152 | /** 153 | * \brief 154 | * Called when another piece moves on the same board. 155 | * 156 | * \param m The position of the piece that moved. 157 | */ 158 | virtual void tick(Position_t const &m) 159 | { 160 | } 161 | 162 | /** 163 | * \brief 164 | * The chesspp::board::Board calls this to change the position of the piece. 165 | * 166 | * \param to The new position of the piece. 167 | */ 168 | void move(Position_t const &to) 169 | { 170 | Position_t from = pos; 171 | p = to; 172 | moveUpdate(from, to); 173 | ++m; 174 | } 175 | 176 | /** 177 | * \brief 178 | * Called after this piece has moved. 179 | * 180 | * \note 181 | * The value of `moves` is not updated until after this member function returns. 182 | * 183 | * \param from The old position of the piece. 184 | * \param to The current position of the piece. 185 | */ 186 | virtual void moveUpdate(Position_t const &from, Position_t const &to) 187 | { 188 | } 189 | 190 | public: 191 | /** 192 | * \brief 193 | * Outputs a basic human-readable description of this piece to an output stream. 194 | * 195 | * \param os The stream to which to describe this piece. 196 | * \param p The piece to describe. 197 | */ 198 | friend std::ostream &operator<<(std::ostream &os, Piece const &p); 199 | }; 200 | } 201 | } 202 | 203 | #endif 204 | -------------------------------------------------------------------------------- /src/piece/Queen.cpp: -------------------------------------------------------------------------------- 1 | #include "Queen.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace piece 8 | { 9 | static auto QueenRegistration = board::Board::registerPieceClass 10 | ( 11 | "Queen", 12 | [](board::Board &b, board::Board::Position_t const &p, board::Board::Suit const &s) 13 | -> board::Board::Pieces_t::value_type 14 | { 15 | return board::Board::Pieces_t::value_type(new Queen(b, p, s, "Queen")); 16 | } 17 | ); 18 | 19 | Queen::Queen(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc) 20 | : Piece{b, pos_, s_, pc} 21 | { 22 | } 23 | 24 | void Queen::calcTrajectory() 25 | { 26 | //Queens can move infinitely in all eight directions 27 | using Dir = util::Direction; 28 | for(Dir d : {Dir::North 29 | ,Dir::NorthEast 30 | ,Dir::East 31 | ,Dir::SouthEast 32 | ,Dir::South 33 | ,Dir::SouthWest 34 | ,Dir::West 35 | ,Dir::NorthWest}) 36 | { 37 | Position_t t; 38 | for(signed i = 1; board.valid(t = Position_t(pos).move(d, i)); ++i) 39 | { 40 | addCapturing(t); 41 | if(!board.occupied(t)) 42 | { 43 | addTrajectory(t); 44 | } 45 | else break; //can't jump over pieces 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/piece/Queen.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_Queen_HeaderPlusPlus 2 | #define chesspp_piece_Queen_HeaderPlusPlus 3 | 4 | #include "board/Board.hpp" 5 | #include "piece/Piece.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace piece 10 | { 11 | class Queen 12 | : public virtual Piece 13 | { 14 | public: 15 | Queen(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc); 16 | 17 | protected: 18 | virtual void calcTrajectory() override; 19 | }; 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/piece/Rook.cpp: -------------------------------------------------------------------------------- 1 | #include "Rook.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace piece 8 | { 9 | static auto RookRegistration = board::Board::registerPieceClass 10 | ( 11 | "Rook", 12 | [](board::Board &b, board::Board::Position_t const &p, board::Board::Suit const &s) 13 | -> board::Board::Pieces_t::value_type 14 | { 15 | return board::Board::Pieces_t::value_type(new Rook(b, p, s, "Rook")); 16 | } 17 | ); 18 | 19 | Rook::Rook(board::Board &b, Position_t const &pos_, Suit_t const &s_, Class_t const &pc) 20 | : Piece{b, pos_, s_, pc} 21 | { 22 | } 23 | 24 | void Rook::calcTrajectory() 25 | { 26 | //Rooks can move infinitely in the four straight directions 27 | using Dir = util::Direction; 28 | for(Dir d : {Dir::North 29 | ,Dir::East 30 | ,Dir::South 31 | ,Dir::West}) 32 | { 33 | Position_t t; 34 | for(signed i = 1; board.valid(t = Position_t(pos).move(d, i)); ++i) 35 | { 36 | addCapturing(t); 37 | if(!board.occupied(t)) 38 | { 39 | addTrajectory(t); 40 | } 41 | else break; //can't jump over pieces 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/piece/Rook.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_piece_Rook_HeaderPlusPlus 2 | #define chesspp_piece_Rook_HeaderPlusPlus 3 | 4 | #include "board/Board.hpp" 5 | #include "piece/Piece.hpp" 6 | 7 | namespace chesspp 8 | { 9 | namespace piece 10 | { 11 | class Rook 12 | : public virtual Piece 13 | { 14 | public: 15 | Rook(board::Board &b, Position_t const &pos, Suit_t const &s, Class_t const &pc); 16 | 17 | protected: 18 | virtual void calcTrajectory() override; 19 | }; 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/res/CMakeLists.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpluspluscom/ChessPlusPlus/74b0d699b092376db58070a5a3ed8ae5f72f879f/src/res/CMakeLists.txt -------------------------------------------------------------------------------- /src/res/ResourceManager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_res_ResourcesManager_HeaderPlusPlus 2 | #define chesspp_res_ResourcesManager_HeaderPlusPlus 3 | 4 | #include "config/Configuration.hpp" 5 | #include "util/Utilities.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace chesspp 16 | { 17 | namespace res 18 | { 19 | /** 20 | * \brief 21 | * Manages resources used by the application. 22 | */ 23 | class ResourceManager final 24 | { 25 | config::Configuration &conf; 26 | 27 | public: 28 | /** 29 | * \brief 30 | * Constructs from a chesspp::config::Configuration 31 | * 32 | * \param conf_ the chesspp::config::Conriguration - it is referenced, not copied. 33 | */ 34 | ResourceManager(config::Configuration &conf_) noexcept 35 | : conf(conf_) //can't use {} 36 | { 37 | } 38 | 39 | /** 40 | * \brief 41 | * The base class for all resource classes. 42 | * 43 | * \par Requirements 44 | * Deriving classes must have a public constructor that accepts one parameter in the 45 | * form of `configuration.setting(path...)`. In other words, the result of 46 | * chesspp::config::Configuration::setting() should be able to be passed as the only 47 | * argument to construct the resource. 48 | */ 49 | class Resource 50 | { 51 | public: 52 | virtual ~Resource() = 0; 53 | }; 54 | 55 | private: 56 | using Res_t = std::map, std::unique_ptr>; 57 | Res_t res; 58 | 59 | public: 60 | /** 61 | * \brief 62 | * Obtains a resource for a config setting, constructing it if it does not yet 63 | * exist. 64 | * 65 | * Example invocation: 66 | * 67 | * using Font_res = res::SfmlFileResource; 68 | * ResourceManager &resmanager = blah; 69 | * Font_res &fontres = resmanager.from_config("menu", "font"); 70 | * sf::Font font (fontres); 71 | * //... 72 | * 73 | * \tparam ResT The type of the Resource to construct or return. 74 | * \tparam Path deduced by the compiler from the argument list. 75 | * \param path The path, forwarded to chesspp::config::Configuration::setting() 76 | * \return a reference to the resource, of type ResT 77 | */ 78 | template 79 | auto from_config(Path const &... path) 80 | -> typename std::enable_if 81 | < 82 | std::is_base_of::value, 83 | ResT & 84 | >::type 85 | { 86 | Res_t::key_type key {util::path_concat(std::string("\0", 1), path...), typeid(ResT)}; 87 | if(res.find(key) == std::end(res)) 88 | { 89 | res.emplace(key, Res_t::mapped_type{new ResT{conf.setting(path...)}}); 90 | } 91 | //This cast is guaranteed to be correct 92 | return static_cast(*res[key]); 93 | } 94 | }; 95 | inline ResourceManager::Resource::~Resource() = default; 96 | } 97 | } 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/res/SfmlFileResource.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_res_SfmlFileResource_HeaderPlusPlus 2 | #define chesspp_res_SfmlFileResource_HeaderPlusPlus 3 | 4 | #include "ResourceManager.hpp" 5 | //#include "SFML.hpp" 6 | 7 | #include 8 | 9 | namespace chesspp 10 | { 11 | namespace res 12 | { 13 | /** 14 | * \brief 15 | * Wrap an SFML class for use with the ResourceManager as a file resource. 16 | * 17 | * Any SFML class which supports `.loadFromFile()` can be used, such as sf::Sprite and 18 | * sf::Font. 19 | * 20 | * \tparam The SFML class to wrap. 21 | */ 22 | template 23 | class SfmlFileResource 24 | : public ResourceManager::Resource 25 | { 26 | public: 27 | /** 28 | * \brief 29 | * The wrapped SFML class instance. 30 | */ 31 | sfmlT res; 32 | 33 | /** 34 | * \brief 35 | * Construct the wrapper from a file. 36 | * 37 | * \note 38 | * This constructor does not throw, instead it logs an error report to std::cerr 39 | * 40 | * \param file_path The file path. 41 | */ 42 | SfmlFileResource(std::string const &file_path) noexcept 43 | { 44 | if(!res.loadFromFile(file_path)) 45 | { 46 | std::cerr << "SFML Resource failed to load \"" 47 | << file_path << "\" for " 48 | << typeid(sfmlT).name() << std::endl; 49 | } 50 | else 51 | { 52 | std::clog << "SFML Resource loaded \"" 53 | << file_path << "\" for " 54 | << typeid(sfmlT).name() << std::endl; 55 | } 56 | } 57 | 58 | /** 59 | * \brief 60 | * Implicitly cast the wrapper to the wrapped resource instance. 61 | */ 62 | operator sfmlT &() noexcept 63 | { 64 | return res; 65 | } 66 | /** 67 | * \brief 68 | * Implicitly cast the wrapper to the wrapped resource instance. 69 | */ 70 | operator sfmlT const &() const noexcept 71 | { 72 | return res; 73 | } 74 | }; 75 | } 76 | } 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/util/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add sources 2 | target_sources(ChessPlusPlus PUBLIC 3 | "${CMAKE_CURRENT_LIST_DIR}/Direction.cpp" 4 | "${CMAKE_CURRENT_LIST_DIR}/JsonReader.cpp" 5 | ) 6 | -------------------------------------------------------------------------------- /src/util/Direction.cpp: -------------------------------------------------------------------------------- 1 | #include "Direction.hpp" 2 | 3 | #include 4 | 5 | namespace chesspp 6 | { 7 | namespace util 8 | { 9 | Direction Rotate(Direction d, signed r) noexcept 10 | { 11 | using D = Direction; 12 | for(signed i = 0; i < r; ++i) //rotate positively 13 | { 14 | switch(d) 15 | { 16 | case D::North: d = D::NorthEast; break; 17 | case D::NorthEast: d = D::East; break; 18 | case D::East: d = D::SouthEast; break; 19 | case D::SouthEast: d = D::South; break; 20 | case D::South: d = D::SouthWest; break; 21 | case D::SouthWest: d = D::West; break; 22 | case D::West: d = D::NorthWest; break; 23 | case D::NorthWest: d = D::North; break; 24 | default: break; 25 | } 26 | } 27 | for(signed i = 0; i > r; --i) //rotate negatively 28 | { 29 | switch(d) 30 | { 31 | case D::North: d = D::NorthWest; break; 32 | case D::NorthEast: d = D::North; break; 33 | case D::East: d = D::NorthEast; break; 34 | case D::SouthEast: d = D::East; break; 35 | case D::South: d = D::SouthEast; break; 36 | case D::SouthWest: d = D::South; break; 37 | case D::West: d = D::SouthWest; break; 38 | case D::NorthWest: d = D::West; break; 39 | default: break; 40 | } 41 | } 42 | return d; 43 | } 44 | std::ostream &operator<<(std::ostream &os, Direction const &d) noexcept 45 | { 46 | using D = Direction; 47 | switch(d) 48 | { 49 | case D::North: return os << "North"; 50 | case D::NorthEast: return os << "NorthEast"; 51 | case D::East: return os << "East"; 52 | case D::SouthEast: return os << "SouthEast"; 53 | case D::South: return os << "South"; 54 | case D::SouthWest: return os << "SouthWest"; 55 | case D::West: return os << "West"; 56 | case D::NorthWest: return os << "NorthWest"; 57 | case D::None: return os << "None"; 58 | } 59 | } 60 | std::istream &operator>>(std::istream &is, Direction &d) 61 | { 62 | using D = Direction; 63 | std::string ds; 64 | is >> ds; 65 | if (ds == "North" ) return (d = D::North ), is; 66 | else if(ds == "NorthEast") return (d = D::NorthEast), is; 67 | else if(ds == "East" ) return (d = D::East ), is; 68 | else if(ds == "SouthEast") return (d = D::SouthEast), is; 69 | else if(ds == "South" ) return (d = D::South ), is; 70 | else if(ds == "SouthWest") return (d = D::SouthWest), is; 71 | else if(ds == "West" ) return (d = D::West ), is; 72 | else if(ds == "NorthWest") return (d = D::NorthWest), is; 73 | else return (d = D::None ), is; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/util/Direction.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_util_Direction_HeaderPlusPlus 2 | #define chesspp_util_Direction_HeaderPlusPlus 3 | 4 | #include 5 | 6 | namespace chesspp 7 | { 8 | namespace util 9 | { 10 | /** 11 | * Represents a direction. 12 | */ 13 | enum class Direction 14 | { 15 | None, 16 | North, 17 | NorthEast, 18 | East, 19 | SouthEast, 20 | South, 21 | SouthWest, 22 | West, 23 | NorthWest 24 | }; 25 | /** 26 | * Returns a new direction which is a rotation of the 27 | * provided direction. 28 | * \param d the direction to rotate. 29 | * \param r the number of times to rotate, may be negative. 30 | * \return the rotated direction. 31 | */ 32 | Direction Rotate(Direction d, signed r) noexcept; 33 | /** 34 | * Serializes a direction to a stream in string format. 35 | * \param os The stream to serialize to. 36 | * \param d the Direction to serialize. 37 | * \return os 38 | */ 39 | std::ostream &operator<<(std::ostream &os, Direction const &d) noexcept; 40 | std::istream &operator>>(std::istream &is, Direction &d); 41 | } 42 | } 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/util/JsonReader.cpp: -------------------------------------------------------------------------------- 1 | #include "JsonReader.hpp" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace chesspp 9 | { 10 | namespace util 11 | { 12 | JsonReader::JsonReader(std::istream &s) 13 | { 14 | if(!s) 15 | { 16 | throw std::invalid_argument{"stream given to JsonReader in bad state"}; 17 | } 18 | std::string str ((std::istreambuf_iterator(s)), std::istreambuf_iterator()); 19 | json_settings options {0U, 0, nullptr, nullptr, nullptr, 0U}; 20 | char error[json_error_max]; 21 | json = json_parse_ex(&options, str.c_str(), str.length(), error); 22 | if(!json) 23 | { 24 | //no manual cleanup needed 25 | throw std::runtime_error{std::string("Error loading JSON: ") + error}; 26 | } 27 | } 28 | JsonReader::JsonReader(std::istream &&s) 29 | : JsonReader{s} 30 | { 31 | } 32 | JsonReader::JsonReader(JsonReader &&from) 33 | : json{from.json} 34 | { 35 | from.json = nullptr; 36 | } 37 | JsonReader &JsonReader::operator=(std::istream &&s) 38 | { 39 | *this = JsonReader(s); //move assign 40 | return *this; 41 | } 42 | JsonReader &JsonReader::operator=(JsonReader &&from) 43 | { 44 | std::swap(json, from.json); 45 | return *this; 46 | } 47 | JsonReader::~JsonReader() 48 | { 49 | json_value_free(json), json = nullptr; 50 | } 51 | JsonReader::NestedValue::NestedValue(json_value const &value_) noexcept 52 | : value(value_) //can't use {} 53 | { 54 | } 55 | JsonReader::Type JsonReader::NestedValue::type() const noexcept 56 | { 57 | switch(value.type) 58 | { 59 | case json_object: return Type::Object; 60 | case json_array: return Type::Array; 61 | case json_integer: return Type::Integer; 62 | case json_double: return Type::Double; 63 | case json_string: return Type::String; 64 | case json_boolean: return Type::Boolean; 65 | case json_null: return Type::Null; 66 | default: return Type::None; 67 | } 68 | } 69 | JsonReader::NestedValue JsonReader::NestedValue::parent() const 70 | { 71 | if(value.parent) 72 | { 73 | return *value.parent; 74 | } 75 | throw std::domain_error{"No parent json value"}; 76 | } 77 | JsonReader::NestedValue JsonReader::NestedValue::operator[](std::string const &name) const noexcept(noexcept(name.c_str())) 78 | { 79 | return value[name.c_str()]; 80 | } 81 | JsonReader::NestedValue JsonReader::NestedValue::operator[](char const *name) const noexcept 82 | { 83 | return value[name]; 84 | } 85 | std::size_t JsonReader::NestedValue::length() const noexcept 86 | { 87 | if(value.type == json_array) 88 | { 89 | return value.u.array.length; 90 | } 91 | return 0; 92 | } 93 | JsonReader::NestedValue JsonReader::NestedValue::operator[](std::size_t index) const noexcept 94 | { 95 | return value[static_cast(index)]; 96 | } 97 | std::map JsonReader::NestedValue::object() const 98 | { 99 | std::map obj; 100 | if(value.type == json_object) 101 | { 102 | for(std::size_t i = 0; i < value.u.object.length; ++i) 103 | { 104 | obj.insert(std::make_pair(value.u.object.values[i].name, *value.u.object.values[i].value)); 105 | } 106 | } 107 | return obj; 108 | } 109 | JsonReader::NestedValue::operator std::string() const noexcept(noexcept(std::string(""))) 110 | { 111 | return static_cast(value); 112 | } 113 | JsonReader::NestedValue::operator std:: int8_t() const noexcept { return static_cast(static_cast(value)); } 114 | JsonReader::NestedValue::operator std:: uint8_t() const noexcept { return static_cast(static_cast(value)); } 115 | JsonReader::NestedValue::operator std:: int16_t() const noexcept { return static_cast(static_cast(value)); } 116 | JsonReader::NestedValue::operator std::uint16_t() const noexcept { return static_cast(static_cast(value)); } 117 | JsonReader::NestedValue::operator std:: int32_t() const noexcept { return static_cast(static_cast(value)); } 118 | JsonReader::NestedValue::operator std::uint32_t() const noexcept { return static_cast(static_cast(value)); } 119 | JsonReader::NestedValue::operator std:: int64_t() const noexcept { return static_cast(static_cast(value)); } 120 | JsonReader::NestedValue::operator std::uint64_t() const noexcept { return static_cast(static_cast(value)); } 121 | JsonReader::NestedValue::operator bool() const noexcept 122 | { 123 | return value; 124 | } 125 | JsonReader::NestedValue::operator double() const noexcept 126 | { 127 | return value; 128 | } 129 | JsonReader::NestedValue JsonReader::access() const noexcept 130 | { 131 | return *json; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/util/JsonReader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_util_JsonReader_HeaderPlusPlus 2 | #define chesspp_util_JsonReader_HeaderPlusPlus 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct _json_value; 13 | 14 | namespace chesspp 15 | { 16 | namespace util 17 | { 18 | /** 19 | * Respresents an immutable JSON file in memory. 20 | * Current implementation uses json-parser by 21 | * James McLaughlin. Thus, it wraps a json_value. 22 | */ 23 | class JsonReader final 24 | { 25 | /** 26 | * Underlying json_value pointer. 27 | */ 28 | _json_value *json {nullptr}; 29 | public: 30 | JsonReader() = delete; 31 | JsonReader(JsonReader const &) = delete; 32 | JsonReader &operator=(JsonReader const &) = delete; 33 | /** 34 | * Constructs this JsonReader from the given stream. 35 | * The stream is read to EOF. 36 | * \param s The stream containing the JSON. 37 | */ 38 | JsonReader(std::istream &s); 39 | /** 40 | * Constructs this JsonReader from the given temporary stream. 41 | * The stream is read to EOF. 42 | * \param s The temporary stream containing the JSON. 43 | */ 44 | JsonReader(std::istream &&s); 45 | /** 46 | * Move-constructs this JsonReader from another. 47 | * \param from the JsonReader to move. 48 | */ 49 | JsonReader(JsonReader &&from); 50 | /** 51 | * Move-assigns this JsonReader from a given stream rvalue. 52 | * The stream is read to EOF. Returns *this 53 | * \param s The stream containing the JSON. 54 | */ 55 | JsonReader &operator=(std::istream &&s); 56 | /** 57 | * Move-assigns this JsonReader from another. 58 | * \param from the JsonReader to move. 59 | */ 60 | JsonReader &operator=(JsonReader &&from); 61 | /** 62 | * Destructs this JsonReader freeing any allocated memory. 63 | */ 64 | ~JsonReader(); 65 | 66 | enum class Type 67 | { 68 | None, 69 | Object, 70 | Array, 71 | Integer, 72 | Double, 73 | String, 74 | Boolean, 75 | Null 76 | }; 77 | 78 | /** 79 | * Represents a value in the JSON. 80 | * Instances of this class should not exceed the 81 | * lifetime of the JsonReader that they came from. 82 | * Attempting to access a NestedValue for which the 83 | * associated JsonReader has been destructed results 84 | * in undefined behavior. Two or more instances of 85 | * this class may represent the same nested value in 86 | * the JSON. 87 | */ 88 | class NestedValue 89 | { 90 | friend class ::chesspp::util::JsonReader; 91 | /** 92 | * The particular value this instance is associated with. 93 | */ 94 | _json_value const &value; 95 | /** 96 | * Constructs this NestedValue from a json_value. 97 | * Note that this constructor is private and for 98 | * implementation use only. 99 | * \param value_ The json_value this instance shall represent. 100 | */ 101 | NestedValue(_json_value const &) noexcept; 102 | NestedValue &operator=(NestedValue const &) noexcept = delete; 103 | public: 104 | NestedValue(NestedValue const &) = default; 105 | NestedValue(NestedValue &&) = default; 106 | NestedValue &operator=(NestedValue &&) = default; 107 | ~NestedValue() = default; 108 | 109 | /** 110 | * Returns the type of this value. 111 | * Note that the return value is a type 112 | * from the json-parse library. It may 113 | * be compared to these constants: 114 | * json_null 115 | * json_boolean 116 | * json_integer 117 | * json_double 118 | * json_string 119 | * json_array 120 | * json_object 121 | * \return The type of value represented. 122 | */ 123 | Type type() const noexcept; 124 | /** 125 | * Returns the parent NestedValue or throws 126 | * ::std::domain_error. 127 | * \return the parent NestedValue. 128 | * \throws ::std::domain_error 129 | */ 130 | NestedValue parent() const; 131 | 132 | /** 133 | * Returns a NestedValue within this one by name. 134 | * Only works if type() == json_object 135 | * \param name the name of the nested JSON value. 136 | * \return the NestedValue by name. 137 | */ 138 | NestedValue operator[](std::string const &name) const noexcept(noexcept(name.c_str())); 139 | /** 140 | * Returns a NestedValue within this one by name. 141 | * Only works if type() == json_object 142 | * \param name the name of the nested JSON value. 143 | * \return the NestedValue by name. 144 | */ 145 | NestedValue operator[](char const *name) const noexcept; 146 | /** 147 | * Returns the length of array values. 148 | * \return The length of the array, or 0 if not an array. 149 | */ 150 | std::size_t length() const noexcept; 151 | /** 152 | * Returns the nested value at the given array index. 153 | * Only works if type() == json_array 154 | * \param index The index within the array. 155 | * \return the nested value at the given index. 156 | */ 157 | NestedValue operator[](std::size_t index) const noexcept; 158 | /** 159 | * Provides a std::map-based view of 160 | * an object value, mapping object keys 161 | * to object values. Only works if 162 | * type() == json_object 163 | * \return a std::map of object keys ot obejct values 164 | */ 165 | std::map object() const; 166 | /** 167 | * Returns the string representation of this string value. 168 | * Only works if type() == json_string 169 | * \return The string 170 | */ 171 | operator std::string() const noexcept(noexcept(std::string(""))); 172 | operator std:: int8_t() const noexcept; 173 | operator std:: uint8_t() const noexcept; 174 | operator std:: int16_t() const noexcept; 175 | operator std::uint16_t() const noexcept; 176 | operator std:: int32_t() const noexcept; 177 | operator std::uint32_t() const noexcept; 178 | operator std:: int64_t() const noexcept; 179 | operator std::uint64_t() const noexcept; 180 | /** 181 | * Returns the boolean state of this bool value. 182 | * Only works if type() == json_bool. 183 | * \return The boolean. 184 | */ 185 | operator bool() const noexcept; 186 | /** 187 | * Returns the floating-point value of this double. 188 | * Only works if type() == json_double 189 | * \return The double. 190 | */ 191 | operator double() const noexcept; 192 | 193 | /** 194 | * Only for extreme use cases: returns the 195 | * underlying json_value being wrapped. 196 | * \return the underlying json_value 197 | */ 198 | _json_value const &implementation() noexcept 199 | { 200 | return value; 201 | } 202 | }; 203 | 204 | /** 205 | * Returns a NestedValue view into 206 | * this JSON. 207 | * \return a NestedValue view into this JSON. 208 | */ 209 | NestedValue access() const noexcept; 210 | /** 211 | * Returns a NestedValue view into 212 | * this JSON. 213 | * \return a NestedValue view into this JSON. 214 | */ 215 | NestedValue operator()() const noexcept 216 | { 217 | return access(); 218 | } 219 | /** 220 | * Navigates through obejct values and array values 221 | * and returns a NestedValue at the destination. 222 | * \tparam Args The types for navigation, generally deduced by the compiler. 223 | * \param path The path to take to arrive at the destination. May be strings and indicies. 224 | */ 225 | template 226 | NestedValue navigate(Args... path) const 227 | { 228 | return navigate(access(), path...); 229 | } 230 | private: 231 | /** 232 | * Helper for the public navigate. 233 | */ 234 | template 235 | NestedValue navigate(NestedValue v, First const &first, Rest const &... rest) const 236 | { 237 | return navigate(v[first], rest...); 238 | } 239 | /** 240 | * Helper for the public navigate. 241 | */ 242 | template 243 | NestedValue navigate(NestedValue v, Last const &last) const 244 | { 245 | return v[last]; 246 | } 247 | }; 248 | } 249 | } 250 | 251 | #endif 252 | -------------------------------------------------------------------------------- /src/util/Position.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_util_Position_HeaderPlusPlus 2 | #define chesspp_util_Position_HeaderPlusPlus 3 | 4 | #include "Utilities.hpp" 5 | #include "Direction.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace chesspp 13 | { 14 | namespace util 15 | { 16 | /** 17 | * Represents a two-dimensional point or position. 18 | * \tparam T coordinate type, must be a scalar type. 19 | */ 20 | template 21 | class Position final 22 | { 23 | public: 24 | static_assert(std::is_scalar::value, "Template parameter must be a scalar type"); 25 | 26 | using value_type = T; 27 | 28 | T x, y; //intentionally public 29 | 30 | /** 31 | * Returns the position at the origin. 32 | * This position is the result of x and y 33 | * being initialized by T's default constructor. 34 | * \return the position at the origin. 35 | */ 36 | static Position const &Origin() noexcept 37 | { 38 | static Position origin; 39 | return origin; 40 | } 41 | 42 | /** 43 | * Initializes x and y to the provided values, 44 | * or from T's default constructor otherwise. 45 | * \param x_ the x coordinate of this position, or T() 46 | * \param y_ the y coordinate of this position, or T() 47 | */ 48 | Position(T x_ = T(), T y_ = T()) noexcept 49 | : x{x_} 50 | , y{y_} 51 | { 52 | } 53 | Position(Position const &) = default; 54 | Position(Position &&) = default; 55 | Position &operator=(Position const &) = default; 56 | Position &operator=(Position &&) = default; 57 | ~Position() = default; 58 | 59 | /** 60 | * Checks if this position is within a boundry of 61 | * top-left and bottom-right positions, including edges. 62 | * \param topleft the lowest coordinate corner 63 | * \param bottomright the highest coordinate corner 64 | * \return whether this position is inclusively within the given boundaries. 65 | */ 66 | bool isWithin(Position const &topleft, Position const &bottomright) const noexcept 67 | { 68 | return topleft.x <= x 69 | && topleft.y <= y 70 | && x <= bottomright.x 71 | && y <= bottomright.y; 72 | } 73 | 74 | /** 75 | * Moves this position relative to itself. 76 | * \param xoff the value to add to x, of type signed T 77 | * \param yoff the value to add to y, of type signed T 78 | * \return *this 79 | */ 80 | Position &move(typename MakeSigned::type xoff, typename MakeSigned::type yoff) noexcept 81 | { 82 | x += xoff; 83 | y += yoff; 84 | return *this; 85 | } 86 | /** 87 | * Move position relative to itself in a direction 88 | * a given number of times. 89 | * \param d the dirction in which to move. 90 | * \param times the number of times to move in direction d. 91 | * \return *this 92 | */ 93 | Position &move(Direction const &d, signed times = 1) noexcept 94 | { 95 | using D = Direction; 96 | for(signed i = 0; i < times; ++i) 97 | { 98 | //move forward 99 | switch(d) 100 | { 101 | case D::North: --y; break; 102 | case D::NorthEast: ++x; --y; break; 103 | case D::East: ++x; break; 104 | case D::SouthEast: ++x; ++y; break; 105 | case D::South: ++y; break; 106 | case D::SouthWest: --x; ++y; break; 107 | case D::West: --x; break; 108 | case D::NorthWest: --x; --y; break; 109 | default: break; 110 | } 111 | } 112 | if(times < 0) 113 | { 114 | //move backward 115 | Position t = Position(0, 0).move(d, -times); 116 | move(-t.x, -t.y); 117 | } 118 | return *this; 119 | } 120 | 121 | /** 122 | * Equality comparison operator. 123 | * \param a the left-hand position. 124 | * \param b the right-hand position 125 | * \return whether a equals b. 126 | */ 127 | friend auto operator==(Position const &a, Position const &b) noexcept -> typename std::enable_if::value, bool>::type 128 | { 129 | return std::tie(a.x, a.y) == std::tie(b.x, b.y); 130 | } 131 | 132 | /** 133 | * Less-than comparison operator, to be used for 134 | * sorting purposes only. 135 | * \param a the left-hand position. 136 | * \param b the right-hand position. 137 | * \return true if a comes before b. 138 | */ 139 | friend bool operator<(Position const &a, Position const &b) noexcept 140 | { 141 | return std::tie(a.x, a.y) < std::tie(b.x, b.y); 142 | } 143 | }; 144 | 145 | /** 146 | * Serializes a position to a stream in the format "(x, y)". 147 | * This is the signed version. 148 | * \param os the stream to write to. 149 | * \param p the position to serialize into the stream. 150 | * \return os 151 | */ 152 | template 153 | auto operator<<(std::ostream &os, Position const &p) noexcept 154 | -> typename std::enable_if 155 | < 156 | std::is_integral::value 157 | && std::is_same::type>::value, 158 | std::ostream & 159 | >::type 160 | { 161 | //cast in case of char 162 | return os << '(' << static_cast(p.x) 163 | << ", " << static_cast(p.y) << ')'; 164 | } 165 | /** 166 | * Serializes a position to a stream in the format "(x, y)". 167 | * This is the unsigned version. 168 | * \param os the stream to write to. 169 | * \param p the position to serialize into the stream. 170 | * \return os 171 | */ 172 | template 173 | auto operator<<(std::ostream &os, Position const &p) noexcept 174 | -> typename std::enable_if 175 | < 176 | std::is_integral::value 177 | && !std::is_same::type>::value, 178 | std::ostream & 179 | >::type 180 | { 181 | //cast in case of char 182 | return os << '(' << static_cast(p.x) 183 | << ", " << static_cast(p.y) << ')'; 184 | } 185 | /** 186 | * Serializes a position to a stream in the format "(x, y)". 187 | * This is the float version. 188 | * \param os the stream to write to. 189 | * \param p the position to serialize into the stream. 190 | * \return os 191 | */ 192 | template 193 | auto operator<<(std::ostream &os, Position const &p) noexcept 194 | -> typename std::enable_if 195 | < 196 | !std::is_integral::value, 197 | std::ostream & 198 | >::type 199 | { 200 | return os << '(' << p.x << ", " << p.y << ')'; 201 | } 202 | } 203 | } 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /src/util/Utilities.hpp: -------------------------------------------------------------------------------- 1 | #ifndef chesspp_util_Utilities_HeaderPlusPlus 2 | #define chesspp_util_Utilities_HeaderPlusPlus 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace chesspp 11 | { 12 | inline namespace util_ops 13 | { 14 | /** 15 | * Alternative global operator!=. 16 | * Smarter template than the one in ::std::rel_ops, 17 | * supports inheritance and non-bool return type. 18 | * \tparam T the left-hand type. 19 | * \tparam U the right-hand type. 20 | * \param t the left-hand value. 21 | * \param u the right-hand value. 22 | * \return !(t == u) 23 | */ 24 | template 25 | auto operator!=(T const &t, U const &u) noexcept(noexcept(!(t == u))) 26 | -> typename std::enable_if 27 | < 28 | std::is_base_of::value 29 | || std::is_base_of::value, 30 | decltype(!(t == u)) 31 | >::type 32 | { 33 | return !(t == u); 34 | } 35 | /** 36 | * Allows comparison of std::unique_ptr and pointer. 37 | * \tparam T the type that std::unique_ptr wraps 38 | * \param up the std::unique_ptr 39 | * \param p the pointer 40 | */ 41 | template 42 | bool operator==(std::unique_ptr const &up, T const *p) noexcept(noexcept(up.get() == p)) 43 | { 44 | return up.get() == p; 45 | } 46 | /** 47 | * Allows comparison of std::unique_ptr and pointer. 48 | * \tparam T the type that std::unique_ptr wraps 49 | * \param p the pointer 50 | * \param up the std::unique_ptr 51 | */ 52 | template 53 | bool operator==(T const *p, std::unique_ptr const &up) noexcept(noexcept(p == up.get())) 54 | { 55 | return p == up.get(); 56 | } 57 | } 58 | namespace util 59 | { 60 | /** 61 | * Better version of std::make_signed that supports 62 | * floating point types, usage is identical. 63 | * \tparam T must be scalar. 64 | */ 65 | template 66 | struct MakeSigned final 67 | { 68 | static_assert(std::is_scalar::value, "Template parameter must be a scalar type"); 69 | /** 70 | * The signed variant of T, or T if T is floating point. 71 | */ 72 | using type = 73 | typename std::conditional 74 | < 75 | std::is_floating_point::value, 76 | std::common_type, 77 | std::make_signed 78 | >::type::type; 79 | }; 80 | /** 81 | * Template alias to complement MakeSigned. 82 | * Used the same way as std::is_signed. 83 | * \tparam T must be scalar. 84 | */ 85 | template 86 | using IsSigned = typename std::is_same::type>/*::value*/; 87 | 88 | /** 89 | * Allows iterating over the keys of a map. 90 | * There does not yet exist a version for iterating 91 | * over the values of a map. 92 | * \tparam Map must be std::map or equivalent container 93 | */ 94 | template 95 | class KeyIter final 96 | : public std::iterator 97 | { 98 | typename Map::const_iterator it; 99 | public: 100 | KeyIter(typename Map::const_iterator mapit) noexcept 101 | : it{mapit} 102 | { 103 | } 104 | KeyIter(KeyIter const &) = default; 105 | KeyIter(KeyIter &&) = default; 106 | KeyIter &operator=(KeyIter const &) = default; 107 | KeyIter &operator=(KeyIter &&) = default; 108 | 109 | KeyIter &operator++() noexcept 110 | { 111 | ++it; 112 | return *this; 113 | } 114 | KeyIter operator++(int) noexcept 115 | { 116 | KeyIter temp = *this; 117 | ++*this; 118 | return temp; 119 | } 120 | 121 | typename Map::key_type const &operator*() const 122 | { 123 | return it->first; 124 | } 125 | typename Map::key_type const *operator->() const 126 | { 127 | return &(it->first); 128 | } 129 | 130 | friend bool operator==(KeyIter const &a, KeyIter const &b) noexcept 131 | { 132 | return a.it == b.it; 133 | } 134 | friend bool operator!=(KeyIter const &a, KeyIter const &b) noexcept 135 | { 136 | return a.it != b.it; 137 | } 138 | }; 139 | 140 | /** 141 | * Takes a std::pair of iterators (e.g. the return value 142 | * of some standard algorithms) and uses it for begin 143 | * and end member functions to be compatible with 144 | * std::begin, std::end, and range-based for loops. 145 | * \tparam ItT The iterator type, e.g. container::const_iterator 146 | */ 147 | template 148 | class Range final 149 | { 150 | std::pair r; 151 | public: 152 | Range(std::pair const &range) noexcept 153 | : r{range} 154 | { 155 | } 156 | ItT begin() const noexcept 157 | { 158 | return r.first; 159 | } 160 | ItT end() const noexcept 161 | { 162 | return r.second; 163 | } 164 | }; 165 | /** 166 | * Takes a std::pair of iterators (e.g. the return value 167 | * of some standard algorithms) and constructs a Range 168 | * object from it as a convenience function. 169 | * \tparam ItT The iterator type, best if left to be deduced by the compiler. 170 | */ 171 | template 172 | auto as_range(std::pair const &range) noexcept 173 | -> Range 174 | { 175 | return range; 176 | } 177 | 178 | /** 179 | * Allows unpacking a template parameter pack 180 | * into a string path with a custom delimiter. 181 | * \tparam ...Args The type parameter pack. 182 | * \param delim The delimiter, between each arg and at the end. 183 | * \param ...args The template parameter pack. 184 | */ 185 | template 186 | std::string path_concat(std::string const &delim, Args const &... args); 187 | template 188 | std::string path_concat(std::string const &delim, First const &first, Rest const &... rest) 189 | { 190 | std::ostringstream oss; 191 | oss << first << delim << path_concat(delim, rest...); 192 | return oss.str(); 193 | } 194 | template<> 195 | inline std::string path_concat(std::string const &delim) 196 | { 197 | return ""; 198 | } 199 | } 200 | } 201 | namespace std //for std::begin and std::end specializations 202 | { 203 | /** 204 | * Allows iterating over the return value of equal_range(). 205 | * \tparam Iter the iterator type, to be decuded by the compiler. 206 | * \param p the return value of equal_range(). 207 | */ 208 | template::iterator_category> 209 | Iter begin(pair const &p) 210 | { 211 | return p.first; 212 | } 213 | /** 214 | * Allows iterating over the return value of equal_range(). 215 | * \tparam Iter the iterator type, to be decuded by the compiler. 216 | * \param p the return value of equal_range(). 217 | */ 218 | template::iterator_category> 219 | Iter end(pair const &p) 220 | { 221 | return p.second; 222 | } 223 | } 224 | 225 | #endif 226 | --------------------------------------------------------------------------------