├── .clang-format ├── .codacy.yml ├── .gitignore ├── .gitmodules ├── .lgtm.yml ├── .travis.yml ├── CMake ├── CodeCoverage.cmake └── CompileOptions.cmake ├── CMakeLists.txt ├── CMakeSettings.json ├── Dockerfile ├── Extensions ├── YahtzeeConsole │ ├── CMakeLists.txt │ ├── Console.cpp │ ├── Console.hpp │ └── main.cpp └── YahtzeePython │ ├── CMakeLists.txt │ ├── Includes │ ├── Commons │ │ └── Constants.hpp │ ├── Enums │ │ └── GameEnums.hpp │ ├── Games │ │ ├── Game.hpp │ │ ├── GameConfig.hpp │ │ ├── GameManager.hpp │ │ └── GameState.hpp │ └── Models │ │ ├── Dice.hpp │ │ ├── Player.hpp │ │ └── ScoreCard.hpp │ ├── Sources │ ├── Commons │ │ └── Constants.cpp │ ├── Enums │ │ └── GameEnums.cpp │ ├── Games │ │ ├── Game.cpp │ │ ├── GameConfig.cpp │ │ ├── GameManager.cpp │ │ └── GameState.cpp │ └── Models │ │ ├── Dice.cpp │ │ ├── Player.cpp │ │ └── ScoreCard.cpp │ └── main.cpp ├── Includes └── YahtzeeMaster │ ├── Commons │ └── Constants.hpp │ ├── Enums │ └── GameEnums.hpp │ ├── Games │ ├── Game.hpp │ ├── GameConfig.hpp │ ├── GameManager.hpp │ └── GameState.hpp │ ├── Models │ ├── Dice.hpp │ ├── Player.hpp │ └── ScoreCard.hpp │ └── YahtzeeMaster.hpp ├── LICENSE ├── Medias └── Logos │ └── Logo.png ├── README.md ├── Scripts ├── Dockerfile.bionic-codecov ├── Dockerfile.focal ├── Dockerfile.focal.clang-latest ├── Dockerfile.focal.gcc-latest ├── azure_pipelines_build_linux.yml ├── azure_pipelines_build_macos.yml ├── azure_pipelines_build_windows_vs2017.yml ├── azure_pipelines_build_windows_vs2019.yml ├── header_gen.py ├── travis_build.sh ├── travis_build_codecov.sh ├── travis_build_codecov_sonar.sh ├── travis_build_docker.sh └── utils.py ├── Sources └── YahtzeeMaster │ ├── CMakeLists.txt │ ├── Games │ ├── Game.cpp │ └── GameManager.cpp │ └── Models │ ├── Dice.cpp │ ├── Player.cpp │ └── ScoreCard.cpp ├── Tests ├── PythonTests │ ├── test_dice.py │ ├── test_game.py │ ├── test_game_manager.py │ ├── test_player.py │ └── test_score_card.py └── UnitTests │ ├── CMakeLists.txt │ ├── Games │ ├── GameManagerTests.cpp │ └── GameTests.cpp │ ├── Models │ ├── DiceTests.cpp │ ├── PlayerTests.cpp │ └── ScoreCardTests.cpp │ ├── doctest_proxy.hpp │ └── main.cpp ├── appveyor.yml ├── azure-pipelines.yml ├── codecov.yml ├── requirements.txt ├── setup.py └── sonar-project.properties /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -3 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: false 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BreakBeforeBraces: Custom 24 | BraceWrapping: 25 | AfterCaseLabel: true 26 | AfterClass: true 27 | AfterControlStatement: true 28 | AfterEnum: true 29 | AfterFunction: true 30 | AfterNamespace: true 31 | AfterObjCDeclaration: true 32 | AfterStruct: true 33 | AfterUnion: true 34 | BeforeCatch: true 35 | BeforeElse: true 36 | IndentBraces: false 37 | BreakBeforeBinaryOperators: None 38 | BreakBeforeTernaryOperators: true 39 | BreakConstructorInitializersBeforeComma: false 40 | BreakAfterJavaFieldAnnotations: false 41 | BreakStringLiterals: true 42 | ColumnLimit: 90 43 | CommentPragmas: '^ IWYU pragma:' 44 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 45 | ConstructorInitializerIndentWidth: 4 46 | ContinuationIndentWidth: 4 47 | Cpp11BracedListStyle: false 48 | DerivePointerAlignment: false 49 | DisableFormat: false 50 | ExperimentalAutoDetectBinPacking: false 51 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 52 | IncludeCategories: 53 | - Regex: '^<.*\.h>' 54 | Priority: 1 55 | - Regex: '^<.*' 56 | Priority: 2 57 | - Regex: '.*' 58 | Priority: 3 59 | IncludeIsMainRegex: '([-_](test|unittest))?$' 60 | IndentCaseLabels: true 61 | IndentWidth: 4 62 | IndentWrappedFunctionNames: false 63 | JavaScriptQuotes: Leave 64 | JavaScriptWrapImports: true 65 | KeepEmptyLinesAtTheStartOfBlocks: false 66 | MacroBlockBegin: '' 67 | MacroBlockEnd: '' 68 | MaxEmptyLinesToKeep: 1 69 | NamespaceIndentation: None 70 | ObjCBlockIndentWidth: 2 71 | ObjCSpaceAfterProperty: false 72 | ObjCSpaceBeforeProtocolList: false 73 | PenaltyBreakBeforeFirstCallParameter: 1 74 | PenaltyBreakComment: 300 75 | PenaltyBreakFirstLessLess: 120 76 | PenaltyBreakString: 1000 77 | PenaltyExcessCharacter: 1000000 78 | PenaltyReturnTypeOnItsOwnLine: 200 79 | PointerAlignment: Left 80 | ReflowComments: true 81 | SortIncludes: true 82 | SpaceAfterCStyleCast: false 83 | SpaceBeforeAssignmentOperators: true 84 | SpaceBeforeParens: ControlStatements 85 | SpaceInEmptyParentheses: false 86 | SpacesBeforeTrailingComments: 2 87 | SpacesInAngles: false 88 | SpacesInContainerLiterals: true 89 | SpacesInCStyleCastParentheses: false 90 | SpacesInParentheses: false 91 | SpacesInSquareBrackets: false 92 | Standard: Auto 93 | TabWidth: 4 94 | UseTab: Never -------------------------------------------------------------------------------- /.codacy.yml: -------------------------------------------------------------------------------- 1 | exclude_paths: 2 | - README.md 3 | - 3RD-PARTY.md 4 | - Dockerfile 5 | - setup.py 6 | - Datas/** 7 | - Documents/** 8 | - Libraries/** 9 | - Resources/** 10 | - Scripts/** -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # CMake temp/cache files 3 | CMakeCache.txt 4 | CMakeFiles 5 | CMakeScripts 6 | Testing 7 | Makefile 8 | cmake_install.cmake 9 | install_manifest.txt 10 | compile_commands.json 11 | CTestTestfile.cmake 12 | build 13 | 14 | # Visual Studio files 15 | /.vs 16 | 17 | # Visual Studio Code files 18 | /.vscode 19 | *.code-workspace 20 | 21 | # OSX files 22 | .DS_Store 23 | 24 | # CLion files 25 | /.idea 26 | cmake-build-debug 27 | 28 | # vi files 29 | .clang_complete 30 | .syntastic_cpp_config 31 | 32 | # Python files 33 | *.pyc 34 | /.pytest_cache -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Libraries/doctest"] 2 | path = Libraries/doctest 3 | url = https://github.com/onqtam/doctest 4 | [submodule "Libraries/random"] 5 | path = Libraries/random 6 | url = https://github.com/effolkronium/random 7 | [submodule "Libraries/pybind11"] 8 | path = Libraries/pybind11 9 | url = https://github.com/pybind/pybind11 10 | [submodule "Libraries/Lyra"] 11 | path = Libraries/Lyra 12 | url = https://github.com/bfgroup/Lyra 13 | [submodule "Libraries/tabulate"] 14 | path = Libraries/tabulate 15 | url = https://github.com/p-ranav/tabulate 16 | [submodule "Libraries/magic_enum"] 17 | path = Libraries/magic_enum 18 | url = https://github.com/Neargye/magic_enum 19 | -------------------------------------------------------------------------------- /.lgtm.yml: -------------------------------------------------------------------------------- 1 | extraction: 2 | cpp: 3 | configure: 4 | command: 5 | - mkdir _lgtm_build_dir 6 | - cd _lgtm_build_dir 7 | - cmake -DBUILD_GTEST=OFF -DINSTALL_GTEST=OFF .. 8 | index: 9 | build_command: 10 | - cd _lgtm_build_dir 11 | - make 12 | python: 13 | python_setup: 14 | version: 3 15 | 16 | path_classifiers: 17 | library: 18 | - Libraries 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | matrix: 4 | include: 5 | - name: Test Ubuntu 16.04 + gcc-7 + codecov + (sonarcloud) 6 | os: linux 7 | addons: 8 | apt: 9 | sources: 10 | - deadsnakes 11 | - ubuntu-toolchain-r-test 12 | packages: 13 | - g++-7 14 | - python3.7-dev 15 | - python3.7-venv 16 | sonarcloud: 17 | organization: "utilforever-github" 18 | token: 19 | secure: "skWCO87jlcLCK1wIvdZ/zJZDIHEtXxZ3Lu52FpwbUBumE/SMr9pLTsXOOfPpRHq6tZ13/3MEhQgcl15OgHuBZx+jDTyl8rnjM+HX1J/t/573nI8oo8obejvn62XP5geb6TkL9lPJvvwpYr23fuPsDYCzmcLAhOqsUBNwle9Fcp/zUm4sIbwktm7v7iHsCgmN4ruXPzHlnzIJ1wuaLorpQN9wXNykMJjjJYxBcevi33P84oMTSEh3YIjJL5UcAdRJoNYyHxXJO0i/SIkB8XDNgNtrTGlMr45aubDvdIlIVtlw/vmuMWbIQdN2CWVTKR6HWd4D9WhV4twLKgpapI1ylfJp2Aq6GelmjWtEmkzlTGflQFwjQ/91KrZUs0AJrf3O1O3TDO2+xy2DYwA/Cle/t9jBrcaimczQ5ZmHOlUv5G62Of6PVhEzleRIQTPASTHJ/sGVXDpoDknGGMY7Cxn1Y+rINAQUaCeursY1cMrx7SK4lHyOJcurGihHIdmzXrh+hlZAelM/7iHR8caf7zNgUNZoHzoxdnH86k47uimf8gyczWW7Vdu4KD+YB9Wp7QgYFntm8ZHE+C8yUy6jUOtmXlF+ZFuTRv7e9Vqqa/gswH4h5HrS4gUf7VsclZrXfNC/8xlpdVSGUbWkJXO2Y438IjeJvIGtynMx5bJ3qVlwH2k=" 20 | dist: xenial 21 | sudo: required 22 | compiler: gcc 23 | env: 24 | - BUILD_TYPE=Debug 25 | script: 26 | - | 27 | if [[ -n "${TRAVIS_PULL_REQUEST_SLUG}" && "${TRAVIS_PULL_REQUEST_SLUG}" != "${TRAVIS_REPO_SLUG}" ]]; then 28 | echo "The pull request from ${TRAVIS_PULL_REQUEST_SLUG} is an EXTERNAL pull request. Skip sonar analysis." 29 | sh Scripts/travis_build_codecov.sh 30 | else 31 | sh Scripts/travis_build_codecov_sonar.sh 32 | fi 33 | - name: Test Docker based on Ubuntu 20.04 LTS + gcc 34 | os: linux 35 | dist: trusty 36 | sudo: required 37 | services: docker 38 | script: 39 | - sh Scripts/travis_build_docker.sh Scripts/Dockerfile.focal focal 40 | - name: Test Docker based on Ubuntu 20.04 LTS + gcc-latest 41 | os: linux 42 | dist: trusty 43 | sudo: required 44 | services: docker 45 | script: 46 | - sh Scripts/travis_build_docker.sh Scripts/Dockerfile.focal.gcc-latest focal-gcc-latest 47 | - name: Test Docker based on Ubuntu 20.04 LTS + clang-latest 48 | os: linux 49 | dist: trusty 50 | sudo: required 51 | services: docker 52 | script: 53 | - sh Scripts/travis_build_docker.sh Scripts/Dockerfile.focal.clang-latest focal-clang-latest 54 | - name: Test OS X 10.14 + Xcode 10.2 + clang 55 | os: osx 56 | osx_image: xcode10.2 57 | compiler: clang 58 | script: 59 | - sh Scripts/travis_build.sh 60 | before_install: 61 | - eval "${MATRIX_EVAL}" -------------------------------------------------------------------------------- /CMake/CodeCoverage.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # 2012-01-31, Lars Bilke 3 | # - Enable Code Coverage 4 | # 5 | # 2013-09-17, Joakim Söderberg 6 | # - Added support for Clang. 7 | # - Some additional usage instructions. 8 | # 9 | # USAGE: 10 | 11 | # 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here: 12 | # http://stackoverflow.com/a/22404544/80480 13 | # 14 | # 1. Copy this file into your cmake modules path. 15 | # 16 | # 2. Add the following line to your CMakeLists.txt: 17 | # INCLUDE(CodeCoverage) 18 | # 19 | # 3. Set compiler flags to turn off optimization and enable coverage: 20 | # SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") 21 | # SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage") 22 | # 23 | # 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target 24 | # which runs your test executable and produces a lcov code coverage report: 25 | # Example: 26 | # SETUP_TARGET_FOR_COVERAGE( 27 | # my_coverage_target # Name for custom target. 28 | # test_driver # Name of the test driver executable that runs the tests. 29 | # # NOTE! This should always have a ZERO as exit code 30 | # # otherwise the coverage generation will not complete. 31 | # coverage # Name of output directory. 32 | # ) 33 | # 34 | # 4. Build a Debug build: 35 | # cmake -DCMAKE_BUILD_TYPE=Debug .. 36 | # make 37 | # make my_coverage_target 38 | # 39 | # 40 | 41 | # Check prereqs 42 | FIND_PROGRAM( GCOV_PATH gcov ) 43 | FIND_PROGRAM( LCOV_PATH lcov ) 44 | FIND_PROGRAM( GENHTML_PATH genhtml ) 45 | FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests) 46 | 47 | IF(NOT GCOV_PATH) 48 | MESSAGE(FATAL_ERROR "gcov not found! Aborting...") 49 | ENDIF() # NOT GCOV_PATH 50 | 51 | IF(NOT CMAKE_COMPILER_IS_GNUCXX) 52 | # Clang version 3.0.0 and greater now supports gcov as well. 53 | MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.") 54 | 55 | IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") 56 | MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") 57 | ENDIF() 58 | ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX 59 | 60 | SET(CMAKE_CXX_FLAGS_COVERAGE 61 | "-g -O0 --coverage -fprofile-arcs -ftest-coverage" 62 | CACHE STRING "Flags used by the C++ compiler during coverage builds." 63 | FORCE ) 64 | SET(CMAKE_C_FLAGS_COVERAGE 65 | "-g -O0 --coverage -fprofile-arcs -ftest-coverage" 66 | CACHE STRING "Flags used by the C compiler during coverage builds." 67 | FORCE ) 68 | SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE 69 | "" 70 | CACHE STRING "Flags used for linking binaries during coverage builds." 71 | FORCE ) 72 | SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE 73 | "" 74 | CACHE STRING "Flags used by the shared libraries linker during coverage builds." 75 | FORCE ) 76 | MARK_AS_ADVANCED( 77 | CMAKE_CXX_FLAGS_COVERAGE 78 | CMAKE_C_FLAGS_COVERAGE 79 | CMAKE_EXE_LINKER_FLAGS_COVERAGE 80 | CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) 81 | 82 | IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage")) 83 | MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) 84 | ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" 85 | 86 | 87 | # Param _targetname The name of new the custom make target 88 | # Param _testrunner The name of the target which runs the tests. 89 | # MUST return ZERO always, even on errors. 90 | # If not, no coverage report will be created! 91 | # Param _outputname lcov output is generated as _outputname.info 92 | # HTML report is generated in _outputname/index.html 93 | # Optional fourth parameter is passed as arguments to _testrunner 94 | # Pass them in list form, e.g.: "-j;2" for -j 2 95 | FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname) 96 | 97 | IF(NOT LCOV_PATH) 98 | MESSAGE(FATAL_ERROR "lcov not found! Aborting...") 99 | ENDIF() # NOT LCOV_PATH 100 | 101 | IF(NOT GENHTML_PATH) 102 | MESSAGE(FATAL_ERROR "genhtml not found! Aborting...") 103 | ENDIF() # NOT GENHTML_PATH 104 | 105 | # Setup target 106 | ADD_CUSTOM_TARGET(${_targetname} 107 | 108 | # Cleanup lcov 109 | ${LCOV_PATH} --directory . --zerocounters 110 | 111 | # Run tests 112 | COMMAND ${_testrunner} ${ARGV3} 113 | 114 | # Capturing lcov counters and generating report 115 | COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info 116 | COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'build/*' 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned 117 | COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned 118 | COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned 119 | 120 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 121 | COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." 122 | ) 123 | 124 | # Show info where to find the report 125 | ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD 126 | COMMAND ; 127 | COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report." 128 | ) 129 | 130 | ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE 131 | 132 | # Param _targetname The name of new the custom make target 133 | # Param _testrunner The name of the target which runs the tests 134 | # Param _outputname cobertura output is generated as _outputname.xml 135 | # Optional fourth parameter is passed as arguments to _testrunner 136 | # Pass them in list form, e.g.: "-j;2" for -j 2 137 | FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname) 138 | 139 | IF(NOT PYTHON_EXECUTABLE) 140 | MESSAGE(FATAL_ERROR "Python not found! Aborting...") 141 | ENDIF() # NOT PYTHON_EXECUTABLE 142 | 143 | IF(NOT GCOVR_PATH) 144 | MESSAGE(FATAL_ERROR "gcovr not found! Aborting...") 145 | ENDIF() # NOT GCOVR_PATH 146 | 147 | ADD_CUSTOM_TARGET(${_targetname} 148 | 149 | # Run tests 150 | ${_testrunner} ${ARGV3} 151 | 152 | # Running gcovr 153 | COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/' -e '${CMAKE_SOURCE_DIR}/build/' -o ${_outputname}.xml 154 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 155 | COMMENT "Running gcovr to produce Cobertura code coverage report." 156 | ) 157 | 158 | # Show info where to find the report 159 | ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD 160 | COMMAND ; 161 | COMMENT "Cobertura code coverage report saved in ${_outputname}.xml." 162 | ) 163 | 164 | ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA -------------------------------------------------------------------------------- /CMake/CompileOptions.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Platform and architecture setup 3 | # 4 | 5 | # Set warnings as errors flag 6 | option(ROSETTASTONE_WARNINGS_AS_ERRORS "Treat all warnings as errors" ON) 7 | if(ROSETTASTONE_WARNINGS_AS_ERRORS) 8 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 9 | set(WARN_AS_ERROR_FLAGS "/WX") 10 | else() 11 | set(WARN_AS_ERROR_FLAGS "-Werror") 12 | endif() 13 | endif() 14 | 15 | # Get upper case system name 16 | string(TOUPPER ${CMAKE_SYSTEM_NAME} SYSTEM_NAME_UPPER) 17 | 18 | # Determine architecture (32/64 bit) 19 | set(X64 OFF) 20 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 21 | set(X64 ON) 22 | endif() 23 | 24 | # 25 | # Project options 26 | # 27 | 28 | set(DEFAULT_PROJECT_OPTIONS 29 | CXX_STANDARD 17 # Not available before CMake 3.8.2; see below for manual command line argument addition 30 | LINKER_LANGUAGE "CXX" 31 | POSITION_INDEPENDENT_CODE ON 32 | ) 33 | 34 | # 35 | # Include directories 36 | # 37 | 38 | set(DEFAULT_INCLUDE_DIRECTORIES) 39 | 40 | # 41 | # Libraries 42 | # 43 | 44 | set(DEFAULT_LIBRARIES) 45 | 46 | # 47 | # Compile definitions 48 | # 49 | 50 | set(DEFAULT_COMPILE_DEFINITIONS 51 | SYSTEM_${SYSTEM_NAME_UPPER} 52 | ) 53 | 54 | # MSVC compiler options 55 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 56 | set(DEFAULT_COMPILE_DEFINITIONS ${DEFAULT_COMPILE_DEFINITIONS} 57 | _SCL_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the Standard C++ Library 58 | _CRT_SECURE_NO_WARNINGS # Calling any one of the potentially unsafe methods in the CRT Library 59 | ) 60 | endif () 61 | 62 | # 63 | # Compile options 64 | # 65 | 66 | set(DEFAULT_COMPILE_OPTIONS) 67 | 68 | # MSVC compiler options 69 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 70 | # remove default warning level from CMAKE_CXX_FLAGS 71 | string (REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 72 | endif() 73 | 74 | # MSVC compiler options 75 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 76 | set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} 77 | /MP # -> build with multiple processes 78 | /W4 # -> warning level 3 79 | ${WARN_AS_ERROR_FLAGS} 80 | 81 | /wd4819 # -> disable warning: The file contains a character that cannot be represented in the current code page (949) (caused by pybind11) 82 | /wd4100 # -> disable warning: Unreferenced formal parameter (caused by lyra) 83 | /wd4458 # -> disable warning: Declaration of 'description' hides class member (caused by lyra) 84 | /wd4456 # -> disable warning: Declaration of 'word_wrapped_text' hides previous local declaration (caused by tabulate) 85 | 86 | #$<$: 87 | #/RTCc # -> value is assigned to a smaller data type and results in a data loss 88 | #> 89 | 90 | $<$: 91 | /Gw # -> whole program global optimization 92 | /GS- # -> buffer security check: no 93 | /GL # -> whole program optimization: enable link-time code generation (disables Zi) 94 | /GF # -> enable string pooling 95 | > 96 | 97 | # No manual c++11 enable for MSVC as all supported MSVC versions for cmake-init have C++11 implicitly enabled (MSVC >=2013) 98 | ) 99 | endif () 100 | 101 | # GCC and Clang compiler options 102 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 103 | set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} 104 | -Wall 105 | -Wno-missing-braces 106 | -Wno-register # -> disable warning: ISO c++1z does not allow 'register' storage class specifier [-wregister] (caused by pybind11) 107 | -Wno-error=register # -> disable warning: ISO c++1z does not allow 'register' storage class specifier [-wregister] (caused by pybind11) 108 | 109 | ${WARN_AS_ERROR_FLAGS} 110 | -std=c++1z 111 | ) 112 | endif () 113 | 114 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 115 | set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} 116 | -Wno-int-in-bool-context 117 | ) 118 | endif () 119 | 120 | # Prevent "no matching function for call to 'operator delete'" error 121 | # https://github.com/pybind/pybind11/issues/1604 122 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 123 | set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} 124 | -fsized-deallocation 125 | ) 126 | endif () 127 | 128 | # 129 | # Linker options 130 | # 131 | 132 | set(DEFAULT_LINKER_OPTIONS) 133 | 134 | # Use pthreads on mingw and linux 135 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_SYSTEM_NAME MATCHES "Linux") 136 | set(DEFAULT_LINKER_OPTIONS 137 | -pthread 138 | -lstdc++fs 139 | ) 140 | endif() 141 | 142 | # Code coverage - Debug only 143 | # NOTE: Code coverage results with an optimized (non-Debug) build may be misleading 144 | if (CMAKE_BUILD_TYPE MATCHES Debug AND (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")) 145 | set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} 146 | -g 147 | -O0 148 | -fprofile-arcs 149 | -ftest-coverage 150 | ) 151 | 152 | set(DEFAULT_LINKER_OPTIONS ${DEFAULT_LINKER_OPTIONS} 153 | -fprofile-arcs 154 | -ftest-coverage 155 | ) 156 | endif() -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake version 2 | cmake_minimum_required(VERSION 3.8.2 FATAL_ERROR) 3 | 4 | # Include cmake modules 5 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") 6 | 7 | # Declare project 8 | project(YahtzeeMaster) 9 | 10 | # Set output directories 11 | set(DEFAULT_CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) 12 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) 13 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 14 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 15 | 16 | # Set enable output of compile commands during generation 17 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 18 | 19 | # Includes 20 | include_directories(Includes) 21 | include_directories(Extensions/YahtzeePython/Includes) 22 | include_directories(Libraries) 23 | include_directories(Libraries/doctest/doctest) 24 | include_directories(Libraries/Lyra/include) 25 | include_directories(Libraries/magic_enum/include) 26 | include_directories(Libraries/pybind11/include) 27 | include_directories(Libraries/random/include) 28 | include_directories(Libraries/tabulate/include) 29 | 30 | # Compile options 31 | include(CMake/CompileOptions.cmake) 32 | 33 | # Build type - Release by default 34 | if(NOT CMAKE_BUILD_TYPE) 35 | set(CMAKE_BUILD_TYPE Release) 36 | endif() 37 | 38 | # Overrides 39 | set(CMAKE_MACOSX_RPATH ON) 40 | 41 | # Project modules 42 | add_subdirectory(Libraries/doctest) 43 | add_subdirectory(Sources/YahtzeeMaster) 44 | add_subdirectory(Tests/UnitTests) 45 | add_subdirectory(Extensions/YahtzeeConsole) 46 | 47 | # Code coverage - Debug only 48 | # NOTE: Code coverage results with an optimized (non-Debug) build may be misleading 49 | option(BUILD_COVERAGE "Build code coverage" OFF) 50 | if (CMAKE_BUILD_TYPE MATCHES Debug AND CMAKE_COMPILER_IS_GNUCXX AND BUILD_COVERAGE) 51 | include(CodeCoverage) 52 | setup_target_for_coverage(${PROJECT_NAME}_coverage UnitTests coverage) 53 | endif() 54 | 55 | add_subdirectory(Libraries/pybind11) 56 | if (BUILD_FROM_PIP) 57 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${DEFAULT_CMAKE_LIBRARY_OUTPUT_DIRECTORY}) 58 | endif() 59 | add_subdirectory(Extensions/YahtzeePython) -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x86-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "buildRoot": "${workspaceRoot}\\build\\${name}", 8 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", 9 | "cmakeCommandArgs": "", 10 | "buildCommandArgs": "-v", 11 | "ctestCommandArgs": "", 12 | "inheritEnvironments": [ "msvc_x86" ], 13 | "variables": [] 14 | }, 15 | { 16 | "name": "x86-Release", 17 | "generator": "Ninja", 18 | "configurationType": "RelWithDebInfo", 19 | "buildRoot": "${workspaceRoot}\\build\\${name}", 20 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", 21 | "cmakeCommandArgs": "", 22 | "buildCommandArgs": "-v", 23 | "ctestCommandArgs": "", 24 | "inheritEnvironments": [ "msvc_x86" ], 25 | "variables": [] 26 | }, 27 | { 28 | "name": "x64-Debug", 29 | "generator": "Ninja", 30 | "configurationType": "Debug", 31 | "buildRoot": "${workspaceRoot}\\build\\${name}", 32 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", 33 | "cmakeCommandArgs": "", 34 | "buildCommandArgs": "-v", 35 | "ctestCommandArgs": "", 36 | "inheritEnvironments": [ "msvc_x64_x64" ], 37 | "variables": [] 38 | }, 39 | { 40 | "name": "x64-Release", 41 | "generator": "Ninja", 42 | "configurationType": "RelWithDebInfo", 43 | "buildRoot": "${workspaceRoot}\\build\\${name}", 44 | "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", 45 | "cmakeCommandArgs": "", 46 | "buildCommandArgs": "-v", 47 | "ctestCommandArgs": "", 48 | "inheritEnvironments": [ "msvc_x64_x64" ], 49 | "variables": [] 50 | } 51 | ] 52 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | LABEL maintainer "Chris Ohk " 3 | 4 | RUN apt-get update && apt-get install -y \ 5 | build-essential \ 6 | python-dev \ 7 | python-pip \ 8 | python3-dev \ 9 | python3-pip \ 10 | python3-venv \ 11 | python3-setuptools \ 12 | cmake \ 13 | --no-install-recommends \ 14 | && rm -rf /var/lib/apt/lists/* 15 | 16 | COPY . /app 17 | 18 | WORKDIR /app/build 19 | RUN cmake .. && \ 20 | make -j "$(nproc)" && \ 21 | make install && \ 22 | bin/UnitTests 23 | 24 | WORKDIR / -------------------------------------------------------------------------------- /Extensions/YahtzeeConsole/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Target name 2 | set(target YahtzeeConsole) 3 | 4 | # Includes 5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 6 | 7 | # Sources 8 | file(GLOB sources 9 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 10 | 11 | # Build executable 12 | add_executable(${target} 13 | ${sources}) 14 | 15 | # Project options 16 | set_target_properties(${target} 17 | PROPERTIES 18 | ${DEFAULT_PROJECT_OPTIONS} 19 | ) 20 | 21 | # Compile options 22 | target_compile_options(${target} 23 | PRIVATE 24 | 25 | PUBLIC 26 | ${DEFAULT_COMPILE_OPTIONS} 27 | 28 | INTERFACE 29 | ) 30 | 31 | # Link libraries 32 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 33 | target_link_libraries(${target} 34 | PRIVATE 35 | ${DEFAULT_LINKER_OPTIONS} 36 | YahtzeeMaster) 37 | else() 38 | target_link_libraries(${target} 39 | PRIVATE 40 | ${DEFAULT_LINKER_OPTIONS} 41 | YahtzeeMaster) 42 | endif() -------------------------------------------------------------------------------- /Extensions/YahtzeeConsole/Console.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include "Console.hpp" 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | namespace YahtzeeMaster 15 | { 16 | Console::Console(Mode mode) : m_mode{ mode } 17 | { 18 | // Do nothing 19 | } 20 | 21 | void Console::ProcessGame() 22 | { 23 | if (m_mode == Mode::SINGLE_HUMAN || m_mode == Mode::SINGLE_COMPUTER) 24 | { 25 | m_game = std::make_unique(GameConfig{ 1 }); 26 | } 27 | else 28 | { 29 | m_game = std::make_unique(GameConfig{ 2 }); 30 | } 31 | 32 | m_game->Start(); 33 | 34 | for (std::size_t i = 0; i < NUM_CATEGORIES; ++i) 35 | { 36 | switch (m_mode) 37 | { 38 | case Mode::SINGLE_HUMAN: 39 | PlayHumanTurn(); 40 | break; 41 | case Mode::SINGLE_COMPUTER: 42 | PlayComputerTurn(); 43 | break; 44 | case Mode::HUMAN_VS_COMPUTER: 45 | PlayHumanTurn(); 46 | PlayComputerTurn(); 47 | break; 48 | case Mode::COMPUTER_VS_COMPUTER: 49 | PlayComputerTurn(); 50 | PlayComputerTurn(); 51 | break; 52 | } 53 | } 54 | 55 | ShowResult(); 56 | } 57 | 58 | void Console::PlayHumanTurn() const 59 | { 60 | ShowScoreCard(); 61 | 62 | Player& player = m_game->GetCurrentPlayer(); 63 | std::cout << "Round " << m_game->GetGameState().curRound << '\n'; 64 | std::cout << m_game->GetGameState().curPlayerIdx + 1 << "P's turn\n"; 65 | 66 | std::vector rerollVals; 67 | rerollVals.reserve(NUM_DICES); 68 | 69 | for (int i = 0; i < NUM_ROLLS; ++i) 70 | { 71 | player.RollDices(i == 0 ? true : false, rerollVals); 72 | ShowDiceValues(); 73 | 74 | if (i < NUM_ROLLS - 1) 75 | { 76 | rerollVals = ProcessReroll(); 77 | } 78 | } 79 | 80 | ChooseCategory(); 81 | } 82 | 83 | void Console::PlayComputerTurn() 84 | { 85 | // TBD 86 | } 87 | 88 | void Console::ShowDiceValues() const 89 | { 90 | Player& player = m_game->GetCurrentPlayer(); 91 | 92 | std::cout << "Your dice values: "; 93 | std::array diceVals = player.GetDiceValues(); 94 | for (const auto& val : diceVals) 95 | { 96 | std::cout << val << " "; 97 | } 98 | std::cout << '\n'; 99 | std::cout << "Remain reroll: " << player.GetRemainReroll() << "\n"; 100 | } 101 | 102 | std::vector Console::ProcessReroll() 103 | { 104 | int numDicesReroll; 105 | 106 | std::cout << "How many dice are you going to reroll? "; 107 | std::cin >> numDicesReroll; 108 | 109 | std::vector rerollVals; 110 | rerollVals.reserve(numDicesReroll); 111 | 112 | if (numDicesReroll > 0) 113 | { 114 | std::cout << "Select dice values to reroll: "; 115 | for (int i = 0; i < numDicesReroll; ++i) 116 | { 117 | int val; 118 | std::cin >> val; 119 | 120 | rerollVals.emplace_back(val); 121 | } 122 | } 123 | 124 | return rerollVals; 125 | } 126 | 127 | void Console::ChooseCategory() const 128 | { 129 | ShowScoresByDice(); 130 | 131 | std::string categoryName; 132 | 133 | while (true) 134 | { 135 | Player& player = m_game->GetCurrentPlayer(); 136 | 137 | std::cout << "Select category: "; 138 | std::cin >> categoryName; 139 | 140 | auto category = magic_enum::enum_cast(categoryName); 141 | if (category.has_value()) 142 | { 143 | if (player.GetScoreCard().IsFilled(category.value())) 144 | { 145 | std::cout << categoryName << " is already filled!\n"; 146 | } 147 | else 148 | { 149 | player.FillScoreCard(category.value()); 150 | break; 151 | } 152 | } 153 | else 154 | { 155 | std::cout << "Invalid category!\n"; 156 | } 157 | } 158 | } 159 | 160 | void Console::ShowScoreCard() const 161 | { 162 | tabulate::Table table; 163 | 164 | std::vector> title; 165 | title.reserve(NUM_CATEGORIES + 3); 166 | 167 | title.emplace_back(""); 168 | for (int i = 0; i < NUM_UPPER_CATEGORIES; ++i) 169 | { 170 | const auto category = static_cast(i); 171 | title.emplace_back(std::string{ magic_enum::enum_name(category) }); 172 | } 173 | title.emplace_back("BONUS"); 174 | for (int i = NUM_UPPER_CATEGORIES; i < NUM_CATEGORIES; ++i) 175 | { 176 | const auto category = static_cast(i); 177 | title.emplace_back(std::string{ magic_enum::enum_name(category) }); 178 | } 179 | title.emplace_back("TOTAL"); 180 | table.add_row(title); 181 | 182 | const std::size_t numPlayers = m_game->GetNumPlayers(); 183 | 184 | for (std::size_t i = 0; i < numPlayers; ++i) 185 | { 186 | Player& player = m_game->GetPlayer(i); 187 | const ScoreCard& scoreCard = player.GetScoreCard(); 188 | 189 | std::vector> scores; 190 | scores.reserve(NUM_CATEGORIES + 3); 191 | 192 | scores.emplace_back(std::to_string(i + 1) + "P"); 193 | for (int j = 0; j < NUM_UPPER_CATEGORIES; ++j) 194 | { 195 | scores.emplace_back( 196 | std::to_string(scoreCard.GetScore(static_cast(j)))); 197 | } 198 | scores.emplace_back(std::to_string(scoreCard.GetUpperCategoryScore()) + "/" + 199 | std::to_string(UPPER_SECTION_BONUS_CONDITION)); 200 | for (int j = NUM_UPPER_CATEGORIES; j < NUM_CATEGORIES; ++j) 201 | { 202 | scores.emplace_back( 203 | std::to_string(scoreCard.GetScore(static_cast(j)))); 204 | } 205 | scores.emplace_back(std::to_string(scoreCard.GetTotalScore())); 206 | table.add_row(scores); 207 | } 208 | 209 | for (int i = 0; i < NUM_UPPER_CATEGORIES; ++i) 210 | { 211 | table[0][i + 1].format().font_color(tabulate::Color::yellow); 212 | } 213 | table[0][NUM_UPPER_CATEGORIES + 1].format().font_color(tabulate::Color::blue); 214 | for (int i = NUM_UPPER_CATEGORIES; i < NUM_CATEGORIES; ++i) 215 | { 216 | table[0][i + 2].format().font_color(tabulate::Color::cyan); 217 | } 218 | table[0][NUM_CATEGORIES + 2].format().font_color(tabulate::Color::blue); 219 | 220 | for (std::size_t i = 1; i <= numPlayers; ++i) 221 | { 222 | table[i][0] 223 | .format() 224 | .font_color(tabulate::Color::magenta) 225 | .font_align(tabulate::FontAlign::center); 226 | } 227 | 228 | for (std::size_t i = 1; i <= numPlayers; ++i) 229 | { 230 | Player& player = m_game->GetPlayer(i - 1); 231 | const ScoreCard& scoreCard = player.GetScoreCard(); 232 | tabulate::Color color; 233 | 234 | for (int j = 0; j < NUM_UPPER_CATEGORIES; ++j) 235 | { 236 | color = scoreCard.IsFilled(static_cast(j)) ? tabulate::Color::red 237 | : tabulate::Color::green; 238 | 239 | table[i][j + 1].format().font_color(color).font_align( 240 | tabulate::FontAlign::center); 241 | } 242 | color = scoreCard.GetUpperCategoryScore() > UPPER_SECTION_BONUS_CONDITION 243 | ? tabulate::Color::green 244 | : tabulate::Color::red; 245 | table[i][NUM_UPPER_CATEGORIES + 1].format().font_color(color).font_align( 246 | tabulate::FontAlign::center); 247 | for (int j = NUM_UPPER_CATEGORIES; j < NUM_CATEGORIES; ++j) 248 | { 249 | color = scoreCard.IsFilled(static_cast(j)) ? tabulate::Color::red 250 | : tabulate::Color::green; 251 | table[i][j + 2].format().font_color(color).font_align( 252 | tabulate::FontAlign::center); 253 | } 254 | table[i][NUM_CATEGORIES + 2] 255 | .format() 256 | .font_color(tabulate::Color::white) 257 | .font_align(tabulate::FontAlign::center); 258 | } 259 | 260 | std::cout << table << std::endl; 261 | } 262 | 263 | void Console::ShowScoresByDice() const 264 | { 265 | Player& player = m_game->GetCurrentPlayer(); 266 | const ScoreCard& scoreCard = player.GetScoreCard(); 267 | std::array diceScores = player.GetScores(); 268 | 269 | tabulate::Table table; 270 | 271 | std::vector> title; 272 | title.reserve(NUM_CATEGORIES + 2); 273 | 274 | for (int i = 0; i < NUM_UPPER_CATEGORIES; ++i) 275 | { 276 | const auto category = static_cast(i); 277 | title.emplace_back(std::string{ magic_enum::enum_name(category) }); 278 | } 279 | title.emplace_back("BONUS"); 280 | for (int i = NUM_UPPER_CATEGORIES; i < NUM_CATEGORIES; ++i) 281 | { 282 | const auto category = static_cast(i); 283 | title.emplace_back(std::string{ magic_enum::enum_name(category) }); 284 | } 285 | title.emplace_back("TOTAL"); 286 | table.add_row(title); 287 | 288 | std::vector> scores; 289 | scores.reserve(NUM_CATEGORIES + 2); 290 | 291 | for (int i = 0; i < NUM_UPPER_CATEGORIES; ++i) 292 | { 293 | const auto category = static_cast(i); 294 | 295 | if (scoreCard.IsFilled(category)) 296 | { 297 | scores.emplace_back(std::to_string(scoreCard.GetScore(category))); 298 | } 299 | else 300 | { 301 | scores.emplace_back(std::to_string(diceScores[i])); 302 | } 303 | } 304 | scores.emplace_back(std::to_string(scoreCard.GetUpperCategoryScore()) + "/" + 305 | std::to_string(UPPER_SECTION_BONUS_CONDITION)); 306 | for (int i = NUM_UPPER_CATEGORIES; i < NUM_CATEGORIES; ++i) 307 | { 308 | const auto category = static_cast(i); 309 | 310 | if (scoreCard.IsFilled(category)) 311 | { 312 | scores.emplace_back(std::to_string(scoreCard.GetScore(category))); 313 | } 314 | else 315 | { 316 | scores.emplace_back(std::to_string(diceScores[i])); 317 | } 318 | } 319 | scores.emplace_back(std::to_string(scoreCard.GetTotalScore())); 320 | table.add_row(scores); 321 | 322 | for (int i = 0; i < NUM_UPPER_CATEGORIES; ++i) 323 | { 324 | table[0][i].format().font_color(tabulate::Color::yellow); 325 | } 326 | table[0][NUM_UPPER_CATEGORIES].format().font_color(tabulate::Color::blue); 327 | for (int i = NUM_UPPER_CATEGORIES; i < NUM_CATEGORIES; ++i) 328 | { 329 | table[0][i + 1].format().font_color(tabulate::Color::cyan); 330 | } 331 | table[0][NUM_CATEGORIES + 1].format().font_color(tabulate::Color::blue); 332 | 333 | tabulate::Color color; 334 | 335 | for (int i = 0; i < NUM_UPPER_CATEGORIES; ++i) 336 | { 337 | color = scoreCard.IsFilled(static_cast(i)) ? tabulate::Color::red 338 | : tabulate::Color::green; 339 | 340 | table[1][i].format().font_color(color).font_align(tabulate::FontAlign::center); 341 | } 342 | color = scoreCard.GetUpperCategoryScore() > UPPER_SECTION_BONUS_CONDITION 343 | ? tabulate::Color::green 344 | : tabulate::Color::red; 345 | table[1][NUM_UPPER_CATEGORIES].format().font_color(color).font_align( 346 | tabulate::FontAlign::center); 347 | for (int i = NUM_UPPER_CATEGORIES; i < NUM_CATEGORIES; ++i) 348 | { 349 | color = scoreCard.IsFilled(static_cast(i)) ? tabulate::Color::red 350 | : tabulate::Color::green; 351 | table[1][i + 1].format().font_color(color).font_align( 352 | tabulate::FontAlign::center); 353 | } 354 | table[1][NUM_CATEGORIES + 1] 355 | .format() 356 | .font_color(tabulate::Color::white) 357 | .font_align(tabulate::FontAlign::center); 358 | 359 | std::cout << table << std::endl; 360 | } 361 | 362 | void Console::ShowResult() const 363 | { 364 | ShowScoreCard(); 365 | 366 | const std::size_t numPlayers = m_game->GetNumPlayers(); 367 | 368 | for (std::size_t i = 0; i < numPlayers; ++i) 369 | { 370 | if (m_game->GetPlayer(i).result == Result::WON) 371 | { 372 | std::cout << "Player " << (i + 1) << " Won!\n"; 373 | break; 374 | } 375 | } 376 | } 377 | } // namespace YahtzeeMaster -------------------------------------------------------------------------------- /Extensions/YahtzeeConsole/Console.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_CONSOLE_HPP 8 | #define YAHTZEE_MASTER_CONSOLE_HPP 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace YahtzeeMaster 15 | { 16 | class Console 17 | { 18 | public: 19 | enum class Mode 20 | { 21 | SINGLE_HUMAN, 22 | SINGLE_COMPUTER, 23 | HUMAN_VS_COMPUTER, 24 | COMPUTER_VS_COMPUTER 25 | }; 26 | 27 | //! Constructs a Console instance with \p mode. 28 | //! \param mode The game mode such as single and versus. 29 | explicit Console(Mode mode); 30 | 31 | //! Process a Yahtzee game. 32 | void ProcessGame(); 33 | 34 | private: 35 | //! Plays a turn for human. 36 | void PlayHumanTurn() const; 37 | 38 | //! Plays a turn for computer. 39 | void PlayComputerTurn(); 40 | 41 | // Shows a list of dice values. 42 | void ShowDiceValues() const; 43 | 44 | //! Processes a reroll. 45 | static std::vector ProcessReroll(); 46 | 47 | //! Chooses a category to record on the score card. 48 | void ChooseCategory() const; 49 | 50 | //! Shows a score card. 51 | void ShowScoreCard() const; 52 | 53 | //! Shows a list of scores by dice. 54 | void ShowScoresByDice() const; 55 | 56 | //! Shows result. 57 | void ShowResult() const; 58 | 59 | std::unique_ptr m_game; 60 | 61 | Mode m_mode; 62 | }; 63 | } // namespace YahtzeeMaster 64 | 65 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeeConsole/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include "Console.hpp" 8 | 9 | #include 10 | 11 | using namespace YahtzeeMaster; 12 | 13 | int main(int argc, char* argv[]) 14 | { 15 | std::size_t mode = 0; 16 | bool showHelp = false; 17 | 18 | // Process CLI 19 | const auto cli = lyra::cli() | lyra::help(showHelp) | 20 | lyra::opt(mode, "mode")["-m"]["--mode"]( 21 | "The game mode. (0 - Single Human, 1 - Single Com, 2 - Human vs " 22 | "Com, 3 - Com vs Com)"); 23 | const auto result = cli.parse({ argc, argv }); 24 | 25 | if (!result) 26 | { 27 | std::cerr << "Error in command line: " << result.errorMessage() << std::endl; 28 | std::cerr << cli << std::endl; 29 | return EXIT_FAILURE; 30 | } 31 | 32 | if (showHelp) 33 | { 34 | std::cout << cli << std::endl; 35 | return EXIT_SUCCESS; 36 | } 37 | 38 | if (mode != 0) 39 | { 40 | std::cerr << "Sorry, the console supports single human mode only." << std::endl; 41 | std::cerr << "I'll implement other modes ASAP." << std::endl; 42 | return EXIT_SUCCESS; 43 | } 44 | 45 | Console console{ static_cast(mode) }; 46 | console.ProcessGame(); 47 | 48 | return EXIT_SUCCESS; 49 | } 50 | -------------------------------------------------------------------------------- /Extensions/YahtzeePython/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Target name 2 | set(target pyYahtzee) 3 | 4 | # Define 5 | set(root_dir ${CMAKE_CURRENT_SOURCE_DIR}/../..) 6 | 7 | # Sources 8 | file(GLOB_RECURSE sources 9 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 10 | 11 | # Add Pybind11 module 12 | pybind11_add_module(${target} ${sources}) 13 | 14 | # Project options 15 | set_target_properties(${target} 16 | PROPERTIES 17 | ${DEFAULT_PROJECT_OPTIONS}) 18 | 19 | # Link libraries 20 | target_link_libraries(${target} 21 | PRIVATE 22 | ${DEFAULT_LINKER_OPTIONS} 23 | YahtzeeMaster) -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Commons/Constants.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_CONSTANTS_HPP 8 | #define YAHTZEE_MASTER_PYTHON_CONSTANTS_HPP 9 | 10 | #include 11 | 12 | void AddConstants(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Enums/GameEnums.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_GAME_ENUMS_HPP 8 | #define YAHTZEE_MASTER_PYTHON_GAME_ENUMS_HPP 9 | 10 | #include 11 | 12 | void AddGameEnums(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Games/Game.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_GAME_HPP 8 | #define YAHTZEE_MASTER_PYTHON_GAME_HPP 9 | 10 | #include 11 | 12 | void AddGame(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Games/GameConfig.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_GAME_CONFIG_HPP 8 | #define YAHTZEE_MASTER_PYTHON_GAME_CONFIG_HPP 9 | 10 | #include 11 | 12 | void AddGameConfig(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Games/GameManager.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_GAME_MANAGER_HPP 8 | #define YAHTZEE_MASTER_PYTHON_GAME_MANAGER_HPP 9 | 10 | #include 11 | 12 | void AddGameManager(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Games/GameState.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_GAME_STATE_HPP 8 | #define YAHTZEE_MASTER_PYTHON_GAME_STATE_HPP 9 | 10 | #include 11 | 12 | void AddGameState(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Models/Dice.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_DICE_HPP 8 | #define YAHTZEE_MASTER_PYTHON_DICE_HPP 9 | 10 | #include 11 | 12 | void AddDice(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Models/Player.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_PLAYER_HPP 8 | #define YAHTZEE_MASTER_PYTHON_PLAYER_HPP 9 | 10 | #include 11 | 12 | void AddPlayer(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Includes/Models/ScoreCard.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PYTHON_SCORE_CARD_HPP 8 | #define YAHTZEE_MASTER_PYTHON_SCORE_CARD_HPP 9 | 10 | #include 11 | 12 | void AddScoreCard(pybind11::module& m); 13 | 14 | #endif -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Commons/Constants.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace YahtzeeMaster; 13 | 14 | void AddConstants(pybind11::module& m) 15 | { 16 | m.attr("NUM_DICES") = pybind11::int_(NUM_DICES); 17 | m.attr("NUM_ROLLS") = pybind11::int_(NUM_ROLLS); 18 | m.attr("NUM_CATEGORIES") = pybind11::int_(NUM_CATEGORIES); 19 | m.attr("UPPER_SECTION_BONUS_CONDITION") = 20 | pybind11::int_(UPPER_SECTION_BONUS_CONDITION); 21 | m.attr("UPPER_SECTION_BONUS") = pybind11::int_(UPPER_SECTION_BONUS); 22 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Enums/GameEnums.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace YahtzeeMaster; 13 | 14 | void AddGameEnums(pybind11::module& m) 15 | { 16 | pybind11::enum_(m, "Step", 17 | R"pbdoc(An enumerator for identifying the step.)pbdoc") 18 | .value("INVALID", Step::INVALID) 19 | .value("PLAY_ROUND", Step::PLAY_ROUND) 20 | .value("COMPLETE", Step::COMPLETE) 21 | .export_values(); 22 | 23 | pybind11::enum_(m, "Category", 24 | R"pbdoc(An enumerator for identifying the category.)pbdoc") 25 | .value("ACES", Category::ACES) 26 | .value("TWOS", Category::TWOS) 27 | .value("THREES", Category::THREES) 28 | .value("FOURS", Category::FOURS) 29 | .value("FIVES", Category::FIVES) 30 | .value("SIXES", Category::SIXES) 31 | .value("THREE_OF_A_KIND", Category::THREE_OF_A_KIND) 32 | .value("FOUR_OF_A_KIND", Category::FOUR_OF_A_KIND) 33 | .value("FULL_HOUSE", Category::FULL_HOUSE) 34 | .value("SMALL_STRAIGHT", Category::SMALL_STRAIGHT) 35 | .value("LARGE_STRAIGHT", Category::LARGE_STRAIGHT) 36 | .value("YAHTZEE", Category::YAHTZEE) 37 | .value("CHANCE", Category::CHANCE) 38 | .export_values(); 39 | 40 | pybind11::enum_(m, "Result", 41 | R"pbdoc(An enumerator for identifying the result.)pbdoc") 42 | .value("INVALID", Result::INVALID) 43 | .value("WON", Result::WON) 44 | .value("LOST", Result::LOST) 45 | .export_values(); 46 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Games/Game.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace YahtzeeMaster; 14 | 15 | void AddGame(pybind11::module& m) 16 | { 17 | pybind11::class_( 18 | m, "Game", 19 | R"pbdoc(This class processes the game, Yahtzee. Yahtzee is a dice game made by Milton Bradley (now owned by Hasbro), which was first marketed as Yatzie by the National Association Service of Toledo, Ohio, in the early 1940s. It was marketed under the name of Yahtzee by game entrepreneur Edwin S. Lowe in 1956. The game is a development of earlier dice games such as Poker Dice, Yacht and Generala. It is also similar to Yatzy, which is popular in Scandinavia.)pbdoc") 20 | .def(pybind11::init(), 21 | R"pbdoc(Constructs game with given game config. 22 | 23 | Parameters 24 | ---------- 25 | - config : The game config holds all configuration values.)pbdoc", 26 | pybind11::arg("config")) 27 | .def("get_game_state", &Game::GetGameState, 28 | R"pbdoc(Returns the game state.)pbdoc") 29 | .def("get_current_player", &Game::GetCurrentPlayer, 30 | R"pbdoc(Returns the current player.)pbdoc") 31 | .def("start", &Game::Start, R"pbdoc(Starts the game.)pbdoc") 32 | .def("play_round", &Game::PlayRound, R"pbdoc(Plays round for each player.)pbdoc") 33 | .def("calculate_result", &Game::CalculateResult, 34 | R"pbdoc(Calculates the result.)pbdoc") 35 | .def_readwrite("step", &Game::step) 36 | .def_readwrite("next_step", &Game::nextStep); 37 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Games/GameConfig.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace YahtzeeMaster; 13 | 14 | void AddGameConfig(pybind11::module& m) 15 | { 16 | pybind11::class_( 17 | m, "GameConfig", 18 | R"pbdoc(This struct holds all configuration values to create a new Game instance.)pbdoc") 19 | .def(pybind11::init<>()) 20 | .def_readwrite("num_players", &GameConfig::numPlayers); 21 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Games/GameManager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace YahtzeeMaster; 13 | 14 | void AddGameManager(pybind11::module& m) 15 | { 16 | pybind11::class_( 17 | m, "GameManager", 18 | R"pbdoc(This class monitors game and invokes method when a state is changed.)pbdoc") 19 | .def(pybind11::init<>()) 20 | .def_static("process_next_step", &GameManager::ProcessNextStep, 21 | R"pbdoc(Invokes method when a state is changed. 22 | 23 | Parameters 24 | ---------- 25 | - game : The game context. 26 | - step : The next step.)pbdoc", 27 | pybind11::arg("game"), pybind11::arg("step")); 28 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Games/GameState.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace YahtzeeMaster; 14 | 15 | void AddGameState(pybind11::module& m) 16 | { 17 | pybind11::class_( 18 | m, "GameState", 19 | R"pbdoc(This struct stores the information of the game state of all players.)pbdoc") 20 | .def(pybind11::init<>()) 21 | .def_readwrite("players", &GameState::players) 22 | .def_readwrite("cur_player_idx", &GameState::curPlayerIdx) 23 | .def_readwrite("cur_round", &GameState::curRound); 24 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Models/Dice.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace YahtzeeMaster; 13 | 14 | void AddDice(pybind11::module& m) 15 | { 16 | pybind11::class_( 17 | m, "Dice", 18 | R"pbdoc(Dice (singular die or dice) are small, throwable objects with marked sides that can rest in multiple positions. They are used for generating random numbers, commonly as part of tabletop games, including dice games, board games, role-playing games, and games of chance.)pbdoc") 19 | .def(pybind11::init<>()) 20 | .def("get_value", &Dice::GetValue, R"pbdoc(Returns the value of the dice.)pbdoc") 21 | .def("set_value", &Dice::SetValue, 22 | R"pbdoc(Sets the value of the dice (for testing purposes). 23 | 24 | Parameters 25 | ---------- 26 | - value : The value of the dice.)pbdoc", 27 | pybind11::arg("value")) 28 | .def("roll", &Dice::Roll, R"pbdoc(Rolls a dice.)pbdoc"); 29 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Models/Player.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace YahtzeeMaster; 14 | 15 | void AddPlayer(pybind11::module& m) 16 | { 17 | pybind11::class_( 18 | m, "Player", 19 | R"pbdoc(This class stores various information that used in Yahtzee game.)pbdoc") 20 | .def(pybind11::init<>()) 21 | .def("initialize", &Player::Initialize, 22 | R"pbdoc(Initializes player to play each round.)pbdoc") 23 | .def("get_score_card", &Player::GetScoreCard, 24 | R"pbdoc(Returns the score card.)pbdoc") 25 | .def("get_dice_values", &Player::GetDiceValues, 26 | R"pbdoc(Returns a list of values of the dice.)pbdoc") 27 | .def("get_scores", &Player::GetScores, 28 | R"pbdoc(Returns a list of scores according to the value of dices.)pbdoc") 29 | .def("set_dice_values", &Player::SetDiceValues, 30 | R"pbdoc(Sets a list of values of the dice (for testing purposes). 31 | 32 | Parameters 33 | ---------- 34 | - dice_values : A list of values of the dice to set.)pbdoc", 35 | pybind11::arg("dice_values")) 36 | .def("roll_dices", &Player::RollDices, R"pbdoc(Rolls a list of dices. 37 | 38 | Parameters 39 | ---------- 40 | - is_first: A flag to indicate it is first roll. 41 | - reroll_values : A list of dice values to roll.)pbdoc", 42 | pybind11::arg("is_first") = false, 43 | pybind11::arg("reroll_values") = std::vector{}) 44 | .def("calculate_scores", &Player::CalculateScores, 45 | R"pbdoc(Calculates a list of scores according to the value of dices.)pbdoc") 46 | .def("fill_score_card", &Player::FillScoreCard, R"pbdoc( 47 | 48 | Parameters 49 | ---------- 50 | - category : The category to fill a score.)pbdoc", 51 | pybind11::arg("category")) 52 | .def_readwrite("result", &Player::result); 53 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/Sources/Models/ScoreCard.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | using namespace YahtzeeMaster; 14 | 15 | void AddScoreCard(pybind11::module& m) 16 | { 17 | pybind11::class_( 18 | m, "ScoreCard", 19 | R"pbdoc(The Yahtzee scorecard contains 13 different category boxes and in each round, after the third roll, the player must choose one of these categories. The score entered in the box depends on how well the five dice match the scoring rule for the category. Details of the scoring rules for each category are given below. As an example, one of the categories is called Three of a Kind. The scoring rule for this category means that a player only scores if at least three of the five dice are the same value. The game is completed after 13 rounds by each player, with each of the 13 boxes filled. The total score is calculated by summing all thirteen boxes, together with any bonuses.)pbdoc") 20 | .def(pybind11::init<>()) 21 | .def("fill_score", &ScoreCard::FillScore, R"pbdoc(Fills a score. 22 | 23 | Parameters 24 | ---------- 25 | - category : The category to fill a score. 26 | - score : The value of score.)pbdoc", 27 | pybind11::arg("category"), pybind11::arg("score")) 28 | .def("get_score", &ScoreCard::GetScore, R"pbdoc(Returns the score of category. 29 | 30 | Parameters 31 | ---------- 32 | - category : The category to get score.)pbdoc", 33 | pybind11::arg("category")) 34 | .def("get_upper_category_score", &ScoreCard::GetUpperCategoryScore, 35 | R"pbdoc(Returns the score of upper categories.)pbdoc") 36 | .def("get_total_score", &ScoreCard::GetTotalScore, 37 | R"pbdoc(Returns the total score.)pbdoc") 38 | .def_static( 39 | "is_three_of_a_kind", &ScoreCard::IsThreeOfAKind, 40 | R"pbdoc(Checks that a list of dices satisfies Three Of A Kind condition. 41 | 42 | Parameters 43 | ---------- 44 | - dice_values : A list of values of the dice.)pbdoc", 45 | pybind11::arg("dice_values")) 46 | .def_static( 47 | "is_four_of_a_kind", &ScoreCard::IsFourOfAKind, 48 | R"pbdoc(Checks that a list of dices satisfies Four Of A Kind condition. 49 | 50 | Parameters 51 | ---------- 52 | - dice_values : A list of values of the dice.)pbdoc", 53 | pybind11::arg("dice_values")) 54 | .def_static("is_full_house", &ScoreCard::IsFullHouse, 55 | R"pbdoc(Checks that a list of dices satisfies Full House condition. 56 | 57 | Parameters 58 | ---------- 59 | - dice_values : A list of values of the dice.)pbdoc", 60 | pybind11::arg("dice_values")) 61 | .def_static( 62 | "is_small_straight", &ScoreCard::IsSmallStraight, 63 | R"pbdoc(Checks that a list of dices satisfies Small Straight condition. 64 | 65 | Parameters 66 | ---------- 67 | - dice_values : A list of values of the dice.)pbdoc", 68 | pybind11::arg("dice_values")) 69 | .def_static( 70 | "is_large_straight", &ScoreCard::IsLargeStraight, 71 | R"pbdoc(Checks that a list of dices satisfies Large Straight condition. 72 | 73 | Parameters 74 | ---------- 75 | - dice_values : A list of values of the dice.)pbdoc", 76 | pybind11::arg("dice_values")) 77 | .def_static("is_yahtzee", &ScoreCard::IsYahtzee, 78 | R"pbdoc(Checks that a list of dices satisfies Yahtzee condition. 79 | 80 | Parameters 81 | ---------- 82 | - dice_values : A list of values of the dice.)pbdoc", 83 | pybind11::arg("dice_values")); 84 | } -------------------------------------------------------------------------------- /Extensions/YahtzeePython/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | PYBIND11_MODULE(pyYahtzee, m) 20 | { 21 | m.doc() = R"pbdoc(Yahtzee simulator with some reinforcement learning)pbdoc"; 22 | 23 | AddConstants(m); 24 | AddGameEnums(m); 25 | AddGame(m); 26 | AddGameConfig(m); 27 | AddGameManager(m); 28 | AddGameState(m); 29 | AddDice(m); 30 | AddPlayer(m); 31 | AddScoreCard(m); 32 | } -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Commons/Constants.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_CONSTANTS_HPP 8 | #define YAHTZEE_MASTER_CONSTANTS_HPP 9 | 10 | namespace YahtzeeMaster 11 | { 12 | //! The number of dices. 13 | constexpr int NUM_DICES = 5; 14 | 15 | //! The number of rolls. 16 | constexpr int NUM_ROLLS = 3; 17 | 18 | //! The number of categories. 19 | constexpr int NUM_CATEGORIES = 13; 20 | 21 | //! The number of upper categories. 22 | constexpr int NUM_UPPER_CATEGORIES = 6; 23 | 24 | //! The value of the upper section to gain the bonus score. 25 | constexpr int UPPER_SECTION_BONUS_CONDITION = 63; 26 | 27 | //! The bonus of the upper section. 28 | constexpr int UPPER_SECTION_BONUS = 35; 29 | } // namespace YahtzeeMaster 30 | 31 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Enums/GameEnums.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_GAME_ENUMS_HPP 8 | #define YAHTZEE_MASTER_GAME_ENUMS_HPP 9 | 10 | namespace YahtzeeMaster 11 | { 12 | //! \brief An enumerator for identifying the step. 13 | enum class Step 14 | { 15 | INVALID, 16 | PLAY_ROUND, 17 | COMPLETE 18 | }; 19 | 20 | //! \brief An enumerator for identifying the category. 21 | enum Category 22 | { 23 | ACES, 24 | TWOS, 25 | THREES, 26 | FOURS, 27 | FIVES, 28 | SIXES, 29 | THREE_OF_A_KIND, 30 | FOUR_OF_A_KIND, 31 | FULL_HOUSE, 32 | SMALL_STRAIGHT, 33 | LARGE_STRAIGHT, 34 | YAHTZEE, 35 | CHANCE 36 | }; 37 | 38 | //! \brief An enumerator for identifying the result. 39 | enum class Result 40 | { 41 | INVALID, 42 | WON, 43 | LOST, 44 | }; 45 | } // namespace YahtzeeMaster 46 | 47 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Games/Game.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_GAME_HPP 8 | #define YAHTZEE_MASTER_GAME_HPP 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace YahtzeeMaster 15 | { 16 | //! 17 | //! \brief Game class. 18 | //! 19 | //! This class processes the game, Yahtzee. Yahtzee is a dice game made by 20 | //! Milton Bradley (now owned by Hasbro), which was first marketed as Yatzie by 21 | //! the National Association Service of Toledo, Ohio, in the early 1940s. It was 22 | //! marketed under the name of Yahtzee by game entrepreneur Edwin S. Lowe in 23 | //! 1956. The game is a development of earlier dice games such as Poker Dice, 24 | //! Yacht and Generala. It is also similar to Yatzy, which is popular in 25 | //! Scandinavia. 26 | //! 27 | class Game 28 | { 29 | public: 30 | //! Constructs game with given \p config. 31 | //! \param config The game config holds all configuration values. 32 | explicit Game(const GameConfig& config); 33 | 34 | //! Returns the game state. 35 | //! \return The game state. 36 | [[nodiscard]] GameState& GetGameState(); 37 | 38 | //! Returns the number of players. 39 | //! \return The number of players. 40 | [[nodiscard]] std::size_t GetNumPlayers() const; 41 | 42 | //! Returns the current player. 43 | //! \return The current player. 44 | [[nodiscard]] Player& GetCurrentPlayer(); 45 | 46 | //! Returns the player at \p idx. 47 | //! \param idx The index of player array. 48 | //! \return The player at \p idx. 49 | [[nodiscard]] Player& GetPlayer(std::size_t idx); 50 | 51 | //! Starts the game. 52 | void Start(); 53 | 54 | //! Plays round for each player. 55 | void PlayRound(); 56 | 57 | //! Calculates the result. 58 | void CalculateResult(); 59 | 60 | Step step = Step::INVALID; 61 | Step nextStep = Step::INVALID; 62 | 63 | private: 64 | GameConfig m_config; 65 | GameState m_gameState; 66 | }; 67 | } // namespace YahtzeeMaster 68 | 69 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Games/GameConfig.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_GAME_CONFIG_HPP 8 | #define YAHTZEE_MASTER_GAME_CONFIG_HPP 9 | 10 | #include 11 | 12 | namespace YahtzeeMaster 13 | { 14 | //! 15 | //! \brief GameConfig struct. 16 | //! 17 | //! This struct holds all configuration values to create a new Game instance. 18 | //! 19 | struct GameConfig 20 | { 21 | //! Default constructor. 22 | GameConfig() = default; 23 | 24 | //! Constructs a game config with given \p _numPlayers. 25 | //! \param _numPlayers The number of players. 26 | explicit GameConfig(std::size_t _numPlayers) : numPlayers(_numPlayers) 27 | { 28 | // Do nothing 29 | } 30 | 31 | std::size_t numPlayers = 0; 32 | }; 33 | } // namespace YahtzeeMaster 34 | 35 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Games/GameManager.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_GAME_MANAGER_HPP 8 | #define YAHTZEE_MASTER_GAME_MANAGER_HPP 9 | 10 | #include 11 | #include 12 | 13 | namespace YahtzeeMaster 14 | { 15 | //! 16 | //! \brief GameManager class. 17 | //! 18 | //! This class monitors game and invokes method when a state is changed. 19 | //! 20 | class GameManager 21 | { 22 | public: 23 | //! Invokes method when a state is changed. 24 | //! \param game The game context. 25 | //! \param step The next step. 26 | static void ProcessNextStep(Game& game, Step step); 27 | }; 28 | } // namespace YahtzeeMaster 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Games/GameState.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_GAME_STATE_HPP 8 | #define YAHTZEE_MASTER_GAME_STATE_HPP 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace YahtzeeMaster 15 | { 16 | //! 17 | //! \brief GameState struct. 18 | //! 19 | //! This struct stores the information of the game state of all players. 20 | //! 21 | struct GameState 22 | { 23 | std::vector players; 24 | std::size_t curPlayerIdx = 0; 25 | std::size_t curRound = 0; 26 | }; 27 | } // namespace YahtzeeMaster 28 | 29 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Models/Dice.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_DICE_HPP 8 | #define YAHTZEE_MASTER_DICE_HPP 9 | 10 | namespace YahtzeeMaster 11 | { 12 | //! 13 | //! \brief Dice class. 14 | //! 15 | //! Dice (singular die or dice) are small, throwable objects with marked 16 | //! sides that can rest in multiple positions. They are used for generating 17 | //! random numbers, commonly as part of tabletop games, including dice games, 18 | //! board games, role-playing games, and games of chance. 19 | //! 20 | class Dice 21 | { 22 | public: 23 | //! Returns the value of the dice. 24 | //! \return The value of the dice. 25 | [[nodiscard]] int GetValue() const; 26 | 27 | //! Sets the value of the dice (for testing purposes). 28 | //! \param value The value of the dice. 29 | void SetValue(int value); 30 | 31 | //! Rolls a dice. 32 | void Roll(); 33 | 34 | private: 35 | int m_value = 0; 36 | }; 37 | } // namespace YahtzeeMaster 38 | 39 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Models/Player.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_PLAYER_HPP 8 | #define YAHTZEE_MASTER_PLAYER_HPP 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | namespace YahtzeeMaster 19 | { 20 | //! 21 | //! \brief Player class. 22 | //! 23 | //! This class stores various information that used in Yahtzee game. 24 | //! 25 | class Player 26 | { 27 | public: 28 | //! Initializes player to play each round. 29 | void Initialize(); 30 | 31 | //! Returns the score card. 32 | //! \return The score card. 33 | [[nodiscard]] const ScoreCard& GetScoreCard() const; 34 | 35 | //! Returns a list of values of the dice. 36 | //! \return A list of values of the dice. 37 | [[nodiscard]] std::array GetDiceValues() const; 38 | 39 | //! Returns a list of scores according to the value of dices. 40 | //! \return A list of scores according to the value of dices. 41 | [[nodiscard]] std::array GetScores() const; 42 | 43 | //! Returns the number of remain reroll. 44 | //! \return The number of remain reroll. 45 | [[nodiscard]] int GetRemainReroll() const; 46 | 47 | //! Sets a list of dice values (for testing purposes). 48 | //! \param diceValues A list of dice values to set. 49 | void SetDiceValues(std::vector diceValues); 50 | 51 | //! Rolls a list of dices. 52 | //! \param isFirst A flag to indicate it is first roll. 53 | //! \param rerollValues A list of dice values to roll. 54 | void RollDices(bool isFirst = false, std::vector rerollValues = {}); 55 | 56 | //! Calculates a list of scores according to the value of dices. 57 | void CalculateScores(); 58 | 59 | //! Fills a score to the score card. 60 | //! \param category The category to fill a score. 61 | void FillScoreCard(Category category); 62 | 63 | std::function processNextPlayerCallback; 64 | 65 | Result result = Result::INVALID; 66 | 67 | private: 68 | ScoreCard m_scoreCard; 69 | 70 | std::array m_dices; 71 | std::array m_scores{}; 72 | 73 | int m_numRoll = 0; 74 | }; 75 | } // namespace YahtzeeMaster 76 | 77 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/Models/ScoreCard.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #ifndef YAHTZEE_MASTER_SCORE_CARD_HPP 8 | #define YAHTZEE_MASTER_SCORE_CARD_HPP 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace YahtzeeMaster 16 | { 17 | //! 18 | //! \brief ScordCard class. 19 | //! 20 | //! The Yahtzee scorecard contains 13 different category boxes and in each 21 | //! round, after the third roll, the player must choose one of these categories. 22 | //! The score entered in the box depends on how well the five dice match the 23 | //! scoring rule for the category. Details of the scoring rules for each 24 | //! category are given below. As an example, one of the categories is called 25 | //! Three of a Kind. The scoring rule for this category means that a player only 26 | //! scores if at least three of the five dice are the same value. The game is 27 | //! completed after 13 rounds by each player, with each of the 13 boxes filled. 28 | //! The total score is calculated by summing all thirteen boxes, together with 29 | //! any bonuses. 30 | //! 31 | class ScoreCard 32 | { 33 | public: 34 | //! Default constructor. 35 | ScoreCard(); 36 | 37 | //! Fills a score. 38 | //! \param category The category to fill a score. 39 | //! \param score The value of score. 40 | //! \return true if a score is filled successfully, false otherwise. 41 | bool FillScore(Category category, int score); 42 | 43 | //! Returns the score of \p category. 44 | //! \param category The category to get score. 45 | //! \return The score of \p category. 46 | [[nodiscard]] int GetScore(Category category) const; 47 | 48 | //! Returns the score of upper categories. 49 | //! \return The score of upper categories. 50 | [[nodiscard]] int GetUpperCategoryScore() const; 51 | 52 | //! Returns the total score. 53 | //! \return The total score. 54 | [[nodiscard]] int GetTotalScore() const; 55 | 56 | //! Checks the value of \p category is filled. 57 | //! \param category The category to check that it is filled. 58 | //! \return true if the value of \p category is filled, false otherwise. 59 | [[nodiscard]] bool IsFilled(Category category) const; 60 | 61 | //! Checks that a list of dices satisfies "Three Of A Kind" condition. 62 | //! \param diceValues A list of values of the dice. 63 | //! \return true if a list of dices satisfies the condition, false otherwise. 64 | static bool IsThreeOfAKind(const std::array& diceValues); 65 | 66 | //! Checks that a list of dices satisfies "Four Of A Kind" condition. 67 | //! \param diceValues A list of values of the dice. 68 | //! \return true if a list of dices satisfies the condition, false otherwise. 69 | static bool IsFourOfAKind(const std::array& diceValues); 70 | 71 | //! Checks that a list of dices satisfies "Full House" condition. 72 | //! \param diceValues A list of values of the dice. 73 | //! \return true if a list of dices satisfies the condition, false otherwise. 74 | static bool IsFullHouse(const std::array& diceValues); 75 | 76 | //! Checks that a list of dices satisfies "Small Straight" condition. 77 | //! \param diceValues A list of values of the dice. 78 | //! \return true if a list of dices satisfies the condition, false otherwise. 79 | static bool IsSmallStraight(const std::array& diceValues); 80 | 81 | //! Checks that a list of dices satisfies "Large Straight" condition. 82 | //! \param diceValues A list of values of the dice. 83 | //! \return true if a list of dices satisfies the condition, false otherwise. 84 | static bool IsLargeStraight(const std::array& diceValues); 85 | 86 | //! Checks that a list of dices satisfies "Yahtzee" condition. 87 | //! \param diceValues A list of values of the dice. 88 | //! \return true if a list of dices satisfies the condition, false otherwise. 89 | static bool IsYahtzee(const std::array& diceValues); 90 | 91 | private: 92 | std::array m_scores{}; 93 | std::array m_scoreMarks{}; 94 | }; 95 | } // namespace YahtzeeMaster 96 | 97 | #endif -------------------------------------------------------------------------------- /Includes/YahtzeeMaster/YahtzeeMaster.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | // We are making my contributions/submissions to this project solely in our 3 | // personal capacity and are not conveying any rights to any intellectual 4 | // property of any third parties. 5 | 6 | #ifndef YAHTZEE_MASTER_HPP 7 | #define YAHTZEE_MASTER_HPP 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #endif // YAHTZEE_MASTER_HPP 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Chris Ohk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Medias/Logos/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/utilForever/YahtzeeMaster/f150cf0805cba5d058b219b1999257c2161c1107/Medias/Logos/Logo.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YahtzeeMaster 2 | 3 | 4 | 5 | [![License](https://img.shields.io/badge/Licence-MIT-blue.svg)](https://github.com/utilForever/YahtzeeMaster/blob/master/LICENSE) [![Build Status](https://travis-ci.com/utilForever/YahtzeeMaster.svg?branch=master)](https://travis-ci.org/utilForever/YahtzeeMaster/branches) [![Build status](https://ci.appveyor.com/api/projects/status/github/utilForever/YahtzeeMaster?branch=master&svg=true)](https://ci.appveyor.com/project/utilForever/YahtzeeMaster/branch/master) [![Build Status](https://utilforever.visualstudio.com/YahtzeeMaster/_apis/build/status/utilForever.YahtzeeMaster?branchName=master)](https://utilforever.visualstudio.com/YahtzeeMaster/_build/latest?definitionId=16&branchName=master) 6 | 7 | [![codecov](https://codecov.io/gh/utilForever/YahtzeeMaster/branch/master/graph/badge.svg)](https://codecov.io/gh/utilForever/YahtzeeMaster) 8 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/e517597fbd0042bcb214fb8586fd74e4)](https://www.codacy.com/manual/utilForever/YahtzeeMaster?utm_source=github.com&utm_medium=referral&utm_content=utilForever/YahtzeeMaster&utm_campaign=Badge_Grade) 9 | [![Total alerts](https://img.shields.io/lgtm/alerts/g/utilForever/YahtzeeMaster.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/utilForever/YahtzeeMaster/alerts/) 10 | [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/utilForever/YahtzeeMaster.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/utilForever/YahtzeeMaster/context:cpp) 11 | [![CodeFactor](https://www.codefactor.io/repository/github/utilforever/YahtzeeMaster/badge)](https://www.codefactor.io/repository/github/utilforever/YahtzeeMaster) 12 | 13 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=YahtzeeMaster&metric=alert_status)](https://sonarcloud.io/dashboard?id=YahtzeeMaster) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=YahtzeeMaster&metric=ncloc)](https://sonarcloud.io/dashboard?id=YahtzeeMaster) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=YahtzeeMaster&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=YahtzeeMaster) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=YahtzeeMaster&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=YahtzeeMaster) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=YahtzeeMaster&metric=security_rating)](https://sonarcloud.io/dashboard?id=YahtzeeMaster) 14 | 15 | YahtzeeMaster is Yahtzee simulator using C++ with some reinforcement learning. The code is built on C++17 and can be compiled with commonly available compilers such as g++, clang++, or Microsoft Visual Studio. YahtzeeMaster currently supports macOS (10.14 or later), Ubuntu (18.04 or later), Windows (Visual Studio 2017 or later), and Windows Subsystem for Linux (WSL). Other untested platforms that support C++17 also should be able to build YahtzeeMaster. 16 | 17 | ## Key Features 18 | 19 | * C++17 based Yahtzee game library 20 | * Console and GUI simulator program 21 | * C++ and Python API 22 | 23 | ## Quick Start 24 | 25 | You will need CMake to build the code. If you're using Windows, you need Visual Studio 2017 in addition to CMake. 26 | 27 | First, clone the code: 28 | 29 | ``` 30 | git clone https://github.com/utilForever/YahtzeeMaster.git --recursive 31 | cd YahtzeeMaster 32 | ``` 33 | 34 | ### C++ API 35 | 36 | For macOS or Linux or Windows Subsystem for Linux (WSL): 37 | 38 | ``` 39 | mkdir build 40 | cd build 41 | cmake .. 42 | make 43 | ``` 44 | 45 | For Windows: 46 | 47 | ``` 48 | mkdir build 49 | cd build 50 | cmake .. -G"Visual Studio 15 2017 Win64" 51 | MSBuild YahtzeeMaster.sln /p:Configuration=Release 52 | ``` 53 | 54 | ### Docker 55 | 56 | ``` 57 | docker pull utilforever/yahtzeemaster:latest 58 | ``` 59 | 60 | ## Documentation 61 | 62 | TBA 63 | 64 | ## How To Contribute 65 | 66 | Contributions are always welcome, either reporting issues/bugs or forking the repository and then issuing pull requests when you have completed some additional coding that you feel will be beneficial to the main project. If you are interested in contributing in a more dedicated capacity, then please contact me. 67 | 68 | ## Contact 69 | 70 | You can contact me via e-mail (utilForever at gmail.com). I am always happy to answer questions or help with any issues you might have, and please be sure to share any additional work or your creations with me, I love seeing what other people are making. 71 | 72 | ## License 73 | 74 | 75 | 76 | The class is licensed under the [MIT License](http://opensource.org/licenses/MIT): 77 | 78 | Copyright © 2020 [Chris Ohk](http://www.github.com/utilForever). 79 | 80 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 81 | 82 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 83 | 84 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Scripts/Dockerfile.bionic-codecov: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | LABEL maintainer "Chris Ohk " 3 | 4 | RUN apt-get update && apt-get install -y \ 5 | build-essential \ 6 | python-dev \ 7 | python-pip \ 8 | python3-dev \ 9 | python3-pip \ 10 | python3-venv \ 11 | cmake \ 12 | lcov \ 13 | --no-install-recommends \ 14 | && rm -rf /var/lib/apt/lists/* 15 | 16 | COPY . /app 17 | 18 | WORKDIR /app/ENV3 19 | RUN cd .. && \ 20 | pip3 install -r requirements.txt && \ 21 | pip3 install . && \ 22 | python3 -m pytest ./Tests/PythonTests 23 | 24 | WORKDIR /app/build 25 | RUN cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_COVERAGE=ON && \ 26 | make -j "$(nproc)" UnitTests && \ 27 | lcov -c -i -d Tests/UnitTests -o base.info && \ 28 | bin/UnitTests && \ 29 | lcov -c -d Tests/UnitTests -o test.info && \ 30 | lcov -a base.info -a test.info -o coverage.info && \ 31 | lcov -r coverage.info '/usr/*' -o coverage.info && \ 32 | lcov -r coverage.info '*/Extensions/*' -o coverage.info && \ 33 | lcov -r coverage.info '*/Includes/*' -o coverage.info && \ 34 | lcov -r coverage.info '*/Libraries/*' -o coverage.info && \ 35 | lcov -l coverage.info 36 | -------------------------------------------------------------------------------- /Scripts/Dockerfile.focal: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | LABEL maintainer "Chris Ohk " 3 | 4 | ENV DEBIAN_FRONTEND noninteractive 5 | 6 | RUN apt-get update && apt-get install -y \ 7 | build-essential \ 8 | python3-dev \ 9 | python3-pip \ 10 | python3-venv \ 11 | python3-setuptools \ 12 | cmake \ 13 | --no-install-recommends \ 14 | && rm -rf /var/lib/apt/lists/* 15 | 16 | COPY . /app 17 | 18 | WORKDIR /app/build 19 | RUN cmake .. && \ 20 | make -j "$(nproc)" && \ 21 | make install && \ 22 | bin/UnitTests 23 | 24 | WORKDIR /app/ENV3 25 | RUN cd .. && \ 26 | pip3 install -r requirements.txt && \ 27 | pip3 install . && \ 28 | python3 -m pytest ./Tests/PythonTests 29 | 30 | WORKDIR / -------------------------------------------------------------------------------- /Scripts/Dockerfile.focal.clang-latest: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | LABEL maintainer "Chris Ohk " 3 | 4 | ENV DEBIAN_FRONTEND noninteractive 5 | 6 | RUN apt-get update && apt-get install -y \ 7 | build-essential \ 8 | python3-dev \ 9 | python3-pip \ 10 | python3-venv \ 11 | python3-setuptools \ 12 | cmake \ 13 | clang-10 \ 14 | --no-install-recommends \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | COPY . /app 18 | 19 | WORKDIR /app/build 20 | RUN cmake .. -DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 && \ 21 | make -j "$(nproc)" && \ 22 | make install && \ 23 | bin/UnitTests 24 | 25 | WORKDIR /app/ENV3 26 | RUN cd .. && \ 27 | pip3 install -r requirements.txt && \ 28 | pip3 install . && \ 29 | python3 -m pytest ./Tests/PythonTests 30 | 31 | WORKDIR / -------------------------------------------------------------------------------- /Scripts/Dockerfile.focal.gcc-latest: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | LABEL maintainer "Chris Ohk " 3 | 4 | ENV DEBIAN_FRONTEND noninteractive 5 | 6 | RUN apt-get update && apt-get install -y \ 7 | build-essential \ 8 | python3-dev \ 9 | python3-pip \ 10 | python3-venv \ 11 | python3-setuptools \ 12 | cmake \ 13 | gcc-10 \ 14 | g++-10 \ 15 | --no-install-recommends \ 16 | && rm -rf /var/lib/apt/lists/* 17 | 18 | COPY . /app 19 | 20 | WORKDIR /app/build 21 | RUN cmake .. -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 && \ 22 | make -j "$(nproc)" && \ 23 | make install && \ 24 | bin/UnitTests 25 | 26 | WORKDIR /app/ENV3 27 | RUN cd .. && \ 28 | pip3 install -r requirements.txt && \ 29 | pip3 install . && \ 30 | python3 -m pytest ./Tests/PythonTests 31 | 32 | WORKDIR / -------------------------------------------------------------------------------- /Scripts/azure_pipelines_build_linux.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - checkout: self 3 | fetchDepth: 2 4 | submodules: true 5 | - script: | 6 | docker build -t utilforever/yahtzeemaster . -------------------------------------------------------------------------------- /Scripts/azure_pipelines_build_macos.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - checkout: self 3 | fetchDepth: 2 4 | submodules: true 5 | - script: | 6 | brew update 7 | mkdir build 8 | cd build 9 | cmake .. 10 | make -j 8 11 | bin/UnitTests 12 | cd .. 13 | pip3 install --user -r requirements.txt 14 | pip3 install --user . 15 | python3 -m pytest ./Tests/PythonTests -------------------------------------------------------------------------------- /Scripts/azure_pipelines_build_windows_vs2017.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - checkout: self 3 | fetchDepth: 2 4 | submodules: true 5 | - script: | 6 | md build 7 | cd build 8 | cmake .. -G "Visual Studio 15 2017 Win64" 9 | - task: VSBuild@1 10 | inputs: 11 | solution: 'D:\a\1\s\build\YahtzeeMaster.sln' 12 | configuration: Release 13 | platform: x64 14 | - script: | 15 | D:\a\1\s\build\bin\Release\UnitTests.exe -------------------------------------------------------------------------------- /Scripts/azure_pipelines_build_windows_vs2019.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - checkout: self 3 | fetchDepth: 2 4 | submodules: true 5 | - script: | 6 | md build 7 | cd build 8 | cmake .. -G "Visual Studio 16 2019" -A x64 9 | - task: VSBuild@1 10 | inputs: 11 | solution: 'D:\a\1\s\build\YahtzeeMaster.sln' 12 | configuration: Release 13 | platform: x64 14 | - script: | 15 | D:\a\1\s\build\bin\Release\UnitTests.exe -------------------------------------------------------------------------------- /Scripts/header_gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import filecmp 4 | import inspect 5 | import shutil 6 | import os 7 | 8 | import utils 9 | 10 | dir_name = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 11 | 12 | 13 | def main(): 14 | include_dir = os.path.join(dir_name, "../Includes/YahtzeeMaster") 15 | 16 | file_names = utils.get_all_files(include_dir, ["*.hpp"]) 17 | file_names.sort() 18 | 19 | header = os.path.join(dir_name, "../Includes/YahtzeeMaster/YahtzeeMaster.hpp") 20 | header_tmp = header + ".tmp" 21 | with open(header_tmp, "w") as header_file: 22 | header_file.write("""// Copyright (c) 2020 Chris Ohk 23 | // We are making my contributions/submissions to this project solely in our 24 | // personal capacity and are not conveying any rights to any intellectual 25 | // property of any third parties.\n 26 | """) 27 | header_file.write("#ifndef YAHTZEE_MASTER_HPP\n") 28 | header_file.write("#define YAHTZEE_MASTER_HPP\n\n") 29 | for filename in file_names: 30 | line = "#include \n" % filename 31 | header_file.write(line) 32 | header_file.write("\n#endif // YAHTZEE_MASTER_HPP\n") 33 | 34 | if not filecmp.cmp(header, header_tmp): 35 | shutil.move(header_tmp, header) 36 | else: 37 | os.remove(header_tmp) 38 | 39 | 40 | if __name__ == "__main__": 41 | main() -------------------------------------------------------------------------------- /Scripts/travis_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | export NUM_JOBS=1 6 | 7 | mkdir build 8 | cd build 9 | cmake .. 10 | make 11 | bin/UnitTests -------------------------------------------------------------------------------- /Scripts/travis_build_codecov.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | export TRAVIS_COMPILER=g++-7 6 | export CXX=g++-7 7 | export CXX_FOR_BUILD=g++-7 8 | export CC=gcc-7 9 | export CC_FOR_BUILD=gcc-7 10 | 11 | export NUM_JOBS=1 12 | 13 | sudo apt-get install -yq gcovr ggcov lcov curl 14 | 15 | mkdir build 16 | cd build 17 | cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_COVERAGE=ON 18 | make UnitTests 19 | lcov --gcov-tool /usr/bin/gcov-7 -c -i -d Tests/UnitTests -o base.info 20 | bin/UnitTests 21 | lcov --gcov-tool /usr/bin/gcov-7 -c -d Tests/UnitTests -o test.info 22 | lcov --gcov-tool /usr/bin/gcov-7 -a base.info -a test.info -o coverage.info 23 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '/usr/*' -o coverage.info 24 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '*/Extensions/*' -o coverage.info 25 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '*/Includes/*' -o coverage.info 26 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '*/Libraries/*' -o coverage.info 27 | lcov --gcov-tool /usr/bin/gcov-7 -l coverage.info 28 | 29 | curl -s --retry 5 https://codecov.io/bash > .codecov 30 | chmod +x .codecov 31 | ./.codecov -------------------------------------------------------------------------------- /Scripts/travis_build_codecov_sonar.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | export TRAVIS_COMPILER=g++-7 6 | export CXX=g++-7 7 | export CXX_FOR_BUILD=g++-7 8 | export CC=gcc-7 9 | export CC_FOR_BUILD=gcc-7 10 | 11 | export NUM_JOBS=1 12 | 13 | sudo apt-get install -yq gcovr ggcov lcov curl 14 | 15 | mkdir build 16 | cd build 17 | cmake .. -DCMAKE_BUILD_TYPE=Debug -DBUILD_COVERAGE=ON 18 | build-wrapper-linux-x86-64 --out-dir ../bw-output make UnitTests 19 | lcov --gcov-tool /usr/bin/gcov-7 -c -i -d Tests/UnitTests -o base.info 20 | bin/UnitTests 21 | lcov --gcov-tool /usr/bin/gcov-7 -c -d Tests/UnitTests -o test.info 22 | lcov --gcov-tool /usr/bin/gcov-7 -a base.info -a test.info -o coverage.info 23 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '/usr/*' -o coverage.info 24 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '*/Extensions/*' -o coverage.info 25 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '*/Includes/*' -o coverage.info 26 | lcov --gcov-tool /usr/bin/gcov-7 -r coverage.info '*/Libraries/*' -o coverage.info 27 | lcov --gcov-tool /usr/bin/gcov-7 -l coverage.info 28 | 29 | curl -s --retry 5 https://codecov.io/bash > .codecov 30 | chmod +x .codecov 31 | ./.codecov 32 | 33 | cd .. 34 | sonar-scanner -------------------------------------------------------------------------------- /Scripts/travis_build_docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [ $# -eq 0 ] 6 | then 7 | docker build -t utilforever/yahtzeemaster . 8 | else 9 | docker build -f $1 -t utilforever/yahtzeemaster:$2 . 10 | fi 11 | docker run utilforever/yahtzeemaster -------------------------------------------------------------------------------- /Scripts/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | YahtzeeMaster uses portions of Chromium V8. 3 | Copyright 2008 the V8 project authors. All rights reserved. 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following 11 | disclaimer in the documentation and/or other materials provided 12 | with the distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | """ 28 | 29 | import platform 30 | import re 31 | import os 32 | import fnmatch 33 | 34 | 35 | def guess_os(): 36 | """ 37 | Returns the name of the operating system. 38 | This will return 'linux' for Linux compatible system, 'macos' for Macs, 39 | 'win32' for Windows, and 'freebsd' for FreeBSD. 40 | """ 41 | _id = platform.system() 42 | if _id == 'Linux': 43 | return 'linux' 44 | elif _id == 'Darwin': 45 | return 'macosx' 46 | elif _id == 'Windows' or _id == 'Microsoft': 47 | return 'win32' 48 | else: 49 | return None 50 | 51 | 52 | def guess_word_size(): 53 | """ 54 | Returns the size of the pointer. For 64-bit systems, this will return '64', 55 | and '32' for 32-bit systems. 56 | """ 57 | if '64' in platform.machine(): 58 | return '64' 59 | else: 60 | archs = platform.architecture() 61 | for a in archs: 62 | if '64' in a: 63 | return '64' 64 | return '32' 65 | 66 | 67 | def guess_arch(): 68 | """ 69 | Returns the architecture name of the system. 70 | """ 71 | if is_windows(): 72 | if guess_word_size() == '64': 73 | return 'x64' 74 | else: 75 | return 'win32' 76 | 77 | _id = platform.machine() 78 | 79 | if is_mac(): 80 | if guess_word_size() == '64' and _id == 'i386': 81 | return 'x86_64' 82 | else: 83 | return _id 84 | 85 | if _id.startswith('arm'): 86 | return 'arm' 87 | elif (not _id) or (not re.match('(x|i[3-6])86', _id) is None): 88 | return _id 89 | else: 90 | return None 91 | 92 | 93 | def detect_num_cpus(): 94 | """ 95 | Detects the number of CPUs on a system. Cribbed from pp. 96 | """ 97 | # Linux, Unix and MacOS: 98 | if hasattr(os, "sysconf"): 99 | if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"): 100 | # Linux & Unix: 101 | ncpus = os.sysconf("SC_NPROCESSORS_ONLN") 102 | if isinstance(ncpus, int) and ncpus > 0: 103 | return ncpus 104 | else: # OSX: 105 | return int(os.popen2("sysctl -n hw.ncpu")[1].read()) 106 | # Windows: 107 | if os.environ.has_key("NUMBER_OF_PROCESSORS"): 108 | ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]); 109 | if ncpus > 0: 110 | return ncpus 111 | return 1 # Default 112 | 113 | 114 | def is_windows(): 115 | """ 116 | Returns True if you are using Windows. 117 | """ 118 | return guess_os() == 'win32' 119 | 120 | 121 | def is_windows64(): 122 | """ 123 | Returns True if you are using Visual Studio compiler in 64-bit mode. 124 | """ 125 | if is_windows(): 126 | return '64' in os.environ['LIB'] 127 | else: 128 | return False 129 | 130 | 131 | def is_unix(): 132 | """ 133 | Returns True if you are using Unix compatible system (Linux, Mac, and 134 | FreeBSD). 135 | """ 136 | return not is_windows() 137 | 138 | 139 | def is_mac(): 140 | """ 141 | Returns True if you are using Mac. 142 | """ 143 | return guess_os() == 'macosx' 144 | 145 | 146 | def is_linux(): 147 | """ 148 | Returns True if you are using Linux. 149 | """ 150 | return guess_os() == 'linux' 151 | 152 | 153 | def is64(): 154 | """ 155 | Returns True if running on 64-bit machine 156 | """ 157 | return guess_word_size() == '64' 158 | 159 | 160 | def navigate_all_files(root_path, patterns): 161 | """ 162 | A generator function that iterates all files that matches the given patterns 163 | from the root_path. 164 | """ 165 | for root, dirs, files in os.walk(root_path): 166 | for pattern in patterns: 167 | for filename in fnmatch.filter(files, pattern): 168 | yield os.path.join(root, filename) 169 | 170 | 171 | def get_all_files(root_path, patterns): 172 | """ 173 | Returns a list of all files that matches the given patterns from the 174 | root_path. 175 | """ 176 | ret = [fp.replace(root_path, '').lstrip('\\').lstrip('/').replace('\\', '/') 177 | for fp in navigate_all_files(root_path, patterns)] 178 | return ret -------------------------------------------------------------------------------- /Sources/YahtzeeMaster/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Target name 2 | set(target YahtzeeMaster) 3 | 4 | # Define 5 | set(root_dir ${CMAKE_CURRENT_SOURCE_DIR}/../..) 6 | 7 | # Includes 8 | include_directories( 9 | ${CMAKE_CURRENT_SOURCE_DIR} 10 | ${CMAKE_CURRENT_SOURCE_DIR}/../Libraries 11 | ) 12 | 13 | # Sources 14 | file(GLOB header_dir 15 | ${root_dir}/Includes) 16 | 17 | file(GLOB_RECURSE headers 18 | ${header_dir}/*.hpp) 19 | 20 | file(GLOB_RECURSE sources 21 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 22 | 23 | # Custom-build event 24 | if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 25 | set(yahtzee_master_header_gen_py ${root_dir}/Scripts/header_gen.py) 26 | add_custom_command( 27 | COMMAND python ${yahtzee_master_header_gen_py} 28 | DEPENDS ${headers} 29 | OUTPUT ${root_dir}/Includes/YahtzeeMaster.hpp 30 | ) 31 | add_custom_target(yahtzee_master_header_gen_py ALL 32 | DEPENDS ${root_dir}/Includes/YahtzeeMaster.hpp) 33 | else () 34 | set(yahtzee_master_header_gen_py ${root_dir}/Scripts/header_gen.py) 35 | add_custom_command( 36 | COMMAND python3 ${yahtzee_master_header_gen_py} 37 | DEPENDS ${headers} 38 | OUTPUT ${root_dir}/Includes/YahtzeeMaster.hpp 39 | ) 40 | add_custom_target(yahtzee_master_header_gen_py ALL 41 | DEPENDS ${root_dir}/Includes/YahtzeeMaster.hpp) 42 | endif () 43 | 44 | # Build library 45 | add_library(${target} 46 | ${sources}) 47 | add_dependencies(${target} yahtzee_master_header_gen_py) 48 | 49 | # Project options 50 | set_target_properties(${target} 51 | PROPERTIES 52 | ${DEFAULT_PROJECT_OPTIONS} 53 | ) 54 | 55 | # Compile options 56 | target_compile_options(${target} 57 | PRIVATE 58 | 59 | PUBLIC 60 | ${DEFAULT_COMPILE_OPTIONS} 61 | 62 | INTERFACE 63 | ) 64 | 65 | target_link_libraries(${target} 66 | PRIVATE 67 | 68 | PUBLIC 69 | ${DEFAULT_LINKER_OPTIONS} 70 | ${DEFAULT_LIBRARIES} 71 | 72 | INTERFACE 73 | ) 74 | 75 | # Install 76 | install(TARGETS ${target} DESTINATION lib) 77 | install(DIRECTORY ${header_dir} DESTINATION include) -------------------------------------------------------------------------------- /Sources/YahtzeeMaster/Games/Game.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace YahtzeeMaster 14 | { 15 | Game::Game(const GameConfig& config) : m_config(config) 16 | { 17 | m_gameState.players.reserve(config.numPlayers); 18 | } 19 | 20 | GameState& Game::GetGameState() 21 | { 22 | return m_gameState; 23 | } 24 | 25 | std::size_t Game::GetNumPlayers() const 26 | { 27 | return m_config.numPlayers; 28 | } 29 | 30 | Player& Game::GetCurrentPlayer() 31 | { 32 | return m_gameState.players[m_gameState.curPlayerIdx]; 33 | } 34 | 35 | Player& Game::GetPlayer(std::size_t idx) 36 | { 37 | assert(idx >= 0 && idx < m_config.numPlayers); 38 | 39 | return m_gameState.players[idx]; 40 | } 41 | 42 | void Game::Start() 43 | { 44 | // Create callback to process the next player 45 | auto processNextPlayerCallback = [this]() { 46 | // Increase the index of the current player 47 | ++m_gameState.curPlayerIdx; 48 | m_gameState.curPlayerIdx %= m_gameState.players.size(); 49 | 50 | if (m_gameState.curPlayerIdx == 0) 51 | { 52 | ++m_gameState.curRound; 53 | } 54 | 55 | if (m_gameState.curRound > NUM_CATEGORIES) 56 | { 57 | // Set next step 58 | nextStep = Step::COMPLETE; 59 | GameManager::ProcessNextStep(*this, nextStep); 60 | } 61 | }; 62 | 63 | // Initialize player related variables 64 | for (std::size_t i = 0; i < m_config.numPlayers; ++i) 65 | { 66 | m_gameState.players.emplace_back(Player{}); 67 | m_gameState.players[i].processNextPlayerCallback = processNextPlayerCallback; 68 | } 69 | 70 | // Initialize game related variables 71 | m_gameState.curPlayerIdx = 0; 72 | m_gameState.curRound = 1; 73 | 74 | // Set next step 75 | nextStep = Step::PLAY_ROUND; 76 | GameManager::ProcessNextStep(*this, nextStep); 77 | } 78 | 79 | void Game::PlayRound() 80 | { 81 | // Initialize the current player 82 | GetCurrentPlayer().Initialize(); 83 | } 84 | 85 | void Game::CalculateResult() 86 | { 87 | std::vector scores; 88 | scores.reserve(m_gameState.players.size()); 89 | 90 | // Collect total scores 91 | for (std::size_t i = 0; i < m_config.numPlayers; ++i) 92 | { 93 | scores.emplace_back(m_gameState.players[i].GetScoreCard().GetTotalScore()); 94 | } 95 | 96 | // Find the maximum value of total scores 97 | const auto result = std::max_element(scores.begin(), scores.end()); 98 | const std::size_t idx = std::distance(scores.begin(), result); 99 | 100 | // Set the result 101 | for (std::size_t i = 0; i < m_config.numPlayers; ++i) 102 | { 103 | m_gameState.players[i].result = 104 | (scores[i] == scores[idx]) ? Result::WON : Result::LOST; 105 | } 106 | } 107 | } // namespace YahtzeeMaster -------------------------------------------------------------------------------- /Sources/YahtzeeMaster/Games/GameManager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | 9 | namespace YahtzeeMaster 10 | { 11 | void GameManager::ProcessNextStep(Game& game, Step step) 12 | { 13 | switch (step) 14 | { 15 | case Step::INVALID: 16 | break; 17 | case Step::PLAY_ROUND: 18 | game.step = step; 19 | game.PlayRound(); 20 | break; 21 | case Step::COMPLETE: 22 | game.step = step; 23 | game.CalculateResult(); 24 | break; 25 | } 26 | } 27 | } // namespace YahtzeeMaster 28 | -------------------------------------------------------------------------------- /Sources/YahtzeeMaster/Models/Dice.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | 9 | #include 10 | 11 | namespace YahtzeeMaster 12 | { 13 | int Dice::GetValue() const 14 | { 15 | return m_value; 16 | } 17 | 18 | void Dice::SetValue(int value) 19 | { 20 | m_value = value; 21 | } 22 | 23 | void Dice::Roll() 24 | { 25 | using Random = effolkronium::random_static; 26 | 27 | m_value = Random::get(1, 6); 28 | } 29 | } // namespace YahtzeeMaster -------------------------------------------------------------------------------- /Sources/YahtzeeMaster/Models/Player.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace YahtzeeMaster 13 | { 14 | void Player::Initialize() 15 | { 16 | m_scores.fill(0); 17 | m_numRoll = 0; 18 | } 19 | 20 | const ScoreCard& Player::GetScoreCard() const 21 | { 22 | return m_scoreCard; 23 | } 24 | 25 | std::array Player::GetDiceValues() const 26 | { 27 | std::array ret{}; 28 | 29 | for (std::size_t i = 0; i < ret.size(); ++i) 30 | { 31 | ret[i] = m_dices[i].GetValue(); 32 | } 33 | 34 | return ret; 35 | } 36 | 37 | std::array Player::GetScores() const 38 | { 39 | return m_scores; 40 | } 41 | 42 | int Player::GetRemainReroll() const 43 | { 44 | return NUM_ROLLS - m_numRoll; 45 | } 46 | 47 | void Player::SetDiceValues(std::vector diceValues) 48 | { 49 | std::sort(diceValues.begin(), diceValues.end()); 50 | 51 | std::size_t i = 0; 52 | 53 | for (const int& diceValue : diceValues) 54 | { 55 | m_dices[i].SetValue(diceValue); 56 | ++i; 57 | } 58 | } 59 | 60 | void Player::RollDices(bool isFirst, std::vector rerollValues) 61 | { 62 | if (m_numRoll == NUM_ROLLS) 63 | { 64 | return; 65 | } 66 | 67 | if (isFirst) 68 | { 69 | for (auto& dice : m_dices) 70 | { 71 | dice.Roll(); 72 | } 73 | } 74 | else 75 | { 76 | if (!rerollValues.empty()) 77 | { 78 | std::sort(rerollValues.begin(), rerollValues.end()); 79 | 80 | std::size_t i = 0, j = 0; 81 | std::vector indices; 82 | indices.reserve(rerollValues.size()); 83 | 84 | while (i < rerollValues.size() && j < m_dices.size()) 85 | { 86 | const int diff = rerollValues.at(i) - m_dices.at(j).GetValue(); 87 | 88 | if (diff == 0) 89 | { 90 | indices.emplace_back(j); 91 | 92 | ++i; 93 | ++j; 94 | } 95 | else if (diff > 0) 96 | { 97 | ++j; 98 | } 99 | else 100 | { 101 | ++i; 102 | } 103 | } 104 | 105 | for (auto& index : indices) 106 | { 107 | m_dices[index].Roll(); 108 | } 109 | } 110 | } 111 | 112 | std::sort(m_dices.begin(), m_dices.end(), 113 | [](const Dice& a, const Dice& b) { return a.GetValue() < b.GetValue(); }); 114 | 115 | CalculateScores(); 116 | 117 | ++m_numRoll; 118 | } 119 | 120 | void Player::CalculateScores() 121 | { 122 | // Initialize scores 123 | m_scores.fill(0); 124 | 125 | std::array values{}; 126 | int sumOfAllDice = 0; 127 | 128 | for (std::size_t i = 0; i < NUM_DICES; ++i) 129 | { 130 | values[i] = m_dices[i].GetValue(); 131 | sumOfAllDice += values[i]; 132 | 133 | // Calculate upper section 134 | m_scores[Category::ACES] += (values[i] == 1) ? 1 : 0; 135 | m_scores[Category::TWOS] += (values[i] == 2) ? 2 : 0; 136 | m_scores[Category::THREES] += (values[i] == 3) ? 3 : 0; 137 | m_scores[Category::FOURS] += (values[i] == 4) ? 4 : 0; 138 | m_scores[Category::FIVES] += (values[i] == 5) ? 5 : 0; 139 | m_scores[Category::SIXES] += (values[i] == 6) ? 6 : 0; 140 | } 141 | 142 | // Calculate lower section 143 | m_scores[Category::THREE_OF_A_KIND] = 144 | ScoreCard::IsThreeOfAKind(values) ? sumOfAllDice : 0; 145 | m_scores[Category::FOUR_OF_A_KIND] = 146 | ScoreCard::IsFourOfAKind(values) ? sumOfAllDice : 0; 147 | m_scores[Category::FULL_HOUSE] = ScoreCard::IsFullHouse(values) ? 25 : 0; 148 | m_scores[Category::SMALL_STRAIGHT] = ScoreCard::IsSmallStraight(values) ? 30 : 0; 149 | m_scores[Category::LARGE_STRAIGHT] = ScoreCard::IsLargeStraight(values) ? 40 : 0; 150 | m_scores[Category::YAHTZEE] = ScoreCard::IsFourOfAKind(values) ? 50 : 0; 151 | m_scores[Category::CHANCE] = sumOfAllDice; 152 | } 153 | 154 | void Player::FillScoreCard(Category category) 155 | { 156 | const bool isFillSuccess = m_scoreCard.FillScore(category, m_scores[category]); 157 | if (isFillSuccess) 158 | { 159 | m_numRoll = 0; 160 | processNextPlayerCallback(); 161 | } 162 | } 163 | } // namespace YahtzeeMaster -------------------------------------------------------------------------------- /Sources/YahtzeeMaster/Models/ScoreCard.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace YahtzeeMaster 13 | { 14 | ScoreCard::ScoreCard() 15 | { 16 | m_scores.fill(0); 17 | m_scoreMarks.fill(false); 18 | } 19 | 20 | bool ScoreCard::FillScore(Category category, int score) 21 | { 22 | if (m_scoreMarks[category] == true) 23 | { 24 | return false; 25 | } 26 | 27 | m_scores[category] = score; 28 | m_scoreMarks[category] = true; 29 | 30 | return true; 31 | } 32 | 33 | int ScoreCard::GetScore(Category category) const 34 | { 35 | return m_scores[category]; 36 | } 37 | 38 | int ScoreCard::GetUpperCategoryScore() const 39 | { 40 | return m_scores[Category::ACES] + m_scores[Category::TWOS] + 41 | m_scores[Category::THREES] + m_scores[Category::FOURS] + 42 | m_scores[Category::FIVES] + m_scores[Category::SIXES]; 43 | } 44 | 45 | int ScoreCard::GetTotalScore() const 46 | { 47 | const int bonus = (GetUpperCategoryScore()) >= UPPER_SECTION_BONUS_CONDITION 48 | ? UPPER_SECTION_BONUS 49 | : 0; 50 | 51 | return std::accumulate(m_scores.begin(), m_scores.end(), 0) + bonus; 52 | } 53 | 54 | bool ScoreCard::IsFilled(Category category) const 55 | { 56 | return m_scoreMarks[category]; 57 | } 58 | 59 | bool ScoreCard::IsThreeOfAKind(const std::array& diceValues) 60 | { 61 | return (diceValues[0] == diceValues[1] && diceValues[1] == diceValues[2]) || 62 | (diceValues[1] == diceValues[2] && diceValues[2] == diceValues[3]) || 63 | (diceValues[2] == diceValues[3] && diceValues[3] == diceValues[4]); 64 | } 65 | 66 | bool ScoreCard::IsFourOfAKind(const std::array& diceValues) 67 | { 68 | return (diceValues[0] == diceValues[1] && diceValues[1] == diceValues[2] && 69 | diceValues[2] == diceValues[3]) || 70 | (diceValues[1] == diceValues[2] && diceValues[2] == diceValues[3] && 71 | diceValues[3] == diceValues[4]); 72 | } 73 | 74 | bool ScoreCard::IsFullHouse(const std::array& diceValues) 75 | { 76 | return (diceValues[0] == diceValues[1] && diceValues[1] != diceValues[2] && 77 | diceValues[2] == diceValues[3] && diceValues[3] == diceValues[4]) || 78 | (diceValues[0] == diceValues[1] && diceValues[1] == diceValues[2] && 79 | diceValues[2] != diceValues[3] && diceValues[3] == diceValues[4]); 80 | } 81 | 82 | bool ScoreCard::IsSmallStraight(const std::array& diceValues) 83 | { 84 | std::array values = diceValues; 85 | [[maybe_unused]] auto last = std::unique(values.begin(), values.end()); 86 | 87 | return (values[0] + 1 == values[1] && values[1] + 1 == values[2] && 88 | values[2] + 1 == values[3]); 89 | } 90 | 91 | bool ScoreCard::IsLargeStraight(const std::array& diceValues) 92 | { 93 | return (diceValues[0] + 1 == diceValues[1] && diceValues[1] + 1 == diceValues[2] && 94 | diceValues[2] + 1 == diceValues[3] && diceValues[3] + 1 == diceValues[4]); 95 | } 96 | 97 | bool ScoreCard::IsYahtzee(const std::array& diceValues) 98 | { 99 | return (diceValues[0] == diceValues[1] && diceValues[1] == diceValues[2] && 100 | diceValues[2] == diceValues[3] && diceValues[3] == diceValues[4]); 101 | } 102 | } // namespace YahtzeeMaster -------------------------------------------------------------------------------- /Tests/PythonTests/test_dice.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2020 Chris Ohk 3 | 4 | I am making my contributions/submissions to this project solely in our 5 | personal capacity and am not conveying any rights to any intellectual 6 | property of any third parties. 7 | """ 8 | 9 | import pyYahtzee 10 | 11 | 12 | def test_dice_roll(): 13 | dice = pyYahtzee.Dice() 14 | 15 | dice.roll() 16 | assert 1 <= dice.get_value() <= 6 17 | -------------------------------------------------------------------------------- /Tests/PythonTests/test_game.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2020 Chris Ohk 3 | 4 | I am making my contributions/submissions to this project solely in our 5 | personal capacity and am not conveying any rights to any intellectual 6 | property of any third parties. 7 | """ 8 | 9 | import pyYahtzee 10 | 11 | 12 | def test_game_get_game_state(): 13 | config = pyYahtzee.GameConfig() 14 | config.num_players = 2 15 | 16 | game = pyYahtzee.Game(config) 17 | game.start() 18 | 19 | game_state = game.get_game_state() 20 | assert game_state.cur_player_idx == 0 21 | 22 | 23 | def test_game_roll_dices(): 24 | config = pyYahtzee.GameConfig() 25 | config.num_players = 2 26 | 27 | game = pyYahtzee.Game(config) 28 | game.start() 29 | 30 | cur_player = game.get_current_player() 31 | cur_player.roll_dices(True) 32 | 33 | values1 = cur_player.get_dice_values() 34 | for value in values1: 35 | assert 1 <= value <= 6 36 | 37 | cur_player.roll_dices(False) 38 | 39 | values2 = cur_player.get_dice_values() 40 | for i in range(0, pyYahtzee.NUM_DICES): 41 | assert values1[i] == values2[i] 42 | 43 | 44 | def test_game_basic(): 45 | config = pyYahtzee.GameConfig() 46 | config.num_players = 2 47 | 48 | game = pyYahtzee.Game(config) 49 | game.start() 50 | 51 | for i in range(config.num_players * pyYahtzee.NUM_CATEGORIES): 52 | game.get_current_player().roll_dices(True) 53 | game.get_current_player().fill_score_card(pyYahtzee.Category(int(i / 2))) 54 | 55 | assert game.get_game_state().players[0].result == pyYahtzee.Result.WON or game.get_game_state( 56 | ).players[0].result == pyYahtzee.Result.LOST 57 | assert game.get_game_state().players[1].result == pyYahtzee.Result.WON or game.get_game_state( 58 | ).players[1].result == pyYahtzee.Result.LOST 59 | -------------------------------------------------------------------------------- /Tests/PythonTests/test_game_manager.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2020 Chris Ohk 3 | 4 | I am making my contributions/submissions to this project solely in our 5 | personal capacity and am not conveying any rights to any intellectual 6 | property of any third parties. 7 | """ 8 | 9 | import pyYahtzee 10 | 11 | 12 | def test_game_manager_basic(): 13 | config = pyYahtzee.GameConfig() 14 | config.num_players = 2 15 | 16 | game = pyYahtzee.Game(config) 17 | game_manager = pyYahtzee.GameManager() 18 | 19 | game_manager.process_next_step(game, pyYahtzee.Step.INVALID) 20 | assert game.step == pyYahtzee.Step.INVALID 21 | 22 | game.start() -------------------------------------------------------------------------------- /Tests/PythonTests/test_player.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2020 Chris Ohk 3 | 4 | I am making my contributions/submissions to this project solely in our 5 | personal capacity and am not conveying any rights to any intellectual 6 | property of any third parties. 7 | """ 8 | 9 | import pyYahtzee 10 | 11 | 12 | def test_player_roll_dices(): 13 | player = pyYahtzee.Player() 14 | 15 | player.roll_dices(True) 16 | 17 | values = player.get_dice_values() 18 | for value in values: 19 | assert 1 <= value <= 6 20 | 21 | 22 | def test_player_calculate_scores(): 23 | player = pyYahtzee.Player() 24 | 25 | player.set_dice_values([6, 1, 2, 3, 4]) 26 | player.calculate_scores() 27 | 28 | scores = player.get_scores() 29 | assert scores[pyYahtzee.Category.ACES] == 1 30 | assert scores[pyYahtzee.Category.TWOS] == 2 31 | assert scores[pyYahtzee.Category.THREES] == 3 32 | assert scores[pyYahtzee.Category.FOURS] == 4 33 | assert scores[pyYahtzee.Category.FIVES] == 0 34 | assert scores[pyYahtzee.Category.SIXES] == 6 35 | assert scores[pyYahtzee.Category.THREE_OF_A_KIND] == 0 36 | assert scores[pyYahtzee.Category.FOUR_OF_A_KIND] == 0 37 | assert scores[pyYahtzee.Category.FULL_HOUSE] == 0 38 | assert scores[pyYahtzee.Category.SMALL_STRAIGHT] == 30 39 | assert scores[pyYahtzee.Category.LARGE_STRAIGHT] == 0 40 | assert scores[pyYahtzee.Category.YAHTZEE] == 0 41 | assert scores[pyYahtzee.Category.CHANCE] == 16 42 | 43 | player.set_dice_values([2, 5, 5, 2, 5]) 44 | player.calculate_scores() 45 | 46 | scores = player.get_scores() 47 | assert scores[pyYahtzee.Category.ACES] == 0 48 | assert scores[pyYahtzee.Category.TWOS] == 4 49 | assert scores[pyYahtzee.Category.THREES] == 0 50 | assert scores[pyYahtzee.Category.FOURS] == 0 51 | assert scores[pyYahtzee.Category.FIVES] == 15 52 | assert scores[pyYahtzee.Category.SIXES] == 0 53 | assert scores[pyYahtzee.Category.THREE_OF_A_KIND] == 19 54 | assert scores[pyYahtzee.Category.FOUR_OF_A_KIND] == 0 55 | assert scores[pyYahtzee.Category.FULL_HOUSE] == 25 56 | assert scores[pyYahtzee.Category.SMALL_STRAIGHT] == 0 57 | assert scores[pyYahtzee.Category.LARGE_STRAIGHT] == 0 58 | assert scores[pyYahtzee.Category.YAHTZEE] == 0 59 | assert scores[pyYahtzee.Category.CHANCE] == 19 60 | 61 | player.set_dice_values([6, 6, 6, 6, 6]) 62 | player.calculate_scores() 63 | 64 | scores = player.get_scores() 65 | assert scores[pyYahtzee.Category.ACES] == 0 66 | assert scores[pyYahtzee.Category.TWOS] == 0 67 | assert scores[pyYahtzee.Category.THREES] == 0 68 | assert scores[pyYahtzee.Category.FOURS] == 0 69 | assert scores[pyYahtzee.Category.FIVES] == 0 70 | assert scores[pyYahtzee.Category.SIXES] == 30 71 | assert scores[pyYahtzee.Category.THREE_OF_A_KIND] == 30 72 | assert scores[pyYahtzee.Category.FOUR_OF_A_KIND] == 30 73 | assert scores[pyYahtzee.Category.FULL_HOUSE] == 0 74 | assert scores[pyYahtzee.Category.SMALL_STRAIGHT] == 0 75 | assert scores[pyYahtzee.Category.LARGE_STRAIGHT] == 0 76 | assert scores[pyYahtzee.Category.YAHTZEE] == 50 77 | assert scores[pyYahtzee.Category.CHANCE] == 30 78 | -------------------------------------------------------------------------------- /Tests/PythonTests/test_score_card.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (c) 2020 Chris Ohk 3 | 4 | I am making my contributions/submissions to this project solely in our 5 | personal capacity and am not conveying any rights to any intellectual 6 | property of any third parties. 7 | """ 8 | 9 | import pyYahtzee 10 | 11 | 12 | def test_score_card_fill_score(): 13 | config = pyYahtzee.GameConfig() 14 | config.num_players = 1 15 | 16 | game = pyYahtzee.Game(config) 17 | game.start() 18 | 19 | assert game.get_game_state().cur_round == 1 20 | 21 | cur_player = game.get_current_player() 22 | 23 | cur_player.roll_dices(True) 24 | cur_player.fill_score_card(pyYahtzee.Category.ACES) 25 | assert game.get_game_state().cur_round == 2 26 | 27 | cur_player.roll_dices(True) 28 | cur_player.fill_score_card(pyYahtzee.Category.ACES) 29 | assert game.get_game_state().cur_round == 2 30 | 31 | 32 | def test_score_card_get_total_score(): 33 | score_card = pyYahtzee.ScoreCard() 34 | 35 | score_card.fill_score(pyYahtzee.Category.ACES, 3) 36 | score_card.fill_score(pyYahtzee.Category.TWOS, 6) 37 | score_card.fill_score(pyYahtzee.Category.THREES, 9) 38 | score_card.fill_score(pyYahtzee.Category.FOURS, 12) 39 | score_card.fill_score(pyYahtzee.Category.FIVES, 15) 40 | assert score_card.get_total_score() == 45 41 | 42 | score_card.fill_score(pyYahtzee.Category.SIXES, 18) 43 | assert score_card.get_total_score() == 98 44 | 45 | 46 | def test_score_card_three_of_a_kind(): 47 | dice_values1 = [1, 2, 4, 4, 5] 48 | dice_values2 = [1, 4, 4, 4, 5] 49 | 50 | assert pyYahtzee.ScoreCard().is_three_of_a_kind(dice_values1) is False 51 | assert pyYahtzee.ScoreCard().is_three_of_a_kind(dice_values2) is True 52 | 53 | 54 | def test_score_card_four_of_a_kind(): 55 | dice_values1 = [1, 4, 4, 4, 5] 56 | dice_values2 = [1, 4, 4, 4, 4] 57 | 58 | assert pyYahtzee.ScoreCard().is_four_of_a_kind(dice_values1) is False 59 | assert pyYahtzee.ScoreCard().is_four_of_a_kind(dice_values2) is True 60 | 61 | 62 | def test_score_card_full_house(): 63 | dice_values1 = [2, 4, 4, 4, 5] 64 | dice_values2 = [2, 2, 4, 4, 4] 65 | 66 | assert pyYahtzee.ScoreCard().is_full_house(dice_values1) is False 67 | assert pyYahtzee.ScoreCard().is_full_house(dice_values2) is True 68 | 69 | 70 | def test_score_card_small_straight(): 71 | dice_values1 = [1, 3, 4, 4, 6] 72 | dice_values2 = [2, 2, 3, 4, 5] 73 | 74 | assert pyYahtzee.ScoreCard().is_small_straight(dice_values1) is False 75 | assert pyYahtzee.ScoreCard().is_small_straight(dice_values2) is True 76 | 77 | 78 | def test_score_card_large_straight(): 79 | dice_values1 = [2, 2, 3, 4, 5] 80 | dice_values2 = [2, 3, 4, 5, 6] 81 | 82 | assert pyYahtzee.ScoreCard().is_large_straight(dice_values1) is False 83 | assert pyYahtzee.ScoreCard().is_large_straight(dice_values2) is True 84 | 85 | 86 | def test_score_card_yahtzee(): 87 | dice_values1 = [3, 4, 4, 4, 4] 88 | dice_values2 = [4, 4, 4, 4, 4] 89 | 90 | assert pyYahtzee.ScoreCard().is_yahtzee(dice_values1) is False 91 | assert pyYahtzee.ScoreCard().is_yahtzee(dice_values2) is True 92 | -------------------------------------------------------------------------------- /Tests/UnitTests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Target name 2 | set(target UnitTests) 3 | 4 | # Includes 5 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 6 | 7 | # Sources 8 | file(GLOB_RECURSE sources 9 | ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 10 | 11 | # Build executable 12 | add_executable(${target} 13 | ${sources}) 14 | 15 | # Project options 16 | set_target_properties(${target} 17 | PROPERTIES 18 | ${DEFAULT_PROJECT_OPTIONS} 19 | ) 20 | 21 | # Compile options 22 | # GCC and Clang compiler options 23 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") 24 | set(DEFAULT_COMPILE_OPTIONS ${DEFAULT_COMPILE_OPTIONS} 25 | # /wd4996 # -> disable warning: non-Standard std::tr1 namespace and TR1-only machinery (because of gtest) 26 | -Wno-unused-variable 27 | ) 28 | endif() 29 | target_compile_options(${target} 30 | PRIVATE 31 | ${DEFAULT_COMPILE_OPTIONS} 32 | ) 33 | target_compile_definitions(${target} 34 | PRIVATE 35 | ) 36 | 37 | # Link libraries 38 | target_link_libraries(${target} 39 | PRIVATE 40 | ${DEFAULT_LINKER_OPTIONS} 41 | YahtzeeMaster 42 | doctest) -------------------------------------------------------------------------------- /Tests/UnitTests/Games/GameManagerTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include "doctest_proxy.hpp" 8 | 9 | #include 10 | 11 | using namespace YahtzeeMaster; 12 | 13 | TEST_CASE("[GameManager] - Basic") 14 | { 15 | GameConfig config; 16 | config.numPlayers = 2; 17 | 18 | Game game{ config }; 19 | 20 | GameManager::ProcessNextStep(game, Step::INVALID); 21 | CHECK_EQ(game.step, Step::INVALID); 22 | 23 | game.Start(); 24 | CHECK_EQ(game.step, Step::PLAY_ROUND); 25 | 26 | for (std::size_t i = 0; i < config.numPlayers * NUM_CATEGORIES; ++i) 27 | { 28 | game.GetCurrentPlayer().RollDices(); 29 | game.GetCurrentPlayer().FillScoreCard(static_cast(i / 2)); 30 | } 31 | 32 | CHECK_EQ(game.step, Step::COMPLETE); 33 | } -------------------------------------------------------------------------------- /Tests/UnitTests/Games/GameTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include "doctest_proxy.hpp" 8 | 9 | #include 10 | 11 | using namespace YahtzeeMaster; 12 | 13 | TEST_CASE("[Game] - GetGameState") 14 | { 15 | GameConfig config; 16 | config.numPlayers = 2; 17 | 18 | Game game{ config }; 19 | game.Start(); 20 | 21 | GameState& gameState = game.GetGameState(); 22 | CHECK_EQ(gameState.curPlayerIdx, 0); 23 | } 24 | 25 | TEST_CASE("[Game] - GetNumPlayers") 26 | { 27 | Game game{ GameConfig{ 2 } }; 28 | game.Start(); 29 | 30 | CHECK_EQ(game.GetNumPlayers(), 2); 31 | } 32 | 33 | TEST_CASE("[Game] - RollDices") 34 | { 35 | GameConfig config; 36 | config.numPlayers = 2; 37 | 38 | Game game{ config }; 39 | game.Start(); 40 | 41 | Player& curPlayer = game.GetCurrentPlayer(); 42 | curPlayer.RollDices(true); 43 | 44 | std::array values1 = curPlayer.GetDiceValues(); 45 | for (auto& value : values1) 46 | { 47 | CHECK((value >= 1 && value <= 6)); 48 | } 49 | 50 | curPlayer.RollDices(false); 51 | 52 | std::array values2 = curPlayer.GetDiceValues(); 53 | for (int i = 0; i < NUM_DICES; ++i) 54 | { 55 | CHECK_EQ(values1[i], values2[i]); 56 | } 57 | 58 | curPlayer.RollDices(false, { 2, 3 }); 59 | } 60 | 61 | TEST_CASE("[Game] - Basic") 62 | { 63 | GameConfig config; 64 | config.numPlayers = 2; 65 | 66 | Game game{ config }; 67 | game.Start(); 68 | 69 | for (std::size_t i = 0; i < config.numPlayers * NUM_CATEGORIES; ++i) 70 | { 71 | game.GetCurrentPlayer().RollDices(); 72 | game.GetCurrentPlayer().FillScoreCard(static_cast(i / 2)); 73 | } 74 | 75 | CHECK((game.GetGameState().players[0].result == Result::WON || 76 | game.GetGameState().players[0].result == Result::LOST)); 77 | CHECK((game.GetGameState().players[1].result == Result::WON || 78 | game.GetGameState().players[1].result == Result::LOST)); 79 | } -------------------------------------------------------------------------------- /Tests/UnitTests/Models/DiceTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include "doctest_proxy.hpp" 8 | 9 | #include 10 | 11 | using namespace YahtzeeMaster; 12 | 13 | TEST_CASE("[Dice] - Roll") 14 | { 15 | Dice dice; 16 | 17 | dice.Roll(); 18 | CHECK((dice.GetValue() >= 1 && dice.GetValue() <= 6)); 19 | } -------------------------------------------------------------------------------- /Tests/UnitTests/Models/PlayerTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include "doctest_proxy.hpp" 8 | 9 | #include 10 | #include 11 | 12 | using namespace YahtzeeMaster; 13 | 14 | TEST_CASE("[Player] - RollDices") 15 | { 16 | Player player; 17 | 18 | player.RollDices(true); 19 | 20 | std::array values = player.GetDiceValues(); 21 | for (auto& value : values) 22 | { 23 | CHECK((value >= 1 && value <= 6)); 24 | } 25 | } 26 | 27 | TEST_CASE("[Player] - CalculateScores") 28 | { 29 | Player player; 30 | 31 | player.SetDiceValues({ 6, 1, 3, 2, 4 }); 32 | player.CalculateScores(); 33 | 34 | std::array scores = player.GetScores(); 35 | CHECK_EQ(scores[Category::ACES], 1); 36 | CHECK_EQ(scores[Category::TWOS], 2); 37 | CHECK_EQ(scores[Category::THREES], 3); 38 | CHECK_EQ(scores[Category::FOURS], 4); 39 | CHECK_EQ(scores[Category::FIVES], 0); 40 | CHECK_EQ(scores[Category::SIXES], 6); 41 | CHECK_EQ(scores[Category::THREE_OF_A_KIND], 0); 42 | CHECK_EQ(scores[Category::FOUR_OF_A_KIND], 0); 43 | CHECK_EQ(scores[Category::FULL_HOUSE], 0); 44 | CHECK_EQ(scores[Category::SMALL_STRAIGHT], 30); 45 | CHECK_EQ(scores[Category::LARGE_STRAIGHT], 0); 46 | CHECK_EQ(scores[Category::YAHTZEE], 0); 47 | CHECK_EQ(scores[Category::CHANCE], 16); 48 | 49 | player.SetDiceValues({ 2, 5, 5, 2, 5 }); 50 | player.CalculateScores(); 51 | 52 | scores = player.GetScores(); 53 | CHECK_EQ(scores[Category::ACES], 0); 54 | CHECK_EQ(scores[Category::TWOS], 4); 55 | CHECK_EQ(scores[Category::THREES], 0); 56 | CHECK_EQ(scores[Category::FOURS], 0); 57 | CHECK_EQ(scores[Category::FIVES], 15); 58 | CHECK_EQ(scores[Category::SIXES], 0); 59 | CHECK_EQ(scores[Category::THREE_OF_A_KIND], 19); 60 | CHECK_EQ(scores[Category::FOUR_OF_A_KIND], 0); 61 | CHECK_EQ(scores[Category::FULL_HOUSE], 25); 62 | CHECK_EQ(scores[Category::SMALL_STRAIGHT], 0); 63 | CHECK_EQ(scores[Category::LARGE_STRAIGHT], 0); 64 | CHECK_EQ(scores[Category::YAHTZEE], 0); 65 | CHECK_EQ(scores[Category::CHANCE], 19); 66 | 67 | player.SetDiceValues({ 6, 6, 6, 6, 6 }); 68 | player.CalculateScores(); 69 | 70 | scores = player.GetScores(); 71 | CHECK_EQ(scores[Category::ACES], 0); 72 | CHECK_EQ(scores[Category::TWOS], 0); 73 | CHECK_EQ(scores[Category::THREES], 0); 74 | CHECK_EQ(scores[Category::FOURS], 0); 75 | CHECK_EQ(scores[Category::FIVES], 0); 76 | CHECK_EQ(scores[Category::SIXES], 30); 77 | CHECK_EQ(scores[Category::THREE_OF_A_KIND], 30); 78 | CHECK_EQ(scores[Category::FOUR_OF_A_KIND], 30); 79 | CHECK_EQ(scores[Category::FULL_HOUSE], 0); 80 | CHECK_EQ(scores[Category::SMALL_STRAIGHT], 0); 81 | CHECK_EQ(scores[Category::LARGE_STRAIGHT], 0); 82 | CHECK_EQ(scores[Category::YAHTZEE], 50); 83 | CHECK_EQ(scores[Category::CHANCE], 30); 84 | } -------------------------------------------------------------------------------- /Tests/UnitTests/Models/ScoreCardTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #include "doctest_proxy.hpp" 8 | 9 | #include 10 | #include 11 | 12 | using namespace YahtzeeMaster; 13 | 14 | TEST_CASE("[ScoreCard] - FillScore") 15 | { 16 | GameConfig config; 17 | config.numPlayers = 1; 18 | 19 | Game game{ config }; 20 | game.Start(); 21 | 22 | CHECK_EQ(game.GetGameState().curRound, 1); 23 | 24 | game.GetCurrentPlayer().RollDices(); 25 | game.GetCurrentPlayer().FillScoreCard(Category::ACES); 26 | CHECK_EQ(game.GetGameState().curRound, 2); 27 | 28 | game.GetCurrentPlayer().RollDices(); 29 | game.GetCurrentPlayer().FillScoreCard(Category::ACES); 30 | CHECK_EQ(game.GetGameState().curRound, 2); 31 | } 32 | 33 | TEST_CASE("[ScoreCard] - GetScore") 34 | { 35 | ScoreCard scoreCard; 36 | 37 | scoreCard.FillScore(Category::TWOS, 10); 38 | scoreCard.FillScore(Category::FOUR_OF_A_KIND, 21); 39 | 40 | CHECK_EQ(scoreCard.GetScore(Category::ACES), 0); 41 | CHECK_EQ(scoreCard.GetScore(Category::TWOS), 10); 42 | CHECK_EQ(scoreCard.GetScore(Category::THREE_OF_A_KIND), 0); 43 | CHECK_EQ(scoreCard.GetScore(Category::FOUR_OF_A_KIND), 21); 44 | } 45 | 46 | TEST_CASE("[ScoreCard] - GetTotalScore") 47 | { 48 | ScoreCard scoreCard; 49 | 50 | scoreCard.FillScore(Category::ACES, 3); 51 | scoreCard.FillScore(Category::TWOS, 6); 52 | scoreCard.FillScore(Category::THREES, 9); 53 | scoreCard.FillScore(Category::FOURS, 12); 54 | scoreCard.FillScore(Category::FIVES, 15); 55 | CHECK_EQ(scoreCard.GetTotalScore(), 45); 56 | 57 | scoreCard.FillScore(Category::SIXES, 18); 58 | CHECK_EQ(scoreCard.GetTotalScore(), 98); 59 | } 60 | 61 | TEST_CASE("[ScoreCard] - IsFilled") 62 | { 63 | ScoreCard scoreCard; 64 | 65 | for (std::size_t i = 0; i < NUM_CATEGORIES; ++i) 66 | { 67 | CHECK_FALSE(scoreCard.IsFilled(static_cast(i))); 68 | } 69 | 70 | scoreCard.FillScore(Category::ACES, 4); 71 | scoreCard.FillScore(Category::FULL_HOUSE, 26); 72 | 73 | for (std::size_t i = 0; i < NUM_CATEGORIES; ++i) 74 | { 75 | if (i == 0 || i == 8) 76 | { 77 | CHECK(scoreCard.IsFilled(static_cast(i))); 78 | } 79 | else 80 | { 81 | CHECK_FALSE(scoreCard.IsFilled(static_cast(i))); 82 | } 83 | } 84 | } 85 | 86 | TEST_CASE("[ScoreCard] - ThreeOfAKind") 87 | { 88 | const std::array diceValues1{ 1, 2, 4, 4, 5 }; 89 | const std::array diceValues2{ 1, 4, 4, 4, 5 }; 90 | 91 | CHECK_EQ(ScoreCard::IsThreeOfAKind(diceValues1), false); 92 | CHECK_EQ(ScoreCard::IsThreeOfAKind(diceValues2), true); 93 | } 94 | 95 | TEST_CASE("[ScoreCard] - FourOfAKind") 96 | { 97 | const std::array diceValues1{ 1, 4, 4, 4, 5 }; 98 | const std::array diceValues2{ 1, 4, 4, 4, 4 }; 99 | 100 | CHECK_EQ(ScoreCard::IsFourOfAKind(diceValues1), false); 101 | CHECK_EQ(ScoreCard::IsFourOfAKind(diceValues2), true); 102 | } 103 | 104 | TEST_CASE("[ScoreCard] - FullHouse") 105 | { 106 | const std::array diceValues1{ 2, 4, 4, 4, 5 }; 107 | const std::array diceValues2{ 2, 2, 4, 4, 4 }; 108 | 109 | CHECK_EQ(ScoreCard::IsFullHouse(diceValues1), false); 110 | CHECK_EQ(ScoreCard::IsFullHouse(diceValues2), true); 111 | } 112 | 113 | TEST_CASE("[ScoreCard] - SmallStraight") 114 | { 115 | const std::array diceValues1{ 1, 3, 4, 4, 6 }; 116 | const std::array diceValues2{ 2, 2, 3, 4, 5 }; 117 | 118 | CHECK_EQ(ScoreCard::IsSmallStraight(diceValues1), false); 119 | CHECK_EQ(ScoreCard::IsSmallStraight(diceValues2), true); 120 | } 121 | 122 | TEST_CASE("[ScoreCard] - LargeStraight") 123 | { 124 | const std::array diceValues1{ 2, 2, 3, 4, 5 }; 125 | const std::array diceValues2{ 2, 3, 4, 5, 6 }; 126 | 127 | CHECK_EQ(ScoreCard::IsLargeStraight(diceValues1), false); 128 | CHECK_EQ(ScoreCard::IsLargeStraight(diceValues2), true); 129 | } 130 | 131 | TEST_CASE("[ScoreCard] - Yahtzee") 132 | { 133 | const std::array diceValues1{ 3, 4, 4, 4, 4 }; 134 | const std::array diceValues2{ 4, 4, 4, 4, 4 }; 135 | 136 | CHECK_EQ(ScoreCard::IsYahtzee(diceValues1), false); 137 | CHECK_EQ(ScoreCard::IsYahtzee(diceValues2), true); 138 | } -------------------------------------------------------------------------------- /Tests/UnitTests/doctest_proxy.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include -------------------------------------------------------------------------------- /Tests/UnitTests/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020 Chris Ohk 2 | 3 | // I am making my contributions/submissions to this project solely in our 4 | // personal capacity and am not conveying any rights to any intellectual 5 | // property of any third parties. 6 | 7 | #define DOCTEST_CONFIG_IMPLEMENT 8 | #include 9 | 10 | int main() 11 | { 12 | doctest::Context context; 13 | 14 | // Run queries, or run tests unless --no-run is specified 15 | const int res = context.run(); 16 | 17 | return res; 18 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 ({build}) 2 | 3 | skip_branch_with_pr: true 4 | 5 | image: 6 | - Visual Studio 2017 7 | - Visual Studio 2019 8 | 9 | platform: 10 | - x64 11 | 12 | matrix: 13 | fast_finish: true # Stop remaining jobs after a job failure 14 | 15 | configuration: 16 | - Release 17 | 18 | clone_folder: C:\YahtzeeMaster 19 | 20 | install: 21 | - git submodule update --init 22 | - ps: | 23 | if ("$env:APPVEYOR_BUILD_WORKER_IMAGE" -eq "Visual Studio 2017") { 24 | $env:CMAKE_GENERATOR = "Visual Studio 15 2017" 25 | } else { 26 | $env:CMAKE_GENERATOR = "Visual Studio 16 2019" 27 | } 28 | $env:PYTHON = "36-x64" 29 | $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" 30 | before_build: 31 | - md C:\YahtzeeMaster\build 32 | - cd C:\YahtzeeMaster\build 33 | - cmake .. -G "%CMAKE_GENERATOR%" -A x64 34 | 35 | build: 36 | project: C:\YahtzeeMaster\build\YahtzeeMaster.sln 37 | parallel: true 38 | verbosity: normal 39 | 40 | after_build: 41 | - C:\YahtzeeMaster\build\bin\Release\UnitTests.exe -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: Windows_VS2017 3 | pool: 4 | vmImage: 'vs2017-win2016' 5 | steps: 6 | - template: Scripts/azure_pipelines_build_windows_vs2017.yml 7 | - job: Windows_VS2019 8 | pool: 9 | vmImage: 'windows-2019' 10 | steps: 11 | - template: Scripts/azure_pipelines_build_windows_vs2019.yml 12 | - job: macOS 13 | pool: 14 | vmImage: 'macOS-10.15' 15 | steps: 16 | - template: Scripts/azure_pipelines_build_macos.yml -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: yes 4 | 5 | coverage: 6 | precision: 2 7 | round: down 8 | range: 50...100 9 | 10 | status: 11 | # Learn more at https://codecov.io/docs#yaml_default_commit_status 12 | project: true 13 | patch: true 14 | changes: false 15 | 16 | # We don't want statistics for the tests themselves and certainly not for the 17 | # benchmarks and boost libraries. Note that while we exclude the gcov data for 18 | # these patterns in the codecov call (codecov --gcov-glob ...), the fact that 19 | # our code references these areas also means we need to tell codecov itself to 20 | # ignore them from the stats. 21 | ignore: 22 | - 'Builds' 23 | - 'Datas' 24 | - 'Documents' 25 | - 'Extensions' 26 | - 'Includes' 27 | - 'Libraries' 28 | - 'Medias' 29 | - 'Resources' 30 | - 'Scripts' 31 | 32 | comment: 33 | layout: "header, diff, changes, uncovered" 34 | behavior: default # update if exists else create new -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | wheel -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | 2 | # Original code from https://github.com/pybind/cmake_example/blob/master/setup.py 3 | 4 | import os 5 | import re 6 | import sys 7 | import platform 8 | import subprocess 9 | import multiprocessing 10 | 11 | from setuptools import setup, Extension 12 | from setuptools.command.build_ext import build_ext 13 | from distutils.version import LooseVersion 14 | 15 | class CMakeExtension(Extension): 16 | def __init__(self, name, sourcedir=''): 17 | Extension.__init__(self, name, sources=[]) 18 | self.sourcedir = os.path.abspath(sourcedir) 19 | 20 | class CMakeBuild(build_ext): 21 | def run(self): 22 | try: 23 | out = subprocess.check_output(['cmake', '--version']) 24 | except OSError: 25 | raise RuntimeError("CMake must be installed to build the following extensions: " + 26 | ", ".join(e.name for e in self.extensions)) 27 | 28 | if platform.system() == "Windows": 29 | cmake_version = LooseVersion( 30 | re.search(r'version\s*([\d.]+)', out.decode()).group(1)) 31 | if cmake_version < '3.8.2': 32 | raise RuntimeError("CMake >= 3.8.2 is required on Windows") 33 | 34 | for ext in self.extensions: 35 | self.build_extension(ext) 36 | 37 | def build_extension(self, ext): 38 | extdir = os.path.abspath(os.path.dirname( 39 | self.get_ext_fullpath(ext.name))) 40 | cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, 41 | '-DPYTHON_EXECUTABLE=' + sys.executable, 42 | '-DBUILD_FROM_PIP=ON'] 43 | 44 | cfg = 'Debug' if self.debug else 'Debug' 45 | build_args = ['--config', cfg] 46 | 47 | env = os.environ.copy() 48 | 49 | if platform.system() == "Windows": 50 | cmake_args += [ 51 | '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] 52 | if sys.maxsize > 2**32: 53 | cmake_args += ['-A', 'x64'] 54 | build_args += ['--', '/m'] 55 | else: 56 | cmake_args += ['-DCMAKE_BUILD_TYPE=' + cfg] 57 | num_jobs = env.get('NUM_JOBS', multiprocessing.cpu_count()) 58 | build_args += ['--', '-j%s' % str(num_jobs), 'pyYahtzee'] 59 | 60 | env['CXXFLAGS'] = '{} -DVERSION_INFO=\\"{}\\"'.format(env.get('CXXFLAGS', ''), 61 | self.distribution.get_version()) 62 | if not os.path.exists(self.build_temp): 63 | os.makedirs(self.build_temp) 64 | subprocess.check_call(['cmake', ext.sourcedir] + 65 | cmake_args, cwd=self.build_temp, env=env) 66 | subprocess.check_call(['cmake', '--build', '.'] + 67 | build_args, cwd=self.build_temp) 68 | 69 | 70 | setup( 71 | name='pyYahtzee', 72 | version='0.1', 73 | author='Chris Ohk', 74 | author_email='utilforever@gmail.com', 75 | description='Yahtzee simulator with some reinforcement learning', 76 | long_description='', 77 | ext_modules=[CMakeExtension('pyYahtzee')], 78 | cmdclass=dict(build_ext=CMakeBuild), 79 | zip_safe=False, 80 | ) -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=YahtzeeMaster 2 | sonar.projectName=YahtzeeMaster 3 | sonar.projectVersion=0.1 4 | 5 | # ===================================================== 6 | # Meta-data for the project 7 | # ===================================================== 8 | 9 | sonar.links.homepage=https://github.com/utilForever/YahtzeeMaster 10 | sonar.links.ci=https://travis-ci.org/utilForever/YahtzeeMaster 11 | sonar.links.scm=https://github.com/utilForever/YahtzeeMaster 12 | sonar.links.issue=https://github.com/utilForever/YahtzeeMaster/issues 13 | 14 | # ===================================================== 15 | # Properties that will be shared amongst all modules 16 | # ===================================================== 17 | 18 | # SQ standard properties 19 | sonar.sources=. 20 | sonar.exclusions=README.md,3RD-PARTY.md,Dockerfile,setup.py,Datas/**,Documents/**,Libraries/**,Resources/**,Scripts/** 21 | 22 | # Properties specific to the C/C++ analyzer: 23 | sonar.cfamily.build-wrapper-output=bw-output 24 | sonar.cfamily.gcov.reportsPath=build/Tests/UnitTests/CMakeFiles --------------------------------------------------------------------------------