Best way to combine a permutation of conditional statements - python

So, I have a series of actions to perform, based on 4 conditional variables - lets say x,y,z & t. Each of these variables have a possible True or False value. So, that is a total of 16 possible permutations. And I need to perform a different action for each permutation.
What is the best way to do this rather than making a huge if-else construct.
Lets see a simplified example. This is how my code would look if I try to contain all the different permutations into a large if-else construct.
if (x == True):
if (y == True):
if (z == True):
if (t == True):
print ("Case 1")
else:
print ("Case 2")
else:
if (t == True):
print ("Case 3")
else:
print ("Case 4")
else:
if (z == True):
if (t == True):
print ("Case 5")
else:
print ("Case 6")
else:
if (t == True):
print ("Case 7")
else:
print ("Case 8")
else:
if (y == True):
if (z == True):
if (t == True):
print ("Case 9")
else:
print ("Case 10")
else:
if (t == True):
print ("Case 11")
else:
print ("Case 12")
else:
if (z == True):
if (t == True):
print ("Case 13")
else:
print ("Case 14")
else:
if (t == True):
print ("Case 15")
else:
print ("Case 16")
Is there any way to simplify this? Obviously, my objective for each case is more complicated than just printing "Case 1".

You can use a map of cases to results:
cases = { (True, True, True, True): "Case 1",
(True, True, True, False): "Case 2",
(True, True, False, True): "Case 3",
(True, True, False, False):"Case 4",
(True, False, True, True): "Case 5",
(True, False, True, False):"Case 6",
(True, False, False, True): "Case 7",
(True, False, False, False):"Case 8",
(False, True, True, True): "Case 9",
(False, True, True, False):"Case 10",
(False, True, False, True): "Case 11",
(False, True, False, False):"Case 12",
(False, False, True, True): "Case 13",
(False, False, True, False):"Case 14",
(False, False, False, True): "Case 15",
(False, False, False, False):"Case 16"}
print(cases[(x,y,z,t])
If you want to do something else/different for each case, you could add a function to that map.
cases = { (True, True, True, True): foo_func,
(True, True, True, False): bar_func,
...}
result = cases[(x,y,x,t)](*args)
You can also use one of the masking solutions to make the code shorter, or if you have too many cases to write out, but for smaller sets of cases, this explicit representation will be clearer and easier to maintain.

you could get your case number directly from binary manipulation of your booleans:
case = (x^1) << 3 | (y^1) << 2 | (z^1) << 1 | (t^1) + 1
print(f'Case {case}')
if you look at John Kugelman's answer you see that x, y, z, t are just the 'bits' of your case number (where True=0 and False=1)... so i construct an int setting those bits (and then add 1 because you start counting at 1).
if the numbering is arbitrary you can simplify that down to x << 3 | y << 2 | z << 1 | t and take it from there.
this is easily extensible to a larger number of boolean variables.
then to do something based on the case number i suggest you create a dictionary containing the case as key and the function or data or whatever as value. something like:
case_functions = {1: func_1, 2: func_2, ...}
res = case_functions(case)(some argument)

You could shove all the values into a tuple and use 16 tuple comparisons.
if (x, y, z, t) == (True, True, True, True): print("Case 1")
elif (x, y, z, t) == (True, True, True, False): print("Case 2")
elif (x, y, z, t) == (True, True, False, True): print("Case 3")
elif (x, y, z, t) == (True, True, False, False): print("Case 4")
elif (x, y, z, t) == (True, False, True, True): print("Case 5")
elif (x, y, z, t) == (True, False, True, False): print("Case 6")
elif (x, y, z, t) == (True, False, False, True): print("Case 7")
elif (x, y, z, t) == (True, False, False, False): print("Case 8")
elif (x, y, z, t) == (False, True, True, True): print("Case 9")
elif (x, y, z, t) == (False, True, True, False): print("Case 10")
elif (x, y, z, t) == (False, True, False, True): print("Case 11")
elif (x, y, z, t) == (False, True, False, False): print("Case 12")
elif (x, y, z, t) == (False, False, True, True): print("Case 13")
elif (x, y, z, t) == (False, False, True, False): print("Case 14")
elif (x, y, z, t) == (False, False, False, True): print("Case 15")
elif (x, y, z, t) == (False, False, False, False): print("Case 16")
This could be converted into a dict lookup or use clever binary packing tricks, but the advantages here are (a) it's straightforward and readable; (b) there's no need for lambdas or functions; and (c) you can put anything into the 16 cases.

This is a flexible solution that offers scalability and a certain level of simplicity.
Firstly, you'll need to create the methods that will run per output. These are the "complicated" versions of your print("case X") statements
#Define your method outcomes here...
#Note that this follows a binary layout starting with
# a + b + c + d = false
def action1(): #binary 0 (a'b'c'd')
print("case 1")
def action2(): #binary 1 (a'b'c'd)
print("case 2")
def action3(): #binary 2 (a'b'cd')
print("case 3")
def action4(): #binary 3 (a'b'cd)
print("case 4")
def action5(): #binary 4 (a'bc'd')
print("case 5") #etc...
def action6():
print("case 6")
def action7():
print("case 7")
def action8():
print("case 8")
def action9():
print("case 9")
def action10():
print("case 10")
def action11():
print("case 11")
def action12():
print("case 12")
def action13():
print("case 13")
def action14():
print("case 14")
def action15():
print("case 15")
def action16():
print("case 16")
def actionDefault():
print("Error!")
Then, you can easily reference these specific action methods later by creating a method name list and then creating a method to reference the method list when called.
import itertools #Generates all permutations
import sys #Allows us to get the current module
#Returns the index of the actionList we should execute
def evaluateActionIndex(varList):
allcombinations = itertools.product([False, True], repeat=len(varList))
i = 0
for subset in allcombinations: #for each of the possible combinations...
if list(subset) == varList: #Check to see if we want to execute this index.
return i
i = i + 1 #Increment the target index
return -1 #Execute default method (-1 index)
def performAction(index):
actionList = [action1.__name__, action2.__name__, action3.__name__, action4.__name__,
action5.__name__, action6.__name__, action7.__name__, action8.__name__,
action9.__name__, action10.__name__, action11.__name__, action12.__name__,
action13.__name__, action14.__name__, action15.__name__, action16.__name__,
actionDefault.__name__]
method = getattr(sys.modules[__name__], actionList[index]) #Get method by name
method() #Execute Method
We can perform some action by using:
#Mock up some control inputs
a = False
b = True
c = False
d = False
controlVariables = [a, b, c, d] #All Your Control Variables
#Execute control sequence
performAction(evaluateActionIndex(controlVariables))
I've tested this and it works effectively. You can add as many control variables as you need to the controlVariables list.

That's genious. Bits! Very clean.
I was searching for a solution to this for a long time.
Here's a javascript version:
//assuming you have your variables in an array
let q = evaluatedQuery = ["wd:Q82955", "wd:Q212238", "", "wd:Q116"]
//lenght of the binary string
let possibleCases = evaluatedQuery.length
let binaryCase = ""
for (let i = 0; i < possibleCases; i++) {
// this "!!" makes a value truthy or falsy,
// and converts that to an integer "!!q[i] ^ 0"
binaryCase = `${binaryCase}${!!q[i] ^ 0}`
}
//this finds out which of (q*q = 16) cases its gonna be
let parsedBinaryCase = parseInt(binaryCase, 2) + 1
//this converts it to an array for easy handling
let binaryCaseArr = binaryCase.split("")
//this filers out falsy values by taking falsy values index
let boundQueryElements = evaluatedQuery.filter((el, i) => {
return !binaryCaseArr[i] != !!el ^ 0
})
console.log(binaryCase) //output: 1101
console.log(parsedBinaryCase) //output: 14
console.log(boundQueryElements) //output: ['wd:Q82955','wd:Q212238','wd:Q116']
//and this is a clean way to handle those 16 cases
//in this example it would go to case 14
switch (parsedBinaryCase) {
case 1:
break
case 2:
break
case 3:
break
case 4:
break
case 5:
break
case 6:
break
case 7:
break
case 8:
break
case 9:
break
case 10:
break
case 11:
break
case 12:
break
case 13:
break
case 14:
// for (let el in boundQueryElements) {
// }
break
case 15:
break
case 16:
break
default:
}
It, like, 'flattens' the tree structure.

Just use the binary-ness of True and False values:
x = True
y = True
z = True
t = True
total = bin(x + 2 * y + 4 * z + 8 * t)
print(total)
print(int(total, 2))
Outputs:
0b1111
15
Whereas
x = False
y = True
z = False
t = True
total = bin(x + 2 * y + 4 * z + 8 * t)
print(total)
print(int(total, 2))
Yields:
0b1010
10
Now you can easily use the int(total, 2) value to determine which case you are dealing with
So you could convert your code to a single level of indents:
case = int(total, 2)
if case == 0:
print('case 0')
elif case == 1:
print('case 1')
elif case == 2:
print('case 2')
...

When there are this many cases I usually prefer writing helper functions that make the code easier to maintain, e.g.:
def compare(xyzt, binaryval):
boolval = tuple(digit == '1' for digit in binaryval)
return all(a == b for a, b in zip(xyzt, boolval))
then your if statement can be written as:
xyzt = (x, y, z, t)
if compare(xyzt, '1111'): ...
elif compare(xyzt, '1110'): ...
elif compare(xyzt, '1100'): ...
etc.
which makes it much easier to verify that you've considered all the cases.

I think this is a nice place for a registry of handlers. This won't give you the shortest code, but I think it gives you code that is easier to read and more maintainable, which is one interpretation of "simpler". I'd do something like this:
registry.py
handlers = dict()
def register(x, y, z, t):
if (x, y, z, t) in handlers:
raise ValueError("Handler already registered for {}/{}/{}/{}".format(
x, y, z, t))
def insert(f):
handlers[(x, y, z, t)] = f
return insert
def handle(x, y, z, t):
if (x, y, z, t) not in handlers:
raise KeyError("No handler registered for {}/{}/{}/{}".format(
x, y, z, t))
return handlers[(x, y, z, t)]()
handlers.py
from delegation import register, handle
#register(x=True, y=True, z=False, t=True)
def some_descriptive_name():
print("hi!")
#register(x=True, y=False, z=True, t=False)
def another_good_name():
print("Yes hello.")
# etc.
main.py
from handlers import handle
x, y, z, t = True, False, False, True
handle(x, y, z, t)
This lets you see exactly the conditions under which each handler will be activated. Separating your handlers into their own functions makes for cleaner testing as well. I've added a check to make sure you're not trying to handle the same conditions more than once and an error message for if a set of conditions isn't handled. It'd be easy to add a check to make sure that all cases are handled, too.
If your actions need to make use of variables (besides the four conditionals) you can do that as well; just change the signature and return value of handle like so:
def handle(x, y, z, t, *args, **kwargs):
...
return handlers[(x, y, z, t)](*args, **kwargs)
and, of course, add arguments to the handlers.

Extending on #Reedinationer's answer:
# your functions
def f0(): print('case 1')
def f1(): print('case 2')
def f2(): print('case 3')
#.
#.
def f15(): print('case 16')
list_of_functions = [f0, f1, f2] # assuming there are 16 functions in total
x = False
y = False
z = False
t = False
total = bin(x + 2 * y + 4 * z + 8 * t)
index = int(total, 2)
list_of_functions[index]() # will print('case 1')
Tested on python 2.7 and 3.7

Related

I am having a problem with my code on Sieve of Eratosthenes

See my code below. I keep getting an error code, and I don't understand what it means too. Where in the code can I look?
def list_true(n):
return [False for x in range(2)] + [x for x in range (2, n+1)]
assert len(list_true(20)) == 21
assert list_true(20)[0] is False
assert list_true(20)[1] is False
def mark_false(bool_list, p):
mark_false = []
for x in bool_list:
if x/p == 1:
mark_false.append(True)
elif x % p == 0:
mark_false.append(False)
else:
mark_false.append(True)
return mark_false
assert mark_false(list_true(6), 2) == [False, False, True, True, False, True, False]
def find_next(bool_list, p):
x = 0
cleared = False
for bool in bool_list:
if cleared:
if bool:
return x
if x == p and bool:
cleared = True
x += 1
return None
assert find_next([True, True, True, True], 2) == 3
assert find_next([True, True, True, False], 2) is None
def prime_from_list(bool_list):
y = [x for x, i in enumerate(bool_list) if i]
prime_from_list = []
for element in bool_list:
if element == True:
return y
return prime_from_list
assert prime_from_list([False, False, True, True, False]) == [2, 3]
def sieve(n):
bool_list = list_true(n)
p = 2
while p is not None:
bool_list = mark_false(bool_list, p)
p = find_next(bool_list, p)
return prime_from_list(bool_list)
Then I get an error message after the below code.
assert sieve(1000) == get_primes(0, 1000)
--------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-50-c49169fabbae> in <module>()
----> 1 assert sieve(1000) == get_primes(0, 1000)
AssertionError:
Why am I getting the error and is there a possible way I can amend it?
This is a strange question. First, you didn't supply get_primes(0, 1000) so we can't compare the results. Second, you, as programmer, put in the assert statements to test for situations you know shouldn't happen, so you shouldn't question the assert itself.
I believe the reason the assert is failing is that your tortured code doesn't produce primes (it includes composite odd numbers) E.g. sieve(20) returns:
[2, 3, 5, 7, 9, 11, 13, 15, 17, 19]
Furthermore, your code isn't actually a sieve! The mark_false() routine should simply strike out multiples of the most recently discovered prime, but instead it tests all the numbers to see if they are divisible by the prime! This is brute force prime searching in the guise of a sieve.
Below is my rework and simplification of your code which should pass the assertion in question:
def list_true(n):
return [False for _ in range(2)] + [True for _ in range(2, n + 1)]
assert len(list_true(20)) == 21
assert list_true(20)[0] is False
assert list_true(20)[19] is True
def mark_false(bool_list, prime):
for index in range(prime * prime, len(bool_list), prime):
if bool_list[index] is False:
continue
bool_list[index] = False
return bool_list
assert mark_false(list_true(6), 2) == [False, False, True, True, False, True, False]
def find_next(bool_list, index):
while index + 1 < len(bool_list):
index += 1
if bool_list[index]:
return index
return None
assert find_next([True, True, True, True], 2) == 3
assert find_next([True, True, True, False], 2) is None
def prime_from_list(bool_list):
return [index for index, boolean in enumerate(bool_list) if boolean]
assert prime_from_list([False, False, True, True, False]) == [2, 3]
def sieve(number):
bool_list = list_true(number)
prime = 2
while prime is not None:
bool_list = mark_false(bool_list, prime)
prime = find_next(bool_list, prime)
return prime_from_list(bool_list)
assert sieve(1000) == get_primes(0, 1000)
Note that bool is a Python class, don't use it as a variable name.

Issue with output from an if...else function in python

I am trying to write a function in Python that would return a colour value after evaluating to a user value. This what I have tried, however, I am not get what I expect, a single colour. I am getting a list of colours. I am new to programming.
uservalue = 100000
def colour(m,i):
if uservalue > (m + i):
clrs == "red"
elif uservalue < (m - i):
clrs == "blue"
else:
clrs == "white"
return clrs
to test I use this input:
colour(20000, 3000)
output:
['red', 'red', 'red', 'white', 'white', 'white', 'white']
I was looking for one colour returned after evaluation.
uservalue = 100000
def colour(m,i):
if uservalue > (m + i):
clrs = "red"
elif uservalue < (m - i):
clrs = "blue"
else:
clrs = "white"
return clrs
print(colour(20000, 3000))
output will be: red
you are almost there. replace == with =. == is referred when checking equality and = is referred when assigning a value to a variable. try below code.
uservalue = 100000
def colour(m,i):
if uservalue > (m + i):
clrs = "red"
elif uservalue < (m - i):
clrs = "blue"
else:
clrs = "white"
return clrs

In a nested if loop, how to return true for multiple matching conditions?

In the below program, even though all the if conditions are matching, it returns true just once. How do i make it return true and print as many times as the conditions match?
lotto_numbers = [1,1,1]
fireball_number = 1
user_input1 = user_input2 = user_input3 = 1
def fbcheck():
if lotto_numbers == [user_input1,user_input2,fireball_number]:
return True
elif lotto_numbers == [fireball_number, user_input2, user_input3]:
return True
elif lotto_numbers == [user_input1, fireball_number, user_input3]:
return True
else:
return False
if (fbcheck() == True):
print ('you won')
You can use all:
def fbcheck():
user_data = [user_input1,user_input2,fireball_number]
lotto_numbers = [1,1,1]
print([a==b for a, b in zip(lotto_numbers, user_data)])
return all(a==b for a, b in zip(lotto_numbers, user_data))
print(fbcheck())
Output:
[True, True, True]
True

Python dict using logic input for truth tables

Alright boys and girls here we go. To start off I have a couple of questions. Since my program is large I will simply ask questions in stages, this question being the first. I'm creating a program that generates a truth table for postfix logical expressions. Here are the operators allowed and their logical equivalents:
Operators:
= Logical Equivalence (≡ or ↔)
`->` or `<=` Logical Implication (→)
+ Disjunction (∨), AKA “or”
* Conjunction (∧), AKA “and”
`~` or `!` Negation (¬), AKA “not”
Here are some examples of input and output:
input
p True =
output
p p True =
False False
True True
input
p !
output
p p !
False True
True False
input
p q =
output
p q p q =
False False True
False True False
True False False
True True True
Ok I don't really know where to begin, but I'm not asking for anybody to write this program for me. I know I need to write code using a Python dict, that matches the keys to the corresponding proposition. But how do I know which ones to put for keys and which ones to put for values? Also, in the case of:
`->` or `<=` Logical Implication (→)
and
`~` or `!` Negation (¬), AKA “not”
How do I assign 2 different inputs to be able to be used in a python dict? I hope this isn't too confusing, I'm very noob at python, any help is appreciated. Thank you!
UPDATE
Ok here is the code I have now:
propositions = {
'=' : (2, {(True, True): True,
(True, False): False,
(False, True) : False,
(False, False): True,
}),
'->' : (2, {(True, True): True,
(True, False): False,
(False, True): True,
(False, False): True,
}),
'+' : (2, {(True, True): True,
(True, False): True,
(False, True): True,
(False, False): False,
}),
'*' : (2, {(True, True): True,
(True, False): False,
(False, True): False,
(False, False): False,
}),
'!' : (1, {True: False,
False: True})}
prop = sys.stdin.readline()
prop = prop.split()
prop = prop[::-1]
for x in prop:
I believe I successfully reversed the string and removed all whitespaces, but I am still a bit confused on iterating through it.
SECOND UPDATE here is my code:
propositions = {
'=' : (2, {(True, True): True,
(True, False): False,
(False, True) : False,
(False, False): True,
}),
'->' : (2, {(True, True): True,
(True, False): False,
(False, True): True,
(False, False): True,
}),
'+' : (2, {(True, True): True,
(True, False): True,
(False, True): True,
(False, False): False,
}),
'*' : (2, {(True, True): True,
(True, False): False,
(False, True): False,
(False, False): False,
}),
'!' : (1, {True: False,
False: True})}
prop = sys.stdin.readline()
prop = prop.strip().split()
prop = reversed(prop)
def evaluate():
token = next(prop)
try:
nargs, table = propositions[token]
except KeyError:
if token.lower() in ('true', '1'):
return True
elif token.lower() in ('false', '0'):
return False
else:
return token
return table[tuple(evaluate() for i in range(nargs))]
You have to build your dicts in the order of resolution from outer to inner:
master_dict = {
'=': (2, {(True, True): True,
(True, False): False,
...
}),
...
'!': (1, {True: False,
False: True})}
The numbers indicate how many operands the operator takes.
To parse an input read it from right to left.
Use a recursive function, that consumes one token from the right.
(1) If the token is an operator (i.e. a key in your dictionary) retrieve the corresponding value from your master dict.
The number stored first is the number of arguments the operator takes. So your function must now call itself as many times as there are arguments. Be sure to keep track of which tokens have already be read. One neat way of doing this is using a list iterator, which will spit out each element exactly once, so you can't get the indexing wrong. Once you have all the arguments you apply the truth table you've just retrieved, read out the result and return it.
(2) If the token is not an oprator your function must just return it.
prop = sys.stdin.readline()
def solve_no_var(prop):
rev_iter = reversed(prop)
def evaluate():
token = next(rev_iter)
try:
nargs, table = propositions[token]
except KeyError:
if token.lower() in ('true', '1'):
return True
elif token.lower() in ('false', '0'):
return False
else:
return token
return table[tuple(evaluate() for i in range(nargs))]
return evaluate()
def solve(prop):
prop = prop.strip().split()
variables = list(set(prop) - set(propositions)
- {'True', 'TRUE', 'true', '1', 'False', 'FALSE', 'false', '0'})
lookup = {v: [j for j, p in enumerate(prop) if p == v] for v in variables}
N = len(variables)
print((N*" {:6} ").format(*variables), 'result')
for p in itertools.product(("True", "False"), repeat=N):
prop_nv = prop.copy()
for v, b in zip (variables, p):
for j in lookup[v]:
prop_nv[j] = b
res = solve_no_var(prop_nv)
print(((N+1)*" {:6} ").format(*(p + (res,))))
solve(prop)

Algorithm to determine the winner of a Texas Hold'em Hand

Ok, so I am making a Texas Hold'em AI for my senior project. I've created the gui and betting/dealing procedures, but I have reached the part where I need to determine who won the hand, and I do not know the best way to approach this. I am using python btw. ATM i have 2 lists, one for the 7 player cards, one for the 7 computer cards. Currently all cards are stored as a struct in the list as {'Number': XX, 'Suit': x}, where number is 2-14, suit is 1-4. The way I was going to approach this, is make a function for each hand type, starting with the highest. Eg. self.CheckRoyal(playerCards), and manually go through the list and evaluate if a royal flush was achieved. There has to be a better, numerically way to do this.
http://www.codingthewheel.com/archives/poker-hand-evaluator-roundup
Best algorithm you will get is 7 looks in lookup table of size 100 MB (if I remember correctly)
import itertools
from collections import Counter
# gets the most common element from a list
def Most_Common(lst):
data = Counter(lst)
return data.most_common(1)[0]
# gets card value from a hand. converts A to 14, is_seq function will convert the 14 to a 1 when necessary to evaluate A 2 3 4 5 straights
def convert_tonums(h, nums = {'T':10, 'J':11, 'Q':12, 'K':13, "A": 14}):
for x in xrange(len(h)):
if (h[x][0]) in nums.keys():
h[x] = str(nums[h[x][0]]) + h[x][1]
return h
# is royal flush
# if a hand is a straight and a flush and the lowest value is a 10 then it is a royal flush
def is_royal(h):
nh = convert_tonums(h)
if is_seq(h):
if is_flush(h):
nn = [int(x[:-1]) for x in nh]
if min(nn) == 10:
return True
else:
return False
# converts hand to number valeus and then evaluates if they are sequential AKA a straight
def is_seq(h):
ace = False
r = h[:]
h = [x[:-1] for x in convert_tonums(h)]
h = [int(x) for x in h]
h = list(sorted(h))
ref = True
for x in xrange(0,len(h)-1):
if not h[x]+1 == h[x+1]:
ref = False
break
if ref:
return True, r
aces = [i for i in h if str(i) == "14"]
if len(aces) == 1:
for x in xrange(len(h)):
if str(h[x]) == "14":
h[x] = 1
h = list(sorted(h))
for x in xrange(0,len(h)-1):
if not h[x]+1 == h[x+1]:
return False
return True, r
# call set() on the suite values of the hand and if it is 1 then they are all the same suit
def is_flush(h):
suits = [x[-1] for x in h]
if len(set(suits)) == 1:
return True, h
else:
return False
# if the most common element occurs 4 times then it is a four of a kind
def is_fourofakind(h):
h = [a[:-1] for a in h]
i = Most_Common(h)
if i[1] == 4:
return True, i[0]
else:
return False
# if the most common element occurs 3 times then it is a three of a kind
def is_threeofakind(h):
h = [a[:-1] for a in h]
i = Most_Common(h)
if i[1] == 3:
return True, i[0]
else:
return False
# if the first 2 most common elements have counts of 3 and 2, then it is a full house
def is_fullhouse(h):
h = [a[:-1] for a in h]
data = Counter(h)
a, b = data.most_common(1)[0], data.most_common(2)[-1]
if str(a[1]) == '3' and str(b[1]) == '2':
return True, (a, b)
return False
# if the first 2 most common elements have counts of 2 and 2 then it is a two pair
def is_twopair(h):
h = [a[:-1] for a in h]
data = Counter(h)
a, b = data.most_common(1)[0], data.most_common(2)[-1]
if str(a[1]) == '2' and str(b[1]) == '2':
return True, (a[0], b[0])
return False
#if the first most common element is 2 then it is a pair
# DISCLAIMER: this will return true if the hand is a two pair, but this should not be a conflict because is_twopair is always evaluated and returned first
def is_pair(h):
h = [a[:-1] for a in h]
data = Counter(h)
a = data.most_common(1)[0]
if str(a[1]) == '2':
return True, (a[0])
else:
return False
#get the high card
def get_high(h):
return list(sorted([int(x[:-1]) for x in convert_tonums(h)], reverse =True))[0]
# FOR HIGH CARD or ties, this function compares two hands by ordering the hands from highest to lowest and comparing each card and returning when one is higher then the other
def compare(xs, ys):
xs, ys = list(sorted(xs, reverse =True)), list(sorted(ys, reverse = True))
for i, c in enumerate(xs):
if ys[i] > c:
return 'RIGHT'
elif ys[i] < c:
return 'LEFT'
return "TIE"
# categorized a hand based on previous functions
def evaluate_hand(h):
if is_royal(h):
return "ROYAL FLUSH", h, 10
elif is_seq(h) and is_flush(h) :
return "STRAIGHT FLUSH", h, 9
elif is_fourofakind(h):
_, fourofakind = is_fourofakind(h)
return "FOUR OF A KIND", fourofakind, 8
elif is_fullhouse(h):
return "FULL HOUSE", h, 7
elif is_flush(h):
_, flush = is_flush(h)
return "FLUSH", h, 6
elif is_seq(h):
_, seq = is_seq(h)
return "STRAIGHT", h, 5
elif is_threeofakind(h):
_, threeofakind = is_threeofakind(h)
return "THREE OF A KIND", threeofakind, 4
elif is_twopair(h):
_, two_pair = is_twopair(h)
return "TWO PAIR", two_pair, 3
elif is_pair(h):
_, pair = is_pair(h)
return "PAIR", pair, 2
else:
return "HIGH CARD", h, 1
#this monster function evaluates two hands and also deals with ties and edge cases
# this probably should be broken up into separate functions but aint no body got time for that
def compare_hands(h1,h2):
one, two = evaluate_hand(h1), evaluate_hand(h2)
if one[0] == two[0]:
if one[0] =="STRAIGHT FLUSH":
sett1, sett2 = convert_tonums(h1), convert_tonums(h2)
sett1, sett2 = [int(x[:-1]) for x in sett1], [int(x[:-1]) for x in sett2]
com = compare(sett1, sett2)
if com == "TIE":
return "none", one[1], two[1]
elif com == "RIGHT":
return "right", two[0], two[1]
else:
return "left", one[0], one[1]
elif one[0] == "TWO PAIR":
leftover1, leftover2 = is_twopair(h1), is_twopair(h2)
twm1, twm2 = max([int(x) for x in list(leftover1[1])]), max([int(x) for x in list(leftover2[1])])
if twm1 > twm2:
return "left", one[0], one[1]
elif twm1 < twm2:
return "right", two[0], two[1]
if compare(list(leftover1[1]), list(leftover2[1])) == "TIE":
l1 = [x[:-1] for x in h1 if x[:-1] not in leftover1[1]]
l2 = [x[:-1] for x in h2 if x[:-1] not in leftover2[1]]
if int(l1[0]) == int(l2[0]):
return "none", one[1], two[1]
elif int(l1[0]) > int(l2[0]):
return "left", one[0], one[1]
else:
return "right", two[0], two[1]
elif compare(list(leftover1[1]), list(leftover2[1])) == "RIGHT":
return "right", two[0], two[1]
elif compare(list(leftover1[1]), list(leftover2[1])) == "LEFT":
return "left", one[0], one[1]
elif one[0] == "PAIR":
sh1, sh2 = int(is_pair(h1)[1]), int(is_pair(h2)[1])
if sh1 == sh2:
c1 = [int(x[:-1]) for x in convert_tonums(h1) if not int(sh1) == int(x[:-1])]
c2 = [int(x[:-1]) for x in convert_tonums(h2) if not int(sh1) == int(x[:-1])]
if compare(c1, c2) == "TIE":
return "none", one[1], two[1]
elif compare(c1, c2) == "RIGHT":
return "right", two[0], two[1]
else:
return "left", one[0], one[1]
elif h1 > h2:
return "right", two[0], two[1]
else:
return "left", one[0], one[1]
elif one[0] == 'FULL HOUSE':
fh1, fh2 = int(is_fullhouse(h1)[1][0][0]), int(is_fullhouse(h2)[1][0][0])
if fh1 > fh2:
return "left", one[0], one[1]
else:
return "right", two[0], two[1]
elif one[0] == "HIGH CARD":
sett1, sett2 = convert_tonums(h1), convert_tonums(h2)
sett1, sett2 = [int(x[:-1]) for x in sett1], [int(x[:-1]) for x in sett2]
com = compare(sett1, sett2)
if com == "TIE":
return "none", one[1], two[1]
elif com == "RIGHT":
return "right", two[0], two[1]
else:
return "left", one[0], one[1]
elif len(one[1]) < 5:
if max(one[1]) == max(two[1]):
return "none", one[1], two[1]
elif max(one[1]) > max(two[1]):
return "left", one[0], one[1]
else:
return "right", two[0], two[1]
else:
n_one, n_two = convert_tonums(one[1]), convert_tonums(two[1])
n_one, n_two = [int(x[:-1]) for x in n_one], [int(x[:-1]) for x in n_two]
if max(n_one) == max(n_two):
return "none", one[1], two[1]
elif max(n_one) > max(n_two):
return "left", one[0], one[1]
else:
return "right", two[0], two[1]
elif one[2] > two[2]:
return "left", one[0], one[1]
else:
return "right", two[0], two[1]
'''
a = ['QD', 'KD', '9D', 'JD', 'TD']
b = ['JS', '8S', 'KS', 'AS', 'QS']
print compare_hands(a,b)
'''
The method used in ralu's post is by far the best alternative I've seen. I used this method in my own project, and its very fast.
Cliffs:
Do some preprocessing, to generate a table, containing one value for each distinct poker-hand. Make sure the table is sorted by hand-strength.
Each card-value has a corresponding prime-value. The table is indexed by the multiplication of each card-value in the hand. So to find the value of the hand AAAAK, you calculate the prime multiplication and use this as index for the table:
int prime = getPrime(hand); // Calculates A.getPrime()...*K.getPrime();
int value = table[prime];
(Sorry for the java syntax).
This way, AAAAK is the same hand as KAAAA, and you dont need a 5-dim table.
Note that you need to go through all combinations of the best 5 card hand, with the 7 cards you can choose from, to find the largest value, which is the real value of the hand.
You use a different table for flushes.
The table gets pretty beefy, as there are lots of wasted cells by this implementation. To counter this, you can create a map during preprocessing, which maps the large prime values to integer values, and use this as you source instead.
An example of a ready made Texas Hold'em 7- and 5-card evaluator can be found here and further explained here. This might help you with performance. All feedback welcome at the e-mail address found therein.
Monte Carlo? That's the first suggestion I see here. It's another senior project. Simple and slow, but otherwise you're probably looking at some complicated combinatorics that I won't pretend to know much about.

Categories

Resources