Hi,
I am making a poker hand evaluator for fun.
the deck of card is represent by the 52 first bits (low value) of a UInt64 like so:
0000/0000/0000/AAAA/KKKK/QQQQ/JJJJ/10,10,10,10/9999/8888/7777/6666/5555/4444/3333/2222
the suits logic is the same for every rank (for example /♣, ♢, ♡, ♠/
)
i.e.
0x0008000000008888 # would be a straight flush "A♣,2♣,3♣,4♣,5♣"
0x000000000000f002 # would be a 4 of a kind "5♣,5♢,5♡,5♠,2♡"
using BenchmarkTools
function hand5() #trigger 5 of the 53 first bits of a UInt64 and return it (generate a 5 cards poker hand)
a=UInt64(1)<<rand(UInt8(0):UInt8(47))
for i in UInt8(0):UInt8(3)
a=a<<UInt8(1) | UInt64(1)
r=rand(UInt8(0):UInt8(48+i))
a= ~UInt64(0)>>(UInt8(15)-i) & a<<r | a>>(UInt8(49)+i-r)
end
return a
end
@inline pext(x::UInt64, y::UInt64) = ccall("llvm.x86.bmi.pext.64", llvmcall, UInt64, (UInt64, UInt64), x, y)#intel pext
@inline function packnibble(x::UInt64) #pack the bits to the right of every nibble in a UInt64
x -= x>>1 & ~x & 0x7777777777777777 # 0b10 ↦ 0b01
x -= x>>1 & ~x & 0x7777777777777777
x -= x>>1 & ~x & 0x7777777777777777
return x
end
@inline function handclass(x::UInt64,y::UInt64) #return a number for the type of poker hand, x is a raw hand , y is nibble packed x
x= x >> trailing_zeros(x)
r= (y-5)%15 # return the type of hand but 0 for straight/flush/highcard
if r != 0
return r << 2 #*4 (we need more values in-between to insert the flush/straight)
elseif x == 0x0000000000011111 #straightflush
return 0x000000000000002e
elseif x == 0x0001000000001111 # weak straightflush
return 0x000000000000002d
elseif count_ones(pext(x, 0x1111111111111)) == 5 #flush
return 0x0000000000000013
elseif y >> trailing_zeros(y) == 0x0000000000011111 #straight
return 0x0000000000000012
elseif y == 0x0001000000001111 #weak straight
return 0x0000000000000011
else
return 0x0000000000000000 #high card
end
end
@inline function handscore(hand)#UInt64 handclass/cards appearing 3times or more/>=2times/>=1time (the bigger the UInt64, the better poker hand)
y=packnibble(hand)
handclass(hand,y) << 39 | pext(y, 0x4444444444444) << 26 | pext(y, 0x2222222222222) << 13 | pext(y, 0x1111111111111)
end
A=[hand5() for i in 1:100000000]#100.000.000 hands
@btime B=map(handscore,$A)
1.531 s (3 allocations: 762.94 MiB)
@btime (for n = $A handscore(n) end)
37.881 ms (0 allocations: 0 bytes) (this time is ridiculous something must be wrong)
I put @inline
everywhere because I don’t quite understand when to use it or not.
I tried to use @profile
but since all the functions are so small it didn’t help much (surely I am doing the wrong way).
I know there is already very efficient way to compare poker hands out there, they are a bit hard to understand for me. https://archive.ph/wM0ER with the files https://github.com/tangentforks/XPokerEval