-
Notifications
You must be signed in to change notification settings - Fork 11
/
strategy.py
157 lines (142 loc) · 6.1 KB
/
strategy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import poker
class StrategyTreeNode:
def __init__(self, pot_size=None, c=None, r=None, f=None):
self.pot_size = pot_size
self.c = c
self.r = r
self.f = f
self.range = poker.Range()
def __repr__(self):
return 'StrategyTreeNode(pot_size=' + str(self.pot_size) + ')'
class StrategyTree:
def __init__(self, board, starting_pot_size, stack_size, bet_size):
self.board = board
self.plans = []
self.starting_pot_size = starting_pot_size
self.root = self._generate_tree(starting_pot_size, stack_size, bet_size)
def amount_gained(self, pot_size):
return (pot_size + self.starting_pot_size) / 2
def amount_lost(self, pot_size):
return (pot_size - self.starting_pot_size) / 2
def get_plans(self, player):
if player == 'ip':
return [p for p in self.plans if len(p) % 2 == 0]
else:
return [p for p in self.plans if len(p) % 2 == 1]
def create_node(self, plan, pot_size):
node = StrategyTreeNode(pot_size)
if plan[-1] == 'r':
try:
self.plans.remove(plan[:-1])
except ValueError:
pass
self.plans.append(plan)
return node
def _generate_tree(self, starting_pot_size, stack_size, bet_size):
root = StrategyTreeNode(starting_pot_size)
root.r = self.create_node('r', starting_pot_size)
root.c = self.create_node('c', starting_pot_size)
root.c.r = self.create_node('cr', starting_pot_size)
root.c.c = self.create_node('cc', starting_pot_size)
expandable_nodes = [(root.r, 'r'), (root.c.r, 'cr')]
while expandable_nodes:
current, path = expandable_nodes.pop()
current_pot_size = current.pot_size
new_pot_size = current_pot_size * (1 + 2 * bet_size)
current.f = self.create_node(path + 'f', current_pot_size)
if (new_pot_size - starting_pot_size) / 2 >= stack_size:
current.c = self.create_node(path + 'c',
2 * stack_size + starting_pot_size)
else:
current.c = self.create_node(path + 'c', new_pot_size)
current.r = self.create_node(path + 'r', new_pot_size)
expandable_nodes.append((current.r, path + 'r'))
return root
def clear_ranges(self):
frontier = [self.root]
while frontier:
current = frontier.pop()
current.range = poker.Range()
if current.f is not None:
frontier.append(current.f)
if current.c is not None:
frontier.append(current.c)
if current.r is not None:
frontier.append(current.r)
def modify_nodes_by_plan(self, plan, plan_range):
current_node = self.root
for action in plan:
current_node = getattr(current_node, action)
current_node.range += plan_range
def create_counter_strategy(self, player, hand_range):
# TODO: Assert opponent plans are filled in
total_ev = 0
if player == 'ip':
for hand, weight in hand_range.items():
ev_r, plan_r = self.get_highest_ev_plan(player, hand, self.root.r, 'r')
ev_c, plan_c = self.get_highest_ev_plan(player, hand, self.root.c, 'c')
total_ev += weight * (ev_r + ev_c)
elif player == 'oop':
for hand, weight in hand_range.items():
ev_r, plan_r = self.get_highest_ev_plan(player, hand, self.root.r, 'r')
ev_c, plan_c = self.get_highest_ev_plan(player, hand, self.root.c, 'c')
total_ev += weight * max(ev_r, ev_c)
else:
raise ValueError("Player must be 'ip' or 'oop'")
return total_ev
def get_highest_ev_plan(self, player, hand, node, start_path):
if player == 'ip':
modval = 0
elif player == 'oop':
modval = 1
else:
raise ValueError("Player must be 'ip' or 'oop'")
ev = 0
max_ev = -1
max_plan = None
frontier = [(node, start_path)]
while frontier:
current, path = frontier.pop()
if (len(path) % 2 == modval):
if current.f is not None:
ev += (current.f.range.size(remove=hand)
* self.amount_gained(current.pot_size))
equity = poker.equity_hand_vs_range(hand, current.c.range, self.board)
ev += (current.c.range.size(remove=hand)
* (equity * current.c.pot_size
- self.amount_lost(current.c.pot_size)))
else:
equity = poker.equity_hand_vs_range(hand, current.range, self.board)
plan_ev = (ev
+ current.range.size(remove=hand)
* (equity
* current.c.pot_size
- self.amount_lost(current.c.pot_size)))
if plan_ev > max_ev:
max_ev = plan_ev
max_plan = path + 'c'
plan_ev = (ev
- current.range.size(remove=hand)
* self.amount_lost(current.pot_size))
if plan_ev > max_ev:
max_ev = plan_ev
max_plan = path + 'f'
if current.r is not None:
frontier.append((current.r, path + 'r'))
elif ev > max_ev:
max_ev = ev
max_plan = path
return (max_ev, max_plan)
def __repr__(self):
result = ''
frontier = [(self.root, '')]
while frontier:
current, path = frontier.pop()
result += path + ': ' + str(current.range) + '\n'
if current.f is not None:
frontier.append((current.f, path + 'f'))
if current.c is not None:
frontier.append((current.c, path + 'c'))
if current.r is not None:
frontier.append((current.r, path + 'r'))
return result