Move over old poker code
This commit is contained in:
@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
mkdir tools
|
||||
cd tools
|
||||
cmake .. -DDAWN_BUILD_TARGET=target-tools
|
||||
make
|
@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
git submodule update --init --recursive
|
@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
sudo apt install build-essential
|
@ -1,38 +0,0 @@
|
||||
#!/bin/bash
|
||||
sudo apt-get install cmake libarchive-tools
|
||||
git clone https://github.com/vitasdk/vdpm ~/vdpm
|
||||
cd ~/vdpm
|
||||
./bootstrap-vitasdk.sh
|
||||
export PATH=$VITASDK/bin:$PATH
|
||||
|
||||
git clone https://github.com/vitasdk/packages.git ~/vitapackages
|
||||
cd ~/vitapackages
|
||||
|
||||
dir_array=(
|
||||
zlib
|
||||
bzip2
|
||||
henkaku
|
||||
taihen
|
||||
kubridge
|
||||
openal-soft
|
||||
openssl
|
||||
curl
|
||||
curlpp
|
||||
expat
|
||||
opus
|
||||
opusfile
|
||||
glm
|
||||
kuio
|
||||
vitaShaRK
|
||||
libmathneon
|
||||
vitaGL
|
||||
SceShaccCgExt
|
||||
)
|
||||
|
||||
curdir=$(pwd)
|
||||
for d in "${dir_array[@]}";do
|
||||
echo "${curdir}${d}"
|
||||
cd "${curdir}/${d}"
|
||||
vita-makepkg
|
||||
vdpm *-arm.tar.xz
|
||||
done
|
@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
mkdir -p vita/build
|
||||
cd vita/build
|
||||
if [ ! -d "src" ]
|
||||
then
|
||||
cmake ../.. -DDAWN_BUILD_TARGET=target-helloworld-vita -DCMAKE_BUILD_TYPE=Debug
|
||||
fi
|
||||
make
|
||||
cp ./src/dawnvita/*.vpk ../
|
||||
cd ../..
|
@ -1,340 +0,0 @@
|
||||
# Compiling, Debugging and Running
|
||||
This document's purpose is to explain how to compile, debug and run the project
|
||||
on your machine. It is assumed that you have a basic understanding of the
|
||||
fundamentals of your operating system and how to use a terminal.
|
||||
|
||||
## Preamble
|
||||
The Dawn project is written almost entirely in C and C++. This includes all of
|
||||
the tooling used to generate the project and assets. The only non-C/C++ code is
|
||||
used by CMake to generate the output compilation files. This provides the Dawn
|
||||
project an extremely high level of portability not typically seen in other
|
||||
projects.
|
||||
|
||||
## TLDR; Version
|
||||
This document is going to go over the installation and configuration of the
|
||||
following items. If you are already familiar with these tools, you can skip to
|
||||
the "Downloading the Source Code" section.
|
||||
- C/C++ Compiler
|
||||
- CMake
|
||||
- Git
|
||||
- IDE
|
||||
You may also need *python*, since we depend on third-party libraries that may use
|
||||
python scripts to generate their own build files. This is not required for the
|
||||
Dawn project itself.
|
||||
|
||||
## Pre-Configuration
|
||||
In order to compile the Dawn project, you are required to have the following
|
||||
tools installed on your machine.
|
||||
|
||||
### 1. A C/C++ Compiler
|
||||
The exact tool(s) will depend on your specific scenario. The compiler is used to
|
||||
take the .cpp/.hpp files and generating binaries that execute on the target
|
||||
machine. Typically you will use your own C/C++ compiler for the machine that you
|
||||
are currently running, e.g. if you are running Windows, you will use the Visual
|
||||
Studio compiler. If you are running Linux, you will use GCC or Clang. If you are
|
||||
running macOS, you will use Clang, and so-on.
|
||||
|
||||
If you are intending to compile on a different machine than the one you are
|
||||
currently running, you will need to use a cross-compiler that is specific for
|
||||
your use-case. You will also need to refer to the documentation for creating a
|
||||
new Dawn engine target.
|
||||
|
||||
Please follow the instructions for your specific operating system to install the
|
||||
appropriate C/C++ compiler.
|
||||
|
||||
**Windows**
|
||||
You will need to download and install [Visual Studio](https://visualstudio.microsoft.com/downloads/).
|
||||
Visual Studio (not to be confused with Visual Studio Code) is a full IDE that
|
||||
bundles the official Microsoft C/C++ compiler. It is the recommended compiler
|
||||
for building the Dawn project on Windows.
|
||||
|
||||
Advanced used can also use [MinGW](http://www.mingw.org/) or another compiler if
|
||||
they wish, however this is not officially supported.
|
||||
|
||||
After installing Visual Studio, you will need to install the C++ development
|
||||
tools. This can be done by opening the Visual Studio Installer and selecting
|
||||
the "Desktop development with C++" workload.
|
||||
|
||||
**Linux**
|
||||
You will need to install the GCC and Clang compilers. The compilers are usually
|
||||
installed either by default or by installing the necessary packages for your
|
||||
Linux distribution.
|
||||
|
||||
For example, on Ubuntu, you can install the GCC and Clang compilers by running
|
||||
the following command:
|
||||
```bash
|
||||
sudo apt install build-essential clang
|
||||
```
|
||||
|
||||
On Arch Linux, you can install the GCC and Clang compilers by running the
|
||||
following command:
|
||||
```bash
|
||||
sudo pacman -S base-devel clang
|
||||
```
|
||||
|
||||
And on Fedora, you can install the GCC and Clang compilers by running the
|
||||
following command:
|
||||
```bash
|
||||
sudo dnf install @development-tools clang
|
||||
```
|
||||
|
||||
For other distributions, please refer to your distribution's documentation.
|
||||
|
||||
**macOS**
|
||||
You will need to install the Xcode command line tools. This can be done by done
|
||||
by running the following command in your terminal:
|
||||
```bash
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
Afterwards you will need to install and enable [Brew](https://brew.sh/). This
|
||||
can be done by running the following command in your terminal:
|
||||
```bash
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
```
|
||||
|
||||
|
||||
### 2. CMake
|
||||
CMake is a tool that is used to generate the compilation files for the Dawn
|
||||
project. In short this is used to create versions of the Dawn project that can
|
||||
be compiled on all different sets of compilers, so if you are compiling on, for
|
||||
example, Windows, you can use CMake to generate the compilation files for the
|
||||
Visual Studio compiler, or if you are compiling on Linux, you can use CMake to
|
||||
generate the compilation files for the GCC or Clang compilers, and so-on.
|
||||
|
||||
To install CMake, please follow the instructions for your specific operating
|
||||
system. All other operating systems can be found on the [CMake downloads page](https://cmake.org/download/).
|
||||
|
||||
**Windows**
|
||||
You will need to download and install [CMake](https://cmake.org/download/). The
|
||||
installer will guide you through the installation process and will install the
|
||||
CMake executable to your system. It is recommended that you add CMake to your
|
||||
system PATH if requested by the installer.
|
||||
|
||||
**Linux**
|
||||
Like installing the C/C++ compilers, you will need to install CMake using your
|
||||
specific Linux distribution's package manager, there is also a chance that CMake
|
||||
can be installed using flatpak or snap. Please refer to your distribution's
|
||||
documentation for more information.
|
||||
|
||||
For Ubuntu and other Debian-based distributions, you can install CMake by using
|
||||
the following command:
|
||||
```bash
|
||||
sudo apt install cmake
|
||||
```
|
||||
|
||||
For Arch Linux, you can install CMake by using the following command:
|
||||
```bash
|
||||
sudo pacman -S cmake
|
||||
```
|
||||
|
||||
And for Fedora, you can install CMake by using the following command:
|
||||
```bash
|
||||
sudo dnf install cmake
|
||||
```
|
||||
|
||||
**macOS**
|
||||
You will install CMake using brew. We detailed how to install brew in the C/C++
|
||||
compiler section. To install CMake, run the following command in your terminal:
|
||||
```bash
|
||||
brew install cmake
|
||||
```
|
||||
|
||||
### 3. Git
|
||||
Git is a version control system that is used to manage the Dawn project's source
|
||||
code. It is used to download the source code, and to update the source code to
|
||||
the latest version. It is also used to manage the project's dependencies. Git is
|
||||
a standard tool in the programming industry and is used to manage complex
|
||||
projects, especially those that are worked on by multiple people.
|
||||
|
||||
Git, like all of the above tools, is installed slightly differently depending on
|
||||
your operating system. Please follow the instructions for your specific
|
||||
operating system.
|
||||
|
||||
**Windows**
|
||||
You will need to download and install [Git](https://git-scm.com/downloads). The
|
||||
installer will guide you through the installation process and will install the
|
||||
Git executable to your system. It is recommended that you add Git to your system
|
||||
PATH if requested by the installer. You do not need to add the Context-menu
|
||||
items to your system.
|
||||
|
||||
**Linux**
|
||||
Like installing the C/C++ compilers, you will need to install Git using your
|
||||
specific Linux distribution's package manager. It is also likely that git would
|
||||
have been installed by default. Please refer to your distribution's docs for
|
||||
more information.
|
||||
|
||||
For Ubuntu and other Debian-based distributions, you can install Git by using
|
||||
the following command:
|
||||
```bash
|
||||
sudo apt install git
|
||||
```
|
||||
|
||||
For Arch Linux, you can install Git by using the following command:
|
||||
```bash
|
||||
sudo pacman -S git
|
||||
```
|
||||
|
||||
And for Fedora, you can install Git by using the following command:
|
||||
```bash
|
||||
sudo dnf install git
|
||||
```
|
||||
|
||||
**macOS**
|
||||
You will install Git using brew. We detailed how to install brew in the C/C++
|
||||
compiler section. To install Git, run the following command in your terminal:
|
||||
```bash
|
||||
brew install git
|
||||
```
|
||||
|
||||
### 4. An IDE
|
||||
An IDE (Integrated Development Environment) is a tool that is used to view and
|
||||
edit code of projects. While it is not required to use an IDE, it is recommended
|
||||
since it can make the process of editing and running the project much easier.
|
||||
|
||||
There are many different IDEs available, and often people chose an IDE that will
|
||||
suit their preferences and needs, however if you are unsure of which IDE you
|
||||
should be using, the Dawn project recommends using [Visual Studio Code](https://code.visualstudio.com/),
|
||||
not to be confused with Visual Studio.
|
||||
|
||||
Visual Studio Code is a free and open-source IDE that is widely used in the
|
||||
industry for its simple, modern and configurable interface. For example you can
|
||||
configure VSCode to work on Web Projects, Game Projects, and more. It acts like
|
||||
a text editor with a bunch of extra tools.
|
||||
|
||||
In addition to installing the VSCode IDE, we will add several recommended
|
||||
plugins that will make editing the Dawn project easier and more seamless.
|
||||
|
||||
To install VSCode follow the instructions for your specific operating system on
|
||||
the [VSCode downloads page](https://code.visualstudio.com/download), as it can
|
||||
vary a lot between operating systems.
|
||||
|
||||
After installing VSCode, you will need to install the following plugins, by
|
||||
clicking on the "Extensions" icon on the left-hand side of the VSCode window,
|
||||
and searching for the following plugins. You may also be able to click on the
|
||||
following links to install the plugins directly, but it may not work.
|
||||
- [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)
|
||||
- [CMake](https://marketplace.visualstudio.com/items?itemName=twxs.cmake)
|
||||
- [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools)
|
||||
|
||||
## Downloading the Source Code
|
||||
Now that you have all of the necessary tools installed, you can download the
|
||||
source code of the project and using it to build. The source code contains all
|
||||
of the code of the project, as well as the assets and the build scripts. This is
|
||||
confidential information and should not be shared with anyone.
|
||||
|
||||
### 1. Cloning the Repository
|
||||
We previously installed the git tool, which is used to download the source code
|
||||
of the project, and some third-party libraries. To download the source code, you
|
||||
will need to open a terminal and navigate to the directory where you want to
|
||||
download the source code to.
|
||||
|
||||
Afterwards, you will need to run the following command:
|
||||
```bash
|
||||
git clone https://git.wish.moe/YourWishes/Dawn.git
|
||||
```
|
||||
This will download the source code of the project to a new subdirectory called
|
||||
"Dawn" and put all of the projects' source code within there.
|
||||
|
||||
### 2. Installing the Dependencies / Libraries.
|
||||
I try to keep dependencies on third-party libraries to a minimum, however there
|
||||
are a few libraries that are required to build the Dawn project. These libraries
|
||||
are not included in the source code, and must be downloaded separately. This is
|
||||
done using the git tool.
|
||||
|
||||
After you have cloned the above repository, you will need to open a terminal and
|
||||
navigate to the directory where you downloaded the source code to. Afterwards,
|
||||
you will need to run the following command:
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
This will fetch all of the third-party libraries that are required to build the
|
||||
Dawn project. This may take a while depending on your internet connection.
|
||||
|
||||
### 3. Loading the Project
|
||||
This step is semi-optional. We are aiming to build the project using CMake, and
|
||||
the easiest way to do this is to use the CMake Tools plugin for VSCode that we
|
||||
installed earlier. This plugin will automatically detect the CMake files in the
|
||||
project and will allow us to build the project using the VSCode interface.
|
||||
|
||||
If you opted out of using VSCode, you will need to set up your CMake environment
|
||||
to suit your IDE or needs. This is outside of the scope of this document, and
|
||||
you will need to refer to your IDE's documentation for more information.
|
||||
|
||||
To load the project, you will need to open VSCode and open the Dawn project
|
||||
directory. Most operating systems will allow you to do this by dragging the
|
||||
Dawn project directory onto the VSCode window. If this does not work, you can
|
||||
open VSCode and click on the "File" menu, and click on "Open Folder". You will
|
||||
then need to navigate to the Dawn project directory and click "Open".
|
||||
|
||||
Afterwards you will likely be prompted autometically to configure the CMake
|
||||
Tools plugin. If you are not, you can click on the "CMake" icon on the left-hand
|
||||
side of the VSCode window, and click on "Configure". This will configure the
|
||||
CMake Tools plugin to use the CMake files in the Dawn project directory.
|
||||
|
||||
You may also be asked to select a compiler. If you are using Windows, you will
|
||||
need to select the Visual Studio compiler. If you are using Linux, you will need
|
||||
to select the GCC or Clang compiler. If you are using macOS, you will need to
|
||||
select the Clang compiler. If you are using a different compiler, you will need
|
||||
to select the appropriate compiler.
|
||||
|
||||
If prompted to select a build type, select "Debug".
|
||||
|
||||
## Compiling the Project
|
||||
Now that we have the project loaded into our IDE, we can compile the project.
|
||||
This is done using the CMake Tools plugin for VSCode. If you are not using
|
||||
VSCode you will need to refer to your IDE's documentation for more information.
|
||||
|
||||
Firstly, you will need to create a settings file to configure the project. In
|
||||
VSCode you can create a new folder called `.vscode` (Including the leading `.`)
|
||||
in the Dawn project root directory. Afterwards, you will need to create a new
|
||||
file called `settings.json` in the `.vscode` directory. You will then need to
|
||||
paste the following into the file:
|
||||
```json
|
||||
{
|
||||
"cmake.configureArgs": [
|
||||
"-DDAWN_BUILD_TOOLS=true",
|
||||
"-DDAWN_BUILD_TARGET=target-liminal-win32-glfw",
|
||||
"-DDAWN_DEBUG_BUILD=true"
|
||||
]
|
||||
}
|
||||
```
|
||||
And save the file. You may want to alter the values to suit your needs. For
|
||||
example, if you are compiling on Linux, you will need to change
|
||||
`target-liminal-win32-glfw` to `target-liminal-linux-glfw`. If you are compiling
|
||||
on macOS, you will need to change it to `target-liminal-osx-glfw`. The specific
|
||||
configure arguments are outside of the scope of this document, and you will need
|
||||
to refer to the specific target documentation for more information.
|
||||
|
||||
After you have created the settings file, you will need to configure and build
|
||||
the project. To perform the build you need to click "Build" button the bottom of
|
||||
the VSCode window. This will compile the project and will output the resulting
|
||||
binaries in to a "build" directory within the Dawn project directory.
|
||||
|
||||
Upon clicking the "Build" button, you will see an output panel appear at the
|
||||
bottom of the VSCode window. This will show the process of the build, and will
|
||||
show any errors that may occur. This is used to debug and fix any issues that
|
||||
may occur during the build process.
|
||||
|
||||
If the build was successful, you will see a "Build finished" message in the
|
||||
output panel, typically read as;
|
||||
```bash
|
||||
[build] Build finished with exit code 0
|
||||
```
|
||||
If you do not see this message, or if the exit code is not 0, you will need to
|
||||
debug the issue. The output panel will show the error message which is helpful
|
||||
so we can debug the issue. If you are unable to debug the issue, you can ask for
|
||||
help in the Discord server.
|
||||
|
||||
## Running the built project
|
||||
After the build process succeeds you will be able to run the project. This is
|
||||
done by clicking on the Run icon, which looks like a play button, on the bottom
|
||||
of the VSCode window. This will run the project in production mode and will not
|
||||
show any debug information.
|
||||
|
||||
If the program hangs, crashes or does not run, you can click the Debug icon,
|
||||
which looks like a ladybug, on the bottom of the VSCode window. This will run
|
||||
the project in debug mode and will show debug information and HALT the program
|
||||
if there is an error detected. If program HALTS in debug mode you can use this
|
||||
information to debug the issue. If you are unable to debug the issue, you can
|
||||
ask for help in the Discord server.
|
15
src/dawnpoker/poker/CMakeLists.txt
Normal file
15
src/dawnpoker/poker/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (c) 2022 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
Card.cpp
|
||||
PokerPot.cpp
|
||||
PokerPlayer.cpp
|
||||
PokerGame.cpp
|
||||
PokerWinning.cpp
|
||||
PokerTurn.cpp
|
||||
)
|
70
src/dawnpoker/poker/Card.cpp
Normal file
70
src/dawnpoker/poker/Card.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "Card.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
void Card::fillDeck(std::vector<struct Card> *deck) {
|
||||
assertNotNull(deck);
|
||||
|
||||
for(uint8_t i = 0; i < CARD_DECK_SIZE; i++) {
|
||||
deck->push_back(Card(i));
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Card::contains(std::vector<struct Card> *deck, struct Card c) {
|
||||
assertNotNull(deck);
|
||||
|
||||
auto it = deck->begin();
|
||||
while(it != deck->end()) {
|
||||
if(it->cardValue == c.cardValue) return (int32_t)(it - deck->begin());
|
||||
++it;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t Card::containsNumber(
|
||||
std::vector<struct Card> *deck,
|
||||
enum CardValue number
|
||||
) {
|
||||
assertNotNull(deck);
|
||||
assertTrue(number < CARD_COUNT_PER_SUIT);
|
||||
|
||||
auto it = deck->begin();
|
||||
while(it != deck->end()) {
|
||||
if(it->getValue() == number) return (int32_t)(it - deck->begin());
|
||||
++it;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<struct Card> Card::countPairs(
|
||||
std::vector<struct Card> *deck,
|
||||
enum CardValue val
|
||||
) {
|
||||
std::vector<struct Card> pairs;
|
||||
|
||||
assertNotNull(deck);
|
||||
assertTrue(deck->size() > 0);
|
||||
|
||||
auto it = deck->begin();
|
||||
while(it != deck->end()) {
|
||||
if(it->getValue() == val) pairs.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
bool_t Card::cardSorter(struct Card left, struct Card right) {
|
||||
return left.cardValue < right.cardValue;
|
||||
}
|
||||
|
||||
void Card::sort(std::vector<struct Card> *deck) {
|
||||
assertNotNull(deck);
|
||||
assertTrue(deck->size() > 1);
|
||||
std::sort(deck->begin(), deck->end(), &Card::cardSorter);
|
||||
}
|
137
src/dawnpoker/poker/Card.hpp
Normal file
137
src/dawnpoker/poker/Card.hpp
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "dawnlibs.hpp"
|
||||
#include "assert/assert.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
enum class CardSuit : uint8_t {
|
||||
Clubs = 0,
|
||||
Diamonds = 1,
|
||||
Hearts = 2,
|
||||
Spades = 3,
|
||||
Invalid = 0xFF
|
||||
};
|
||||
|
||||
enum class CardValue : uint8_t {
|
||||
Two = 0,
|
||||
Three = 1,
|
||||
Four = 2,
|
||||
Five = 3,
|
||||
Six = 4,
|
||||
Seven = 5,
|
||||
Eight = 6,
|
||||
Nine = 7,
|
||||
Ten = 8,
|
||||
Jack = 9,
|
||||
Queen = 10,
|
||||
King = 11,
|
||||
Ace = 12,
|
||||
Invalid = 0xFF
|
||||
};
|
||||
|
||||
/** Count of cards in each suit */
|
||||
#define CARD_COUNT_PER_SUIT 13
|
||||
|
||||
/** Count of suits */
|
||||
#define CARD_SUIT_COUNT 4
|
||||
|
||||
/** Standard Card Deck Size */
|
||||
#define CARD_DECK_SIZE CARD_COUNT_PER_SUIT*CARD_SUIT_COUNT
|
||||
|
||||
struct Card {
|
||||
public:
|
||||
uint8_t cardValue;
|
||||
|
||||
/**
|
||||
* Shuffles a hand / deck
|
||||
*
|
||||
* @param deck Array of cards to shuffle.
|
||||
*/
|
||||
static void shuffle(std::vector<struct Card> *deck);
|
||||
|
||||
/**
|
||||
* Fills a vector with all of the cards in a deck, in order.
|
||||
*
|
||||
* @param deck Deck to fill.
|
||||
*/
|
||||
static void fillDeck(std::vector<struct Card> *deck);
|
||||
|
||||
/**
|
||||
* Check if an array of cards contains a specific card.
|
||||
*
|
||||
* @param deck Deck/Hand/Array of cards to check.
|
||||
* @param card Card to look for
|
||||
* @returns The index within the array that the card is. -1 if not found.
|
||||
*/
|
||||
static int32_t contains(std::vector<struct Card> *deck, struct Card card);
|
||||
|
||||
/**
|
||||
* Check if the array of cards contains a specific number.
|
||||
*
|
||||
* @param deck Array of cards to check
|
||||
* @param number The number to look for.
|
||||
* @returns The index that the first card is. -1 if not found.
|
||||
*/
|
||||
static int32_t containsNumber(
|
||||
std::vector<struct Card> *deck,
|
||||
enum CardValue number
|
||||
);
|
||||
|
||||
/**
|
||||
* Counts the amount of times a card's number appears within the given
|
||||
* hand.
|
||||
*
|
||||
* @param deck The hand to check
|
||||
* @param val Value of pairs to find.
|
||||
* @return Card pairs in the deck.
|
||||
*/
|
||||
static std::vector<struct Card> countPairs(
|
||||
std::vector<struct Card> *deck,
|
||||
enum CardValue val
|
||||
);
|
||||
|
||||
static bool_t cardSorter(struct Card left, struct Card right);
|
||||
|
||||
/**
|
||||
* Sort a hand of cards. Cards are ordered in descending weight, aces are
|
||||
* high. Cards will be grouped by their suits, e.g. CARD_CLUBS_TWO will
|
||||
* appear before CARD_DIAMONDS_KING.
|
||||
*
|
||||
* @param deck Hand of cards to sort.
|
||||
*/
|
||||
static void sort(std::vector<struct Card> *deck);
|
||||
|
||||
Card(CardSuit suit, CardValue num) :
|
||||
cardValue(((uint8_t)suit * CARD_COUNT_PER_SUIT) + num)
|
||||
{
|
||||
if(suit == CardSuit::Invalid || num == CardValue::Invalid) {
|
||||
this->cardValue = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
Card(uint8_t cv) : cardValue(cv) {
|
||||
// assertTrue(cv < CARD_DECK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of a given card.
|
||||
* @returns The card number.
|
||||
*/
|
||||
CardValue getValue() {
|
||||
return (CardValue)(cardValue % CARD_COUNT_PER_SUIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the suit of a given card.
|
||||
* @returns The suit.
|
||||
*/
|
||||
CardSuit getSuit() {
|
||||
return (CardSuit)(cardValue / CARD_COUNT_PER_SUIT);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
197
src/dawnpoker/poker/PokerGame.cpp
Normal file
197
src/dawnpoker/poker/PokerGame.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "PokerGame.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
PokerGame::PokerGame(SceneItem *item) : SceneItemComponent(item) {
|
||||
|
||||
}
|
||||
|
||||
void PokerGame::onStart() {
|
||||
SceneItemComponent::onStart();
|
||||
|
||||
this->players = this->getScene()->findComponents<PokerPlayer>();
|
||||
assertTrue(this->players.size() > 0);
|
||||
this->newGame();
|
||||
}
|
||||
|
||||
void PokerGame::newGame() {
|
||||
this->newRound();
|
||||
|
||||
auto it = this->players.begin();
|
||||
while(it != this->players.end()) {
|
||||
auto player = *it;
|
||||
player->setChips(POKER_PLAYER_CHIPS_DEFAULT);
|
||||
player->isOut = false;
|
||||
++it;
|
||||
}
|
||||
|
||||
this->setDealer(0x00);
|
||||
}
|
||||
|
||||
void PokerGame::newRound() {
|
||||
this->deck.clear();
|
||||
Card::fillDeck(&this->deck);
|
||||
|
||||
this->smallBlind = POKER_BLIND_SMALL_DEFAULT;
|
||||
this->bigBlind = POKER_BLIND_BIG_DEFAULT;
|
||||
this->grave.clear();
|
||||
this->community.clear();
|
||||
this->pots.clear();
|
||||
this->pots.push_back(PokerPot());
|
||||
|
||||
auto it = this->players.begin();
|
||||
while(it != this->players.end()) {
|
||||
auto player = *it;
|
||||
player->hand.clear();
|
||||
player->currentBet = 0;
|
||||
player->isFolded = false;
|
||||
player->isShowingHand = false;
|
||||
player->hasBetThisRound = false;
|
||||
player->timesRaised = 0;
|
||||
player->currentBet = 0;
|
||||
++it;
|
||||
}
|
||||
|
||||
this->setDealer(this->dealerIndex + 0x01);
|
||||
}
|
||||
|
||||
void PokerGame::newBettingRound() {
|
||||
auto it = this->players.begin();
|
||||
while(it != this->players.end()) {
|
||||
auto player = *it;
|
||||
player->hasBetThisRound = false;
|
||||
player->timesRaised = 0;
|
||||
++it;
|
||||
}
|
||||
|
||||
this->betterIndex = this->bigBlindIndex;
|
||||
this->betterIndex = this->getNextBetterIndex();
|
||||
}
|
||||
|
||||
uint8_t PokerGame::getNextBetterIndex() {
|
||||
uint8_t j, i;
|
||||
for(i = 0; i < this->players.size(); i++) {
|
||||
j = (i + this->betterIndex) % this->players.size();
|
||||
auto player = this->players[j];
|
||||
if(player->needsToBetThisRound()) return j;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
void PokerGame::takeBlinds() {
|
||||
auto playerSmallBlind = this->players[this->smallBlindIndex];
|
||||
auto playerBigBlind = this->players[this->bigBlindIndex];
|
||||
|
||||
playerSmallBlind->bet(this->smallBlind);
|
||||
playerBigBlind->bet(this->bigBlind);
|
||||
|
||||
playerSmallBlind->hasBetThisRound = false;
|
||||
playerBigBlind->hasBetThisRound = false;
|
||||
}
|
||||
|
||||
void PokerGame::setDealer(uint8_t dealer) {
|
||||
uint8_t i, k;
|
||||
PokerPlayer *player;
|
||||
bool_t foundDealer;
|
||||
bool_t foundSmall;
|
||||
|
||||
foundDealer = false;
|
||||
foundSmall = false;
|
||||
this->dealerIndex = dealer;
|
||||
|
||||
for(i = 0; i < this->players.size(); i++) {
|
||||
k = (dealer + i) % this->players.size();
|
||||
player = this->players[k];
|
||||
if(player->isOut) continue;
|
||||
if(!foundDealer) {
|
||||
this->dealerIndex = k;
|
||||
foundDealer = true;
|
||||
} else if(!foundSmall) {
|
||||
this->smallBlindIndex = k;
|
||||
foundSmall = true;
|
||||
} else {
|
||||
this->bigBlindIndex = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PokerGame::getRemainingBettersCount() {
|
||||
uint8_t count = 0;
|
||||
auto it = this->players.begin();
|
||||
while(it != this->players.end()) {
|
||||
if((*it)->needsToBetThisRound()) count++;
|
||||
++it;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t PokerGame::getRemainingPlayersCount() {
|
||||
uint8_t count = 0;
|
||||
auto it = this->players.begin();
|
||||
while(it != this->players.end()) {
|
||||
auto player = *it;
|
||||
if(!player->isFolded && !player->isOut) count++;
|
||||
++it;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int32_t PokerGame::getCurrentCallValue() {
|
||||
assertTrue(this->pots.size() > 0);
|
||||
return this->pots.back().call;
|
||||
}
|
||||
|
||||
void PokerGame::burnCard() {
|
||||
assertTrue(this->deck.size() > 0);
|
||||
auto card = this->deck.back();
|
||||
this->deck.pop_back();
|
||||
this->grave.push_back(card);
|
||||
}
|
||||
|
||||
void PokerGame::dealCard(PokerPlayer *player) {
|
||||
assertTrue(this->deck.size() > 0);
|
||||
auto card = this->deck.back();
|
||||
this->deck.pop_back();
|
||||
player->hand.push_back(card);
|
||||
}
|
||||
|
||||
void PokerGame::dealToEveryone(uint8_t count) {
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
auto it = this->players.begin();
|
||||
while(it != this->players.end()) {
|
||||
this->dealCard(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PokerGame::turn(uint8_t count) {
|
||||
assertTrue(this->deck.size() >= count);
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
auto card = this->deck.back();
|
||||
this->deck.pop_back();
|
||||
this->community.push_back(card);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t PokerGame::getCountOfCardsToTurn() {
|
||||
switch(this->community.size()) {
|
||||
case 0x00:
|
||||
return POKER_FLOP_CARD_COUNT;
|
||||
|
||||
case 0x03:
|
||||
return POKER_TURN_CARD_COUNT;
|
||||
|
||||
case 0x04:
|
||||
return POKER_RIVER_CARD_COUNT;
|
||||
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
59
src/dawnpoker/poker/PokerGame.hpp
Normal file
59
src/dawnpoker/poker/PokerGame.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "PokerPlayer.hpp"
|
||||
|
||||
/** The default blind cost for the big blind. */
|
||||
#define POKER_BLIND_BIG_DEFAULT 600
|
||||
/** The default blind cost for the small blind. (Defaults half big blind) */
|
||||
#define POKER_BLIND_SMALL_DEFAULT (POKER_BLIND_BIG_DEFAULT/2)
|
||||
|
||||
/** How many cards are dealt for the flop, turn and river */
|
||||
#define POKER_FLOP_CARD_COUNT 3
|
||||
#define POKER_TURN_CARD_COUNT 1
|
||||
#define POKER_RIVER_CARD_COUNT 1
|
||||
|
||||
namespace Dawn {
|
||||
class PokerGame : public SceneItemComponent {
|
||||
protected:
|
||||
std::vector<struct Card> deck;
|
||||
std::vector<struct Card> grave;
|
||||
std::vector<struct Card> community;
|
||||
uint8_t dealerIndex;
|
||||
uint8_t smallBlindIndex;
|
||||
uint8_t bigBlindIndex;
|
||||
int32_t smallBlind = POKER_BLIND_SMALL_DEFAULT;
|
||||
int32_t bigBlind = POKER_BLIND_BIG_DEFAULT;
|
||||
|
||||
public:
|
||||
std::vector<PokerPlayer*> players;
|
||||
std::vector<struct PokerPot> pots;
|
||||
uint8_t betterIndex;
|
||||
|
||||
PokerGame(SceneItem *item);
|
||||
|
||||
void onStart() override;
|
||||
|
||||
void newGame();
|
||||
void newRound();
|
||||
void newBettingRound();
|
||||
void takeBlinds();
|
||||
void setBlinds(int32_t small, int32_t big);
|
||||
uint8_t getRemainingBettersCount();
|
||||
int32_t getCurrentCallValue();
|
||||
uint8_t getNextBetterIndex();
|
||||
void setDealer(uint8_t dealer);
|
||||
void newDealer();
|
||||
void burnCard();
|
||||
void dealCard(PokerPlayer *player);
|
||||
void dealToEveryone(uint8_t count);
|
||||
void turn(uint8_t count);
|
||||
uint8_t getCountOfCardsToTurn();
|
||||
uint8_t getRemainingPlayersCount();
|
||||
|
||||
friend class PokerPlayer;
|
||||
};
|
||||
}
|
450
src/dawnpoker/poker/PokerPlayer.cpp
Normal file
450
src/dawnpoker/poker/PokerPlayer.cpp
Normal file
@ -0,0 +1,450 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "PokerPlayer.hpp"
|
||||
#include "PokerGame.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
PokerPlayer::PokerPlayer(SceneItem *item) : SceneItemComponent(item) {
|
||||
|
||||
}
|
||||
|
||||
void PokerPlayer::onStart() {
|
||||
SceneItemComponent::onStart();
|
||||
this->pokerGame = this->getScene()->findComponent<PokerGame>();
|
||||
}
|
||||
|
||||
void PokerPlayer::addChips(int32_t chips) {
|
||||
assertTrue(chips > 0);
|
||||
|
||||
this->chips += chips;
|
||||
if(this->chips > 0) this->isOut = false;
|
||||
eventChipsChanged.invoke();
|
||||
}
|
||||
|
||||
void PokerPlayer::setChips(int32_t chips) {
|
||||
this->chips = 0;
|
||||
this->addChips(chips);
|
||||
}
|
||||
|
||||
bool_t PokerPlayer::needsToBetThisRound() {
|
||||
if(this->isFolded) return false;
|
||||
if(this->chips <= 0) return false;
|
||||
if(!this->hasBetThisRound) return true;
|
||||
if(this->currentBet < this->pokerGame->getCurrentCallValue()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PokerPlayer::bet(struct PokerPot *pot, int32_t chips) {
|
||||
assertNotNull(pot);
|
||||
assertTrue(chips >= 0);
|
||||
assertTrue(!this->isFolded);
|
||||
assertTrue(!this->isOut);
|
||||
this->setChips(this->chips - chips);
|
||||
this->currentBet += chips;
|
||||
this->hasBetThisRound = true;
|
||||
if(chips > 0) {
|
||||
this->timesRaised++;
|
||||
} else {
|
||||
this->timesRaised = 0;
|
||||
}
|
||||
|
||||
pot->chips += chips;
|
||||
pot->call = mathMax<int32_t>(pot->call, this ->currentBet);
|
||||
|
||||
auto existing = std::find(pot->players.begin(), pot->players.end(), this);
|
||||
if(existing == pot->players.end()) pot->players.push_back(this);
|
||||
}
|
||||
|
||||
void PokerPlayer::bet(int32_t chips) {
|
||||
assertTrue(this->pokerGame->pots.size() > 0);
|
||||
this->bet(&this->pokerGame->pots.back(), chips);
|
||||
}
|
||||
|
||||
void PokerPlayer::fold() {
|
||||
this->isFolded = true;
|
||||
this->hasBetThisRound = true;
|
||||
this->timesRaised = 0;
|
||||
}
|
||||
|
||||
bool_t PokerPlayer::canCheck() {
|
||||
return this->pokerGame->getCurrentCallValue() <= this->currentBet;
|
||||
}
|
||||
|
||||
struct PokerTurn PokerPlayer::getAITurn() {
|
||||
struct PokerTurn turn;
|
||||
float_t confidence;
|
||||
int32_t callBet;
|
||||
float_t potOdds;
|
||||
|
||||
// Can the player do anything?
|
||||
if(this->isFolded || this->isOut) {
|
||||
turn.type = POKER_TURN_TYPE_OUT;
|
||||
return turn;
|
||||
}
|
||||
|
||||
// The following logic is heavily inspired by;
|
||||
// https://github.com/gorel/C-Poker-AI/blob/master/src/common/pokerai.c
|
||||
// But with some changes and smarts added by me. The original source code will
|
||||
// essentially just run a crap tun of simulated games and get the times that
|
||||
// they are expected to win from those games, but I'm just going to use the
|
||||
// odds of the winning hand.
|
||||
|
||||
|
||||
// Is this preflop?
|
||||
if(this->pokerGame->community.size() == 0) {
|
||||
assertTrue(this->hand.size() == POKER_PLAYER_HAND_SIZE_MAX);
|
||||
|
||||
// Get the hand weight
|
||||
auto cardNumber0 = this->hand[0].getValue();
|
||||
auto suitNumber0 = this->hand[0].getSuit();
|
||||
auto cardNumber1 = this->hand[1].getValue();
|
||||
auto suitNumber1 = this->hand[1].getSuit();
|
||||
|
||||
// Get delta between cards
|
||||
auto i = (uint8_t)mathAbs<int8_t>(
|
||||
(int8_t)cardNumber0 - (int8_t)cardNumber1
|
||||
);
|
||||
|
||||
// Get card weight
|
||||
confidence = (float_t)cardNumber0 + (float_t)cardNumber1;
|
||||
if(cardNumber0 == cardNumber1) {// Pairs
|
||||
confidence += 6;
|
||||
} else if(suitNumber0 == suitNumber1) {// Same suit
|
||||
confidence += 4;
|
||||
}
|
||||
|
||||
// Get difference from cards for guessing flush
|
||||
if(i > 4) {
|
||||
confidence -= 4;
|
||||
} else if(i > 2) {
|
||||
confidence -= i;
|
||||
}
|
||||
|
||||
// Get the confidence delta 0-1
|
||||
confidence = confidence / 30.0f;
|
||||
|
||||
// This may change in future, but I was finding the AI did not want to bet
|
||||
// during the preflop enough, this curves the AI to want to preflop call
|
||||
// often.
|
||||
confidence = easeOutCubic(confidence);
|
||||
} else {
|
||||
// Simulate my hand being the winning hand, use that as the confidence
|
||||
auto winning = this->getWinning();
|
||||
confidence = PokerWinning::getWinningTypeConfidence(winning.type);
|
||||
}
|
||||
|
||||
// Now we know how confident the AI is, let's put a chip value to that weight
|
||||
// How many chips to call?
|
||||
callBet = this->getCallBet();
|
||||
|
||||
// Do they need chips to call, or is it possible to check?
|
||||
if(callBet > 0) {
|
||||
potOdds = (float_t)callBet / (
|
||||
(float_t)callBet +
|
||||
(float_t)this->getSumOfChips()
|
||||
);
|
||||
} else {
|
||||
potOdds = 1.0f / (float_t)this->pokerGame->getRemainingBettersCount();
|
||||
}
|
||||
|
||||
// Now determine the expected ROI
|
||||
auto expectedGain = confidence / potOdds;
|
||||
|
||||
// Now get a random 0-100
|
||||
auto random = randomGenerate<int32_t>() % 100;
|
||||
|
||||
// Determine the max bet that the AI is willing to make
|
||||
auto maxBet = (int32_t)((float_t)this->chips / 1.75f) - (random / 2);
|
||||
maxBet -= callBet;
|
||||
|
||||
// Determine what's a good bluff bet.
|
||||
auto bluffBet = random * maxBet / 100 / 2;
|
||||
|
||||
// Now prep the output
|
||||
auto isBluff = false;
|
||||
auto amount = 0;
|
||||
|
||||
// Now the actual AI can happen. This is basically a weight to confidence
|
||||
// ratio. The higher the gains and the confidence then the more likely the AI
|
||||
// is to betting. There are also bluff chances within here.
|
||||
if(expectedGain < 0.8f && confidence < 0.8f) {
|
||||
if(random < 85) {
|
||||
amount = 0;
|
||||
} else {
|
||||
amount = bluffBet;
|
||||
isBluff = true;
|
||||
}
|
||||
} else if((expectedGain < 1.0f && confidence < 0.85f) || confidence < 0.1f) {
|
||||
if(random < 80) {
|
||||
amount = 0;
|
||||
} else if(random < 5) {
|
||||
amount = callBet;
|
||||
isBluff = true;
|
||||
} else {
|
||||
amount = bluffBet;
|
||||
isBluff = true;
|
||||
}
|
||||
} else if((expectedGain < 1.3f && confidence < 0.9f) || confidence < 0.5f) {
|
||||
if(random < 60 || confidence < 0.5f) {
|
||||
amount = callBet;
|
||||
} else {
|
||||
amount = maxBet;
|
||||
}
|
||||
} else if(confidence < 0.95f || this->pokerGame->community.size() < 4) {
|
||||
if(random < 20) {
|
||||
amount = callBet;
|
||||
} else {
|
||||
amount = maxBet;
|
||||
}
|
||||
} else {
|
||||
amount = (this->chips - callBet) * 9 / 10;
|
||||
}
|
||||
|
||||
// TODO: We can nicely round the amounts here to get us to a more "human"
|
||||
// number.
|
||||
|
||||
// If this is the first round... make it a lot less likely I'll bet
|
||||
if(this->pokerGame->community.size() == 0 && amount > callBet) {
|
||||
if(random > 5) amount = callBet;
|
||||
}
|
||||
|
||||
// Did we actually bet?
|
||||
if(amount > 0) {
|
||||
std::cout << "AI is betting " << amount << " chips, bluff:" << isBluff << std::endl;
|
||||
|
||||
// Let's not get caught in a raising loop with AI.
|
||||
if(this->timesRaised >= POKER_PLAYER_MAX_RAISES) {
|
||||
amount = callBet;
|
||||
}
|
||||
|
||||
amount = mathMax<int32_t>(amount, callBet);
|
||||
turn = PokerTurn::bet(this, amount);
|
||||
turn.confidence = confidence;
|
||||
} else if(this->canCheck()) {
|
||||
turn = PokerTurn::bet(this, 0);
|
||||
turn.confidence = 1;
|
||||
} else {
|
||||
turn = PokerTurn::fold(this);
|
||||
turn.confidence = 1 - confidence;
|
||||
}
|
||||
|
||||
return turn;
|
||||
}
|
||||
|
||||
int32_t PokerPlayer::getCallBet() {
|
||||
return this->pokerGame->getCurrentCallValue() - this->currentBet;
|
||||
}
|
||||
|
||||
int32_t PokerPlayer::getSumOfChips() {
|
||||
int32_t count = 0;
|
||||
auto it = this->pokerGame->pots.begin();
|
||||
while(it != this->pokerGame->pots.end()) {
|
||||
if(std::find(it->players.begin(), it->players.end(), this) != it->players.end()) {
|
||||
count += it->chips;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
struct PokerWinning PokerPlayer::getWinning() {
|
||||
struct PokerWinning winning;
|
||||
struct Card card(0x00);
|
||||
uint8_t i, j;
|
||||
int32_t index;
|
||||
enum CardValue number, look;
|
||||
enum CardSuit suit;
|
||||
std::vector<struct Card> pairs;
|
||||
|
||||
winning.player = this;
|
||||
|
||||
// Get the full poker hand (should be a 7 card hand, but MAY not be)
|
||||
for(i = 0; i < this->pokerGame->community.size(); i++) {
|
||||
winning.full.push_back(this->pokerGame->community[i]);
|
||||
}
|
||||
for(i = 0; i < this->hand.size(); i++) {
|
||||
winning.full.push_back(this->hand[i]);
|
||||
}
|
||||
Card::sort(&winning.full);
|
||||
|
||||
//////////////////////// Now look for the winning set ////////////////////////
|
||||
|
||||
// Royal / Straight Flush
|
||||
for(i = 0; i < winning.full.size(); i++) {
|
||||
card = winning.full[i];
|
||||
number = card.getValue();
|
||||
if(number < CARD_FIVE) continue;
|
||||
|
||||
suit = card.getSuit();
|
||||
|
||||
winning.set.clear();
|
||||
winning.set.push_back(card);
|
||||
|
||||
// Now look for the matching cards (Reverse order to order from A to 10)
|
||||
for(j = 1; j <= 4; j++) {
|
||||
// Ace low.
|
||||
look = (
|
||||
number == CARD_FIVE && j == 4 ?
|
||||
(enum CardValue)CARD_ACE :
|
||||
(enum CardValue)(number - j)
|
||||
);
|
||||
index = Card::contains(&winning.full, Card(suit, look));
|
||||
if(index == -1) break;
|
||||
winning.set.push_back(winning.full[index]);
|
||||
}
|
||||
|
||||
// Check if has all necessary cards.
|
||||
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
|
||||
|
||||
// Add self to array
|
||||
winning.type = (
|
||||
number == CARD_ACE ? POKER_WINNING_TYPE_ROYAL_FLUSH :
|
||||
POKER_WINNING_TYPE_STRAIGHT_FLUSH
|
||||
);
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// Four of a kind.
|
||||
for(i = 0; i < winning.full.size(); i++) {
|
||||
card = winning.full[i];
|
||||
number = card.getValue();
|
||||
pairs = Card::countPairs(&winning.full, number);
|
||||
if(pairs.size() < CARD_SUIT_COUNT) continue;
|
||||
|
||||
winning.set = pairs;
|
||||
winning.type = POKER_WINNING_TYPE_FOUR_OF_A_KIND;
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// Full House
|
||||
winning.set.clear();
|
||||
for(i = 0; i < winning.full.size(); i++) {
|
||||
// Check we haven't already added this card.
|
||||
card = winning.full[i];
|
||||
if(Card::contains(&winning.set, card) != -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
number = card.getValue();
|
||||
pairs = Card::countPairs(&winning.full, number);
|
||||
|
||||
// Did we find either two pair or three pair?
|
||||
if(pairs.size() != 2 && pairs.size() != 3) continue;
|
||||
if(winning.set.size() == 3) {//Clamp to 5 max.
|
||||
pairs.pop_back();
|
||||
}
|
||||
|
||||
// Copy found pairs.
|
||||
for(j = 0; j < pairs.size(); j++) {
|
||||
winning.set.push_back(pairs[j]);
|
||||
}
|
||||
|
||||
// Winned?
|
||||
if(winning.set.size() != POKER_WINNING_SET_SIZE) continue;
|
||||
winning.type = POKER_WINNING_TYPE_FULL_HOUSE;
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// Flush (5 same suit)
|
||||
for(i = 0; i < winning.full.size(); i++) {
|
||||
card = winning.full[i];
|
||||
suit = card.getSuit();
|
||||
|
||||
winning.set.clear();
|
||||
winning.set.push_back(card);
|
||||
|
||||
for(j = i+1; j < winning.full.size(); j++) {
|
||||
if(winning.full[j].getSuit() != suit) continue;
|
||||
winning.set.push_back(winning.full[j]);
|
||||
if(winning.set.size() == POKER_WINNING_SET_SIZE) break;
|
||||
}
|
||||
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
|
||||
winning.type = POKER_WINNING_TYPE_FLUSH;
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// Straight (sequence any suit)
|
||||
for(i = 0; i < winning.full.size(); i++) {
|
||||
card = winning.full[i];
|
||||
number = card.getValue();
|
||||
if(number < CARD_FIVE) continue;
|
||||
|
||||
winning.set.clear();
|
||||
winning.set.push_back(card);
|
||||
|
||||
for(j = 1; j <= 4; j++) {
|
||||
// Ace low.
|
||||
look = (
|
||||
number == CARD_FIVE && j == 4 ?
|
||||
(enum CardValue)CARD_ACE :
|
||||
(enum CardValue)(number - j)
|
||||
);
|
||||
index = Card::containsNumber(&winning.full, look);
|
||||
if(index == -1) break;
|
||||
winning.set.push_back(winning.full[index]);
|
||||
}
|
||||
|
||||
// Check if has all necessary cards.
|
||||
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
|
||||
winning.type = POKER_WINNING_TYPE_STRAIGHT;
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// Three of a kind
|
||||
for(i = 0; i < winning.full.size(); i++) {
|
||||
card = winning.full[i];
|
||||
number = card.getValue();
|
||||
pairs = Card::countPairs(&winning.full, number);
|
||||
if(pairs.size() != 3) continue;
|
||||
|
||||
winning.set = pairs;
|
||||
winning.type = POKER_WINNING_TYPE_THREE_OF_A_KIND;
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// Two Pair
|
||||
winning.set.clear();
|
||||
for(i = 0; i < winning.full.size(); i++) {
|
||||
card = winning.full[i];// Check we haven't already added this card.
|
||||
if(
|
||||
winning.set.size() > 0 &&
|
||||
Card::contains(&winning.set, card) != -1
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
number = card.getValue();
|
||||
pairs = Card::countPairs(&winning.full, number);
|
||||
if(pairs.size() != 2) continue;
|
||||
for(j = 0; j < pairs.size(); j++) {
|
||||
winning.set.push_back(pairs[j]);
|
||||
}
|
||||
if(winning.set.size() != 4) continue;
|
||||
winning.type = POKER_WINNING_TYPE_TWO_PAIR;
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// Pair
|
||||
if(winning.set.size() == 2) {
|
||||
winning.type = POKER_WINNING_TYPE_PAIR;
|
||||
winning.fillRemaining();
|
||||
return winning;
|
||||
}
|
||||
|
||||
// High card
|
||||
winning.set.clear();
|
||||
winning.fillRemaining();
|
||||
winning.type = POKER_WINNING_TYPE_HIGH_CARD;
|
||||
return winning;
|
||||
}
|
132
src/dawnpoker/poker/PokerPlayer.hpp
Normal file
132
src/dawnpoker/poker/PokerPlayer.hpp
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "scene/SceneItemComponent.hpp"
|
||||
#include "Card.hpp"
|
||||
#include "PokerPot.hpp"
|
||||
#include "util/mathutils.hpp"
|
||||
#include "display/animation/Easing.hpp"
|
||||
#include "PokerWinning.hpp"
|
||||
#include "PokerTurn.hpp"
|
||||
#include "util/random.hpp"
|
||||
|
||||
#define POKER_PLAYER_CHIPS_DEFAULT 10000
|
||||
|
||||
/** Maximum cards a players' hand can hold */
|
||||
#define POKER_PLAYER_HAND_SIZE_MAX 2
|
||||
|
||||
#define POKER_PLAYER_MAX_RAISES 0x02
|
||||
|
||||
namespace Dawn {
|
||||
class PokerGame;
|
||||
|
||||
class PokerPlayer : public SceneItemComponent {
|
||||
public:
|
||||
PokerGame *pokerGame;
|
||||
int32_t chips = 0;
|
||||
int32_t currentBet = 0;
|
||||
uint8_t timesRaised = 0;
|
||||
bool_t isFolded = false;
|
||||
bool_t isOut = false;
|
||||
bool_t hasBetThisRound = false;
|
||||
bool_t isShowingHand = false;
|
||||
bool_t isHuman = false;
|
||||
std::vector<struct Card> hand;
|
||||
|
||||
Event<> eventChipsChanged;
|
||||
|
||||
/**
|
||||
* Creates a PokerPlayer instance.
|
||||
*
|
||||
* @param item Item that this poker player belongs to.
|
||||
*/
|
||||
PokerPlayer(SceneItem *item);
|
||||
|
||||
/** Override for scene item component event for init */
|
||||
void onStart() override;
|
||||
|
||||
/**
|
||||
* Adds chips to the player. This will also update the players' state.
|
||||
*
|
||||
* @param chips Count of chips to add.
|
||||
*/
|
||||
void addChips(int32_t chips);
|
||||
|
||||
/**
|
||||
* Sets the chips a player has.
|
||||
*
|
||||
* @param chips Chips to set to the player.
|
||||
*/
|
||||
void setChips(int32_t chips);
|
||||
|
||||
/**
|
||||
* Returns true if the player still needs to bet this betting round.
|
||||
*
|
||||
* @return True if betting is still required by this player.
|
||||
*/
|
||||
bool_t needsToBetThisRound();
|
||||
|
||||
/**
|
||||
* Let a player bet chips into the pot.
|
||||
*
|
||||
* @param pot Poker pot to bet in to.
|
||||
* @param amount The amount of chips the player is betting.
|
||||
*/
|
||||
void bet(struct PokerPot *pot, int32_t amount);
|
||||
|
||||
/**
|
||||
* Let a player bet chips into the current pot.
|
||||
*
|
||||
* @param amount The amount of chips the player is betting.
|
||||
*/
|
||||
void bet(int32_t amount);
|
||||
|
||||
/**
|
||||
* Player folds.
|
||||
*/
|
||||
void fold();
|
||||
|
||||
/**
|
||||
* Returns the AI result for a turn done by a non human player.
|
||||
*
|
||||
* @return Some information about the move the player is trying to perform
|
||||
*/
|
||||
struct PokerTurn getAITurn();
|
||||
|
||||
/**
|
||||
* Calculates and returns the winning state for a given player
|
||||
*
|
||||
* @return The winning state for this current players hand.
|
||||
*/
|
||||
struct PokerWinning getWinning();
|
||||
|
||||
/**
|
||||
* Returns the sum of chips in the pot(s) that the specified player is in.
|
||||
* This does not consider the pot, player or hand, just the pure sum of
|
||||
* chips.
|
||||
*
|
||||
* @return The sum of chips from the pots the player is within.
|
||||
*/
|
||||
int32_t getSumOfChips();
|
||||
|
||||
/**
|
||||
* Get the bet necessary for a specific player to make a call. This takes
|
||||
* the players current bet and the bet necessary to call into the pot and
|
||||
* will return the difference.
|
||||
*
|
||||
* @return The count of chips needed to call into the current active pot.
|
||||
*/
|
||||
int32_t getCallBet();
|
||||
|
||||
/**
|
||||
* Returns whether or not the player can check, or if they need to either
|
||||
* fold, call or bet.
|
||||
*
|
||||
* @return True if they can check.
|
||||
*/
|
||||
bool_t canCheck();
|
||||
};
|
||||
}
|
111
src/dawnpoker/poker/PokerPot.cpp
Normal file
111
src/dawnpoker/poker/PokerPot.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "PokerPot.hpp"
|
||||
#include "PokerGame.hpp"
|
||||
#include "PokerPlayer.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
void PokerPotWinning::award() {
|
||||
auto it = this->winners.begin();
|
||||
while(it != this->winners.end()) {
|
||||
if(it == this->winners.begin()) {
|
||||
(*it)->addChips(this->chipsEach + this->chipsOverflow);
|
||||
} else {
|
||||
(*it)->addChips(this->chipsEach);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
struct PokerPotWinning PokerPot::getWinners(PokerGame *game) {
|
||||
struct PokerPotWinning winning;
|
||||
|
||||
winning.pot = this;
|
||||
|
||||
// Calculate the winnings first.
|
||||
auto it = this->players.begin();
|
||||
while(it != this->players.end()) {
|
||||
auto player = *it;
|
||||
|
||||
if(player->isOut || player->isFolded) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
winning.participants.push_back(player);
|
||||
winning.winnings[player] = player->getWinning();
|
||||
++it;
|
||||
}
|
||||
|
||||
// Compare participating players
|
||||
auto it2 = winning.participants.begin();
|
||||
while(it2 != winning.participants.end()) {
|
||||
auto playerLeft = *it2;
|
||||
auto winnerLeft = &winning.winnings[playerLeft];
|
||||
bool_t isWinner = true;
|
||||
enum CardValue highNumber = CARD_VALUE_INVALD;
|
||||
enum CardValue number = CARD_VALUE_INVALD;
|
||||
struct Card highCard(0xFF);
|
||||
struct Card card(0xFF);
|
||||
|
||||
auto it3 = winning.participants.begin();
|
||||
while(it3 != winning.participants.end()) {
|
||||
if(it2 == it3) {
|
||||
++it3;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto playerRight = *it3;
|
||||
auto winnerRight = &winning.winnings[playerRight];
|
||||
|
||||
// Am I the better hand / Is it the better hand?
|
||||
if(winnerLeft->type < winnerRight->type) {
|
||||
++it3;
|
||||
continue;
|
||||
}
|
||||
if(winnerLeft->type > winnerRight->type) {
|
||||
isWinner = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Equal, compare hands.
|
||||
card = PokerWinning::compare(winnerLeft, winnerRight);
|
||||
if(card.cardValue == 0xFF) {
|
||||
isWinner = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Determine high card.
|
||||
number = card.getValue();
|
||||
if(
|
||||
highNumber == CARD_VALUE_INVALD ||
|
||||
number == CARD_ACE ||
|
||||
number > highNumber
|
||||
) {
|
||||
highCard = card;
|
||||
highNumber = number;
|
||||
}
|
||||
++it3;
|
||||
}
|
||||
|
||||
if(!isWinner) {
|
||||
++it2;
|
||||
continue;
|
||||
}
|
||||
|
||||
winnerLeft->kicker = highCard;
|
||||
winning.winners.push_back(playerLeft);
|
||||
++it2;
|
||||
}
|
||||
|
||||
winning.chipsEach = this->chips / (int32_t)winning.winners.size();
|
||||
winning.chipsOverflow = this->chips - (
|
||||
winning.chipsEach * (int32_t)winning.winners.size()
|
||||
);
|
||||
|
||||
return winning;
|
||||
}
|
34
src/dawnpoker/poker/PokerPot.hpp
Normal file
34
src/dawnpoker/poker/PokerPot.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "PokerWinning.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class PokerPlayer;
|
||||
class PokerGame;
|
||||
struct PokerPot;
|
||||
|
||||
struct PokerPotWinning {
|
||||
public:
|
||||
std::map<PokerPlayer*,struct PokerWinning> winnings;
|
||||
std::vector<PokerPlayer*> winners;
|
||||
std::vector<PokerPlayer*> participants;
|
||||
struct PokerPot *pot;
|
||||
int32_t chipsEach;
|
||||
int32_t chipsOverflow;
|
||||
|
||||
void award();
|
||||
};
|
||||
|
||||
struct PokerPot {
|
||||
public:
|
||||
int32_t chips;
|
||||
int32_t call;
|
||||
std::vector<PokerPlayer*> players;
|
||||
|
||||
struct PokerPotWinning getWinners(PokerGame *game);
|
||||
};
|
||||
}
|
72
src/dawnpoker/poker/PokerTurn.cpp
Normal file
72
src/dawnpoker/poker/PokerTurn.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "PokerTurn.hpp"
|
||||
#include "PokerPlayer.hpp"
|
||||
#include "PokerGame.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
struct PokerTurn PokerTurn::bet(PokerPlayer *player, int32_t chips) {
|
||||
struct PokerTurn turn;
|
||||
int32_t i;
|
||||
|
||||
assertNotNull(player);
|
||||
assertTrue(chips >= 0);
|
||||
|
||||
turn.player = player;
|
||||
turn.confidence = 1;
|
||||
|
||||
if(chips == 0) {
|
||||
turn.type = POKER_TURN_TYPE_CHECK;
|
||||
turn.chips = 0;
|
||||
} else if(player->chips <= chips) {
|
||||
turn.chips = player->chips;
|
||||
turn.type = POKER_TURN_TYPE_ALL_IN;
|
||||
} else {
|
||||
turn.chips = chips;
|
||||
turn.type = POKER_TURN_TYPE_BET;
|
||||
i = player->pokerGame->getCurrentCallValue();
|
||||
|
||||
if(chips == (i - player->currentBet)) {
|
||||
turn.type = POKER_TURN_TYPE_CALL;
|
||||
}
|
||||
}
|
||||
|
||||
return turn;
|
||||
}
|
||||
|
||||
struct PokerTurn PokerTurn::fold(PokerPlayer *player) {
|
||||
struct PokerTurn turn;
|
||||
turn.player = player;
|
||||
turn.chips = 0;
|
||||
turn.confidence = 1;
|
||||
turn.type = POKER_TURN_TYPE_FOLD;
|
||||
return turn;
|
||||
}
|
||||
|
||||
void PokerTurn::action() {
|
||||
assertNotNull(this->player);
|
||||
|
||||
switch(this->type) {
|
||||
case POKER_TURN_TYPE_BET:
|
||||
case POKER_TURN_TYPE_CALL:
|
||||
case POKER_TURN_TYPE_ALL_IN:
|
||||
this->player->bet(this->chips);
|
||||
break;
|
||||
|
||||
case POKER_TURN_TYPE_CHECK:
|
||||
player->bet(0);
|
||||
break;
|
||||
|
||||
case POKER_TURN_TYPE_FOLD:
|
||||
player->fold();
|
||||
break;
|
||||
|
||||
default:
|
||||
assertUnreachable();
|
||||
break;
|
||||
}
|
||||
}
|
53
src/dawnpoker/poker/PokerTurn.hpp
Normal file
53
src/dawnpoker/poker/PokerTurn.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "dawnlibs.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class PokerPlayer;
|
||||
|
||||
enum PokerTurnType {
|
||||
POKER_TURN_TYPE_OUT,
|
||||
POKER_TURN_TYPE_FOLD,
|
||||
POKER_TURN_TYPE_BET,
|
||||
POKER_TURN_TYPE_CALL,
|
||||
POKER_TURN_TYPE_CHECK,
|
||||
POKER_TURN_TYPE_ALL_IN
|
||||
};
|
||||
|
||||
struct PokerTurn {
|
||||
public:
|
||||
/** What type of action the turn is */
|
||||
enum PokerTurnType type;
|
||||
/** How many chips they did in their turn (if applicable) */
|
||||
int32_t chips;
|
||||
/** How confident the AI is about their turn. 0 = none, 1 = full */
|
||||
float_t confidence;
|
||||
/** Player that this action belongs to */
|
||||
PokerPlayer *player;
|
||||
|
||||
/**
|
||||
* Generate a turn action for betting as a player.
|
||||
*
|
||||
* @param player Player index who is betting.
|
||||
* @param chips Chips to raise by.
|
||||
* @return A turn for a bet action.
|
||||
*/
|
||||
static struct PokerTurn bet(PokerPlayer *player, int32_t chips);
|
||||
|
||||
/**
|
||||
* Return a turn action for the given player to fold.
|
||||
*
|
||||
* @return A turn for a fold action.
|
||||
*/
|
||||
static struct PokerTurn fold(PokerPlayer *player);
|
||||
|
||||
/**
|
||||
* Actions / Performs this turn against the defined player.
|
||||
*/
|
||||
void action();
|
||||
};
|
||||
}
|
159
src/dawnpoker/poker/PokerWinning.cpp
Normal file
159
src/dawnpoker/poker/PokerWinning.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "PokerWinning.hpp"
|
||||
#include "PokerPlayer.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
float_t PokerWinning::getWinningTypeConfidence(enum PokerWinningType type) {
|
||||
switch(type) {
|
||||
case POKER_WINNING_TYPE_ROYAL_FLUSH:
|
||||
return POKER_WINNING_CONFIDENCE_ROYAL_FLUSH;
|
||||
case POKER_WINNING_TYPE_STRAIGHT_FLUSH:
|
||||
return POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH;
|
||||
case POKER_WINNING_TYPE_FOUR_OF_A_KIND:
|
||||
return POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND;
|
||||
case POKER_WINNING_TYPE_FULL_HOUSE:
|
||||
return POKER_WINNING_CONFIDENCE_FULL_HOUSE;
|
||||
case POKER_WINNING_TYPE_FLUSH:
|
||||
return POKER_WINNING_CONFIDENCE_FLUSH;
|
||||
case POKER_WINNING_TYPE_STRAIGHT:
|
||||
return POKER_WINNING_CONFIDENCE_STRAIGHT;
|
||||
case POKER_WINNING_TYPE_THREE_OF_A_KIND:
|
||||
return POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND;
|
||||
case POKER_WINNING_TYPE_TWO_PAIR:
|
||||
return POKER_WINNING_CONFIDENCE_TWO_PAIR;
|
||||
case POKER_WINNING_TYPE_PAIR:
|
||||
return POKER_WINNING_CONFIDENCE_PAIR;
|
||||
default:
|
||||
return POKER_WINNING_CONFIDENCE_HIGH_CARD;
|
||||
}
|
||||
}
|
||||
|
||||
struct Card PokerWinning::compare(
|
||||
struct PokerWinning *left,
|
||||
struct PokerWinning *right
|
||||
) {
|
||||
assertNotNull(left);
|
||||
assertNotNull(right);
|
||||
|
||||
uint8_t i;
|
||||
enum CardValue number = CARD_VALUE_INVALD;
|
||||
enum CardValue highNumberLeft = CARD_VALUE_INVALD;
|
||||
enum CardValue highNumberRight = CARD_VALUE_INVALD;
|
||||
struct Card card(0xFF), highCardLeft(0xFF), highCardRight(0xFF);
|
||||
int32_t index;
|
||||
uint8_t countCardsSame;
|
||||
|
||||
countCardsSame = 0;
|
||||
|
||||
for(i = 0; i < left->set.size(); i++) {
|
||||
card = left->set[i];
|
||||
number = card.getValue();
|
||||
// Quick check
|
||||
if(highNumberLeft != CARD_VALUE_INVALD && number < highNumberLeft) continue;
|
||||
|
||||
// Check if this number is within the other hand or not
|
||||
index = Card::containsNumber(&right->set, number);
|
||||
if(index != -1) {
|
||||
// This number IS within the other hand, let's check that the EXACT card
|
||||
// is a match/isn't a match.
|
||||
index = Card::contains(&right->set, card);
|
||||
|
||||
// Exact card match
|
||||
if(index != -1) {
|
||||
countCardsSame++;
|
||||
continue;
|
||||
}
|
||||
// Not exact card match.. ?
|
||||
}
|
||||
|
||||
if(
|
||||
highNumberLeft == CARD_VALUE_INVALD ||
|
||||
number == CARD_ACE ||
|
||||
highNumberLeft < number
|
||||
) {
|
||||
highNumberLeft = number;
|
||||
highCardLeft = card;
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i < right->set.size(); i++) {
|
||||
card = right->set[i];
|
||||
number = card.getValue();
|
||||
if(highNumberRight != CARD_VALUE_INVALD && number < highNumberRight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
index = Card::containsNumber(&left->set, number);
|
||||
if(index != -1) {
|
||||
index = Card::contains(&left->set, card);
|
||||
if(index != -1) continue;
|
||||
}
|
||||
|
||||
if(
|
||||
highNumberRight == CARD_VALUE_INVALD ||
|
||||
number == CARD_ACE || highNumberRight < number
|
||||
) {
|
||||
highNumberRight = number;
|
||||
highCardRight = card;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(countCardsSame == left->set.size()) {
|
||||
for(i = 0; i < left->set.size(); i++) {
|
||||
card = left->set[i];
|
||||
number = card.getValue();
|
||||
if(
|
||||
highNumberLeft == CARD_VALUE_INVALD ||
|
||||
number == CARD_ACE ||
|
||||
highNumberLeft < number
|
||||
) {
|
||||
highNumberLeft = number;
|
||||
highCardLeft = card;
|
||||
}
|
||||
}
|
||||
return highCardLeft;
|
||||
}
|
||||
|
||||
if(highCardLeft.cardValue == CARD_VALUE_INVALD) return 0xFF;
|
||||
if(highNumberLeft < highNumberRight) return 0xFF;
|
||||
return highCardLeft;// Greater or Equal to.
|
||||
}
|
||||
|
||||
void PokerWinning::fillRemaining() {
|
||||
uint8_t i, highest, current;
|
||||
struct Card highestCard(0x00);
|
||||
struct Card currentCard(0x00);
|
||||
|
||||
// Set the kicker
|
||||
this->kicker = 0xFF;
|
||||
|
||||
// Fill the remaining cards
|
||||
while(this->set.size() < POKER_WINNING_SET_SIZE) {
|
||||
highest = 0xFF;
|
||||
|
||||
for(i = 0; i < this->full.size(); i++) {
|
||||
currentCard = this->full[i];
|
||||
if(Card::contains(&this->set, currentCard) != -1) continue;
|
||||
|
||||
if(highest == 0xFF) {
|
||||
highestCard = currentCard;
|
||||
highest = highestCard.getValue();
|
||||
} else {
|
||||
current = currentCard.getValue();
|
||||
if(current != CARD_ACE && current < highest) continue;
|
||||
highestCard = currentCard;
|
||||
highest = current;
|
||||
}
|
||||
}
|
||||
|
||||
if(highest == 0xFF) break;
|
||||
this->set.push_back(highestCard);
|
||||
}
|
||||
Card::sort(&this->set);
|
||||
}
|
86
src/dawnpoker/poker/PokerWinning.hpp
Normal file
86
src/dawnpoker/poker/PokerWinning.hpp
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2022 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "dawnlibs.hpp"
|
||||
#include "Card.hpp"
|
||||
|
||||
#define POKER_WINNING_CONFIDENCE_ROYAL_FLUSH 1.0f
|
||||
#define POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH 0.99f
|
||||
#define POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND 0.9f
|
||||
#define POKER_WINNING_CONFIDENCE_FULL_HOUSE 0.85f
|
||||
#define POKER_WINNING_CONFIDENCE_FLUSH 0.8f
|
||||
#define POKER_WINNING_CONFIDENCE_STRAIGHT 0.7f
|
||||
#define POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND 0.5f
|
||||
#define POKER_WINNING_CONFIDENCE_TWO_PAIR 0.4f
|
||||
#define POKER_WINNING_CONFIDENCE_PAIR 0.2f
|
||||
#define POKER_WINNING_CONFIDENCE_HIGH_CARD 0.1f
|
||||
|
||||
/** How many cards in the winning set */
|
||||
#define POKER_WINNING_SET_SIZE 5
|
||||
|
||||
namespace Dawn {
|
||||
class PokerPlayer;
|
||||
|
||||
enum PokerWinningType {
|
||||
POKER_WINNING_TYPE_NULL,
|
||||
POKER_WINNING_TYPE_ROYAL_FLUSH,
|
||||
POKER_WINNING_TYPE_STRAIGHT_FLUSH,
|
||||
POKER_WINNING_TYPE_FOUR_OF_A_KIND,
|
||||
POKER_WINNING_TYPE_FULL_HOUSE,
|
||||
POKER_WINNING_TYPE_FLUSH,
|
||||
POKER_WINNING_TYPE_STRAIGHT,
|
||||
POKER_WINNING_TYPE_THREE_OF_A_KIND,
|
||||
POKER_WINNING_TYPE_TWO_PAIR,
|
||||
POKER_WINNING_TYPE_PAIR,
|
||||
POKER_WINNING_TYPE_HIGH_CARD
|
||||
};
|
||||
|
||||
struct PokerWinning {
|
||||
public:
|
||||
/**
|
||||
* Get the confidence of the bet for a given winning type.
|
||||
*
|
||||
* @param type Winning type type.
|
||||
* @return The confidence.
|
||||
*/
|
||||
static float_t getWinningTypeConfidence(enum PokerWinningType type);
|
||||
|
||||
/**
|
||||
* Compares two winning sets. The returned card is the kicker if the LEFT
|
||||
* side is the winner. If LEFT is not a winner then 0xFF will be returned.
|
||||
*
|
||||
* @param left Left winning set.
|
||||
* @param right Right winning set.
|
||||
* @return The kicker card from left's hand or 0xFF if not the winner.
|
||||
*/
|
||||
static struct Card compare(
|
||||
struct PokerWinning *left,
|
||||
struct PokerWinning *right
|
||||
);
|
||||
|
||||
/** Winning Type */
|
||||
enum PokerWinningType type;
|
||||
/** The full set of both the dealer and player's hand */
|
||||
std::vector<struct Card> full;
|
||||
/** Holds the winning set */
|
||||
std::vector<struct Card> set;
|
||||
/** If there was a kicker card it will be here */
|
||||
struct Card kicker;
|
||||
/* The player this winning state belongs to */
|
||||
PokerPlayer *player;
|
||||
|
||||
PokerWinning() : kicker(0xFF) {}
|
||||
|
||||
/**
|
||||
* Fills the remaining cards for a given poker player winning hand.
|
||||
* Essentially this will just take the highest cards and slot them into
|
||||
* the array. This also sorts the cards.
|
||||
*
|
||||
* @param winning Pointer to the poker winning to fill out.
|
||||
*/
|
||||
void fillRemaining();
|
||||
};
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2023 Dominic Masters
|
||||
# Copyright (c) 2024 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
Reference in New Issue
Block a user