#java #concurrency #combinatorics #fork-join
Вопрос:
У меня есть приложение на базе java для Техасского холдем-покера. Я должен ввести функцию, в которой всякий раз, когда все игроки, кроме одного, идут ва-банк, мы должны рассчитать вероятность выигрыша для каждого игрока.
Это может произойти на префлопе, флопе и терне. Как вы, возможно, знаете, на стадии префлопа должны быть выбраны все 5 карт, на флопе в таблице уже есть 3 общие карты, необходимо выбрать еще 2 карты, в то время как в свою очередь необходимо выбрать еще 1 карту. Предположим, что есть 2 игрока.
Таким образом, у каждого из игроков есть 2 карты с отверстиями. Итак, мы должны выбрать 2 карты из колоды из 52 -2*2 = 48 карт. Используя комбинаторику, я могу это сделать. Но на этапе до флопа слишком много комбинаций, из 48 мы должны выбрать 5 карт, что составляет 48Cr5 = 1712304 комбинации. Если я буду делать так много вычислений, игра застрянет. Поэтому попытался использовать ForkJoinPool. Я обнаружил, что в машине, на которой развернуто приложение, всего 2 ядра. Это вообще не работает. Подходит ли ForkJoinPool для этой задачи или мне следует обратиться к какой-то другой службе исполнителей. Вот часть моего кода рекурсивной задачи
public class WinnerPercentageTask extends RecursiveTask<List<String>> {
private static int threshold = 50_000;
private List<List<Byte>> combinations;
private int start;
private int end;
private ITexasHoldemGame game;
public WinnerPercentageTask(ITexasHoldemGame game, List<List<Byte>> allCombinations, int start, int end) {
super();
this.game = game;
this.combinations = allCombinations;
this.start = start;
this.end = end;
}
@Override
protected List<String> compute() {
if (end - start < threshold) {
return computeDirectly();
}
else {
int middle = (end start) / 2;
WinnerPercentageTask subTask1 = new WinnerPercentageTask(game,combinations, start, middle);
WinnerPercentageTask subTask2 = new WinnerPercentageTask(game,combinations, middle, end);
invokeAll(subTask1, subTask2);
List<String> ret = new ArrayList<>();
ret.addAll(subTask1.join());
ret.addAll(subTask1.join());
return ret;
}
}
private List<String> computeDirectly() {
List<Card> communityCards = game.getCommunityCardsDealt();
List<Winner> winners = new ArrayList<>();
List<String> winnersForAllCombinations = new ArrayList<>();
for(List<Byte> combination: combinations.subList(start, end)) {
List<Card> allCommunityCard = new ArrayList<>();
for (Card c : communityCards) {
allCommunityCard.add(new Card(c.getId(), c.getFaceValue(), c.getSuit()));
}
for (Byte b : combination) {
Card fakeCommunityCard = new Card(b);
allCommunityCard.add(fakeCommunityCard);
}
List<PokerHand> playerRankList = calculateHandRank(allCommunityCard);
winners = prepareWinnerList(playerRankList);
winnersForAllCombinations.addAll(
winners.stream()
.map(Winner:: getPlayerId)
.collect(Collectors.toList()));
}
return winnersForAllCombinations;
}
private List<PokerHand> calculateHandRank(List<Card> communityCardsDealt) {
List<PokerHand> playerRankList = new ArrayList<>();
Map<String, List<Card>> playersCards = this.game.getCardsDealtForAllPlayers();
playersCards.forEach((player, holeCards) -> {
PokerHand pokerHand = new PokerHand(player, holeCards);
HandRanker.checkRanking(pokerHand, communityCardsDealt);
playerRankList.add(pokerHand);
});
Collections.sort(playerRankList);
// if (logger.isInfoEnabled()) {
// logger.info("Player Rank List : {}", playerRankList);
// }
return playerRankList;
}
private List<Winner> prepareWinnerList(List<PokerHand> playerRankList) {
List<Winner> winnerList = new ArrayList<>();
if (!CollectionUtils.isEmpty(playerRankList)) {
// Grouping Players based on similar Ranks
Map<HandRankingEnum, List<PokerHand>> groupByRankMap = playerRankList.stream()
.collect(Collectors.groupingBy(PokerHand::getRankingEnum));
// Sort the Map based on Rank, highest rank first
List<Map.Entry<HandRankingEnum, List<PokerHand>>> sortedWinnerList = groupByRankMap
.entrySet().stream().sorted(reverseOrder(Map.Entry.comparingByKey()))
.collect(Collectors.toList());
// Reading the Winners of highest rank
Map.Entry<HandRankingEnum, List<PokerHand>> winnerListMap = sortedWinnerList
.get(0);
List<PokerHand> highestRankList = winnerListMap.getValue();
List<PokerHand> winnerHandList = new WinnerIdentifier().getWinners(highestRankList);
for (PokerHand hand : winnerHandList) {
Winner winnerPlayer = Winner.builder()
.rank((byte) hand.getRankingEnum().getValue())
.rankName(hand.getRankingEnum().name())
.playerId(hand.getGamePlayerId()).cards(hand.getRankingList())
.pots(new ArrayList<>()).build();
winnerList.add(winnerPlayer);
}
}
return winnerList;
}
}
`
Комментарии:
1. Я не уверен, что это правильный подход. Я не эксперт по покеру, но, насколько я знаю, хорошие игроки подсчитывают «приблизительные» шансы на выигрыш в своих головах (или они их запомнили), и поэтому я бы сказал, что есть лучший способ рассчитать шансы, которые проверяют 1,7 миллиона комбинаций (например, на самом деле используют комбинаторику так, как они предназначены для использования).
2. Вам, вероятно, не нужно вычислять каждую возможную комбинацию, а просто общий «класс» (например, фулл-хаус, 2 вида и т. Д.) Из того, что уже известно, И суммировать вероятности (дополнительная информация здесь: en.wikipedia.org/wiki/Poker_probability ). Пример: если у игрока уже есть 2 равные карты, которые исключают флеши, стрейты и «без пары», поэтому вы должны иметь возможность рассчитать вероятности для каждого из других классов и сделать то же самое для других игроков. Затем сравните их соответствующим образом (мне не хватает математического банкомата).