Сейчас вышла бета вёб-версии на реакте. И в целом у неё хорошее будущее я думаю. Мне понравилась визуализация префлоп рук.
Типа мы выбираем сначала две старшие карты как в холдеме
а потом видим как выглядит диапазон по оставшимся двум картам
По-моему решение красивое и довольно физиологичное. Помогает таки запомнить префлоп. Всё же разделение на кучу категорий и выучивание каждой это труд. А тут визуальная память будет помогать усвоить диапазоны.
Я думаю, что FlopHero изобрел этот стиль матрицы рук для Омахи.
Я всегда думал, что это всего лишь вопрос времени, прежде чем более крупная компания украдет их концепцию.
У FlopHero довольно хорошее приложение, и оно стоит своих денег, но большинству веб-решателей не хватает эквити на префлопе и возможности видеть, из каких линий руки получают свое EV на префлопе/постфлопе.
Большинство пользователей, похоже, даже не заботятся о том, откуда берется EV диапазонов и рук, и забывают, что решатели по сути являются просто продвинутыми калькуляторами EV.
Написано с переводчиком. Надеюсь, это имеет смысл.
Eyjafjallajokul, подобный способ отображать руки был придуман задолго до flophero. Кажеться первыми были пломатрикс или как то так.
Да, все у друг друга тырят идеи. Это норм. Я даже не думал, что тут ребята первые придумали. Просто персонально сам не встречал.
Я забыл о PloMatrix.
И да, воровство идей всегда является частью бизнеса, просто удивительно, что это не было реализовано как опция во многих других продуктах.
Я думаю, основатель GTOWizard в видео с ChicagoJoey упомянул о планах включить PLO в свой продукт и что он не нашел никаких похожих методов отображения рук, прежде чем раскрыть свой собственный метод, что было удивительно, учитывая эти продукты, упомянутые ранее.
Затем он показал свой собственный метод, который мне показался немного неудобным и немного слишком с точки зрения игрока в холдем.
есть толковая классификация с точки зрения обучения. В принципе для профессиональной игры ничего другого и не нужно. Визуальная память для рук в омахе скорее баловство. Но почему нет... если это продаётся и народ берёт на это подписку, то с точки зрения бизнеса это успех.
Eyjafjallajokul, это даже не всегда воровство, кстати, некоторые идеи лежат на поверхности.
SnowBeaver, Это какая?
bokbor, у меня в первой статье по омахе https://forum.gipsyteam.ru/index.php?viewtopic=172401&view=findpost&p=7638271
Исходно Чемберс, потом ребята из команды Фернандо допилили.
Всегда было любопытно как много омашистов прочитало Чемберса
Привет!
Интересный блог!!! Чем сейчас занимаешься, какими проектами?
regular1990 @ 02.05.25Привет!
Интересный блог!!! Чем сейчас занимаешься, какими проектами?
привет, я сейчас захожу на форум только почитать любимых авторов, иногда где-то что-то спизднуть. Для ведения своего блога не хватает времени и энергии. Хотя идей о чём писать до жопы, всё же мой формат требует нескольких дней для написания одного поста-статьи. Сейчас у меня много хорошо оплаченной работы, заботы о семье и приятного досуга. Всё это у меня как правило не способствует глубокой рефлексии в эпистолярном жанре. А если я буду просто писать про свою жизнь, то тут это половине аудитории будет не интересно, а другая передохнет от зависти :) Брать что-то с рабочего конвейера и делать это публичным я тоже далеко не всегда могу. Т.е. мне надо кроме работы-околопокера ещё и хобби-околопокер чтобы что-то писать. В плане проектов, ну я всё ещё один из основных программистов в https://plomastermind.com/, ребята активно развиваются и работы для меня у них много. Ещё мечтаю переметнуться на другую сторону околопокера (с помощью ув. awakening) чтобы уже окончательно (хотябы года на 2-3) обрести дзен, спокойствие и материальную стабильность.
скучно живу в общем, стараюсь :)
SnowBeaver @ 03.05.25Ещё мечтаю переметнуться на другую сторону околопокера (с помощью ув. awakening) чтобы уже окончательно (хотябы года на 2-3) обрести дзен, спокойствие и материальную стабильность.
А зачем? Чтобы не зависеть от проектной работы?
Дизель @ 03.05.25А зачем? Чтобы не зависеть от проектной работы?
ну если не рассматривать деньги как мотивацию (а там правда больше денег платят за меньшие трудозатраты), то у меня есть амбиции в профессиональном росте и любопытство.
Профессиональный рост заключается в том, что я уже почти специалист в ML, но у меня нет адекватных проектов для применения там своих знаний, и собственно это практическая дисциплина. Хочу чтобы кто-то мне в долгую оплатил моё саморазвитие. Т.е. идти куда-то делать абы какую хуйню я могу себе позволить и пасануть.
А любопытство в том, что я с "этой стороны покера" уже всё что можно поделал за свою жизнь. Так или иначе покер это и есть моя жизнь (половина сознательной), хочу ещё с другой стороны посмотреть на предмет. По крайней мере не упущу возможность при наличии таковой.
а так проектность работы и поиск заказчиков меня уже давно не пугает. По сути у меня очередь сейчас тех кто мне готов заплатить за мой труд. Приходится иногда с тоской отказываться от задач, которые лет 5 назад я б мечтал поделать.
SnowBeaver, в контексте ML выглядит так, что тебе в аналитики надо скорее. Ну и более актуальны всегда бизнес-задачи, часто не связанные с покером непосредственно. Например, умение предсказывать поведение игрока.
Дизель @ 04.05.25SnowBeaver, в контексте ML выглядит так, что тебе в аналитики надо скорее. Ну и более актуальны всегда бизнес-задачи, часто не связанные с покером непосредственно. Например, умение предсказывать поведение игрока.
я вырос программистом до современной парадигмы разделения специалистов на миллион типов. Быть программистом для меня значит уметь переводить галлюцинации заказчика на формальную логику, которую может реализовать машина. Современный "аналитик" для меня это такой же программист, ну возможно не full-stack, но это всё не значащие детали. Хорошие деньги в любом случае платят только тем, кто умеет решать актуальные бизнес-задачи. А как и с помощью чего ты этого достигаешь людей обычно тревожит слабо.
Как и обещал сегодня я собираюсь нырнуть в волшебный мир LLM моделей для программирования. С покером оно связано довольно опосредовано. Так уж случилось, что я решил обрести немного стабильности и продаться в компанию где хорошо платят и я буду иметь релевантные навыки чтобы занять сеньёрскую позицию. Самое ближайшее что нашлось это Senior ML Python developer. Я решил попытаться честно заявив на старте, что я не работал в серьёзном ML в крупных проектах и я ни разу не python developer. Но я прям такой сеньёр-помидор, что вам и не снилось и считаю что знание языков и стека конкретных ML библиотек не особо важно :) В целом я нашёл понимание, меня не послали сразу нахер, а дали попробовать доказать на практике своё утверждение. В итоге я получил сначала одно тестовое задание никакой сложности и второе чуть повышенной. Их собственно я и попробую решать с помощью популярных моделей доступных сейчас.
Сразу скажу, что я не являюсь AI хейтером и не пытаюсь здесь оскорбить чью-то религию. Я нашёл для себя области применимости в ежедневной практике и использую. Но увы не для написания непосредственно кода. Для меня это всё ещё линейный преобразователь текста + частичная замена гуглу (stackoverflow). Два года назад я высказался тезисно в блоге уважаемого awakening, что де кодить с LLM увы нельзя и программистов оно в ближайшем будущем не заменит. Вот тот пост где я описал тезисно почему я так думаю. Прошло очень дохрена времени по любым айтишным меркам. А уж хайпа в AI уже столько протекло, что думаю никто не сравнивает даже год 25 с 23м.
Сравнивать буду по тому, что мне сейчас доступно. Что-то уже относительно старое. Итак наши лошадки:
- Claude 3.5 Sonnet
- OpenAI GPT-4o mini
- OpenAI GPT-4o
- DeepSeek
- Gemini
- Claude Sonnet 4
- Claude Opus 4
первые 5 доступны мне по подписке на chatbotapp.ai, последний Claude оплатил подписку на официальном сайте claude.ai

