Написал несколько классов.
Класс Card представляет собой карту для игры в Холдем (или любую другую карточную игру можно). В этом классе содержится информация о масти карты и её значении (ранге).
package com.ultimate.objects;
public class Card {
// Масть карты (пики, черви, бубны, трефы)
private String suit;
// Ранг карты (2-10, J, Q, K, A)
private int rank;
public Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
public String getSuit() {
return suit;
}
public void setSuit(String suit) {
this.suit = suit;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
@Override
public String toString() {
return rankToString(rank) + " of " + suit;
}
private String rankToString(int rank) {
if (rank == 11) return "Jack";
if (rank == 12) return "Queen";
if (rank == 13) return "King";
if (rank == 14) return "Ace";
return String.valueOf(rank);
}
}
Класс Player будет представлять игрока. Он будет содержать информацию о руках игрока , а также методы для взаимодействия с этими картами и позже добавлю поле количество чипов.
package com.ultimate.objects;
import java.util.ArrayList;
import java.util.List;
public class Player {
private String name;
List<Card> hand = new ArrayList<>();
public Player(String name) {
this.name = name;
}
public void addCard(Card card) {
hand.add(card);
}
@Override
public String toString() {
return name + "'s hand: " + hand;
}
}
Класс BestHand буду использовать для оценки и представления лучшей руки игрока. Этот класс будет содержать логику для оценки комбинаций карт (например, Пара, Стрит, Флеш и т. д.) и вычисления лучшей возможной руки, которую игрок может собрать, используя свои две карты и общие карты (комьюнити карты). Но еще нужно как-то придумать чтобы находить победителя, между двумя игроками, особенно когда старшая карта будет и определить киккер (в будущем может больше игроков будет), но поскольку это Ультимейт покер, то 2 игрока - Хедзап
package com.ultimate.objects;
import java.util.*;
import java.util.stream.Collectors;
public class BestHand {
private List<Card> playerCards;
private List<Card> communityCards;
private List<Card> getAllCards;
private List<Card> allCards;
public BestHand(List<Card> playerCards, List<Card> communityCards) {
allCards = new ArrayList<>(communityCards);
allCards.addAll(playerCards); // Объединяем карты игрока и общие карты
}
// Проверка на флеш
private static boolean isFlush(List<Card> cards) {
return cards.stream()
.collect(Collectors.groupingBy(card -> card.getSuit()))
.values().stream()
.anyMatch(suitGroup -> suitGroup.size() >= 5);
}
// Проверка на стрит
private static boolean isStraight(List<Card> cards) {
List<Integer> ranks = cards.stream()
.map(card -> card.getRank())
.distinct()
.sorted()
.collect(Collectors.toList());
for (int i = 0; i <= ranks.size() - 5; i++) {
if (ranks.get(i + 4) - ranks.get(i) == 4) return true;
}
// Проверка на стрит с тузом (A, 2, 3, 4, 5)
return ranks.containsAll(Arrays.asList(2, 3, 4, 5, 14));
}
// Проверка на стрит-флеш
private static boolean isStraightFlush(List<Card> cards) {
Map<String, List<Card>> suits = cards.stream()
.collect(Collectors.groupingBy(card -> card.getSuit()));
for (List<Card> suitCards : suits.values()) {
if (isStraight(suitCards)) return true;
}
return false;
}
// Проверка на каре
private static boolean isFourOfAKind(List<Card> cards) {
return hasOfAKind(cards, 4);
}
// Проверка на фул-хаус
private static boolean isFullHouse(List<Card> cards) {
return isThreeOfAKind(cards) && isPair(cards);
}
// Проверка на тройку
private static boolean isThreeOfAKind(List<Card> cards) {
return hasOfAKind(cards, 3);
}
// Проверка на две пары
private static boolean isTwoPair(List<Card> cards) {
return cards.stream()
.collect(Collectors.groupingBy(card -> card.getRank()))
.values().stream()
.filter(rankGroup -> rankGroup.size() == 2)
.count() >= 2;
}
// Проверка на пару
private static boolean isPair(List<Card> cards) {
return hasOfAKind(cards, 2);
}
// Проверка на конкретное количество одинаковых карт
private static boolean hasOfAKind(List<Card> cards, int count) {
return cards.stream()
.collect(Collectors.groupingBy(card -> card.getRank()))
.values().stream()
.anyMatch(rankGroup -> rankGroup.size() == count);
}
public String getCombination() {
// Проверяем комбинации от самой сильной к самой слабой
if (isStraightFlush(allCards)) return "Straight Flush";
if (isFourOfAKind(allCards)) return "Four of a Kind";
if (isFullHouse(allCards)) return "Full House";
if (isFlush(allCards)) return "Flush";
if (isStraight(allCards)) return "Straight";
if (isThreeOfAKind(allCards)) return "Three of a Kind";
if (isTwoPair(allCards)) return "Two Pair";
if (isPair(allCards)) return "Pair";
return "High Card";
}
}
Вообще наверное лучше на GitHub ветку создам и там буду код выставлять.
Пока самое сложное это составить алгоритмы для проверки киккеров, когда комбинации игроков одинаковые. Особенно для фулхаусов трудновато было и стрит-флешей. Роял Флеш- это особый случай стритфлеша, я не стал его выделять пока, поскольку он выпадает очень редко (в живом покере только отчаянный лудоман будет за ним гонятся чтобы получить выплату 1к500 в Ультимейт покере на ставке Блайнд). Хотя я видел в Розвадове некоторых игроков, которые гонялись за стритфлешем и поймав его однажды, радовались как слоны на все казино! Как закончу с киккерами, перейду к созданию класса Колода, где буду реализовывать пресловутое ГСЧ (пока не знаю как).
Создал класс Deck - Колода. ГСЧ использую стандартное из библиотеки Java, класс Random. Позже может быть по желанию, сделаю свой, например зависящий от температуры на Гавайских островах 🤩. Дальше буду реализовывать более расширено класс Player (Казино это тоже игрок будет, только оппонент наш с безмерным количеством денег, с которого мы всегда будем добирать в идеале 😆). Потом еще надо написать класс для обработки ставок и подсчета выигранных/проигранных денег
package com.ultimate;
import com.ultimate.objects.Card;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Deck {
private List<Card> cards;
/**
* Конструктор создаёт новую колоду карт и тасует её
*/
public Deck() {
cards = new ArrayList<>();
initializeDeck();
shuffle();
}
/**
* Метод для инициализации колоды (добавления всех карт)
*/
private void initializeDeck() {
String[] suits = {"♥", "♦", "♣", "♠"};
int[] ranks = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
for (String suit : suits) {
for (int rank : ranks) {
cards.add(new Card(suit, rank));
}
}
}
/**
* Метод для тасовки колоды
*/
public void shuffle() {
Collections.shuffle(cards);
}
/**
* Метод для раздачи карты из колоды
*/
public Card dealCard() {
if (cards.isEmpty()) {
System.out.println("The deck is empty. Cannot deal any more cards.");
return null;
}
return cards.remove(cards.size() - 1);
}
/**
* Метод для раздачи нескольких карт
*/
public List<Card> dealMultipleCards(int count) {
List<Card> dealtCards = new ArrayList<>();
for (int i = 0; i < count; i++) {
Card card = dealCard();
if (card != null) {
dealtCards.add(card);
} else {
break;
}
}
return dealtCards;
}
/**
* Метод для добавления карты в колоду
*/
public void addCard(Card card) {
cards.add(card);
}
/**
* Метод для проверки, пуста ли колода
*/
public boolean isEmpty() {
return cards.isEmpty();
}
/**
* Метод для удаления конкретной карты из колоды
*/
public boolean removeCard(Card card) {
return cards.remove(card);
}
/**
* Метод для проверки, содержит ли колода конкретную карту
*/
public boolean containsCard(Card card) {
return cards.contains(card);
}
/**
* Метод для получения количества оставшихся карт в колоде
*/
public int remainingCards() {
return cards.size();
}
/**
* Метод для восполнения колоды и её тасовки
*/
public void resetDeck() {
cards.clear();
initializeDeck();
shuffle();
}
/**
* Метод для отображения всех карт в колоде
*/
public void displayDeck() {
for (Card card : cards) {
System.out.println(card);
}
}
/**
* Метод для "сжигания" (удаления) карты из колоды
*/
public Card burnCard() {
if (cards.isEmpty()) {
System.out.println("The deck is empty. No card to burn.");
return null;
}
return cards.remove(cards.size() - 1);
}
}
Вроде бы реализовал алгоритмы префлоп раздач, кроме флопа и терна до вскрытия на ривере. Из 1м раздач в хедзапе дает следующий результат, думаю можно доверять ГСЧ.
Пишу класс Game для реализаци процесса игры. Пока в тестовом варианте использую только ситуацию хедзап, и всего 2 ставки Анте и Блайнд. При ничьей никто не выиграл, если диллер-анте и Блайнд забирается, если игрок выиграл по то Анте и Блайнд удваивается. Запустил в цикле на 100, 1000 и 10000 раздач. Результаты выводил на график для наглядности. В итоге как и ожидалось, не выигрываем и не проигрываем на дистанции. Хорошо, что рейка нет, а так бы были в минус. Хорошо, что люди не компьютеры и не играют по ГТО, а то только Рейк бы месили и все. Завтра буду реализовывать пуш анте и ставку Play
Первые результаты. Если мы будем все время на любых картах ставить одинаковые ставки в размере х1 на Ante, Blind, Play, то после 10000 раздач получим такие результаты. Явно минусовая игра. Завтра попробую какую-нибудь стратегию из интернета реализовать, может график поменяется как-то....во что я очень слабо верю. Больше всего забирает денег - невыплата Blind, когда мы выигрываем (Blind выплачивается только со стрита и то 1 к1).
Применив одну из стратегий из интернета (https://www.pokernews.com/casino/video-poker/ultimate-texas-holdem.htm) (возможно где-то что-то не так написал в коде, но он открыт и если у кого будет желание, то можем ещё поработать), можно с уверенностью сказать что Ultimate Poker минусовая игра, намного больше чем 2-3%, если мы не учитываем игру по тейк-профит/стоплосс, а просто игру на дистанцию. Да, на кратком промежутке раздач (например 30-50 раздач) мы можем идти в плюс и будет складываться впечатление, что мы "бьем поляну", но уже на 1000 рук или даже 10000 рук мы будем идти в минус. Использование тейкпрофит/стоплосс позволит минимизировать потери, но все равно не убережёт от банкротства (моделировал ситуацию из ежедневной игры по 300 раздач в день, 300 дней в год - можно так 3 года идти в плюс, а потом 2 года в минус). Итог- не рассматривать как источник дохода.
System. out. println("Hello world")!
После нескольких часов игры в казино с Ультимейт покер против диллера, мне стало интересно насколько это плюсовая игра. Некоторые говорят, что можно играть в плюс, некоторые что это слабоминусовая игра. Хочу проверить не на реальные деньги. Раньше учил программирование на Java. Хочу написать небольшую програмку, чтобы можно было использовать разные стратегии и понимать вообще как это будет действовать, максимально имитируя живую игру. Буду сюда постить код, идеи разные и результаты. Если не получится ничего, то хоть останутся записи для истории, может кому-то будет интересно в будущем!
https://github.com/knighenko/UltimatePoker