Итак собственно задача:
Для тех кто не в теме программирования (или покера :) ) скажу, что это очень простая задача, которую можно считать проходной на уровень "не дебил". У меня время заняло изучение и привыкание к стандарту PEP8. Благо я нашёл IDE, которое проверяет его по умолчанию. Для программиста уверенно владеющего каким-то языком (любым) это задачка часа на 2. Если затупит, то на 3-4. Опытный сможет решить её на бумажке перед интервьювером за полчаса. Т.е. я уверен, что абсолютно любой программист способен сделать эту задачу за разумное время. Я ожидал, что gtp-4o справится, и был почти наверняка уверен, что Claude Sonnet 4 выдаст качественный код без ошибок (Opus тем более).
Я решил не проверять культуру кода и формат вывода. Хочу просто решить как-то задачу сначала и убедиться что LLM справляется с самой задачей. Благо что я её уже решил и могу проверить ответ. Красоту наводить попробую позднее. Тем более я не сомневаюсь, что это как раз та задача с которой LLM справляется неплохо. Что ещё хорошего в этой задаче это то, что надо немного думать, и решение не сводится к вызову стандартных библиотек. Плюс контекст здесь минимальный.
Claude 3.5
грусть-печаль
задача не решена, программа не запускается. просто какой-то кусок кода ни о чём
OpenAI GPT-4o mini
уже лучше, в коде появляются какие-то абстракции из области задачи. есть комментарии
# Constants
BET_AMOUNT = 10
RAISE_AMOUNT = 20
START_POT = 60
PLAYERS = ['P1', 'P2', 'P3']
ACTIONS = ['x', 'b', 'f', 'c', 'r']
def generate_sequences():
sequences = []
# Recurse function to explore all possible sequences
def recurse(sequence, current_player_idx, current_bet, has_bet, has_raised, players_folded):
# sequence: list of (player, action)
# players_folded: set of players who have folded (end of that branch)
# If all players have folded or just one remaining, sequence ends
if len(players_folded) == 3 or len(players_folded) == 2:
# Calculate pot
contributions = {'P1':0,'P2':0,'P3':0}
highest_bet = 0
for p, act in sequence:
if act == 'b':
contributions[p] += BET_AMOUNT
elif act == 'r':
contributions[p] += RAISE_AMOUNT
elif act == 'c':
contributions[p] += max(current_bet, contributions[p])
# 'x' and 'f' do not add chips
total_pot = START_POT + sum(contributions.values())
# Format sequence string
seq_str = ":".join([f"{p}:{a}" for p,a in sequence])
sequences.append( (seq_str, total_pot) )
return
current_player = PLAYERS[current_player_idx]
if current_player in players_folded:
# Skip folded players
recurse(sequence, (current_player_idx +1) %3, current_bet, has_bet, has_raised, players_folded)
return
# Try all actions
for action in ACTIONS:
valid = True
# 'b' can only be played if no bet yet
if action == 'b' and has_bet:
valid=False
# 'r' can only be played if a bet exists and not already raised
if action == 'r' and (not has_bet or has_raised):
valid=False
# 'c' can only be played if there's an existing bet
if action == 'c' and not has_bet:
valid=False
if not valid:
continue
new_sequence = sequence + [(current_player, action)]
# Update game state
new_has_bet = has_bet
new_has_raised = has_raised
new_bet = current_bet
new_players_folded = set(players_folded)
if action == 'f':
new_players_folded.add(current_player)
recurse(new_sequence, (current_player_idx +1) %3, current_bet, has_bet, has_raised, new_players_folded)
elif action == 'b':
new_bet = BET_AMOUNT
new_has_bet = True
recurse(new_sequence, (current_player_idx +1) %3, new_bet, new_has_bet, new_has_raised, new_players_folded)
elif action == 'r':
new_bet = RAISE_AMOUNT
new_has_raised = True
recurse(new_sequence, (current_player_idx +1) %3, new_bet, new_has_bet, new_has_raised, new_players_folded)
elif action == 'c':
# Call matches the current bet
recurse(new_sequence, (current_player_idx +1) %3, new_bet, new_has_bet, new_has_raised, new_players_folded)
elif action == 'x':
recurse(new_sequence, (current_player_idx +1) %3, new_bet, new_has_bet, new_has_raised, new_players_folded)
# Start recursion from each player with each initial action
for start_player_idx in range(3):
for action in ['x', 'b', 'f', 'r']:
recurse([], start_player_idx, 0, False, False, set())
return sequences
# Generate and print all sequences
all_sequences = generate_sequences()
for seq, pot in all_sequences:
print(f"{seq}, pot={pot}")
но при вызове падает с ошибкой "Recursionerror: maximum recursion depth exceeded while calling a Python object". Ну чтош....
OpenAI GPT-4o
Выдаёт короткий лаконичный код, который уже работает. Но неправильно
def calculate_pot(sequence):
pot = 60
current_bet = 0
player_contributions = [0, 0, 0]
raise_made = False
for action in sequence:
if action == 'b10':
current_bet = 10
pot += 10
raise_made = False
elif action == 'r20':
current_bet = 20
pot += (20 - player_contributions[sequence.index(action) % 3])
raise_made = True
elif action == 'c':
pot += (current_bet - player_contributions[sequence.index(action) % 3])
player_contributions[sequence.index(action) % 3] = current_bet
return pot
def generate_sequences(sequence=[], player=0, bet_round=[]):
if player == 3:
if 'b10' in bet_round and 'c' not in bet_round and 'f' not in bet_round:
return [] # Invalid sequence: an uncalled bet
if 'r20' in bet_round and 'c' not in bet_round and 'f' not in bet_round:
return [] # Invalid sequence: an uncalled raise
return [sequence]
results = []
if player == 0:
# First player has more options
actions = ['x', 'b10']
elif 'b10' in bet_round:
# If there's been a bet, players can call, raise, or fold
actions = ['c', 'r20', 'f']
elif 'r20' in bet_round or ('b10' in bet_round and 'c' in bet_round):
# If there's been a raise, players can call or fold
actions = ['c', 'f']
else:
# Otherwise they can check, bet, or fold
actions = ['x', 'b10', 'f']
for action in actions:
new_sequence = sequence + [action]
new_bet_round = bet_round + [action]
results.extend(generate_sequences(new_sequence, player + 1, new_bet_round))
return results
def main():
sequences = generate_sequences()
for seq in sequences:
pot = calculate_pot(seq)
print(f"Sequence: {''.join(seq)}, pot={pot}")
if __name__ == "__main__":
main()
результат выполнения
Sequence: xxx, pot=60
Sequence: xxf, pot=60
Sequence: xb10c, pot=80
Sequence: xb10f, pot=70
Sequence: xfx, pot=60
Sequence: xfb10, pot=70
Sequence: xff, pot=60
Sequence: b10cc, pot=80
Sequence: b10cr20, pot=100
Sequence: b10cf, pot=80
Sequence: b10r20c, pot=110
Sequence: b10r20f, pot=90
Sequence: b10fc, pot=80
Sequence: b10fr20, pot=90
Sequence: b10ff, pot=70
Увы решением здесь и не пахнет
DeepSeek
решил задача глубоко :) выдал самый длинный ответ в 350 раз длинне правильного. может быть даже в этой помойке строчек есть подмножество, которое требовали в задаче.
код.
from itertools import product
# Define possible actions for each player
actions = ['x', 'b', 'f', 'c', 'r']
# Starting pot size
initial_pot = 60
# Bet and raise amounts
bet_amount = 10
raise_amount = 20
# Function to calculate the pot size for a given sequence
def calculate_pot(sequence):
pot = initial_pot
for action in sequence:
if action == 'b':
pot += bet_amount
elif action == 'r':
pot += raise_amount
elif action == 'c':
pot += bet_amount
return pot
# Function to check if a sequence is valid
def is_valid_sequence(sequence):
# No re-raises are allowed, so only one 'r' is allowed in the sequence
if sequence.count('r') > 1:
return False
# After a raise, no further bets or raises are allowed
if 'r' in sequence:
r_index = sequence.index('r')
for action in sequence[r_index + 1:]:
if action in ['b', 'r']:
return False
return True
# Generate all possible sequences of actions
all_sequences = []
for length in range(1, 7): # Sequences can be up to 6 actions long
for seq in product(actions, repeat=length):
if is_valid_sequence(seq):
all_sequences.append(seq)
# Print all valid sequences and their pot sizes
for seq in all_sequences:
sequence_str = ':'.join(seq)
# Replace 'b' and 'r' with their amounts
sequence_str = sequence_str.replace('b', f'b{bet_amount}').replace('r', f'r{raise_amount}')
pot_size = calculate_pot(seq)
print(f"{sequence_str}, pot={pot_size}")
результат выполения не буду прикладывать, а то не удастся опубликовать пост
Gemini
точно также написало кривую рекурсию без выхода как 4o-mini
def generate_sequences():
"""Generates all valid action sequences and calculates pot sizes."""
starting_pot = 60
bet_size = 10
raise_size = 20
sequences = []
def calculate_pot(actions):
pot = starting_pot
current_bet = 0
player_contributions = [0, 0, 0] # Track each player's contribution
for i, action in enumerate(actions):
player_index = i % 3
if action == 'b':
pot += bet_size
player_contributions[player_index] += bet_size
current_bet = bet_size
elif action == 'r':
pot += raise_size
player_contributions[player_index] += raise_size
current_bet = raise_size # No re-raises
elif action == 'c':
pot += current_bet - player_contributions[player_index]
player_contributions[player_index] = current_bet
elif action != 'f' and action != 'x':
#Invalid action, return none
return None
return pot
def generate_recursive(current_sequence):
player_count = len(current_sequence) % 3
# Sequence end condition. A sequence ends if two players fold OR
# if all players have acted and the betting is settled.
folds = sum([1 for action in current_sequence if action == 'f'])
if folds >= 2:
pot = calculate_pot(current_sequence)
if pot is not None:
sequence_string = ":".join(current_sequence)
sequences.append((sequence_string, pot))
return
# Check if all players have acted in a round and betting is settled
if len(current_sequence) % 3 == 0 and len(current_sequence) > 0:
bets = sum([1 for action in current_sequence[-3:] if action in ('b', 'r', 'c')])
raises = sum([1 for action in current_sequence[-3:] if action == 'r'])
if bets == 0: #Everyone checked
pot = calculate_pot(current_sequence)
if pot is not None:
sequence_string = ":".join(current_sequence)
sequences.append((sequence_string, pot))
return
elif raises == 0:
calls = sum([1 for action in current_sequence[-3:] if action == 'c'])
folds_in_round = sum([1 for action in current_sequence[-3:] if action == 'f'])
if calls + folds_in_round == 3:
pot = calculate_pot(current_sequence)
if pot is not None:
sequence_string = ":".join(current_sequence)
sequences.append((sequence_string, pot))
return
# Generate possible actions
possible_actions = ['x', 'b', 'f'] # Always possible
# Can call if there's a bet
if any(action in ('b', 'r') for action in current_sequence):
possible_actions.append('c')
#Can raise if there is a bet but no raise yet
if any(action in ('b') for action in current_sequence) and not any(action in ('r') for action in current_sequence):
possible_actions.append('r')
# Recursively generate sequences
for action in possible_actions:
generate_recursive(current_sequence + [action])
generate_recursive([])
# Print the results
for sequence, pot in sequences:
print(f"{sequence}, pot={pot}")
generate_sequences()
падает с той же ошибкой
Claude Sonnet 4
Так, добрались до флагманов. Я выкинул впустую свои трудовые 20$. Зато никто не упрекнёт, что я не проверил "современные AI"
Код меня порадовал :) Такое ощущение, что сетка захотела меня наебать. Типа написала какой-то говнокод, который ничего не делает, а потом не вызывая его просто перебрало все варианты ответов. код под спойлером ооочень длинный.
def generate_poker_sequences():
"""
Generate all valid action sequences for a 3-player Texas Hold'em flop round.
Rules:
- 3 players total
- Actions: check(x), bet(b), fold(f), call©, raise®
- Bet = 10 chips, Raise = 20 chips
- Starting pot = 60 chips
- No reraises allowed
"""
sequences = []
starting_pot = 60
bet_amount = 10
raise_amount = 20
def calculate_pot(sequence):
"""Calculate pot size based on action sequence"""
pot = starting_pot
actions = sequence.split(':')
for action in actions:
if action == 'b10':
pot += bet_amount
elif action == 'c':
# Call matches the current bet/raise amount
if any('r20' in a for a in actions[:actions.index(action)]):
pot += raise_amount
else:
pot += bet_amount
elif action == 'r20':
pot += raise_amount
# fold (f) and check (x) add 0 to pot
return pot
def is_valid_sequence(actions):
"""Check if a sequence of actions is valid according to poker rules"""
if not actions:
return False
# Track if there's been a bet or raise
has_bet = False
has_raise = False
active_players = 3
for i, action in enumerate(actions):
if action == 'f': # fold
active_players -= 1
if active_players <= 1:
return True # Game ends when only 1 player left
elif action == 'x': # check
if has_bet or has_raise:
return False # Can't check after bet/raise
elif action == 'b10': # bet
if has_bet or has_raise:
return False # Can't bet after bet/raise
has_bet = True
elif action == 'c': # call
if not (has_bet or has_raise):
return False # Can't call without bet/raise
elif action == 'r20': # raise
if not has_bet:
return False # Can't raise without initial bet
if has_raise:
return False # No reraises allowed
has_raise = True
return True
def generate_sequences_recursive(current_sequence, player_turn, has_bet, has_raise, active_players, players_acted):
"""Recursively generate all valid sequences"""
# If only one player left, game ends
if active_players <= 1:
if current_sequence:
sequences.append(current_sequence)
return
# If all active players have acted and the betting is complete
if players_acted >= active_players:
# Check if betting round is complete
if not has_bet and not has_raise:
# All players checked
sequences.append(current_sequence if current_sequence else "x:x:x")
return
elif has_bet or has_raise:
# All players have responded to bet/raise
sequences.append(current_sequence)
return
# Generate actions for current player
current_player = player_turn % 3
# Available actions based on game state
possible_actions = []
if not has_bet and not has_raise:
# No betting yet - can check or bet
possible_actions = ['x', 'b10', 'f']
elif has_bet and not has_raise:
# There's a bet - can call, raise, or fold
possible_actions = ['c', 'r20', 'f']
elif has_raise:
# There's a raise - can call or fold
possible_actions = ['c', 'f']
for action in possible_actions:
new_sequence = current_sequence + ':' + action if current_sequence else action
new_active = active_players
new_has_bet = has_bet
new_has_raise = has_raise
new_players_acted = players_acted + 1
if action == 'f':
new_active -= 1
elif action == 'b10':
new_has_bet = True
elif action == 'r20':
new_has_raise = True
# Continue with next player
generate_sequences_recursive(
new_sequence,
player_turn + 1,
new_has_bet,
new_has_raise,
new_active,
new_players_acted
)
# Start the recursive generation
generate_sequences_recursive("", 0, False, False, 3, 0)
# Remove duplicates and sort
sequences = list(set(sequences))
sequences.sort()
return sequences
def main():
"""Main function to generate and display all valid poker sequences"""
print("=== Texas Hold'em Flop Action Sequences (3 Players) ===")
print("Rules:")
print("- Actions: check(x), bet(b10), fold(f), call©, raise(r20)")
print("- Starting pot: 60 chips")
print("- Bet: 10 chips, Raise: 20 chips")
print("- No reraises allowed")
print("\n" + "="*60 + "\n")
# Generate sequences using a more systematic approach
sequences_with_pots = []
# Let's manually generate the main categories of sequences
# 1. All players check
seq = "x:x:x"
pot = 60
sequences_with_pots.append((seq, pot))
# 2. First player bets
# 2a. Second player folds
# 2a1. Third player folds
seq = "b10:f:f"
pot = 60 + 10
sequences_with_pots.append((seq, pot))
# 2a2. Third player calls
seq = "b10:f:c"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 2b. Second player calls
# 2b1. Third player folds
seq = "b10:c:f"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 2b2. Third player calls
seq = "b10:c:c"
pot = 60 + 10 + 10 + 10
sequences_with_pots.append((seq, pot))
# 2c. Second player raises
# 2c1. Third player folds, first player folds
seq = "b10:r20:f:f"
pot = 60 + 10 + 20
sequences_with_pots.append((seq, pot))
# 2c2. Third player folds, first player calls
seq = "b10:r20:f:c"
pot = 60 + 10 + 20 + 20
sequences_with_pots.append((seq, pot))
# 2c3. Third player calls, first player folds
seq = "b10:r20:c:f"
pot = 60 + 10 + 20 + 20
sequences_with_pots.append((seq, pot))
# 2c4. Third player calls, first player calls
seq = "b10:r20:c:c"
pot = 60 + 10 + 20 + 20 + 20
sequences_with_pots.append((seq, pot))
# 3. First player checks, second player bets
# 3a. Third player folds
# 3a1. First player folds
seq = "x:b10:f:f"
pot = 60 + 10
sequences_with_pots.append((seq, pot))
# 3a2. First player calls
seq = "x:b10:f:c"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 3b. Third player calls
# 3b1. First player folds
seq = "x:b10:c:f"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 3b2. First player calls
seq = "x:b10:c:c"
pot = 60 + 10 + 10 + 10
sequences_with_pots.append((seq, pot))
# 3c. Third player raises
# 3c1. First player folds, second player folds
seq = "x:b10:r20:f:f"
pot = 60 + 10 + 20
sequences_with_pots.append((seq, pot))
# 3c2. First player folds, second player calls
seq = "x:b10:r20:f:c"
pot = 60 + 10 + 20 + 20
sequences_with_pots.append((seq, pot))
# 3c3. First player calls, second player folds
seq = "x:b10:r20:c:f"
pot = 60 + 10 + 20 + 20
sequences_with_pots.append((seq, pot))
# 3c4. First player calls, second player calls
seq = "x:b10:r20:c:c"
pot = 60 + 10 + 20 + 20 + 20
sequences_with_pots.append((seq, pot))
# 4. First player checks, second player checks, third player bets
# 4a. First player folds
# 4a1. Second player folds
seq = "x:x:b10:f:f"
pot = 60 + 10
sequences_with_pots.append((seq, pot))
# 4a2. Second player calls
seq = "x:x:b10:f:c"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 4b. First player calls
# 4b1. Second player folds
seq = "x:x:b10:c:f"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 4b2. Second player calls
seq = "x:x:b10:c:c"
pot = 60 + 10 + 10 + 10
sequences_with_pots.append((seq, pot))
# 5. First player folds immediately
# 5a. Second player checks, third player checks
seq = "f:x:x"
pot = 60
sequences_with_pots.append((seq, pot))
# 5b. Second player bets
# 5b1. Third player folds
seq = "f:b10:f"
pot = 60 + 10
sequences_with_pots.append((seq, pot))
# 5b2. Third player calls
seq = "f:b10:c"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 5c. Second player checks, third player bets
# 5c1. Second player folds
seq = "f:x:b10:f"
pot = 60 + 10
sequences_with_pots.append((seq, pot))
# 5c2. Second player calls
seq = "f:x:b10:c"
pot = 60 + 10 + 10
sequences_with_pots.append((seq, pot))
# 6. Early folds
seq = "f:f:x" # First two fold, third checks (wins)
pot = 60
sequences_with_pots.append((seq, pot))
seq = "x:f:f" # First checks, others fold
pot = 60
sequences_with_pots.append((seq, pot))
# Sort sequences
sequences_with_pots.sort(key=lambda x: x[0])
# Display results
print(f"Total valid sequences found: {len(sequences_with_pots)}\n")
for i, (sequence, pot) in enumerate(sequences_with_pots, 1):
print(f"{i:2d}. Sequence: {sequence:<15} | Pot: {pot:3d} chips")
print(f"\n{'='*60}")
print(f"Summary: {len(sequences_with_pots)} total valid action sequences")
if __name__ == "__main__":
main()
Собственно ответ ближе всего к правильному пока что
Но неправильно посчитаны результирующие поты. Ну и вообще конечно мне сложно принять ответ из категории.
- "Дядь, скажи сколько время"
- "Сколько время"
типа руками перебрать 28 вариантов большого ума не надо. Заодно ещё и с потом не ошибёшься. Но если надо какую-то задачу чисто практически решить, мне вообще насрать как получен результат, то почему нет собственно... LLM прошла кратчайшим (с точки зрения приложения усилий) путём. Жаль результат с ошибками.
Claude Opus 4
Ответ мне правда очень понравился. Сетка разложила в комментариях что она делает и почему. Т.е. мне понравились комментарии к коду, а сам код неправильный.
код
def generate_betting_sequences():
"""Generate all valid betting sequences for 3 players in Texas Hold'em flop"""
# Initial state
initial_pot = 60
bet_amount = 10
raise_amount = 20
# Store all valid sequences
sequences = []
def simulate_round(actions, pot, active_players, current_player, current_bet, players_to_act):
"""Recursively simulate all possible betting sequences"""
# If only one player left, round is over
if len(active_players) == 1:
sequences.append((actions, pot))
return
# If no more players need to act, round is over
if not players_to_act:
sequences.append((actions, pot))
return
# Skip if current player is not active
if current_player not in active_players:
next_player = (current_player + 1) % 3
simulate_round(actions, pot, active_players, next_player, current_bet, players_to_act)
return
# Current player must be in players_to_act
if current_player not in players_to_act:
next_player = (current_player + 1) % 3
simulate_round(actions, pot, active_players, next_player, current_bet, players_to_act)
return
# Remove current player from players_to_act for their turn
new_players_to_act = players_to_act.copy()
new_players_to_act.remove(current_player)
# Determine valid actions
valid_actions = []
if current_bet == 0:
# No bet yet
valid_actions.append('x') # Check
valid_actions.append('b10') # Bet
else:
# There's a bet
valid_actions.append('f') # Fold
valid_actions.append('c') # Call
# Can raise only if current bet is 10 (not already raised)
if current_bet == 10:
valid_actions.append('r20') # Raise
# Try each valid action
for action in valid_actions:
new_actions = actions + [(current_player, action)]
new_pot = pot
new_active = active_players.copy()
new_current_bet = current_bet
new_to_act = new_players_to_act.copy()
if action == 'f':
# Fold - remove from active players
new_active.remove(current_player)
elif action == 'x':
# Check - no change to pot
pass
elif action == 'b10':
# Bet - increase pot and set current bet
new_pot += 10
new_current_bet = 10
# All other active players must act
new_to_act = [p for p in new_active if p != current_player]
elif action == 'c':
# Call - match current bet
new_pot += current_bet
elif action == 'r20':
# Raise - increase pot and current bet
new_pot += 20
new_current_bet = 20
# All other active players must act again
new_to_act = [p for p in new_active if p != current_player]
# Move to next player
next_player = (current_player + 1) % 3
simulate_round(new_actions, new_pot, new_active, next_player,
new_current_bet, new_to_act)
# Start with player 0, all players active and need to act
simulate_round([], initial_pot, [0, 1, 2], 0, 0, [0, 1, 2])
return sequences
def format_sequence(actions):
"""Format action sequence as string"""
result = []
for player, action in actions:
result.append(action)
return ':'.join(result)
# Generate all sequences
all_sequences = generate_betting_sequences()
# Remove duplicates
unique_sequences = []
seen = set()
for actions, pot in all_sequences:
seq_str = format_sequence(actions)
if seq_str not in seen:
seen.add(seq_str)
unique_sequences.append((seq_str, pot))
# Sort by sequence length and then alphabetically
unique_sequences.sort(key=lambda x: (len(x[0].split(':')), x[0]))
# Print results
print(f"Total number of valid sequences: {len(unique_sequences)}\n")
print("All valid betting sequences:")
print("-" * 50)
for seq, pot in unique_sequences:
print(f"Sequence: {seq}")
print(f"Pot size: {pot} chips")
print("-" * 50)
# Group by pot size for summary
from collections import defaultdict
pot_groups = defaultdict(list)
for seq, pot in unique_sequences:
pot_groups[pot].append(seq)
print("\nSummary by pot size:")
for pot in sorted(pot_groups.keys()):
print(f"\nPot size {pot} chips: {len(pot_groups[pot])} sequences")
for seq in pot_groups[pot][:5]: # Show first 5 examples
print(f" Example: {seq}")
if len(pot_groups[pot]) > 5:
print(f" ... and {len(pot_groups[pot]) - 5} more")
К сожалению ответ избыточен и также неправильно указаны поты. Т.е. Opus в целом выдало более красивый и документированный вариант кода чем бесплатный Sonnet, но при этом результат выполнения объективно хуже. Те же ошибки что в Sonnet, только добавились лишнее строки.
Но система правда с виду крутая. Если вникать в то что оно выдаёт и пытаться ей подсказать что не так в ответе, то возможно оно найдёт ошибку и исправится. Решил добавить ей на ввод инфу о примере вывода из задачи. Там правильно посчитаны поты, в которых сетка накосячила.
новый код
def generate_all_sequences():
"""Generate all valid betting sequences for 3 players in Texas Hold'em flop"""
sequences = []
def simulate_round(actions, active_players, current_bets, pot, last_aggressor, betting_closed):
"""
Recursively generate all possible action sequences
actions: list of actions taken so far
active_players: set of players still in the hand (not folded)
current_bets: dict of current bet amounts for each player
pot: current pot size
last_aggressor: player who made the last aggressive action (bet/raise)
betting_closed: whether betting round is complete
"""
# Check if betting round is complete
if betting_closed or len(active_players) <= 1:
# Format the sequence string
action_str = ":".join(actions)
sequences.append((action_str, pot))
return
# Determine current player (cycles through 0, 1, 2)
current_player = len(actions) % 3
# Skip folded players
if current_player not in active_players:
simulate_round(actions, active_players, current_bets, pot, last_aggressor, betting_closed)
return
# Check if all active players have acted and matched the highest bet
max_bet = max(current_bets.values())
all_matched = all(current_bets[p] == max_bet for p in active_players)
everyone_acted = len(actions) >= 3
if everyone_acted and all_matched and last_aggressor is not None:
# Betting round is complete
simulate_round(actions, active_players, current_bets, pot, last_aggressor, True)
return
# Determine valid actions for current player
player_bet = current_bets[current_player]
to_call = max_bet - player_bet
valid_actions = []
if to_call == 0: # No bet to call
valid_actions.append('x') # Check
if max_bet == 0: # No one has bet yet
valid_actions.append('b10') # Bet
else: # Someone has bet, can raise if no one has raised yet
if max_bet == 10: # Only a bet has been made, can raise
valid_actions.append('r20') # Raise
else: # There's a bet to call
valid_actions.append('f') # Fold
valid_actions.append('c') # Call
if max_bet == 10: # Can still raise if only bet (not raise) has been made
valid_actions.append('r20') # Raise
# Try each valid action
for action in valid_actions:
new_actions = actions + [action]
new_active = active_players.copy()
new_bets = current_bets.copy()
new_pot = pot
new_aggressor = last_aggressor
if action == 'x': # Check
pass
elif action == 'b10': # Bet
new_bets[current_player] += 10
new_pot += 10
new_aggressor = current_player
elif action == 'c': # Call
call_amount = to_call
new_bets[current_player] += call_amount
new_pot += call_amount
elif action == 'r20': # Raise
raise_amount = 20 - player_bet
new_bets[current_player] = 20
new_pot += raise_amount
new_aggressor = current_player
elif action == 'f': # Fold
new_active.remove(current_player)
simulate_round(new_actions, new_active, new_bets, new_pot, new_aggressor, False)
# Start simulation
initial_active = {0, 1, 2}
initial_bets = {0: 0, 1: 0, 2: 0}
initial_pot = 60
simulate_round([], initial_active, initial_bets, initial_pot, None, False)
return sequences
# Generate all sequences
all_sequences = generate_all_sequences()
# Sort sequences for consistent output
all_sequences.sort()
# Print all sequences
for sequence, pot in all_sequences:
print(f"{sequence}, pot={pot}")
print(f"\nTotal number of sequences: {len(all_sequences)}")
И хуяк, та же ошибка с кривой рекурсией что у Gemini и gtp-4o mini
Итого
Ни одна из систем не решает мою простейшую минимально требующую думанья задачу на питоне. Но Claude при этом выглядит солиднее остальных и умеет наёбывать вопрошающего. Отличный инструмент для имитационной деятельности где-то в госконторе где пишут какую-то одноразовую хуйню, которая не будет в итоге нигде запускаться. Также можно наводить лоск в уже написанном коде. Но может прилететь нежданчик и код начнёт работать иначе. Короче инструмент есть, но надо аккуратнее с ним.
P.S. увы, я не вижу смысла пытаться решить более сложную задачу если не получается более простую. Но если вдруг кому интересно попробовать (c LLM или без), то черканите в личку. Там надо на питоне реализовать CFR vanilla солвер для упрощённого покера и обобщить так чтобы код работал для чуть более сложного упрощённого покера. Нахожу задачу красивой, получил удовольствие её делать.
P.P.S. За два года в LLM подсказчиках для меня ничего не произошло. То что было тогда мне помогало, то что есть сейчас помогает не больше. Все мои тезисы в блоге awakening для меня не изменились.
SnowBeaver, да, завышенные у тебя ожидания)) любую нетипичную либо большую задачу нужно дробить на несколько частей, копипастить ей ошибки на исправление и тд и тп.
SnowBeaver, ну такое )
это агентского уровня задача, как раз эти LLM не способны это сделать )
забавно наблюдать подобного уровня эксперименты...
внесу свои пять копеек коррекции :
1. Температуру на 0
что это значит в нейросетях , то что ответ правильный и топ2 вариант будет выдаваться всегда правильный ответ
а когда ставится температура не 0 , а например 0.5 то след токен будет с 0.5 вероятностью выдавать топ2 вариант , для различных ответов
Поэтому температуру только 0 для программирования, ну 0.1 можно попробовать
2. ты просишь создать алгоритм , нейросети пока что являются ассистентами программирования, если ты опишешь алгоритм, нейронка за 1 минуту выдаст почти идеальный чистый код ...
в твоей задаче же ничего подобного нету, ты пытаешься создать алгоритм , для игры которую почти не видела нейросеть , вообще не знает ничего о ней...
вы серьезно думаете раздачи попадают в датасет LLMкам ?
для нейронки которая вообще ничего не знает про игру , решение достаточно неплохое ...
если хочется же без описания алгоритма, то нужен RL
это когда нейронка начинает раскапывать сама алгоритмы
это o1-pro, это o1, это o3
посл наработки это эволюционные алгоритмы от deepmind'a , это AlphaEvolve (пока никому не доступный и стоящий как крыло боинга)
почитать тут https://deepmind.google/discover/blog/alphaevolve-a-gemini-powered-coding-agent-for-designing-advanced-algorithms/?_gl=1*q17ujm*_up*MQ..*_ga*MTAzODc0NTM5NC4xNzQ4MzUwMDgw*_ga_LS8HVHCNQ0*czE3NDgzNTAwODAkbzEkZzAkdDE3NDgzNTAwODAkajAkbDAkaD
A.
ждем повторения с нулевой температурой , например o3 решает эту задачу за 5 минут за два запроса(первый код, второй исправить ошибки) пока я наливал себе кофе и задачу не видел еще глазами) , ничего не проверял), просто скинул картинку , но выглядит достойно :
c00l0ne,
#include <iostream>
#include <vector>
#include <string>
constexpr int N_PLAYERS = 3;
constexpr int STARTING_POT = 60;
constexpr int BET_AMOUNT = 10;
constexpr int RAISE_AMOUNT = 20;
struct State
{
std::vector<int> put;
std::vector<bool> active;
int currentPlayer;
int currentBet;
int pot;
bool raiseDone;
int lastAggressor;
std::string history;
};
inline void addAction(std::string& hist, const std::string& action)
{
if (!hist.empty()) hist += ':';
hist += action;
}
int nextActive(const State& s, int after)
{
int p = after;
for (int i = 0; i < N_PLAYERS; ++i)
{
p = (p + 1) % N_PLAYERS;
if (s.active[p]) return p;
}
return -1;
}
bool everyoneCalled(const State& s)
{
for (int i = 0; i < N_PLAYERS; ++i)
if (s.active && s.put < s.currentBet)
return false;
return true;
}
void printLine(const std::string& hist, int pot)
{
std::cout << hist << ", pot=" << pot << '\n';
}
void dfs(const State& st)
{
State s = st;
int p = s.currentPlayer;
if (s.currentBet == 0) // ещё никто не ставил
{
// CHECK
{
State ns = s;
addAction(ns.history, "x");
int nxt = nextActive(ns, p);
if (nxt == 0) // все чекнули
printLine(ns.history, ns.pot);
else
{
ns.currentPlayer = nxt;
dfs(ns);
}
}
// BET 10
{
State ns = s;
ns.put[p] += BET_AMOUNT;
ns.pot += BET_AMOUNT;
ns.currentBet = BET_AMOUNT;
ns.lastAggressor = p;
addAction(ns.history, "b10");
ns.currentPlayer = nextActive(ns, p);
dfs(ns);
}
}
else // ставка уже есть
{
// CALL
{
State ns = s;
int need = ns.currentBet - ns.put[p];
ns.put[p] += need;
ns.pot += need;
addAction(ns.history, "c");
int nxt = nextActive(ns, p);
if (everyoneCalled(ns) && nxt == ns.lastAggressor)
printLine(ns.history, ns.pot);
else
{
ns.currentPlayer = nxt;
dfs(ns);
}
}
// FOLD
{
State ns = s;
ns.active[p] = false;
addAction(ns.history, "f");
int alive = 0;
for (bool a : ns.active) if (a) ++alive;
if (alive == 1)
printLine(ns.history, ns.pot);
else
{
int nxt = nextActive(ns, p);
if (nxt != -1)
{
ns.currentPlayer = nxt;
dfs(ns);
}
}
}
// RAISE to 20 (если ещё не было)
if (!s.raiseDone && s.currentBet == BET_AMOUNT)
{
State ns = s;
int need = RAISE_AMOUNT - ns.put[p];
ns.put[p] += need;
ns.pot += need;
ns.currentBet = RAISE_AMOUNT;
ns.raiseDone = true;
ns.lastAggressor = p;
addAction(ns.history, "r20");
ns.currentPlayer = nextActive(ns, p);
dfs(ns);
}
}
}
int main()
{
State root;
root.put = std::vector<int>(N_PLAYERS, 0);
root.active = std::vector<bool>(N_PLAYERS, true);
root.currentPlayer = 0;
root.currentBet = 0;
root.pot = STARTING_POT;
root.raiseDone = false;
root.lastAggressor = -1;
dfs(root);
return 0;
}
пользоваться для программирования устаревшими нейронками кринж
такими как gtp-4o mini, gpt-4o, claude 3.5 и т.д. когда уже есть более мощные o3 , o1-pro , claude thinking
сейчас идёт обсуждение комьюнити. Не все в восторге от такого решения. Особенно учитывая, что софт входил в подписку. Типа будто бы платишь столько же, а получаешь меньше (даже если не использовал ранее).
Но проблема в том, что реально компетентных профессионалов всегда очень мало. И в итоге софт пишут не для них. Тут если перекличку сделать на джипси кто из омашистов в курсе возможностей этой тулзы и сам пробовал, то наверное будут все те люди, кого я знаю лично. Причём не по форуму. Я отслеживаю дискорд по западным пользователям. Люди переживают в основном за обзор частот по текстурам бордов. А это легко добавляется в вёб. Пока не видел никого, кто бы написал что-то вроде "не отбирайте у меня бакет-матрицу" )))
Я бы хотел взять ключевые функции из этого софта, добавить другие идеи, допилить калькулятор под другие форматы омахи и сделать standalone приложение типа flopzilla. Но тут должны звёзды сойтись... Я не могу себе позволить работать бесплатно. А покер как-будто бы не растущий бизнес, и инвесторов под такое сейчас тупо нет. Я и так работаю с самой прогрессивной конторой, которая давала мне максимум свободы в разработке. За что я бесконечно признателен. Заинтересовать реально работающим и помогающим расти в игре софтом сейчас крайне сложно. Людям нужна геймификация, и чтобы они начали тащить без усилий. Они этого разумеется не получат, но будет куча контор, которые продадут им эту иллюзию.