├── .gitignore ├── AUTHORS ├── INSTALL.md ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── firstnuts_test.py ├── ngbbtest.py ├── postflop.py ├── rayeval ├── __init__.py └── rayeval.py ├── raytest.py ├── setup.py └── src ├── _rayevalmodule.cpp ├── arrays.cpp ├── arrays.h ├── raygen7.cpp ├── raygen7.h ├── raygen9.cpp ├── raygen9.h ├── rayutils.cpp └── rayutils.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.slo 2 | *.lo 3 | *.o 4 | *.pyc 5 | *.so 6 | *.dylib 7 | *.lai 8 | *.la 9 | *.a 10 | *.out 11 | *.dat 12 | *.trace 13 | *.dtrace 14 | *.plist 15 | rayeval.egg-info 16 | build/ 17 | temp/ 18 | rayeval/__rayeval_ranks_path__.txt 19 | .idea/ -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Authors: 2 | -------- 3 | 4 | Ivan Smirnov 5 | Timothy N. Tsvetkov 6 | Ray Wooten 7 | Kevin L. Suffecool 8 | 9 | Contributors: 10 | ------------- 11 | 12 | Sergey Pastukhov 13 | Egor Azanov 14 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | Rayeval is distributed as a package and build on top of the C library and can be installed using setuptools. 5 | 6 | Rayeval uses special data-files which are tricky indexed precalculated hand ranks. You need 7-card hand ranks file for 7 | evaluating 7 card combinations (like in Holdem) and 9-card hand ranks file for evaluating 9 card combinations (for Omaha). 8 | 9 | These files are generated at the inatall_data stage and can be re-generated using command: 10 | 11 | python setup.py install_data 12 | 13 | 14 | Hand ranks data files generation 15 | -------------------------------- 16 | 17 | Hand ranks files are generated when you installing the Rayeval or using: 18 | 19 | python setup.py install_data 20 | 21 | By default generated files are installed to the shared directory in the default data_files location. 22 | For example in Mac OS X it is: 23 | 24 | /System/Library/Frameworks/Python.framework/Versions/2.7/shared 25 | 26 | Also files are linked to the package it was generated from (you can find a __rayeval_ranks_path__.txt file in 27 | the package where path to the hand ranks files are stored). So you can access these files from the package. 28 | 29 | ### Hand rank files generation options 30 | 31 | There are several command line arguments that you may use with: 32 | 33 | python setup.py install 34 | 35 | or 36 | 37 | python setup.py install_data 38 | 39 | --no-ranks-test will not run tests after generating ranks, false by default. 40 | 41 | --without-build-ranks install Rayeval without generating hand ranks files. 42 | 43 | --ranks-dir=/path/to/place/ sets path where to put generated hand ranks files. 44 | 45 | --ranks-7-filename=rayeval_hand_ranks_7.dat name of the 7-card hand ranks file, rayeval_hand_ranks_7.dat is the default. 46 | 47 | --ranks-9-filename=rayeval_hand_ranks_9.dat name of the 9-card hand ranks file, rayeval_hand_ranks_9.dat is the default. 48 | 49 | --use-ranks-7=/path/to/rayeval_hand_ranks_7.dat links already generated 7-card hand ranks file to the package 50 | 51 | --use-ranks-9=/path/to/rayeval_hand_ranks_7.dat links already generated 9-card hand ranks file to the package 52 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include rayeval/__rayeval_ranks_path__.txt 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rayeval is a poker evaluation library. It is based on the idea of the LUT-based 7-cards evaluator and 2 | Ray Wotton's (RayW) hand evaluator. But Rayeval can also evaluate 9-card hands, what makes it one of the 3 | fastes Omaha hand evaluator. 4 | 5 | 6 | Installation 7 | ============ 8 | 9 | Rayeval is distributed as a package and build on top of the C library and can be installed using setuptools. 10 | 11 | Rayeval uses special data-files which are tricky indexed precalculated hand ranks. You need 7-card hand ranks file for 12 | evaluating 7 card combinations (like in Holdem) and 9-card hand ranks file for evaluating 9 card combinations (for Omaha). 13 | 14 | These files are generated at the inatall_data stage and can be re-generated using command: 15 | 16 | python setup.py install_data 17 | 18 | 19 | Hand ranks data files generation 20 | -------------------------------- 21 | 22 | Hand ranks files are generated when you installing the Rayeval or using: 23 | 24 | python setup.py install_data 25 | 26 | By default generated files are installed to the shared directory in the default data_files location. 27 | For example in Mac OS X it is: 28 | 29 | /System/Library/Frameworks/Python.framework/Versions/2.7/shared 30 | 31 | Also files are linked to the package it was generated from (you can find a __rayeval_ranks_path__.txt file in 32 | the package where path to the hand ranks files are stored). So you can access these files from the package. 33 | 34 | ### Hand rank files generation options 35 | 36 | There are several command line arguments that you may use with: 37 | 38 | python setup.py install 39 | 40 | or 41 | 42 | python setup.py install_data 43 | 44 | --no-ranks-test will not run tests after generating ranks, false by default. 45 | 46 | --without-build-ranks install Rayeval without generating hand ranks files. 47 | 48 | --ranks-dir=/path/to/place/ sets path where to put generated hand ranks files. 49 | 50 | --ranks-7-filename=rayeval_hand_ranks_7.dat name of the 7-card hand ranks file, rayeval_hand_ranks_7.dat is the default. 51 | 52 | --ranks-9-filename=rayeval_hand_ranks_9.dat name of the 9-card hand ranks file, rayeval_hand_ranks_9.dat is the default. 53 | 54 | --use-ranks-7=/path/to/rayeval_hand_ranks_7.dat links already generated 7-card hand ranks file to the package 55 | 56 | --use-ranks-9=/path/to/rayeval_hand_ranks_7.dat links already generated 9-card hand ranks file to the package 57 | 58 | Shared memory 59 | ============= 60 | 61 | Hand ranks files can be loaded to the shared memory (http://fscked.org/writings/SHM/shm-5.html). 62 | 63 | But shared memory routines will only work if your OS is properly configured. In most operating systems default shared memory settings allows to use just few pages per segment. 64 | 65 | In different OS there are different ways to change shared memory settings. For example in Mac OS X you can find them in /etc/sysctl.conf and update them (without reboot) via sysctl command. 66 | 67 | Note that max shared memory segment set in bytes and must be divisible by the size of the page. Total size of the shared memory segments system wide set in pages. 68 | 69 | Mac OS X 70 | -------- 71 | For example in Mac OS X you can update your shm settings (reboot is not required): 72 | 73 | sudo sysctl -w kern.sysv.shmmax=1598029824 74 | sudo sysctl -w kern.sysv.shmall=700000 75 | 76 | Linux 77 | ----- 78 | For example in Mac OS X you can update your shm settings (reboot is not required): 79 | 80 | sudo sysctl -w kernel.shmmax=1598029824 81 | sudo sysctl -w kernel.shmall=700000 82 | 83 | License 84 | ======= 85 | Rayeval is Copyright © 2013 Whatmatters, also some parts of the code shares copyright 86 | with Ray Wooten, Kevin L. Suffecool and probabply some of participants in the 2+2 forum thread 87 | (see http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&page=0&fpart=13&vc=1) 88 | 89 | It is free software, and may be redistributed under the terms specified in the 90 | GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt). 91 | 92 | For list of authors see AUTHORS.txt 93 | 94 | This project is based on Cactus Kev's evaluator and inherits code from it, also it is based on the 95 | idea of LUT-based 7-cards evaluator and Ray Wotton's (RayW) hand evaluator and inherits code from it 96 | and on the twoplustwo forum thread (http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&page=0&fpart=13&vc=1) 97 | in general. 98 | 99 | The Ray Wooten, twoplustwo, and all derivations of it are made available 100 | through GPL v2 or later, based on the following: 101 | 102 | (See http://pokerai.org/pf3/viewtopic.php?f=3&t=1451) 103 | 104 | 1) Also mykey1961 claimed that he first came with the idea of LUT-based 7-cards 105 | hand evaluator, and that he makes this available in the public domain. We aren't sure 106 | that he should be mentioned in AUTHORS.txt file, but he definetly must be mentioned here. 107 | 108 | 2) RayW posted his code on 2+2 udner GPL: 109 | // HandRankSetup.cpp : Sets up the HandRank File for VERY fast Lookups 110 | // by Ray Wotton and the 2+2 list My code is GPL, use it as you like 111 | (see http://archives1.twoplustwo.com/showflat.php?Cat=0&Number=8513906&page=0&fpart=13&vc=1) 112 | 113 | As RayW used Cactus Kev 114 | (Cactus Kev's Eval Routine ref http://www.suffecool.net/poker/evaluator.html) 115 | in his original work, and the resulting code was published and made 116 | available under GPL, and stayed in the public domain without any complains, 117 | we believe that the Cactus Kev code is hence made available under GPL. 118 | 119 | As a result, we make all of the code as part of this distribution 120 | available under GPL. 121 | 122 | Authors 123 | ======= 124 | 125 | Ivan Smirnov 126 | 127 | Timothy N. Tsvetkov 128 | 129 | Ray Wooten 130 | 131 | Kevin L. Suffecool 132 | 133 | Contributors 134 | ============ 135 | 136 | Sergey Pastukhov 137 | 138 | Egor Azanov 139 | -------------------------------------------------------------------------------- /firstnuts_test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ### 3 | ## Copyright (c) 2013-2015, Whatmatters Inc. 4 | ## 5 | ## It is free software, and may be redistributed under the terms specified in the 6 | ## GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 7 | ## 8 | ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 9 | ## INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 10 | ## DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 11 | ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 12 | ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 13 | ## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 14 | ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | ### 16 | 17 | import csv 18 | 19 | import itertools 20 | import rayeval 21 | 22 | 23 | def sort_flop(flop): 24 | a = flop[0][1] 25 | b = flop[1][1] 26 | c = flop[2][1] 27 | if a == b and b == c: 28 | return 1 29 | if a == b or b == c or a == c: 30 | return 2 31 | return 3 32 | 33 | 34 | if __name__ == '__main__': 35 | card_list = list(''.join(c) for c in itertools.product('23456789TJQKA', 'cdhs')) 36 | 37 | all_flops = itertools.combinations(card_list, 3) 38 | 39 | all_flops = sorted(all_flops, key=sort_flop) 40 | 41 | outs = csv.writer(open("flop_dynamica.csv", "wb")) 42 | 43 | cnt = 0 44 | for board_cards in all_flops: 45 | 46 | board = ' '.join(board_cards) 47 | 48 | first_nuts = rayeval.find_first_nuts_holdem(board) 49 | 50 | dynamica = rayeval.get_first_nuts_change_next_street(board) 51 | 52 | outs.writerow([ 53 | board.replace(' ', ''), 54 | first_nuts.replace(' ', ''), 55 | len(dynamica), 56 | ''.join(dynamica.keys()) 57 | ]) 58 | 59 | cnt += 1 60 | if cnt % 1000 == 0: 61 | print cnt 62 | -------------------------------------------------------------------------------- /ngbbtest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ### 3 | ## Copyright (c) 2013-2015, Whatmatters Inc. 4 | ## 5 | ## It is free software, and may be redistributed under the terms specified in the 6 | ## GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 7 | ## 8 | ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 9 | ## INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 10 | ## DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 11 | ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 12 | ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 13 | ## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 14 | ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | ### 16 | 17 | import rayeval 18 | import time 19 | 20 | if __name__ == '__main__': 21 | # rayeval.load_handranks('HandRanks.dat') 22 | rayeval.load_handranks_9('/usr/local/shared/rayeval_hand_ranks_9.dat') 23 | 24 | board = 'Ks Jd 9d * *' 25 | pockets = ['9s Kd 4d 6c'] 26 | iterations = 1e6 27 | 28 | t0 = time.time() 29 | ngbb = rayeval.eval_turn_outs_vs_random_omaha(board, pockets[0], iterations) 30 | elapsed = time.time() - t0 31 | 32 | print '[%s]' % pockets, 'on board [%s]' % board 33 | print 'Elapsed: %.2f seconds (%.2fM iterations / sec).\n' % (elapsed, iterations / elapsed / 1e6) 34 | for item in sorted(ngbb.items(), key=lambda x : x[1], reverse=True): 35 | print item 36 | 37 | flop_ev = ngbb['flop_ev'] 38 | outs = ngbb['outs'] 39 | nuts = good = blank = bad = 0 40 | for k in outs: 41 | ev = outs[k] 42 | if ev >= 0.98: 43 | nuts = nuts + 1 44 | elif ev >= flop_ev + 0.1: 45 | good = good + 1 46 | elif ev <= flop_ev - 0.1: 47 | bad = bad + 1 48 | else: 49 | blank = blank + 1 50 | 51 | print '[Nuts-Good-Blank-Bad] = [%d-%d-%d-%d]' % (nuts, good, blank, bad) 52 | 53 | print '' 54 | 55 | import pokereval 56 | pockets = pockets + ['* * * *'] 57 | t0 = time.time() 58 | board_pe = ['__' if b == '*' else b for b in board.split(' ')] 59 | pocket_pe = [['__' if c == '*' else c for c in p.split(' ')] for p in pockets] 60 | res = pokereval.PokerEval().poker_eval(game='omaha', board=board_pe, 61 | pockets=pocket_pe, iterations=int(iterations)) 62 | elapsed = time.time() - t0 63 | print '[%s]' % 'pokereval', pockets[:1], 'vs', pockets[1:], 'on board [%s]' % board, ( 64 | ': EV = %.4f%% (%.2gM iterations).' % (res['eval'][0]['ev'] / 10., iterations / 1e6)) 65 | print 'Elapsed: %.2f seconds (%.2fM iterations / sec).' % ( 66 | elapsed, iterations / elapsed / 1e6) 67 | -------------------------------------------------------------------------------- /postflop.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | 3 | ### 4 | ## Copyright (c) 2013-2015, Whatmatters Inc. 5 | ## 6 | ## It is free software, and may be redistributed under the terms specified in the 7 | ## GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 8 | ## 9 | ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 10 | ## INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 11 | ## DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 12 | ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 13 | ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 14 | ## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 15 | ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | ### 17 | 18 | import rayeval 19 | 20 | if __name__ == '__main__': 21 | rayeval.load_handranks_9('/usr/local/shared/rayeval_hand_ranks_9.dat') 22 | rayeval.load_handranks_7('/usr/local/shared/rayeval_hand_ranks_7.dat') 23 | 24 | rayeval.made_hand_type_fast("Js 7c 5s", "6d 2c 4c 2h") 25 | rayeval.draw_type("Js 7c 5s", "6d 2c 4c 2h") 26 | 27 | rayeval.made_hand_type_fast("As Tc 3s 7d", "6d 2c 4c 2h") 28 | rayeval.draw_type("As Tc 3s 7d", "6d 2c 4c 2h") 29 | # print rayeval.draw_type("Js 7c 5s", "6d 2c 4c 2h") 30 | -------------------------------------------------------------------------------- /rayeval/__init__.py: -------------------------------------------------------------------------------- 1 | from rayeval import * 2 | -------------------------------------------------------------------------------- /rayeval/rayeval.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Shared memory routines will only work if your OS is properly configured, 5 | e.g. in Mac OS X run the following prior to using shm functionality: 6 | 7 | sudo sysctl -w kern.sysv.shmmax=1598029824 8 | sudo sysctl -w kern.sysv.shmall=700000 9 | """ 10 | 11 | import _rayeval 12 | import itertools 13 | import joblib 14 | import pkg_resources 15 | 16 | __card_list = list(''.join(c) for c in itertools.product( 17 | '23456789TJQKA', 'cdhs')) 18 | 19 | __hand_rank_str__ = [ 20 | "n/a", 21 | "high card", 22 | "one pair", 23 | "two pair", 24 | "three of a kind", 25 | "straight", 26 | "flush", 27 | "full house", 28 | "four of a kind", 29 | "straight flush" 30 | ] 31 | 32 | 33 | def card_to_rank(card): 34 | """ 35 | Convert a string representation of a card to 0:51+255 value. 36 | """ 37 | if card in ('*', '__', '_'): 38 | return 255 39 | else: 40 | index = card[0].upper() + card[1].lower() 41 | return __card_list.index(index) 42 | 43 | 44 | def rank_to_card(rank): 45 | """ 46 | Convert 0:51+255 card rank to a string value. 47 | """ 48 | return __card_list[rank] 49 | 50 | 51 | def is_iterable(x): 52 | return isinstance(x, list) or isinstance(x, tuple) 53 | 54 | 55 | def split_string(x): 56 | if not x.strip(): 57 | return [] 58 | else: 59 | s = x.split(' ') 60 | return [c.strip() for c in s] 61 | 62 | 63 | def split_string_fast(x): 64 | return x.split(' ') 65 | 66 | 67 | def get_handranks_7_filename(): 68 | """ 69 | Returns 7-card handranks filename (with path) generated from the setup.py 70 | """ 71 | return pkg_resources.resource_string(__name__, '__rayeval_ranks_path__.txt').split('\n')[0] 72 | 73 | 74 | def get_handranks_9_filename(): 75 | """ 76 | Returns 9-card handranks filename (with path) generated from the setup.py 77 | """ 78 | return pkg_resources.resource_string(__name__, '__rayeval_ranks_path__.txt').split('\n')[1] 79 | 80 | 81 | def load_handranks_7(filename=None): 82 | """ 83 | Load 7-card handranks from file into process memory 84 | 85 | filename : 7-card hand ranks file 86 | """ 87 | filename = get_handranks_7_filename() if filename is None else filename 88 | _rayeval.load_handranks_7(filename) 89 | 90 | 91 | def load_handranks_9(filename=None): 92 | """ 93 | Load 9-card handranks from file into process memory 94 | 95 | filename : 9-card hand ranks file 96 | """ 97 | filename = get_handranks_9_filename() if filename is None else filename 98 | _rayeval.load_handranks_9(filename) 99 | 100 | 101 | def generate_handranks_7(filename, test=True): 102 | """ 103 | Generate 7-card handranks 104 | 105 | filename : 7-card hand ranks file 106 | test : run the verification test 107 | """ 108 | _rayeval.generate_handranks_7(filename, test) 109 | 110 | 111 | def generate_handranks_9(filename, filename7='', test=True): 112 | """ 113 | Generate 9-card handranks 114 | 115 | filename : 9-card hand ranks file 116 | filename7 : 7-card hand ranks file 117 | test : run the verification test 118 | """ 119 | _rayeval.generate_handranks_9(filename, filename7, test) 120 | 121 | 122 | def load_handranks_7_to_shm(filename=None, path=None, ftok_id=0): 123 | """ 124 | Load 7-card handranks from file to IPC shared memory 125 | 126 | filename : 7-card hand ranks file 127 | path : ftok path param for generating shm key 128 | ftok_id : ftok id param for generating shm key 129 | """ 130 | filename = get_handranks_7_filename() if filename is None else filename 131 | path = path if path is not None else filename 132 | _rayeval.load_handranks_7_to_shm(filename, path, ftok_id) 133 | 134 | 135 | def load_handranks_9_to_shm(filename=None, path=None, ftok_id=0): 136 | """ 137 | Load 9-card handranks from file to IPC shared memory 138 | 139 | filename : 9-card hand ranks file 140 | path : ftok path param to generate shm key 141 | ftok_id : ftok id param to generate shm key 142 | """ 143 | filename = get_handranks_9_filename() if filename is None else filename 144 | path = path if path is not None else filename 145 | _rayeval.load_handranks_9_to_shm(filename, path, ftok_id) 146 | 147 | 148 | def attach_handranks_7(path=None, ftok_id=0): 149 | """ 150 | Attach 7-card handranks shared memory segment 151 | 152 | path : ftok path param to generate shm key 153 | ftok_id : ftok id param to generate shm key 154 | """ 155 | path = get_handranks_7_filename() if path is None else path 156 | _rayeval.attach_handranks_7(path, ftok_id) 157 | 158 | 159 | def attach_handranks_9(path=None, ftok_id=0): 160 | """ 161 | Attach 9-card handranks shared memory segment 162 | 163 | path : ftok path param to generate shm key 164 | ftok_id : ftok id param to generate shm key 165 | """ 166 | path = get_handranks_9_filename() if path is None else path 167 | _rayeval.attach_handranks_9(path, ftok_id) 168 | 169 | 170 | def detach_handranks_7(): 171 | """ 172 | Detach 7-card handranks shared memory segment 173 | """ 174 | _rayeval.detach_handranks_7() 175 | 176 | 177 | def detach_handranks_9(): 178 | """ 179 | Detach 9-card handranks shared memory segment 180 | """ 181 | _rayeval.detach_handranks_9() 182 | 183 | 184 | def del_handranks_shm_7(path=None, ftok_id=0): 185 | """ 186 | Deletes 7-card handranks shared memory segment 187 | 188 | Note that only the super-user or a process with an effective uid equal 189 | to the shm_perm.cuid or shm_perm.uid values in the data structure 190 | associated with the queue can do this. 191 | 192 | path : ftok path param to generate shm key 193 | ftok_id : ftok id param to generate shm key 194 | """ 195 | path = get_handranks_7_filename() if path is None else path 196 | _rayeval.del_handranks_shm(path, ftok_id) 197 | 198 | 199 | def del_handranks_shm_9(path=None, ftok_id=0): 200 | """ 201 | Deletes 9-card handranks shared memory segment 202 | 203 | Note that only the super-user or a process with an effective uid equal 204 | to the shm_perm.cuid or shm_perm.uid values in the data structure 205 | associated with the queue can do this. 206 | 207 | path : ftok path param to generate shm key 208 | ftok_id : ftok id param to generate shm key 209 | """ 210 | path = get_handranks_9_filename() if path is None else path 211 | _rayeval.del_handranks_shm(path, ftok_id) 212 | 213 | 214 | def del_handranks_shm(path, ftok_id=0): 215 | """ 216 | Deletes handranks shared memory segment 217 | 218 | Note that only the super-user or a process with an effective uid equal 219 | to the shm_perm.cuid or shm_perm.uid values in the data structure 220 | associated with the queue can do this. 221 | 222 | path : ftok path param to generate shm key 223 | ftok_id : ftok id param to generate shm key 224 | """ 225 | _rayeval.del_handranks_shm(path, ftok_id) 226 | 227 | 228 | def is_loaded_to_shm_7(path=None, ftok_id=0): 229 | """ 230 | Returns True if 7-card hand rank file loaded to shm and False otherwise or on error 231 | 232 | path : ftok path param to generate shm key 233 | ftok_id : ftok id param to generate shm key 234 | """ 235 | path = get_handranks_7_filename() if path is None else path 236 | return _rayeval.is_loaded_to_shm(path, ftok_id) 237 | 238 | 239 | def is_loaded_to_shm_9(path=None, ftok_id=0): 240 | """ 241 | Returns True if 9-card hand rank file loaded to shm and False otherwise or on error 242 | 243 | path : ftok path param to generate shm key 244 | ftok_id : ftok id param to generate shm key 245 | """ 246 | path = get_handranks_9_filename() if path is None else path 247 | return _rayeval.is_loaded_to_shm(path, ftok_id) 248 | 249 | 250 | def is_loaded_to_shm(path, ftok_id=0): 251 | """ 252 | Returns True if hand rank file loaded to shm and False otherwise or on error 253 | 254 | path : ftok path param to generate shm key 255 | ftok_id : ftok id param to generate shm key 256 | """ 257 | return _rayeval.is_loaded_to_shm(path, ftok_id) 258 | 259 | 260 | def seed(n): 261 | """ 262 | Set the random seed for sampling to a specified values. 263 | """ 264 | _rayeval.seed(n) 265 | 266 | 267 | def parse_board(board): 268 | if isinstance(board, basestring): 269 | board = split_string(board) 270 | if not is_iterable(board): 271 | raise TypeError('Board must be a list, a tuple or a string.') 272 | n_board = len(board) 273 | if n_board is 0: 274 | board = ['*'] * 5 275 | elif n_board < 3 or n_board > 5: 276 | raise ValueError('Invalid board size.') 277 | return [card_to_rank(c) for c in board] 278 | 279 | 280 | def parse_pocket(pocket, game): 281 | pocket_size = 2 if game == 'holdem' else 4 282 | if isinstance(pocket, basestring): 283 | pocket = split_string(pocket) 284 | if not is_iterable(pocket): 285 | raise TypeError('Pocket must be a list, a tuple or a string.') 286 | if len(pocket) > pocket_size: 287 | raise ValueError('Invalid pocket size for selected game type.') 288 | return [card_to_rank(c) for c in pocket] + [255] * (pocket_size - len(pocket)) 289 | 290 | 291 | def parse_board_fast(board): 292 | board = split_string_fast(board) 293 | return [card_to_rank(c) for c in board] 294 | 295 | 296 | def parse_pocket_fast(pocket, game): 297 | pocket = split_string_fast(pocket) 298 | return [card_to_rank(c) for c in pocket] 299 | 300 | 301 | def parse_pockets(pockets, game): 302 | if isinstance(pockets, basestring): 303 | pockets = split_string(pockets) 304 | if not is_iterable(pockets): 305 | raise TypeError('Pockets must be a list or a tuple.') 306 | n_players = len(pockets) 307 | if n_players <= 1 or n_players > 10: 308 | raise ValueError('Invalid number of players.') 309 | i_pockets = [] 310 | map(i_pockets.extend, map(lambda p: parse_pocket(p, game), pockets)) 311 | return i_pockets 312 | 313 | 314 | def parse_game(game): 315 | if game not in ('holdem', 'omaha'): 316 | raise ValueError('Invalid game type.') 317 | return game 318 | 319 | 320 | def eval_hand(game='holdem', board='', pocket=''): 321 | game = parse_game(game) 322 | i_board = parse_board(board) 323 | i_pocket = parse_pocket(pocket, game) 324 | return eval_hand_i(game, i_board, i_pocket) 325 | 326 | 327 | def eval_hand_i(game, i_board, i_pocket): 328 | return _rayeval.eval_hand(game, i_board, i_pocket) 329 | 330 | 331 | def hand_rank_str(game='holdem', board='', pocket=''): 332 | return __hand_rank_str__[eval_hand(game=game, board=board, pocket=pocket) >> 12] 333 | 334 | 335 | def hand_rank_str_i(game, i_board, i_pocket): 336 | return __hand_rank_str__[eval_hand_i(game=game, i_board=i_board, i_pocket=i_pocket) >> 12] 337 | 338 | 339 | def hand_draw_outs(game='omaha', board='', pocket='', draw='straight'): 340 | i_board = parse_board(board) 341 | i_pocket = parse_pocket(pocket, game) 342 | 343 | return hand_draw_outs_i(game, i_board, i_pocket, draw) 344 | 345 | 346 | def hand_draw_outs_i(game, i_board, i_pocket, draw): 347 | outs = 0 348 | nut_outs = 0 349 | 350 | for i_c in range(52): 351 | if i_c in i_board or i_c in i_pocket: 352 | continue 353 | if texture_change(i_board, i_c) is draw: 354 | i_board.append(i_c) 355 | if hand_rank_str_i(game, i_board, i_pocket) is draw: 356 | outs += 1 357 | if is_first_nuts(i_board, i_pocket, draw): 358 | nut_outs += 1 359 | i_board.pop() 360 | return outs, nut_outs 361 | 362 | 363 | def draw_type(board='', pocket=''): 364 | i_board = parse_board_fast(board) 365 | i_pocket = parse_pocket_fast(pocket, 'omaha') 366 | return draw_type_i(i_board, i_pocket) 367 | 368 | 369 | def draw_type_i(i_board, i_pocket): 370 | s_outs, s_nut_outs = hand_draw_outs_i('omaha', i_board, i_pocket, 'straight') 371 | f_outs, f_nut_outs = hand_draw_outs_i('omaha', i_board, i_pocket, 'flush') 372 | outs = s_outs + f_outs 373 | nut_outs = s_nut_outs + f_nut_outs 374 | if outs == 0: 375 | return 'ND' 376 | if outs < 8: 377 | return 'WD' 378 | if outs < 11: 379 | if nut_outs < 4: 380 | return 'WD' 381 | return 'GD' 382 | return 'SD' 383 | 384 | 385 | def made_hand_type_i(i_board, i_pocket): 386 | bc = [] 387 | pc = [] 388 | 389 | for card in i_board: 390 | c, s = divmod(card, 4) 391 | bc.append(c) 392 | 393 | for card in i_pocket: 394 | c, s = divmod(card, 4) 395 | pc.append(c) 396 | 397 | bc.sort() 398 | bc.reverse() 399 | pc.sort() 400 | pc.reverse() 401 | 402 | hand = hand_rank_str_i('omaha', i_board, i_pocket) 403 | 404 | if hand in ["two pair", "three of a kind", "straight", "flush", "full house", "four of a kind", "straight flush"]: 405 | return '2P+' 406 | if hand in ["n/a", "high card"]: 407 | return 'WMH' 408 | 409 | prev = -1 410 | pp = -1 411 | for c in pc: 412 | if c == prev: 413 | pp = c 414 | break 415 | prev = c 416 | 417 | if pp > bc[0]: 418 | return 'OP' 419 | 420 | if bc[0] in pc and (pc[0] is 12 or pc[0] is 11 or pc[1] is 11): 421 | return 'TPGK' 422 | 423 | return 'WMH' 424 | 425 | 426 | def made_hand_type(board='', pocket=''): 427 | i_board = parse_board(board) 428 | i_pocket = parse_pocket(pocket, 'omaha') 429 | return made_hand_type_i(i_board, i_pocket) 430 | 431 | 432 | def made_hand_type_fast(board='', pocket=''): 433 | i_board = parse_board_fast(board) 434 | i_pocket = parse_pocket_fast(pocket, 'omaha') 435 | return made_hand_type_i(i_board, i_pocket) 436 | 437 | 438 | def texture_changing_cards_count(i_board): 439 | count = 0 440 | for i_c in range(52): 441 | if i_c in i_board: 442 | continue 443 | if texture_change(i_board, i_c) is not 'blank': 444 | count += 1 445 | return count 446 | 447 | 448 | def is_first_nuts(i_board, i_pocket, draw): 449 | if first_nuts_type(i_board) != draw: 450 | return False 451 | 452 | cards_by_suit = [[], [], [], []] 453 | board_cards = [] 454 | pocket_cards = [] 455 | 456 | for card in i_board: 457 | c, s = divmod(card, 4) 458 | cards_by_suit[s].append(c) 459 | board_cards.append(c) 460 | 461 | for card in i_pocket: 462 | c, s = divmod(card, 4) 463 | cards_by_suit[s].append(c) 464 | pocket_cards.append(c) 465 | 466 | if draw is 'flush': 467 | for cards in cards_by_suit: 468 | if len(cards) > 2: 469 | cards.sort() 470 | cards.reverse() 471 | if cards[0] == 12 and cards[1] == 11 and cards[2] == 10: 472 | return True 473 | 474 | if draw is 'straight': 475 | board_cards.sort() 476 | board_cards.reverse() 477 | si = -1 478 | if board_cards[0] - board_cards[2] < 5: 479 | si = 0 480 | elif len(board_cards) is 4 and board_cards[1] - board_cards[3] < 5: 481 | si = 1 482 | elif len(board_cards) is 5 and board_cards[2] - board_cards[4] < 5: 483 | si = 2 484 | 485 | nut_card = board_cards[si] + (4 - board_cards[si] + board_cards[si + 2]) 486 | 487 | if si > -1: 488 | for i in range(5): 489 | if nut_card - i not in board_cards and nut_card - i not in pocket_cards: 490 | return False 491 | return True 492 | 493 | return False 494 | 495 | 496 | def hand_draw_nut_outs(game, i_board, i_pocket, draw): 497 | outs = 0 498 | for i_c in range(52): 499 | if i_c in i_board or i_c in i_pocket: 500 | continue 501 | i_board.append(i_c) 502 | if hand_rank_str_i(game, i_board, i_pocket) is draw: 503 | outs += 1 504 | i_board.pop() 505 | return outs 506 | 507 | 508 | def first_nuts_type(i_board): 509 | 510 | suits = [0, 0, 0, 0] 511 | cards = [] 512 | 513 | for i in i_board: 514 | c, s = divmod(i, 4) 515 | suits[s] += 1 516 | cards.append(c) 517 | 518 | cards.sort() 519 | cards.reverse() 520 | 521 | p_c = -1 522 | for c in cards: 523 | if c == p_c: 524 | return 'full house' 525 | p_c = c 526 | 527 | if suits[0] > 2 or suits[1] > 2 or suits[2] > 2 or suits[3] > 2: 528 | return 'flush' 529 | 530 | if (len(cards) == 3 and cards[0] - cards[2] < 5) or \ 531 | (len(cards) == 4 and (cards[0] - cards[2] < 5 or cards[1] - cards[3] < 5)) or \ 532 | (len(cards) == 5 and (cards[0] - cards[2] < 5 or cards[1] - cards[3] < 5 or cards[2] - cards[4] < 5)): 533 | return 'straight' 534 | 535 | return 'set' 536 | 537 | 538 | def texture_change(i_board, next_card): 539 | current_nuts = first_nuts_type(i_board) 540 | i_board.append(next_card) 541 | new_nuts = first_nuts_type(i_board) 542 | i_board.pop() 543 | 544 | if current_nuts == new_nuts: 545 | return 'blank' 546 | return new_nuts 547 | 548 | 549 | def eval_mc(game='holdem', board='', pockets=['', ''], 550 | iterations=1e6, n_jobs=1): 551 | game = parse_game(game) 552 | i_board = parse_board(board) 553 | i_pockets = parse_pockets(pockets, game) 554 | iterations = int(iterations) 555 | if not isinstance(n_jobs, int) or n_jobs <= 0: 556 | raise ValueError('Invalid number of jobs.') 557 | if n_jobs is 1: 558 | return _rayeval.eval_mc(game, i_board, i_pockets, iterations) 559 | else: 560 | result = joblib.Parallel(n_jobs=n_jobs)(joblib.delayed( 561 | _rayeval.eval_mc)(game, i_board, i_pockets, iterations / n_jobs) 562 | for i in xrange(n_jobs)) 563 | return [sum(c) / float(n_jobs) for c in zip(*result)] 564 | 565 | 566 | def eval_turn_outs_vs_random_omaha(flopBoard, pocket, iterations): 567 | i_board = parse_board(flopBoard) 568 | i_pocket = parse_pocket(pocket, 'omaha') 569 | iterations = int(iterations) 570 | cppresult = _rayeval.eval_turn_outs_vs_random_omaha(i_board, i_pocket, iterations) 571 | result = {} 572 | result['flop_ev'] = cppresult[0] 573 | outs = {} 574 | result['outs'] = outs 575 | for i in xrange(52): 576 | if cppresult[i + 1] > -0.00001: 577 | outs[rank_to_card(i)] = cppresult[i + 1] 578 | return result 579 | 580 | 581 | def find_first_nuts_holdem(flopBoard): 582 | i_board = parse_board(flopBoard) 583 | cppresult = _rayeval.find_first_nuts_holdem(i_board) 584 | return __card_list[cppresult[0]] + ' ' + __card_list[cppresult[1]] 585 | 586 | 587 | def get_first_nuts_change_next_street(flopBoard): 588 | i_board = parse_board(flopBoard) 589 | nuts = _rayeval.find_first_nuts_holdem(i_board) 590 | 591 | result = {} 592 | for c in __card_list: 593 | i_c = card_to_rank(c) 594 | if i_c in i_board: continue 595 | 596 | turn_arr = i_board + [i_c] 597 | turnnuts = _rayeval.find_first_nuts_holdem(turn_arr) 598 | if turnnuts[0] != nuts[0] or turnnuts[1] != nuts[1]: 599 | s = __card_list[turnnuts[0]] + ' ' + __card_list[turnnuts[1]] 600 | result[c] = s 601 | 602 | return result -------------------------------------------------------------------------------- /raytest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ### 4 | ## Copyright (c) 2013-2015, Whatmatters Inc. 5 | ## 6 | ## It is free software, and may be redistributed under the terms specified in the 7 | ## GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 8 | ## 9 | ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 10 | ## INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 11 | ## DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 12 | ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 13 | ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 14 | ## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 15 | ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | ### 17 | 18 | import rayeval 19 | import time 20 | 21 | if __name__ == '__main__': 22 | # rayeval.load_handranks('HandRanks.dat') 23 | rayeval.load_handranks_9('/usr/local/shared/rayeval_hand_ranks_9.dat') 24 | 25 | game = 'omaha' 26 | # board = '* * * * *' 27 | # pocket = ['Kh 9h Ks 9s', '* * * *', '* * * *', '* * * *'] 28 | board = '2d 2h 3c * *' 29 | pocket = ['Kh 9h * *', 'As * * *'] 30 | iterations = 1e7 31 | n_jobs = 1 32 | 33 | t0 = time.time() 34 | ev = rayeval.eval_mc('omaha', board, pocket, iterations, n_jobs) 35 | elapsed = time.time() - t0 36 | print '[%s]' % 'omaha', pocket[:1], 'vs', pocket[1:], 'on board [%s]' % board, ( 37 | ': EV = %.4f%% (%.2gM iterations).' % (100. * ev[0], iterations / 1e6)) 38 | print 'Elapsed: %.2f seconds (%.2fM iterations / sec).\n' % ( 39 | elapsed, iterations / elapsed / 1e6) 40 | 41 | import pokereval 42 | t0 = time.time() 43 | board_pe = ['__' if b == '*' else b for b in board.split(' ')] 44 | pocket_pe = [['__' if c == '*' else c for c in p.split(' ')] for p in pocket] 45 | res = pokereval.PokerEval().poker_eval(game='omaha', board=board_pe, 46 | pockets=pocket_pe, iterations=int(iterations)) 47 | elapsed = time.time() - t0 48 | print '[%s]' % 'pokereval', pocket[:1], 'vs', pocket[1:], 'on board [%s]' % board, ( 49 | ': EV = %.4f%% (%.2gM iterations).' % (res['eval'][0]['ev'] / 10., iterations / 1e6)) 50 | print 'Elapsed: %.2f seconds (%.2fM iterations / sec).' % ( 51 | elapsed, iterations / elapsed / 1e6) 52 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | ### 5 | ## Copyright (c) 2013-2015, Whatmatters Inc. 6 | ## 7 | ## It is free software, and may be redistributed under the terms specified in the 8 | ## GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 9 | ## 10 | ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 11 | ## INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 12 | ## DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 13 | ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 14 | ## SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 15 | ## WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 16 | ## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 17 | ### 18 | 19 | from setuptools import Extension, find_packages, setup 20 | from distutils.command.install_data import install_data 21 | import sys 22 | import os 23 | from os.path import join as file_join 24 | from setuptools.command.install import install as _install 25 | import imp 26 | from pkg_resources import resource_filename 27 | import platform 28 | 29 | 30 | if platform.system() == 'Darwin': 31 | extra_compile_args = ['-O3', '-msse4', '-fPIC', '-g', '-gdwarf-2', '-stdlib=libstdc++'] 32 | extra_link_args = ['-O3', '-msse4', '-fPIC', '-g', '-gdwarf-2', '-stdlib=libstdc++'] 33 | else: 34 | extra_compile_args = ['-O3', '-msse4', '-fPIC', '-g', '-gdwarf-2'] 35 | extra_link_args = ['-O3', '-msse4', '-fPIC', '-g', '-gdwarf-2'] 36 | 37 | 38 | INSTALL_LIB_PATH = '' 39 | INSTALL_DATA_FILES_PATH = '' 40 | ARGV_OPTIONS = { 41 | 'no_ranks_test': False, 42 | 'without_build_ranks': False, 43 | 'ranks_dir': None, 44 | 'hand_ranks_7_file_name': 'rayeval_hand_ranks_7.dat', 45 | 'hand_ranks_9_file_name': 'rayeval_hand_ranks_9.dat', 46 | 'use_ranks_7': None, 47 | 'use_ranks_9': None 48 | } 49 | GENERATE_HAND_RANKS = False 50 | 51 | 52 | filtered_args = [] 53 | for arg in sys.argv: 54 | if arg == '--no-ranks-test': 55 | ARGV_OPTIONS['no_ranks_test'] = True 56 | elif arg == '--without-build-ranks': 57 | ARGV_OPTIONS['without_build_ranks'] = True 58 | elif arg.startswith('--ranks-dir'): 59 | ARGV_OPTIONS['ranks_dir'] = arg.split('=')[1] 60 | elif arg.startswith('--ranks-7-filename'): 61 | ARGV_OPTIONS['hand_ranks_7_file_name'] = arg.split('=')[1] 62 | elif arg.startswith('--ranks-9-filename'): 63 | ARGV_OPTIONS['hand_ranks_9_file_name'] = arg.split('=')[1] 64 | elif arg.startswith('--use-ranks-7'): 65 | ARGV_OPTIONS['use_ranks_7'] = arg.split('=')[1] 66 | elif arg.startswith('--use-ranks-9'): 67 | ARGV_OPTIONS['use_ranks_9'] = arg.split('=')[1] 68 | else: 69 | filtered_args.append(arg) 70 | 71 | sys.argv = filtered_args 72 | 73 | 74 | def build_and_install_ranks(): 75 | print "Running: build_and_install_ranks" 76 | # Loading rayeval from the path it was installed to 77 | f, filename, description = imp.find_module('rayeval', [INSTALL_LIB_PATH]) 78 | if f is None: 79 | f, filename, description = imp.find_module('rayeval', [file_join(INSTALL_LIB_PATH, 'rayeval')]) 80 | rayeval_module = imp.load_module('rayeval', f, filename, description) 81 | 82 | global INSTALL_DATA_FILES_PATH 83 | global ARGV_OPTIONS 84 | if ARGV_OPTIONS['use_ranks_7'] is None: 85 | ranks_7_path = file_join(INSTALL_DATA_FILES_PATH, ARGV_OPTIONS['hand_ranks_7_file_name']) 86 | else: 87 | if not os.path.exists(ARGV_OPTIONS['use_ranks_7']): 88 | raise IOError, "Hand ranks 7 file not found: %s" % ARGV_OPTIONS['use_ranks_7'] 89 | ranks_7_path = os.path.abspath(ARGV_OPTIONS['use_ranks_7']) 90 | 91 | if ARGV_OPTIONS['use_ranks_9'] is None: 92 | ranks_9_path = file_join(INSTALL_DATA_FILES_PATH, ARGV_OPTIONS['hand_ranks_9_file_name']) 93 | else: 94 | if not os.path.exists(ARGV_OPTIONS['use_ranks_9']): 95 | raise IOError, "Hand ranks 9 file not found: %s" % ARGV_OPTIONS['use_ranks_9'] 96 | ranks_9_path = os.path.abspath(ARGV_OPTIONS['use_ranks_9']) 97 | 98 | if GENERATE_HAND_RANKS and ARGV_OPTIONS['use_ranks_7'] is None: 99 | rayeval_module.generate_handranks_7(ranks_7_path, not ARGV_OPTIONS['no_ranks_test']) 100 | 101 | if GENERATE_HAND_RANKS and ARGV_OPTIONS['use_ranks_9'] is None: 102 | rayeval_module.generate_handranks_9(ranks_9_path, ranks_7_path, not ARGV_OPTIONS['no_ranks_test']) 103 | 104 | if GENERATE_HAND_RANKS or ARGV_OPTIONS['use_ranks_7'] is not None or ARGV_OPTIONS['use_ranks_9'] is not None: 105 | f = open(resource_filename('rayeval', "__rayeval_ranks_path__.txt"), 'w') 106 | f.write(ranks_7_path + "\n") 107 | f.write(ranks_9_path) 108 | f.close() 109 | 110 | 111 | class install_hand_ranks(install_data): 112 | def run(self): 113 | filtered_data_files = [] 114 | for item in self.data_files: 115 | if len(item) == 2 and len(item[1]) > 0 and item[1][0] == ':hand_ranks': 116 | global INSTALL_DATA_FILES_PATH 117 | global GENERATE_HAND_RANKS 118 | global ARGV_OPTIONS 119 | GENERATE_HAND_RANKS = not ARGV_OPTIONS['without_build_ranks'] 120 | 121 | if ARGV_OPTIONS['ranks_dir'] is not None: 122 | INSTALL_DATA_FILES_PATH = ARGV_OPTIONS['ranks_dir'] 123 | else: 124 | INSTALL_DATA_FILES_PATH = file_join(self.install_dir, item[0]) 125 | 126 | if not os.path.exists(INSTALL_DATA_FILES_PATH): 127 | os.mkdir(INSTALL_DATA_FILES_PATH) 128 | else: 129 | filtered_data_files.append(item) 130 | 131 | self.data_files = filtered_data_files 132 | install_data.run(self) 133 | 134 | 135 | class install(_install): 136 | def run(self): 137 | global INSTALL_LIB_PATH 138 | INSTALL_LIB_PATH = self.install_lib 139 | _install.run(self) 140 | 141 | 142 | setup(name='rayeval', 143 | version='0.1', 144 | cmdclass={'install': install, 'install_data': install_hand_ranks}, 145 | description='Ray Eval', 146 | author='Aldanor', 147 | author_email='i.s.smirnov@gmail.com', 148 | url='https://github.com/WhatMatters/ray_eval', 149 | install_requires=['joblib'], 150 | packages=find_packages(), 151 | package_data = {'rayeval': ['rayeval/__rayeval_ranks_path__.txt']}, 152 | data_files=[('shared', [':hand_ranks'])], 153 | ext_modules=[ 154 | Extension('_rayeval', 155 | sources=[ 156 | 'src/_rayevalmodule.cpp', 157 | 'src/arrays.cpp', 158 | 'src/rayutils.cpp', 159 | 'src/raygen7.cpp', 160 | 'src/raygen9.cpp'], 161 | extra_compile_args=extra_compile_args, 162 | extra_link_args=extra_link_args) 163 | ], 164 | keywords='poker monte-carlo eval omaha holdem', 165 | license='LICENSE.txt' 166 | ) 167 | 168 | 169 | if GENERATE_HAND_RANKS or ARGV_OPTIONS['use_ranks_7'] is not None or ARGV_OPTIONS['use_ranks_9'] is not None: 170 | build_and_install_ranks() 171 | -------------------------------------------------------------------------------- /src/_rayevalmodule.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "rayutils.h" 14 | #include "raygen7.h" 15 | #include "raygen9.h" 16 | 17 | int *HR = 0, *HR9 = 0; 18 | 19 | void extract_cards(uint64_t *deck, int card) 20 | { 21 | *deck ^= (1LLU << card); 22 | } 23 | 24 | void get_cards(uint64_t deck, int *cards, int shift) 25 | { 26 | int pos = 0; 27 | for (int i = 0; i < 52; i++) 28 | if (deck & (1LLU << i)) 29 | cards[pos++] = i + shift; 30 | } 31 | 32 | uint64_t new_deck() 33 | { 34 | uint64_t deck = 0; 35 | int i; 36 | for (i = 0; i < 52; i++) 37 | deck |= (1LLU << i); 38 | return deck; 39 | } 40 | 41 | void find_first_nuts_holdem(int *board, int n_board, int *result_pocket) 42 | { 43 | int deck[52]; 44 | int dead[52]; 45 | int wcards[7]; 46 | memset(dead, 0, 52 * sizeof(int)); 47 | init_deck_another_way(deck); 48 | 49 | for (int i = 0; i < n_board; i++) 50 | { 51 | dead[board[i]] = 1; 52 | wcards[i] = deck[board[i]]; 53 | } 54 | 55 | short best_score = 9999; 56 | 57 | for (int c1 = 0; c1 < 52; c1++) 58 | for (int c2 = c1 + 1; c2 < 52; c2++) 59 | { 60 | if (dead[c1] == 1 || dead[c2] == 1) continue; 61 | 62 | int card1 = deck[c1]; 63 | int card2 = deck[c2]; 64 | 65 | short score; 66 | switch (n_board) 67 | { 68 | case 3: 69 | score = eval_5cards(wcards[0], wcards[1], wcards[2], card1, card2); 70 | break; 71 | case 4: 72 | score = eval_5cards(wcards[0], wcards[1], wcards[2], wcards[3], card1); 73 | score = MIN(score, eval_5cards(wcards[0], wcards[1], wcards[2], wcards[3], card2)); 74 | score = MIN(score, eval_5cards(wcards[0], wcards[1], wcards[2], card1, card2)); 75 | score = MIN(score, eval_5cards(wcards[0], wcards[1], wcards[3], card1, card2)); 76 | score = MIN(score, eval_5cards(wcards[0], wcards[2], wcards[3], card1, card2)); 77 | score = MIN(score, eval_5cards(wcards[1], wcards[2], wcards[3], card1, card2)); 78 | break; 79 | case 5: 80 | wcards[5] = card1; 81 | wcards[6] = card2; 82 | score = eval_7hand(wcards); 83 | break; 84 | default: 85 | return; 86 | } 87 | 88 | if (score < best_score) 89 | { 90 | best_score = score; 91 | result_pocket[0] = c1; 92 | result_pocket[1] = c2; 93 | } 94 | } 95 | } 96 | 97 | int eval_monte_carlo_holdem(int N, int *board, int n_board, 98 | int *pocket, int n_players, double *ev) 99 | { 100 | int mask[52], cards[52], n_cards = 0, n_mask = 0, n_available, i, j, k; 101 | memset(mask, 0, 52 * sizeof(int)); 102 | memset(cards, 0, 52 * sizeof(int)); 103 | memset(ev, 0, n_players * sizeof(double)); 104 | uint64_t deck = new_deck(); 105 | int available_cards[52]; 106 | if (n_board != 3 && n_board != 4 && n_board != 5) 107 | return 1; 108 | for (i = 0; i < n_board; i++) 109 | { 110 | if (board[i] == 255) 111 | mask[n_mask++] = n_cards; 112 | else 113 | extract_cards(&deck, board[i]); 114 | cards[n_cards++] = board[i] + 1; // convert 0-51 to 1-52 115 | } 116 | for (i = 0; i < 2 * n_players; i++) 117 | { 118 | if (pocket[i] == 255) 119 | mask[n_mask++] = n_cards; 120 | else 121 | extract_cards(&deck, pocket[i]); 122 | cards[n_cards++] = pocket[i] + 1; // convert 0-51 to 1-52 123 | } 124 | n_available = 52 - n_board - 2 * n_players + n_mask; 125 | get_cards(deck, available_cards, 1); // convert 0-51 to 1-52 126 | for (i = 0; i < N; i++) 127 | { 128 | int sample[52], scores[MAX_PLAYERS], best_score = -1, tied = 0; 129 | random_sample_52_ross(n_available, n_mask, sample); 130 | for (j = 0; j < n_mask; j++) 131 | cards[mask[j]] = available_cards[sample[j]]; 132 | int path = 53; 133 | for (j = 0; j < n_board; j++) 134 | path = HR[path + cards[j]]; 135 | int *player_cards = cards + n_board; 136 | for (k = 0; k < n_players; k++) 137 | { 138 | int score = HR[HR[path + player_cards[0]] + player_cards[1]]; 139 | scores[k] = score; 140 | if (score > best_score) 141 | { 142 | best_score = score; 143 | tied = 1; 144 | } 145 | else if (score == best_score) 146 | tied++; 147 | player_cards += 2; 148 | } 149 | double delta_ev = 1.0 / tied; 150 | for (k = 0; k < n_players; k++) 151 | if (scores[k] == best_score) 152 | ev[k] += delta_ev; 153 | } 154 | for (k = 0; k < n_players; k++) 155 | ev[k] /= (double)N; 156 | return 0; 157 | } 158 | 159 | int eval_monte_carlo_omaha(int N, int *board, int n_board, 160 | int *pocket, int n_players, double *ev) 161 | { 162 | int mask[52], cards[52], n_cards = 0, n_mask = 0, n_available, i, j, k; 163 | memset(mask, 0, 52 * sizeof(int)); 164 | memset(cards, 0, 52 * sizeof(int)); 165 | memset(ev, 0, n_players * sizeof(double)); 166 | uint64_t deck = new_deck(); 167 | int available_cards[52]; 168 | if (n_board != 3 && n_board != 4 && n_board != 5) 169 | return 1; 170 | int fs_offset = (n_board == 5) ? 106 : ((n_board == 4) ? HR9[106] : HR9[HR9[106]]); 171 | int snf_offset = (n_board == 5) ? (HR9[0] + 53) : 172 | ((n_board == 4) ? HR9[HR9[0] + 53] : HR9[HR9[HR9[0] + 53]]); 173 | int flush_offset = (n_board == 5) ? (HR9[1] + 56) : 174 | ((n_board == 4) ? HR9[HR9[1] + 56] : HR9[HR9[HR9[1] + 56]]); 175 | for (i = 0; i < n_board; i++) 176 | { 177 | if (board[i] == 255) 178 | mask[n_mask++] = n_cards; 179 | else 180 | extract_cards(&deck, board[i]); 181 | cards[n_cards++] = board[i] + 1; // convert 0-51 to 1-52 182 | } 183 | for (i = 0; i < 4 * n_players; i++) 184 | { 185 | if (pocket[i] == 255) 186 | mask[n_mask++] = n_cards; 187 | else 188 | extract_cards(&deck, pocket[i]); 189 | cards[n_cards++] = pocket[i] + 1; // convert 0-51 to 1-52 190 | } 191 | n_available = 52 - n_board - 4 * n_players + n_mask; 192 | get_cards(deck, available_cards, 1); // convert 0-51 to 1-52 193 | int *HR9_f; 194 | for (i = 0; i < N; i++) 195 | { 196 | int sample[52], scores[MAX_PLAYERS], best_score = -1, tied = 0; 197 | random_sample_52_ross(n_available, n_mask, sample); 198 | for (j = 0; j < n_mask; j++) 199 | cards[mask[j]] = available_cards[sample[j]]; 200 | int board_fs = fs_offset; 201 | int board_snf = snf_offset; 202 | for (j = 0; j < n_board; j++) 203 | { 204 | board_fs = HR9[board_fs + cards[j]]; 205 | board_snf = HR9[board_snf + cards[j]]; 206 | } 207 | int *player_cards = cards + n_board; 208 | for (k = 0; k < n_players; k++) 209 | { 210 | int fs = board_fs; 211 | int score = board_snf; 212 | for (j = 0; j < 4; j++) 213 | { 214 | fs = HR9[fs + player_cards[j]]; 215 | score = HR9[score + player_cards[j]]; 216 | } 217 | if (fs != 0) 218 | { 219 | HR9_f = HR9 + (4 - fs); 220 | int sf = flush_offset; 221 | for (j = 0; j < n_board; j++) 222 | sf = HR9_f[sf + cards[j]]; 223 | for (j = 0; j < 4; j++) 224 | sf = HR9_f[sf + player_cards[j]]; 225 | score = MAX(score, sf); 226 | } 227 | 228 | scores[k] = score; 229 | if (score > best_score) 230 | { 231 | best_score = score; 232 | tied = 1; 233 | } 234 | else if (score == best_score) 235 | tied++; 236 | player_cards += 4; 237 | } 238 | double delta_ev = 1.0 / tied; 239 | for (k = 0; k < n_players; k++) 240 | if (scores[k] == best_score) 241 | ev[k] += delta_ev; 242 | } 243 | for (k = 0; k < n_players; k++) 244 | ev[k] /= (double)N; 245 | return 0; 246 | } 247 | 248 | static int pocket_perms[2][6] = {{0, 0, 0, 1, 1, 2}, {1, 2, 3, 2, 3, 3}}; 249 | static int n_pocket_perms = 6; 250 | static int board_perms[10][3] = { 251 | {0, 1, 2}, // 3, 4, 5 252 | {0, 1, 3}, {0, 2, 3}, {1, 2, 3}, // 4, 5 253 | {0, 1, 4}, {0, 2, 4}, {0, 3, 4}, {1, 2, 4}, {1, 3, 4}, {2, 3, 4}}; // 5 254 | 255 | // naive old method, left here for reference 256 | int eval_monte_carlo_omaha_old(int N, int *board, int n_board, 257 | int *pocket, int n_players, double *ev) 258 | { 259 | int n_board_perms = n_board == 5 ? 10 : n_board == 4 ? 4 : n_board == 3 ? 1 : -1; 260 | if (n_board_perms == -1) 261 | return 1; 262 | 263 | int mask[52], cards[52], n_mask = 0, n_available, 264 | i, available_cards[52], sample[52]; 265 | 266 | memset(mask, 0, 52 * sizeof(int)); 267 | memset(cards, 0, 52 * sizeof(int)); 268 | memset(ev, 0, n_players * sizeof(double)); 269 | uint64_t deck = new_deck(); 270 | 271 | for (i = 0; i < n_board; i++) 272 | { 273 | if (board[i] == 255) 274 | mask[n_mask++] = i; 275 | else 276 | extract_cards(&deck, board[i]); 277 | cards[i] = board[i] + 1; // convert 0-51 to 1-52 278 | } 279 | for (i = 0; i < 4 * n_players; i++) 280 | { 281 | if (pocket[i] == 255) 282 | mask[n_mask++] = n_board + i; 283 | else 284 | extract_cards(&deck, pocket[i]); 285 | cards[n_board + i] = pocket[i] + 1; // convert 0-51 to 1-52 286 | } 287 | 288 | n_available = 52 - n_board - 4 * n_players + n_mask; 289 | get_cards(deck, available_cards, 1); // convert 0-51 to 1-52 290 | 291 | for (i = 0; i < N; ++i) 292 | { 293 | int best_score = 0; 294 | int tied = 0; 295 | random_sample_52_ross(n_available, n_mask, sample); 296 | int j = 0, nb = 0, np = 0; 297 | for (j = 0; j < n_mask; ++j) 298 | cards[mask[j]] = available_cards[sample[j]]; 299 | int *player_cards = cards + n_board; 300 | int board_paths[10]; 301 | for (nb = 0; nb < n_board_perms; nb++) 302 | board_paths[nb] = HR[HR[HR[53 + 303 | cards[board_perms[nb][0]]] + 304 | cards[board_perms[nb][1]]] + 305 | cards[board_perms[nb][2]]]; 306 | int scores[MAX_PLAYERS]; 307 | for (j = 0; j < n_players; ++j) 308 | { 309 | int score = 0; 310 | for (nb = 0; nb < n_board_perms; ++nb) 311 | for (np = 0; np < n_pocket_perms; ++np) 312 | // Aldanor: don't forget to use extra HR[...] for 5/6-card eval 313 | score = MAX(score, HR[HR[HR[board_paths[nb] + 314 | player_cards[pocket_perms[0][np]]] + 315 | player_cards[pocket_perms[1][np]]]]); 316 | scores[j] = score; 317 | if (score > best_score) 318 | { 319 | best_score = score; 320 | tied = 1; 321 | } 322 | else if (score == best_score) 323 | tied++; 324 | player_cards += 4; 325 | } 326 | double delta_ev = 1.0 / tied; 327 | for (j = 0; j < n_players; j++) 328 | if (scores[j] == best_score) 329 | ev[j] += delta_ev; 330 | } 331 | for (i = 0; i < n_players; ++i) 332 | ev[i] /= N; 333 | return 0; 334 | } 335 | 336 | //////////////////////////////////////////////////////////////////////////////// 337 | // PYTHON WRAPPERS 338 | //////////////////////////////////////////////////////////////////////////////// 339 | 340 | #define RAISE_EXCEPTION(exception, message) {PyErr_SetString((exception), (message)); return NULL;} 341 | 342 | static PyObject *_rayeval_seed(PyObject *self, PyObject *args) 343 | { 344 | unsigned int seed; 345 | if (!PyArg_ParseTuple(args, "i", &seed)) 346 | return NULL; 347 | srand(seed); 348 | Py_RETURN_NONE; 349 | } 350 | 351 | 352 | static PyObject *_rayeval_load_handranks_7(PyObject *self, PyObject *args) 353 | { 354 | char *filename; 355 | if (!PyArg_ParseTuple(args, "s", &filename)) 356 | return NULL; 357 | if (!HR) 358 | if (!(HR = smart_load(filename))) 359 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to load hand ranks from file."); 360 | Py_RETURN_NONE; 361 | } 362 | 363 | static PyObject *_rayeval_load_handranks_9(PyObject *self, PyObject *args) 364 | { 365 | char *filename; 366 | if (!PyArg_ParseTuple(args, "s", &filename)) 367 | return NULL; 368 | if (!HR9) 369 | if (!(HR9 = smart_load(filename))) 370 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to load hand ranks [9] from file."); 371 | Py_RETURN_NONE; 372 | } 373 | 374 | static PyObject *_rayeval_load_handranks_7_to_shm(PyObject *self, PyObject *args) 375 | { 376 | char *filename; 377 | char *path; 378 | int user_id; 379 | if (!PyArg_ParseTuple(args, "ssi", &filename, &path, &user_id)) 380 | return NULL; 381 | if (!HR) 382 | if (!(HR = smart_load_to_shm(filename, ftok(path, user_id)))) 383 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to load hand ranks from file."); 384 | HR = HR + 1; 385 | Py_RETURN_NONE; 386 | } 387 | 388 | static PyObject *_rayeval_load_handranks_9_to_shm(PyObject *self, PyObject *args) 389 | { 390 | char *filename; 391 | char *path; 392 | int user_id; 393 | if (!PyArg_ParseTuple(args, "ssi", &filename, &path, &user_id)) 394 | return NULL; 395 | if (!HR9) 396 | if (!(HR9 = smart_load_to_shm(filename, ftok(path, user_id)))) 397 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to load hand ranks [9] from file."); 398 | HR9 = HR9 + 1; 399 | Py_RETURN_NONE; 400 | } 401 | 402 | static PyObject *_rayeval_generate_handranks_7(PyObject *self, PyObject *args) 403 | { 404 | char *filename; 405 | int test; 406 | if (!PyArg_ParseTuple(args, "si", &filename, &test)) 407 | return NULL; 408 | if (raygen7(filename, (test != 0))) 409 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to generate hand ranks [7] file."); 410 | Py_RETURN_NONE; 411 | } 412 | 413 | static PyObject *_rayeval_generate_handranks_9(PyObject *self, PyObject *args) 414 | { 415 | char *filename, *filename7; 416 | int test; 417 | if (!PyArg_ParseTuple(args, "ssi", &filename, &filename7, &test)) 418 | return NULL; 419 | if (raygen9(filename, filename7, (test != 0))) 420 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to generate hand ranks [9] file."); 421 | Py_RETURN_NONE; 422 | } 423 | 424 | 425 | static PyObject *_rayeval_attach_handranks_7(PyObject *self, PyObject *args) 426 | { 427 | char *path; 428 | int user_id; 429 | if (!PyArg_ParseTuple(args, "si", &path, &user_id)) 430 | return NULL; 431 | if (!HR) 432 | if (!(HR = attach_hr(ftok(path, user_id)))) 433 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to load hand ranks [7] from shared memory."); 434 | Py_RETURN_NONE; 435 | } 436 | 437 | static PyObject *_rayeval_attach_handranks_9(PyObject *self, PyObject *args) 438 | { 439 | char *path; 440 | int user_id; 441 | if (!PyArg_ParseTuple(args, "si", &path, &user_id)) 442 | return NULL; 443 | if (!HR9) 444 | if (!(HR9 = attach_hr(ftok(path, user_id)))) 445 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to load hand ranks [9] from shared memory."); 446 | Py_RETURN_NONE; 447 | } 448 | 449 | static PyObject *_rayeval_detach_handranks_7(PyObject *self, PyObject *args) 450 | { 451 | if (shmdt(HR - 1) == -1) 452 | { 453 | perror("shmdt"); 454 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to detach hand ranks [7] from shared memory."); 455 | } 456 | Py_RETURN_NONE; 457 | } 458 | 459 | static PyObject *_rayeval_detach_handranks_9(PyObject *self, PyObject *args) 460 | { 461 | if (shmdt(HR9 - 1) == -1) 462 | { 463 | perror("shmdt"); 464 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to detach hand ranks [9] from shared memory."); 465 | } 466 | Py_RETURN_NONE; 467 | } 468 | 469 | static PyObject *_rayeval_del_handranks_shm(PyObject *self, PyObject *args) 470 | { 471 | char *path; 472 | int user_id; 473 | if (!PyArg_ParseTuple(args, "si", &path, &user_id)) 474 | return NULL; 475 | if (del_shm(ftok(path, user_id)) == -1) 476 | RAISE_EXCEPTION(PyExc_RuntimeError, "Failed to detach hand ranks from shared memory."); 477 | Py_RETURN_NONE; 478 | } 479 | 480 | static PyObject *_rayeval_is_loaded_to_shm(PyObject *self, PyObject *args) 481 | { 482 | char *path; 483 | int user_id; 484 | if (!PyArg_ParseTuple(args, "si", &path, &user_id)) 485 | return NULL; 486 | if (shmget(ftok(path, user_id), 1, 0600) < 0) 487 | Py_RETURN_FALSE; 488 | Py_RETURN_TRUE; 489 | } 490 | 491 | int *parse_board_and_pockets(char *game, PyObject *py_board, PyObject *py_pocket, 492 | int *board, int *pocket, int *n_board, int *n_pocket, int *n_players, int *is_omaha) 493 | { 494 | int i, pocket_size = 2; 495 | static int ok = 1; 496 | *is_omaha = 0; 497 | 498 | if (!strcmp(game, "omaha")) 499 | *is_omaha = 1; 500 | else if (strcmp(game, "holdem")) 501 | RAISE_EXCEPTION(PyExc_ValueError, "Game type must be holdem or omaha."); 502 | pocket_size += 2 * (*is_omaha); 503 | 504 | if (!PyList_Check(py_board)) 505 | RAISE_EXCEPTION(PyExc_TypeError, "Board must be a list."); 506 | if (!PyList_Check(py_pocket)) 507 | RAISE_EXCEPTION(PyExc_TypeError, "Pockets must be a list."); 508 | 509 | *n_pocket = (int) PyList_Size(py_pocket); 510 | *n_board = (int) PyList_Size(py_board); 511 | *n_players = *n_pocket / pocket_size; 512 | if ((*n_players < 1) || (*n_players > MAX_PLAYERS)) 513 | RAISE_EXCEPTION(PyExc_ValueError, "Invalid number of players."); 514 | if ((*n_players) * pocket_size != (*n_pocket)) 515 | RAISE_EXCEPTION(PyExc_ValueError, "Invalid number of pocket cards."); 516 | if ((*n_board != 3) && (*n_board != 4) && (*n_board != 5)) 517 | RAISE_EXCEPTION(PyExc_ValueError, "Board must contain 3-5 cards."); 518 | 519 | for (i = 0; i < (*n_pocket); i++) 520 | { 521 | PyObject *item = PyList_GetItem(py_pocket, i); 522 | if (!PyInt_Check(item)) 523 | RAISE_EXCEPTION(PyExc_TypeError, "Pocket cards must be integers."); 524 | pocket[i] = (int) PyInt_AsLong(item); 525 | if ((pocket[i] < 0 || pocket[i] > 51) && pocket[i] != 255) 526 | RAISE_EXCEPTION(PyExc_TypeError, "Pocket cards must be 0-51 or 255."); 527 | } 528 | 529 | for (i = 0; i < (*n_board); i++) 530 | { 531 | PyObject *item = PyList_GetItem(py_board, i); 532 | if (!PyInt_Check(item)) 533 | RAISE_EXCEPTION(PyExc_TypeError, "Board cards must be integers."); 534 | board[i] = (int) PyInt_AsLong(item); 535 | if ((board[i] < 0 || board[i] > 51) && board[i] != 255) 536 | RAISE_EXCEPTION(PyExc_TypeError, "Board cards must be 0-51 or 255."); 537 | } 538 | 539 | return &ok; 540 | } 541 | 542 | static PyObject *_rayeval_eval_hand(PyObject *self, PyObject *args) 543 | { 544 | char *game; 545 | PyObject *py_board, *py_pocket; 546 | int n_board, n_pocket, i, value; 547 | int n_players, board[5], pocket[4 * MAX_PLAYERS], is_omaha; 548 | 549 | if (!PyArg_ParseTuple(args, "sOO", &game, &py_board, &py_pocket)) 550 | return NULL; 551 | 552 | if (!parse_board_and_pockets(game, py_board, py_pocket, board, pocket, 553 | &n_board, &n_pocket, &n_players, &is_omaha)) 554 | return NULL; 555 | 556 | if (!is_omaha && !HR) 557 | RAISE_EXCEPTION(PyExc_RuntimeError, "Please load 7-card hand ranks first."); 558 | if (is_omaha && !HR9) 559 | RAISE_EXCEPTION(PyExc_RuntimeError, "Please load 9-card hand ranks first."); 560 | 561 | if (n_players > 1) 562 | RAISE_EXCEPTION(PyExc_ValueError, "One player expected, found more."); 563 | 564 | for (i = 0; i < n_board; i++) 565 | if (board[i] == 255) 566 | RAISE_EXCEPTION(PyExc_ValueError, "Masked board is not allowed."); 567 | for (i = 0; i < n_pocket; i++) 568 | if (pocket[i] == 255) 569 | RAISE_EXCEPTION(PyExc_ValueError, "Masked pocket is not allowed."); 570 | 571 | if (is_omaha) 572 | { 573 | int fs = 106, snf = HR9[0] + 53, fo = HR9[1] + 56; 574 | if (n_board < 5) { fs = HR9[fs]; snf = HR9[snf]; fo = HR9[fo]; } 575 | if (n_board < 4) { fs = HR9[fs]; snf = HR9[snf]; fo = HR9[fo]; } 576 | for (i = 0; i < n_board; i++) 577 | { 578 | fs = HR9[fs + board[i] + 1]; 579 | snf = HR9[snf + board[i] + 1]; 580 | } 581 | for (i = 0; i < n_pocket; i++) 582 | { 583 | fs = HR9[fs + pocket[i] + 1]; 584 | snf = HR9[snf + pocket[i] + 1]; 585 | } 586 | value = snf; 587 | if (fs != 0) 588 | { 589 | int *HR9_f = HR9 + (4 - fs); 590 | int flush_score = fo; 591 | for (i = 0; i < n_board; i++) 592 | flush_score = HR9_f[flush_score + board[i] + 1]; 593 | for (i = 0; i < n_pocket; i++) 594 | flush_score = HR9_f[flush_score + pocket[i] + 1]; 595 | value = (flush_score > value) ? flush_score : value; 596 | } 597 | return PyInt_FromLong((long)value); 598 | } 599 | else 600 | { 601 | value = 53; 602 | for (i = 0; i < n_board; i++) 603 | value = HR[value + board[i] + 1]; 604 | for (i = 0; i < n_pocket; i++) 605 | value = HR[value + pocket[i] + 1]; 606 | if (n_board + n_pocket < 7) 607 | value = HR[value]; 608 | return PyInt_FromLong((long)value); 609 | } 610 | } 611 | 612 | /* 613 | INPUT: 614 | game: "omaha" | "holdem" 615 | board: list (int) 616 | pockets: list (int) 617 | iterations: int 618 | OUTPUT: 619 | ev: list (doble) 620 | */ 621 | static PyObject *_rayeval_eval_mc(PyObject *self, PyObject *args) 622 | { 623 | char *game; 624 | PyObject *py_board, *py_pocket, *py_ev; 625 | int i, n_board, n_pocket; 626 | int iterations, n_players, board[5], pocket[4 * MAX_PLAYERS], is_omaha; 627 | double ev[MAX_PLAYERS]; 628 | 629 | if (!PyArg_ParseTuple(args, "sOOi", &game, &py_board, &py_pocket, &iterations)) 630 | return NULL; 631 | 632 | if (iterations <= 0) 633 | RAISE_EXCEPTION(PyExc_ValueError, "Iterations must be a positive integer."); 634 | 635 | if (!parse_board_and_pockets(game, py_board, py_pocket, board, pocket, 636 | &n_board, &n_pocket, &n_players, &is_omaha)) 637 | return NULL; 638 | 639 | if (!is_omaha && !HR) 640 | RAISE_EXCEPTION(PyExc_RuntimeError, "Please load 7-card hand ranks first."); 641 | if (is_omaha && !HR9) 642 | RAISE_EXCEPTION(PyExc_RuntimeError, "Please load 9-card hand ranks first."); 643 | 644 | if (is_omaha) 645 | eval_monte_carlo_omaha(iterations, board, n_board, pocket, n_players, ev); 646 | else 647 | eval_monte_carlo_holdem(iterations, board, n_board, pocket, n_players, ev); 648 | 649 | py_ev = PyList_New(n_players); 650 | for (i = 0; i < n_players; i++) 651 | PyList_SET_ITEM(py_ev, (Py_ssize_t) i, PyFloat_FromDouble(ev[i])); 652 | 653 | return py_ev; 654 | } 655 | 656 | // INPUT: 657 | // board: list(int) 658 | // OUTPUT: 659 | // result: list (int) size 2 - 2 cards of the nuts 660 | static PyObject *_find_first_nuts_holdem(PyObject *self, PyObject *args) 661 | { 662 | int board[5], result_pocket[2]; 663 | 664 | PyObject *py_board; 665 | if (!PyArg_ParseTuple(args, "O", &py_board)) 666 | return NULL; 667 | 668 | if (!PyList_Check(py_board)) 669 | RAISE_EXCEPTION(PyExc_TypeError, "Board must be a list."); 670 | 671 | int n_board = (int) PyList_Size(py_board); 672 | 673 | if (n_board != 3 && n_board != 4 && n_board != 5) 674 | RAISE_EXCEPTION(PyExc_ValueError, "Board must contain 3-5 cards."); 675 | 676 | for (int i = 0; i < n_board; i++) 677 | { 678 | PyObject *item = PyList_GetItem(py_board, i); 679 | if (!PyInt_Check(item)) 680 | RAISE_EXCEPTION(PyExc_TypeError, "Board cards must be integers."); 681 | board[i] = (int) PyInt_AsLong(item); 682 | if (board[i] < 0 || board[i] > 51) 683 | RAISE_EXCEPTION(PyExc_TypeError, "Board cards must be 0-51."); 684 | } 685 | 686 | find_first_nuts_holdem(board, n_board, result_pocket); 687 | 688 | PyObject *py_result = PyList_New(2); 689 | 690 | PyList_SET_ITEM(py_result, (Py_ssize_t) 0, PyInt_FromLong((long)result_pocket[0])); 691 | PyList_SET_ITEM(py_result, (Py_ssize_t) 1, PyInt_FromLong((long)result_pocket[1])); 692 | 693 | return py_result; 694 | } 695 | 696 | /* 697 | INPUT: 698 | board: list (int) 699 | pockets: list (int) - must contain 4 cards 700 | iterations: int 701 | OUTPUT: 702 | result: list (doble) 703 | result[0] - flop ev 704 | result[1-52] - ev by turn outs 705 | */ 706 | static PyObject *_eval_turn_outs_vs_random_omaha(PyObject *self, PyObject *args) 707 | { 708 | PyObject *py_board, *py_pocket, *py_result; 709 | int i, n_board, n_pocket, n_players, iterations; 710 | int board[5], pocket[4 * MAX_PLAYERS], is_omaha, turnouts[52]; 711 | char game[] = "omaha"; 712 | double ev[MAX_PLAYERS]; 713 | 714 | if (!PyArg_ParseTuple(args, "OOi", &py_board, &py_pocket, &iterations)) 715 | return NULL; 716 | 717 | if (!parse_board_and_pockets(game, py_board, py_pocket, board, pocket, 718 | &n_board, &n_pocket, &n_players, &is_omaha)) 719 | return NULL; 720 | 721 | if (!HR9) 722 | RAISE_EXCEPTION(PyExc_RuntimeError, "Please load 9-card hand ranks first."); 723 | 724 | if (n_players != 1) 725 | RAISE_EXCEPTION(PyExc_RuntimeError, "Number of players must be one."); 726 | 727 | // Villain spectre is all possible pockets 728 | pocket[4] = pocket[5] = pocket[6] = pocket[7] = 255; 729 | 730 | // Calculate possible turn outs 731 | uint64_t deck = new_deck(); 732 | for (i = 0; i < n_board; i++) 733 | if (board[i] != 255) 734 | extract_cards(&deck, board[i]); 735 | for (i = 0; i < 4; i++) 736 | if (pocket[i] != 255) 737 | extract_cards(&deck, pocket[i]); 738 | for (int i = 0; i < 52; i++) 739 | if (deck & (1LLU << i)) 740 | turnouts[i] = i; 741 | else 742 | turnouts[i] = -1; 743 | 744 | py_result = PyList_New(53); 745 | 746 | eval_monte_carlo_omaha(iterations, board, n_board, pocket, 2, ev); 747 | PyList_SET_ITEM(py_result, (Py_ssize_t) 0, PyFloat_FromDouble(ev[0])); 748 | 749 | for (i = 0; i < 52; i++) 750 | { 751 | if (turnouts[i] != -1) 752 | { 753 | board[3] = turnouts[i]; 754 | eval_monte_carlo_omaha(iterations, board, n_board, pocket, 2, ev); 755 | PyList_SET_ITEM(py_result, (Py_ssize_t) (i + 1), PyFloat_FromDouble(ev[0])); 756 | } 757 | else 758 | { 759 | PyList_SET_ITEM(py_result, (Py_ssize_t) (i + 1), PyFloat_FromDouble(-1.0)); 760 | } 761 | } 762 | 763 | return py_result; 764 | } 765 | 766 | 767 | //////////////////////////////////////////////////////////////////////////////// 768 | // MODULE INITIALIZATION 769 | //////////////////////////////////////////////////////////////////////////////// 770 | 771 | static PyObject *_rayeval_test(PyObject *self, PyObject *args) 772 | { 773 | Py_RETURN_NONE; 774 | } 775 | 776 | static PyMethodDef _rayeval_methods[] = { 777 | {"seed", (PyCFunction) _rayeval_seed, METH_VARARGS, ""}, 778 | {"generate_handranks_7", (PyCFunction) _rayeval_generate_handranks_7, METH_VARARGS, ""}, 779 | {"generate_handranks_9", (PyCFunction) _rayeval_generate_handranks_9, METH_VARARGS, ""}, 780 | {"load_handranks_7", (PyCFunction) _rayeval_load_handranks_7, METH_VARARGS, ""}, 781 | {"load_handranks_7_to_shm", (PyCFunction) _rayeval_load_handranks_7_to_shm, METH_VARARGS, ""}, 782 | {"load_handranks_9", (PyCFunction) _rayeval_load_handranks_9, METH_VARARGS, ""}, 783 | {"load_handranks_9_to_shm", (PyCFunction) _rayeval_load_handranks_9_to_shm, METH_VARARGS, ""}, 784 | {"attach_handranks_7", (PyCFunction) _rayeval_attach_handranks_7, METH_VARARGS, ""}, 785 | {"attach_handranks_9", (PyCFunction) _rayeval_attach_handranks_9, METH_VARARGS, ""}, 786 | {"detach_handranks_7", (PyCFunction) _rayeval_detach_handranks_7, METH_VARARGS, ""}, 787 | {"detach_handranks_9", (PyCFunction) _rayeval_detach_handranks_9, METH_VARARGS, ""}, 788 | {"del_handranks_shm", (PyCFunction) _rayeval_del_handranks_shm, METH_VARARGS, ""}, 789 | {"is_loaded_to_shm", (PyCFunction) _rayeval_is_loaded_to_shm, METH_VARARGS, ""}, 790 | {"eval_mc", (PyCFunction) _rayeval_eval_mc, METH_VARARGS, ""}, 791 | {"eval_hand", (PyCFunction) _rayeval_eval_hand, METH_VARARGS, ""}, 792 | {"test", (PyCFunction) _rayeval_test, METH_NOARGS, ""}, 793 | {"eval_turn_outs_vs_random_omaha", (PyCFunction) _eval_turn_outs_vs_random_omaha, METH_VARARGS, ""}, 794 | {"find_first_nuts_holdem", (PyCFunction) _find_first_nuts_holdem, METH_VARARGS, ""}, 795 | {NULL, NULL, 0, NULL} 796 | }; 797 | 798 | PyMODINIT_FUNC init_rayeval(void) 799 | { 800 | init_random_int_52(); 801 | Py_InitModule("_rayeval", _rayeval_methods); 802 | } 803 | -------------------------------------------------------------------------------- /src/arrays.h: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Copyright (c) 2013-2015, Whatmatters Inc. 3 | ** 4 | ** It is free software, and may be redistributed under the terms specified in the 5 | ** GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 6 | ** 7 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 8 | ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 9 | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 10 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 11 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 12 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 13 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | ** 15 | ***/ 16 | extern short flushes[]; 17 | extern short unique5[]; 18 | extern int products[]; 19 | extern short values[]; 20 | extern int primes[13]; 21 | extern int perm7[21][5]; -------------------------------------------------------------------------------- /src/raygen7.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Copyright (c) 2013-2015, Whatmatters Inc. 3 | ** Copyright (c) Ray Wooten 4 | ** Copyright (c) Kevin L. Suffecool 5 | ** 6 | ** It is free software, and may be redistributed under the terms specified in the 7 | ** GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 8 | ** 9 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 10 | ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 11 | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 12 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 13 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 14 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 15 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | ***/ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "arrays.h" 24 | #include "rayutils.h" 25 | 26 | void init_deck(int *deck) 27 | { 28 | int n = 0, suit = 0x8000; 29 | for (int i = 0; i < 4; i++, suit >>= 1) 30 | for (int j = 0; j < 13; j++, n++) 31 | deck[n] = primes[j] | (j << 8) | suit | (1 << (16 + j)); 32 | } 33 | 34 | void init_deck_another_way(int *deck) 35 | { 36 | int suit = 0x8000; 37 | for (int i = 0; i < 4; i++, suit >>= 1) 38 | for (int j = 0; j < 13; j++) 39 | deck[j * 4 + i] = primes[j] | (j << 8) | suit | (1 << (16 + j)); 40 | } 41 | 42 | short eval_5cards(int c1, int c2, int c3, int c4, int c5) 43 | { 44 | int q; 45 | short s; 46 | q = (c1 | c2 | c3 | c4 | c5) >> 16; 47 | if (c1 & c2 & c3 & c4 & c5 & 0xF000) // check for flushes and straight-flushes 48 | return flushes[q]; 49 | s = unique5[q]; // check for straights and high-card hands 50 | if (s) 51 | return s; 52 | q = (c1 & 0xFF) * (c2 & 0xFF) * (c3 & 0xFF) * (c4 & 0xFF) * (c5 & 0xFF); 53 | q = cactus_findit(q); 54 | return values[q]; 55 | } 56 | 57 | short eval_5hand(int *hand) 58 | { 59 | int c1, c2, c3, c4, c5; 60 | c1 = *hand++; c2 = *hand++; c3 = *hand++; c4 = *hand++; c5 = *hand; 61 | return eval_5cards(c1, c2, c3, c4, c5); 62 | } 63 | 64 | short eval_7hand(int *hand) 65 | { 66 | int q, best = 9999, subhand[5]; 67 | for (int i = 0; i < 21; i++) 68 | { 69 | for (int j = 0; j < 5; j++) 70 | subhand[j] = hand[perm7[i][j]]; 71 | q = eval_5hand(subhand); 72 | if (q < best) 73 | best = q; 74 | } 75 | return best; 76 | } 77 | 78 | int n_cards = 0; 79 | 80 | int64_t make_id(int64_t id_in, int new_card) 81 | { 82 | int n_suit[4 + 1], n_rank[13 + 1], n, done = 0, needsuited; 83 | int cards[8]; // intentially keep an extra one as 0 end 84 | 85 | memset(cards, 0, sizeof(cards)); 86 | memset(n_rank, 0, sizeof(n_rank)); 87 | memset(n_suit, 0, sizeof(n_suit)); 88 | 89 | for (n = 0; n < 6; n++) // can't have more than 6 cards 90 | cards[n + 1] = (int) ((id_in >> (8 * n)) & 0xff); // leave the 0 index for new card 91 | 92 | new_card--; // 1-52 -> 0-51 93 | cards[0] = (((new_card >> 2) + 1) << 4) + (new_card & 3) + 1; 94 | // add next card formats card to rrrr00ss 95 | 96 | for (n_cards = 0; cards[n_cards]; n_cards++) 97 | { 98 | n_suit[cards[n_cards] & 0xf]++; // need to see if suit is significant 99 | n_rank[(cards[n_cards] >> 4) & 0xf]++; // and rank to be sure we don't have 4 100 | if (n_cards && (cards[0] == cards[n_cards])) // can't have the same card twice 101 | done = 1; // if so - need to quit after counting n_cards 102 | } 103 | 104 | if (done) 105 | return 0; // duplicate of another card (ignore this one) 106 | 107 | needsuited = n_cards - 2; // for suit to be significant - need to have n-2 of same suit 108 | 109 | int rank; 110 | if (n_cards > 4) 111 | for (rank = 1; rank < 14; rank++) 112 | if (n_rank[rank] > 4) // >= 4 of a rank => shouldn't do this one 113 | return 0; // can't have > 4 of a rank so return an invalid id 114 | 115 | // In the ID process, if we have 116 | // 2s = 0x21, 3s = 0x31,.... Kc = 0xD4, Ac = 0xE4 117 | // then it allows us to sort in rank then suit order 118 | 119 | // if we don't have at least 2 cards of the same suit for 4, we make this card suit 0. 120 | 121 | if (needsuited > 1) 122 | for (n = 0; n < n_cards; n++) // for each card 123 | if (n_suit[cards[n] & 0xf] < needsuited) // check n_suit to the number I need to have suits significant 124 | cards[n] &= 0xf0; // if not enough - 0 out the suit - now this suit would be a 0 vs 1-4 125 | 126 | // sort using XOR (network for N = 7, using Bose-Nelson algorithm) 127 | #define SWAP(I, J) {if (cards[I] < cards[J]) {cards[I] ^= cards[J]; cards[J] ^= cards[I]; cards[I] ^= cards[J];}} 128 | 129 | SWAP(0, 4); SWAP(1, 5); SWAP(2, 6); SWAP(0, 2); 130 | SWAP(1, 3); SWAP(4, 6); SWAP(2, 4); SWAP(3, 5); 131 | SWAP(0, 1); SWAP(2, 3); SWAP(4, 5); SWAP(1, 4); 132 | SWAP(3, 6); SWAP(1, 2); SWAP(3, 4); SWAP(5, 6); 133 | 134 | // store cards in bytes --66554433221100 135 | // the resulting ID is a 64 bit value with each card represented by 8 bits. 136 | return ((int64_t) cards[0] + 137 | ((int64_t) cards[1] << 8) + 138 | ((int64_t) cards[2] << 16) + 139 | ((int64_t) cards[3] << 24) + 140 | ((int64_t) cards[4] << 32) + 141 | ((int64_t) cards[5] << 40) + 142 | ((int64_t) cards[6] << 48)); 143 | } 144 | 145 | int save_id(int64_t id, int64_t *ids, int64_t *max_id, int *num_ids) 146 | { 147 | if (id == 0) 148 | return 0; 149 | 150 | if (id >= (*max_id)) 151 | { 152 | if (id > (*max_id)) 153 | { 154 | ids[(*num_ids)++] = id; 155 | (*max_id) = id; 156 | } 157 | return (*num_ids) - 1; 158 | } 159 | 160 | // pseudo bsearch 161 | int holdtest, low = 0, high = (*num_ids) - 1; 162 | int64_t testval; 163 | 164 | while (high - low > 1) 165 | { 166 | holdtest = (high + low + 1) / 2; 167 | testval = ids[holdtest] - id; 168 | if (testval > 0) 169 | high = holdtest; 170 | else if (testval < 0) 171 | low = holdtest; 172 | else 173 | return holdtest; 174 | } 175 | memmove(&ids[high + 1], &ids[high], ((*num_ids) - high) * sizeof(ids[0])); 176 | 177 | ids[high] = id; 178 | (*num_ids)++; 179 | return high; 180 | } 181 | 182 | int do_eval(int64_t id_in) 183 | { 184 | int n, handrank = 0, wcard, rank, suit, suititerator = 0, holdrank, 185 | mainsuit = 20, wcards[8], holdcards[8], numevalcards = 0; 186 | // const int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41}; 187 | 188 | memset(wcards, 0, sizeof(wcards)); 189 | memset(holdcards, 0, sizeof(holdcards)); 190 | 191 | if (id_in) 192 | { 193 | for (n = 0; n < 7; n++) 194 | { 195 | holdcards[n] = (int) ((id_in >> (8 * n)) & 0xff); 196 | if (holdcards[n] == 0) 197 | break; 198 | numevalcards++; 199 | if ((suit = holdcards[n] & 0xf)) 200 | mainsuit = suit; 201 | } 202 | 203 | for (n = 0; n < numevalcards; n++) 204 | { 205 | wcard = holdcards[n]; 206 | 207 | // convert to cactus kevs way, ref: http://www.suffecool.net/poker/evaluator.html 208 | // +--------+--------+--------+--------+ 209 | // |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp| 210 | // +--------+--------+--------+--------+ 211 | // p = prime number of rank (deuce=2,trey=3,four=5,five=7,...,ace=41) 212 | // r = rank of card (deuce=0,trey=1,four=2,five=3,...,ace=12) 213 | // cdhs = suit of card 214 | // b = bit turned on depending on rank of card 215 | 216 | rank = (wcard >> 4) - 1; // my rank is top 4 bits 1-13 so convert 217 | suit = wcard & 0xf; // my suit is bottom 4 bits 1-4, order is different, but who cares? 218 | if (suit == 0) // if suit wasn't significant though... 219 | { 220 | suit = suititerator++; // Cactus Kev needs a suit! 221 | if (suititerator == 5) // loop through available suits 222 | suititerator = 1; 223 | if (suit == mainsuit) // if it was the sigificant suit... Don't want extras!! 224 | { 225 | suit = suititerator++; // skip it 226 | if (suititerator == 5) // roll 1-4 227 | suititerator = 1; 228 | } 229 | } 230 | wcards[n] = primes[rank] | (rank << 8) | (1 << (suit + 11)) | (1 << (16 + rank)); 231 | } 232 | 233 | switch (numevalcards) 234 | { 235 | case 5: 236 | holdrank = eval_5cards(wcards[0],wcards[1],wcards[2],wcards[3],wcards[4]); 237 | break; 238 | case 6: 239 | holdrank = eval_5cards(wcards[0], wcards[1], wcards[2], wcards[3], wcards[4]); 240 | holdrank = MIN(holdrank, eval_5cards(wcards[0], wcards[1], wcards[2], wcards[3], wcards[5])); 241 | holdrank = MIN(holdrank, eval_5cards(wcards[0], wcards[1], wcards[2], wcards[4], wcards[5])); 242 | holdrank = MIN(holdrank, eval_5cards(wcards[0], wcards[1], wcards[3], wcards[4], wcards[5])); 243 | holdrank = MIN(holdrank, eval_5cards(wcards[0], wcards[2], wcards[3], wcards[4], wcards[5])); 244 | holdrank = MIN(holdrank, eval_5cards(wcards[1], wcards[2], wcards[3], wcards[4], wcards[5])); 245 | break; 246 | case 7: 247 | holdrank = eval_7hand(wcards); 248 | break; 249 | default: 250 | return -1; 251 | } 252 | 253 | handrank = cactus_to_ray(holdrank); 254 | } 255 | return handrank; 256 | } 257 | 258 | int raygen7(const char *filename, bool test=true) 259 | { 260 | int card, count = 0, num_ids = 1, n, id_slot, max_handrank = 0, handTypeSum[10], holdid; 261 | int64_t ID, max_id = 0LLU; 262 | int *_HR = (int *) malloc(32487834 * sizeof(int)); 263 | int64_t *ids = (int64_t *) malloc(612978 * sizeof(int64_t)); 264 | 265 | memset(handTypeSum, 0, sizeof(handTypeSum)); 266 | memset((long long *) ids, 0LLU, sizeof(ids)); 267 | memset((int *) _HR, 0, sizeof(_HR)); 268 | 269 | for (n = 0; ids[n] || n == 0; n++) 270 | { 271 | for (card = 1; card < 53; card++) 272 | { 273 | ID = make_id(ids[n], card); 274 | if (n_cards < 7) 275 | holdid = save_id(ID, ids, &max_id, &num_ids); 276 | } 277 | if ((n + 2) % 19 == 0) 278 | std::cout << "\rGenerating card IDs... " << std::fixed << std::setw(6) << 279 | (n + 1) << " / 612977"; 280 | } 281 | std::cout << "\n"; 282 | for (n = 0; ids[n] || n == 0; n++) 283 | { 284 | for (card = 1; card < 53; card++) 285 | { 286 | ID = make_id(ids[n], card); 287 | if (n_cards < 7) 288 | id_slot = save_id(ID, ids, &max_id, &num_ids) * 53 + 53; 289 | else 290 | id_slot = do_eval(ID); 291 | if (id_slot == -1) 292 | { 293 | std::cout << " Error: problem with n_cards = " << n_cards << ".\n"; 294 | free(_HR); free(ids); _HR = 0; ids = 0; return 1; 295 | } 296 | max_handrank = n * 53 + card + 53; 297 | _HR[max_handrank] = id_slot; 298 | } 299 | if (n_cards == 6 || n_cards == 7) 300 | { 301 | int value = do_eval(ids[n]); 302 | if (value == -1) 303 | { 304 | std::cout << " Error: problem with n_cards = " << n_cards << ".\n"; 305 | free(_HR); free(ids); _HR = 0; ids = 0; return 1; 306 | } 307 | _HR[n * 53 + 53] = value; 308 | } 309 | if ((n + 2) % 19 == 0) 310 | std::cout << "\rSetting hand ranks... " << std::fixed << std::setw(6) << 311 | (n + 1) << " / 612977"; 312 | } 313 | std::cout << "\nThe highest hand rank: " << max_handrank << "."; 314 | 315 | int result = smart_save(_HR, 32487834, filename); 316 | free(_HR); free(ids); _HR = 0; ids = 0; 317 | if (result) 318 | return result; 319 | 320 | if (test) 321 | { 322 | std::cout << "\nRunning tests..."; 323 | _HR = smart_load(filename); 324 | for (int c0 = 1; c0 < 53; c0++) { 325 | int u0 = _HR[53+c0]; 326 | for (int c1 = c0+1; c1 < 53; c1++) { 327 | int u1 = _HR[u0+c1]; 328 | for (int c2 = c1+1; c2 < 53; c2++) { 329 | int u2 = _HR[u1+c2]; 330 | for (int c3 = c2+1; c3 < 53; c3++) { 331 | int u3 = _HR[u2+c3]; 332 | for (int c4 = c3+1; c4 < 53; c4++) { 333 | int u4 = _HR[u3+c4]; 334 | for (int c5 = c4+1; c5 < 53; c5++) { 335 | int u5 = _HR[u4+c5]; 336 | for (int c6 = c5+1; c6 < 53; c6++) { 337 | handTypeSum[_HR[u5+c6] >> 12]++; 338 | count++; 339 | } 340 | } 341 | } 342 | } 343 | } 344 | } 345 | } 346 | for (int i = 0; i <= 9; i++) 347 | std::cout << std::endl << std::fixed << std::setw(16) << hand_rank_str(i) << 348 | ": " << handTypeSum[i]; 349 | std::cout << "\nTotal hands = " << count << "\n"; 350 | free(_HR); _HR = 0; 351 | } 352 | 353 | return 0; 354 | 355 | // FILE * fout = fopen(filename, "wb"); 356 | // int result = 1; 357 | // if (fout) 358 | // { 359 | // result = 0; 360 | // fwrite(_HR, sizeof(int), 32487834, fout); 361 | // fclose(fout); 362 | // } 363 | // free(_HR); free(ids); _HR = 0; ids = 0; 364 | // return result; 365 | } 366 | -------------------------------------------------------------------------------- /src/raygen7.h: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Copyright (c) 2013-2015, Whatmatters Inc. 3 | ** 4 | ** It is free software, and may be redistributed under the terms specified in the 5 | ** GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 6 | ** 7 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 8 | ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 9 | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 10 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 11 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 12 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 13 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | ***/ 15 | 16 | int raygen7(const char *filename, bool test=true); 17 | 18 | void init_deck(int *deck); 19 | void init_deck_another_way(int *deck); 20 | 21 | short eval_5cards(int c1, int c2, int c3, int c4, int c5); 22 | short eval_7hand(int *hand); 23 | -------------------------------------------------------------------------------- /src/raygen9.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Copyright (c) 2013-2015, Whatmatters Inc. 3 | ** Copyright (c) Ray Wooten 4 | ** Copyright (c) Kevin L. Suffecool 5 | ** 6 | ** It is free software, and may be redistributed under the terms specified in the 7 | ** GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 8 | ** 9 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 10 | ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 11 | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 12 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 13 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 14 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 15 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | ***/ 17 | 18 | 19 | /* 20 | 21 | HOW TO USE THE DATA FILE TO EVALUATE 7-, 8- AND 9-CARD OMAHA HANDS 22 | 23 | # Evaluating a 9-card hand 24 | 25 | Assume that you are given a board: b0, b1, b2, b3, b4 and 26 | a pocket: p0, p1, p2, p3. Denote the hand ranks array 27 | by HR. 28 | 29 | # Check for flushes and save the suit 30 | 31 | The flush suit can be found by indexing HR nine times, 32 | board first, then pocket, with initial offset of 106: 33 | 34 | board_offset := HR[HR[HR[HR[HR[ 35 | 106 + b0] + b1] + b2] + b3] + b4] 36 | flush_suit := HR[HR[HR[HR[board_offset + p0] + p1] + p2] + p3] 37 | 38 | # Evaluate hand ignoring all suits 39 | 40 | The base offset for non-flush hands is stored in HR[0], 41 | from there you add 53 and index nine times: 42 | 43 | board_offset := HR[HR[HR[HR[HR[ 44 | HR[0] + 53 + b0] + b1] + b2] + b3] + b4] 45 | score := HR[HR[HR[HR[board_offset + p0] + p1] + p2] + p3] 46 | 47 | # Only if flush_suit != 0, check if flush score is better than non-flush: 48 | 49 | The base offset for flushes is stored in HR[1], plus 56, however you 50 | have to add (4 - flush_suit) to this pointer. 51 | 52 | HR_f = HR + (4 - flush_suit) 53 | board_offset := HR_f[HR_f[HR_f[HR_f[HR_f[ 54 | HR[1] + 56 + b0] + b1] + b2] + b3] + b4] 55 | flush_score := HR[HR[HR[HR[board_offset + p0] + p1] + p2] + p3] 56 | if (flush_score > score) 57 | score := flush_score 58 | 59 | Alternatively, you can add (4 - flush_suit) manually every time you 60 | index, but it will just take longer. 61 | 62 | # Evaluating 7- and 8-card hands 63 | 64 | Pass zero for the first board card to evaluate a 7-card hand, and pass 65 | two zeros for the first two board cards to evaluate a 8-card hand. 66 | 67 | This has to be done for all three offset blocks, i.e. for 7-card hand 68 | 69 | Summarizing, 70 | 71 | Offsets for a 9-card hand: 72 | flush suits (HR) 106 73 | flush ranks (HR_f): HR[1] + 56 74 | non-flush ranks (HR): HR[0] + 53 75 | 76 | Offsets for a 8-card hand: 77 | flush suits (HR): HR[106] 78 | flush ranks (HR_f): HR_f[HR[1] + 56] 79 | non-flush ranks (HR): HR[HR[0] + 53] 80 | 81 | Offsets for a 8-card hand: 82 | flush suits (HR): HR[HR[106]] 83 | flush ranks (HR_f): HR_f[HR_f[HR[1] + 56]] 84 | non-flush ranks (HR): HR[HR[HR[0] + 53]] 85 | */ 86 | 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | 98 | #include "rayutils.h" 99 | #include "arrays.h" 100 | 101 | const int ANY_CARD = 1; // placeholder for flush ranks eval 102 | const int SKIP_BOARD = 53; // placeholder for 7-8 card hands 103 | 104 | const int pocket_perms[6][2] = { 105 | {0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3} 106 | }; 107 | const int board_perms[10][3] = { 108 | {0, 1, 2}, // 3-5 cards 109 | {0, 1, 3}, {0, 2, 3}, {1, 2, 3}, // 4-5 cards 110 | {0, 1, 4}, {0, 2, 4}, {0, 3, 4}, {1, 2, 4}, {1, 3, 4}, {2, 3, 4} // 5 cards 111 | }; 112 | const int n_pocket_perms = 6; 113 | 114 | void skip_board(int *board, int &n_board) 115 | { 116 | int temp[5] = {0, 0, 0, 0, 0}; 117 | n_board = 0; 118 | for (int i = 0; i < 5; i++) 119 | if (board[i] != SKIP_BOARD) 120 | temp[n_board++] = board[i]; 121 | memcpy(board, temp, 5 * sizeof(int)); 122 | } 123 | 124 | int count_cards(int64_t id) 125 | { 126 | return (((id) & 0x7F) != 0) + 127 | (((id >> 7) & 0x7F) != 0) + 128 | (((id >> 14) & 0x7F) != 0) + 129 | (((id >> 21) & 0x7F) != 0) + 130 | (((id >> 28) & 0x7F) != 0) + 131 | (((id >> 35) & 0x7F) != 0) + 132 | (((id >> 42) & 0x7F) != 0) + 133 | (((id >> 49) & 0x7F) != 0) + 134 | (((id >> 56) & 0x7F) != 0); 135 | } 136 | 137 | void add_card(int new_card, int *pocket, int *board, int &n_pocket, int &n_board) 138 | { 139 | if (n_board < 5) 140 | board[n_board++] = new_card; 141 | else 142 | pocket[n_pocket++] = new_card; 143 | } 144 | 145 | void unpack64(int64_t id, int *pocket, int *board, int &n_pocket, int &n_board) 146 | { 147 | memset(pocket, 0, 4 * sizeof(int)); 148 | memset(board, 0, 5 * sizeof(int)); 149 | n_pocket = 0; 150 | n_board = 0; 151 | int card; 152 | for (int i = 0; i < 9; i++) 153 | { 154 | if ((card = (int) ((id >> (7 * i)) & 0x7F)) != 0) 155 | { 156 | if (i < 5) 157 | board[n_board++] = card; 158 | else 159 | pocket[n_pocket++] = card; 160 | } 161 | } 162 | } 163 | 164 | int64_t pack64(int *pocket, int *board) 165 | { 166 | std::sort(pocket, pocket + 4); 167 | std::sort(board, board + 5); 168 | std::reverse(pocket, pocket + 4); 169 | std::reverse(board, board + 5); 170 | 171 | return ((int64_t) board[0] + 172 | ((int64_t) board[1] << 7) + 173 | ((int64_t) board[2] << 14) + 174 | ((int64_t) board[3] << 21) + 175 | ((int64_t) board[4] << 28) + 176 | ((int64_t) pocket[0] << 35) + 177 | ((int64_t) pocket[1] << 42) + 178 | ((int64_t) pocket[2] << 49) + 179 | ((int64_t) pocket[3] << 56)); 180 | } 181 | 182 | void print_id(int64_t id, bool indent=false) 183 | { 184 | int board[5], pocket[4], n_pocket, n_board; 185 | unpack64(id, pocket, board, n_pocket, n_board); 186 | std::cout << (indent ? "\t" : "") << "id: " << id << "\n"; 187 | std::cout << (indent ? "\t" : "") << "n_board: " << n_board << "\n"; 188 | std::cout << (indent ? "\t" : "") << "board: ["; 189 | for (int i = 0; i < n_board; i++) 190 | std::cout << board[i] << (((i + 1) == n_board) ? "" : ", "); 191 | std::cout << "]\n"; 192 | std::cout << (indent ? "\t" : "") << "n_pocket: " << n_pocket << "\n"; 193 | std::cout << (indent ? "\t" : "") << "pocket: ["; 194 | for (int i = 0; i < n_pocket; i++) 195 | std::cout << pocket[i] << (((i + 1) == n_pocket) ? "" : ", "); 196 | std::cout << "]\n"; 197 | } 198 | 199 | int64_t add_card_to_id_flush_suits(int64_t id, int new_card) 200 | { 201 | int pocket[4], board[5], n_pocket, n_board; 202 | new_card = new_card == 0 ? SKIP_BOARD : ((new_card - 1) & 3) + 1; 203 | unpack64(id, pocket, board, n_pocket, n_board); 204 | add_card(new_card, pocket, board, n_pocket, n_board); 205 | return pack64(pocket, board); 206 | } 207 | 208 | int eval_flush_suits(int64_t id) 209 | { 210 | int pocket[4], board[5], n_pocket, n_board, 211 | nsp[5] = {0, 0, 0, 0, 0}, nsb[5] = {0, 0, 0, 0, 0}; 212 | unpack64(id, pocket, board, n_pocket, n_board); 213 | for (int i = 0; i < n_pocket; i++) 214 | nsp[pocket[i]] = MIN(nsp[pocket[i]] + 1, 2); 215 | for (int i = 0; i < n_board; i++) 216 | if (board[i] != SKIP_BOARD) 217 | nsb[board[i]] = MIN(nsb[board[i]] + 1, 3); 218 | for (int suit = 1; suit <= 4; suit++) 219 | if ((nsp[suit] + nsb[suit]) >= 5) 220 | return suit; 221 | return -1; 222 | } 223 | 224 | int64_t add_card_to_id_flush_ranks(int64_t id, int new_card, int flush_suit) 225 | { 226 | int pocket[4], board[5], n_pocket, n_board, nsp = 0, nsb = 0, i; 227 | bool debug = false; 228 | 229 | // the rank is 2-14 if suited or 1 otherwise (for sorting purposes) 230 | if (new_card == 0) 231 | new_card = SKIP_BOARD; 232 | else 233 | new_card = ((((new_card - 1) & 3) + 1) == flush_suit) ? 234 | (2 + ((new_card - 1) >> 2) & 0xF) : ANY_CARD; 235 | 236 | if (debug) std::cout << "old deck:\n"; 237 | if (debug) print_id(id, true); 238 | if (debug) std::cout << "new_card: " << new_card << "\n"; 239 | 240 | unpack64(id, pocket, board, n_pocket, n_board); 241 | 242 | for (i = 0; i < n_pocket; i++) 243 | if (pocket[i] != ANY_CARD && pocket[i] != SKIP_BOARD && pocket[i] == new_card) 244 | { 245 | if (debug) std::cout << "halting, duplicate pocket: " << pocket[i] << "\n"; 246 | return 0; 247 | } 248 | for (i = 0; i < n_board; i++) 249 | if (board[i] != ANY_CARD && board[i] != SKIP_BOARD && board[i] == new_card) 250 | { 251 | if (debug) std::cout << "halting, duplicate board: " << board[i] << "\n"; 252 | return 0; 253 | } 254 | 255 | if (debug) std::cout << "no duplicates, adding card...\n"; 256 | 257 | add_card(new_card, pocket, board, n_pocket, n_board); 258 | 259 | for (i = 0; i < n_pocket; i++) 260 | if (pocket[i] != ANY_CARD && pocket[i] != 0) 261 | nsp++; 262 | for (i = 0; i < n_board; i++) 263 | if (board[i] != ANY_CARD && board[i] != SKIP_BOARD && board[i] != 0) 264 | nsb++; 265 | 266 | if (debug) std::cout << "nsp = " << nsp << ", nsb = " << nsb << "\n"; 267 | 268 | if (n_board == 4 && nsb <= 1) 269 | return 0; 270 | if (n_board == 5 && nsb <= 2) 271 | return 0; 272 | if (n_board == 5 && n_pocket == 3 && nsp == 0) 273 | return 0; 274 | if (n_board == 5 && n_pocket == 4 && nsp <= 1) 275 | return 0; 276 | 277 | if (debug) std::cout << "packing...\n"; 278 | 279 | if (debug) std::cout << "new deck:\n"; 280 | if (debug) print_id(pack64(pocket, board), true); 281 | 282 | return pack64(pocket, board); 283 | } 284 | 285 | int64_t add_card_to_id_flush_ranks_1(int64_t id, int new_card) 286 | { 287 | return add_card_to_id_flush_ranks(id, new_card, 1); 288 | } 289 | 290 | int64_t add_card_to_id_flush_ranks_2(int64_t id, int new_card) 291 | { 292 | return add_card_to_id_flush_ranks(id, new_card, 2); 293 | } 294 | 295 | int64_t add_card_to_id_flush_ranks_3(int64_t id, int new_card) 296 | { 297 | return add_card_to_id_flush_ranks(id, new_card, 3); 298 | } 299 | 300 | int64_t add_card_to_id_flush_ranks_4(int64_t id, int new_card) 301 | { 302 | return add_card_to_id_flush_ranks(id, new_card, 4); 303 | } 304 | 305 | int eval_flush_ranks(int64_t id) 306 | { 307 | int pocket[4], board[5], n_pocket, n_board; 308 | unpack64(id, pocket, board, n_pocket, n_board); 309 | skip_board(board, n_board); 310 | if (!pocket[0] || !pocket[1] || !board[0] || !board[1] || !board[2]) 311 | { 312 | std::cout << "\neval_flush_ranks(): " << id << ": zero encountered.\n"; 313 | return -1; 314 | } 315 | if (pocket[0] == ANY_CARD || pocket[1] == ANY_CARD || board[0] == ANY_CARD || 316 | board[1] == ANY_CARD || board[2] == ANY_CARD) 317 | return -1; 318 | 319 | // sadly, we have to account for straight flushes... 320 | int n = n_pocket + n_board; 321 | const int pocket_perms[2][6] = {{0, 0, 0, 1, 1, 2}, {1, 2, 3, 2, 3, 3}}; 322 | const int n_pocket_perms = 6; 323 | const int board_perms[10][3] = { 324 | {0, 1, 2}, // 3, 4, 5 325 | {0, 1, 3}, {0, 2, 3}, {1, 2, 3}, // 4, 5 326 | {0, 1, 4}, {0, 2, 4}, {0, 3, 4}, {1, 2, 4}, {1, 3, 4}, {2, 3, 4}}; // 5 327 | int n_board_perms = n == 9 ? 10 : n == 8 ? 4 : n == 7 ? 1 : -1; 328 | int np, nb, best = 8191, q = 0; 329 | for (np = 0; np < n_pocket_perms; np++) 330 | { 331 | for (nb = 0; nb < n_board_perms; nb++) 332 | { 333 | int rank1 = pocket[pocket_perms[0][np]] - 2, 334 | rank2 = pocket[pocket_perms[1][np]] - 2, 335 | rank3 = board[board_perms[nb][0]] - 2, 336 | rank4 = board[board_perms[nb][1]] - 2, 337 | rank5 = board[board_perms[nb][2]] - 2; 338 | if (rank1 < 0 || rank2 < 0 || rank3 < 0 || rank4 < 0 || rank5 < 0 || 339 | rank1 > 12 || rank2 > 12 || rank3 > 12 || rank4 > 12 || rank5 > 12) 340 | continue; 341 | q = flushes[(1 << rank1) | (1 << rank2) | (1 << rank3) | (1 << rank4) | (1 << rank5)]; 342 | if (q < best) 343 | best = q; 344 | } 345 | } 346 | return cactus_to_ray(best); 347 | } 348 | 349 | int64_t add_card_to_id_no_flush(int64_t id, int new_card) 350 | { 351 | int pocket[4], board[5], n_pocket, n_board, i, 352 | n_rank[14] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 353 | if (new_card == 0) 354 | new_card = SKIP_BOARD; 355 | else 356 | new_card = 1 + ((new_card - 1) >> 2) & 0xF; 357 | unpack64(id, pocket, board, n_pocket, n_board); 358 | for (i = 0; i < n_pocket; i++) 359 | n_rank[pocket[i]]++; 360 | for (i = 0; i < n_board; i++) 361 | if (board[i] != SKIP_BOARD) 362 | n_rank[board[i]]++; 363 | add_card(new_card, pocket, board, n_pocket, n_board); 364 | if (new_card != SKIP_BOARD) 365 | n_rank[new_card]++; 366 | for (i = 1; i <= 13; i++) 367 | if (n_rank[i] > 4) 368 | return 0; 369 | return pack64(pocket, board); 370 | } 371 | 372 | int card_to_cactus(int rank, int suit) 373 | { 374 | static bool buffered = false; 375 | static int cards[14][5]; 376 | const int primes[13] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41}; 377 | if (!buffered) 378 | { 379 | for (int r = 0; r < 14; r++) 380 | cards[r][0] = 0; 381 | for (int s = 0; s < 5; s++) 382 | cards[0][s] = 0; 383 | for (int r = 1; r < 14; r++) 384 | for (int s = 1; s < 5; s++) 385 | cards[r][s] = primes[(r - 1)] | ((r - 1) << 8) | 386 | (1 << (s + 11)) | (1 << (16 + (r - 1))); 387 | buffered = true; 388 | } 389 | return cards[rank][suit]; 390 | } 391 | 392 | int eval_cactus_no_flush(int c1, int c2, int c3, int c4, int c5) 393 | { 394 | int s = unique5[(c1 | c2 | c3 | c4 | c5) >> 16]; 395 | if (s) 396 | return s; 397 | else 398 | return values[cactus_findit((c1 & 0xFF) * (c2 & 0xFF) * 399 | (c3 & 0xFF) * (c4 & 0xFF) * (c5 & 0xFF))]; 400 | } 401 | 402 | int eval_no_flush(int64_t id) 403 | { 404 | 405 | int pocket[4], board[5], n_pocket, n_board, n, n_board_perms; 406 | unpack64(id, pocket, board, n_pocket, n_board); 407 | skip_board(board, n_board); 408 | n = n_pocket + n_board; 409 | n_board_perms = n == 9 ? 10 : n == 8 ? 4 : n == 7 ? 1 : -1; 410 | if (n_pocket < 4 || n_board < 3) 411 | { 412 | std::cout << "\neval_no_flush() encountered invalid # of cards, shouldn't happen...\n"; 413 | return 0; 414 | } 415 | 416 | // convert to cactus and add random suits 417 | int suit = 0; 418 | for (int i = 0; i < n_pocket; i++) 419 | pocket[i] = card_to_cactus(pocket[i], ((suit++) % 4) + 1); 420 | for (int i = 0; i < n_board; i++) 421 | board[i] = card_to_cactus(board[i], ((suit++) % 4) + 1); 422 | 423 | int np, nb, best = 8191, q = 0; 424 | for (np = 0; np < n_pocket_perms; np++) 425 | { 426 | for (nb = 0; nb < n_board_perms; nb++) 427 | { 428 | q = eval_cactus_no_flush(pocket[pocket_perms[np][0]], 429 | pocket[pocket_perms[np][1]], 430 | board[board_perms[nb][0]], 431 | board[board_perms[nb][1]], 432 | board[board_perms[nb][2]]); 433 | if (q < best) 434 | best = q; 435 | } 436 | } 437 | return cactus_to_ray(best); 438 | } 439 | 440 | /* 441 | Allow to "skip" a board card only when there are no cards on the 442 | table or just one skipped. A board card can be skipped by passing 443 | the offset of 0, however internally it will be stored as 53. 444 | Skipping is only allowed for the first and second cards (essentially, 445 | we just start off from a different offset). 446 | */ 447 | 448 | void generate_ids(size_t size, std::vector &id_list, 449 | int64_t (*add_card_to_id) (int64_t, int)) 450 | 451 | { 452 | std::vector id_queue_1, id_queue_2; 453 | int64_t new_id = 0; 454 | id_list.reserve(size); 455 | id_list.clear(); 456 | id_list.push_back(0LL); 457 | id_queue_1.reserve(size); 458 | id_queue_2.reserve(size); 459 | id_queue_1.push_back(0LL); 460 | for (int n_cards = 1; n_cards <= 8; n_cards++) 461 | { 462 | std::cout << "\nGenerating " << n_cards << "-card IDs:\n"; 463 | 464 | int n1 = (int) id_queue_1.size(); // LOL, if we don't do this, expect nasty bugs 465 | for (int i = 0; i < n1; i++) 466 | { 467 | std::cout << "\r\t" << "Processing ID " << i + 1 << " / " << 468 | n1 << "..."; 469 | 470 | int64_t id = id_queue_1[i]; 471 | int min_card = (n_cards <= 2) ? 0 : 1; // board skipping 472 | for (int new_card = min_card; new_card <= 52; new_card++) 473 | if ((new_id = (*add_card_to_id)(id, new_card)) != 0) 474 | id_queue_2.push_back(new_id); 475 | } 476 | std::cout << "\n\t" << id_queue_1.size() << ", " << id_queue_2.size() << "\n"; 477 | size_t size = id_queue_2.size(); 478 | std::cout << "\n\tGenerated " << id_queue_2.size() << " IDs." << 479 | "\n\tSorting and dropping duplicates..."; 480 | std::sort(id_queue_2.begin(), id_queue_2.end()); 481 | id_queue_2.erase(std::unique(id_queue_2.begin(), id_queue_2.end()), 482 | id_queue_2.end()); 483 | std::cout << " dropped " << (size - id_queue_2.size()) << " IDS."; 484 | std::cout << "\n\tInserting IDS into the final list..."; 485 | id_list.insert(id_list.end(), id_queue_2.begin(), id_queue_2.end()); 486 | std::cout << " total: " << id_list.size() << " IDS."; 487 | std::cout << "\n\tResetting the queue..."; 488 | id_queue_1.swap(id_queue_2); 489 | id_queue_2.clear(); 490 | } 491 | std::cout << "\n\tFinished: generated " << id_list.size() << " IDs, sorting...."; 492 | std::sort(id_list.begin(), id_list.end()); 493 | std::vector().swap(id_queue_1); 494 | std::vector().swap(id_queue_2); 495 | } 496 | 497 | /* 498 | 499 | GLOBAL 500 | 501 | 0-53 502 | 0: flush ranks base offset (add suit * 53 to get true offset) 503 | 1: 504 | 505 | FLUSH_RANKS 506 | 0-53: reserved 507 | 53*1 + 1-52: suit 1 starting point 508 | 53*2 + 1-52: suit 2 starting point 509 | 53*3 + 1-52: suit 3 starting point 510 | 53*4 + 1-52: suit 4 starting point 511 | >= 53*5: normal layers 512 | 513 | FLUSH_RANKS SHIFTING 514 | 515 | Assume: there are only 2 ranks, 1-2 and 4 suits, 1-4 516 | Denote: -- , [1], [2] ~ any card, rank 1, rank 2 517 | [b], [d] ~ base offset, dummy slot 518 | 519 | [b] 1(1) 1(2) 1(3) 1(4) 2(1) 2(2) 2(3) 2(4) [d] [d] [d] 520 | 521 | suit 1: [1] -- -- -- [2] -- -- -- -- -- -- 522 | suit 2: -- [1] -- -- -- [2] -- -- -- -- -- 523 | suit 4: -- -- [1] -- -- -- [2] -- -- -- -- 524 | suit 3: -- -- -- [1] -- -- -- [2] -- -- -- 525 | */ 526 | 527 | void process_ids(std::vector ids, int offset, int offset_value, 528 | std::vector &hand_ranks, int64_t (*add_card_to_id)(int64_t, int), 529 | int (*eval_id)(int64_t), int n_dummy, int dummy_card, 530 | std::tr1::unordered_map map=std::tr1::unordered_map()) 531 | { 532 | // offset + 0: special value 533 | // offset + 1-52: loop back to offset + 0 534 | // offset + 53: starting point 535 | // offset + 54+: normal operation 536 | 537 | int n = (int) ids.size(), i, id_index, num_cards, new_card; 538 | int64_t id, new_id; 539 | int block_size = 53 + n_dummy; 540 | std::tr1::unordered_map hash_table; 541 | 542 | for (i = 0; i < n; i++) 543 | { 544 | std::cout << "\r\tCreating unordered map... " << (i + 1) << " / " << n; 545 | hash_table.insert(std::tr1::unordered_map::value_type(ids[i], i)); 546 | } 547 | std::cout << "\n"; 548 | 549 | hand_ranks[offset] = offset_value; 550 | for (i = 1; i <= 52; i++) 551 | hand_ranks[offset + i] = offset; 552 | for (i = 53; i < block_size; i++) 553 | hand_ranks[offset + i] = offset; 554 | for (i = 0; i < n; i++) 555 | { 556 | id = ids[i]; 557 | std::cout << "\r\tProcessing ID " << i + 1 << " out of " << n << " (" << id << ")..." ; 558 | id_index = offset + block_size + i * block_size; 559 | num_cards = count_cards(id); 560 | hand_ranks[id_index] = offset; // safety backup 561 | 562 | int min_card = (num_cards <= 1) ? 0 : 1; // board skipping 563 | int dummy_value = -1; 564 | for (new_card = min_card; new_card <= 52; new_card++) 565 | { 566 | new_id = (*add_card_to_id)(id, new_card); 567 | if (new_id && ((num_cards + 1) == 9)) 568 | { 569 | int value = (*eval_id)(new_id); 570 | hand_ranks[id_index + new_card] = map.count(value) ? map[value] : value; 571 | } 572 | else if (new_id) 573 | hand_ranks[id_index + new_card] = offset + block_size + 574 | hash_table[new_id] * block_size; 575 | else 576 | hand_ranks[id_index + new_card] = offset; // < 9 cards and id is not valid 577 | if (new_card == dummy_card) 578 | dummy_value = hand_ranks[id_index + new_card]; 579 | } 580 | if (dummy_value != -1) 581 | for (new_card = 53; new_card < block_size; new_card++) 582 | hand_ranks[id_index + new_card] = dummy_value; 583 | } 584 | 585 | } 586 | 587 | // int generate_handranks(int *hand_ranks) 588 | int generate_handranks(std::vector &hand_ranks) 589 | { 590 | std::vector id_fs, id_fr1, id_fr2, id_fr3, id_fr4, id_nf; 591 | 592 | std::cout << "\n====== PHASE 1 (GENERATE IDS) ======"; 593 | 594 | std::cout << "\n\n>> IDs for flush suits... \n"; 595 | generate_ids(100e3, id_fs, add_card_to_id_flush_suits); 596 | 597 | std::cout << "\n\n>> IDs for flush ranks (suit #4)... \n"; 598 | generate_ids(10e6, id_fr4, add_card_to_id_flush_ranks_4); 599 | 600 | std::cout << "\n\n>> IDs for non-flush hands... \n"; 601 | generate_ids(100e6, id_nf, add_card_to_id_no_flush); 602 | 603 | int n_fs = (int) id_fs.size(), n_fr4 = (int) id_fr4.size(), 604 | n_nf = (int) id_nf.size(); 605 | 606 | std::cout << "\n\n\n====== PHASE 2 (PROCESS IDS) ======"; 607 | 608 | int offset_fs = 53, offset_fr4, offset_nf, max_rank; 609 | offset_fr4 = offset_fs + 53 + n_fs * 53; 610 | offset_nf = offset_fr4 + (53 + 3) + n_fr4 * (53 + 3); 611 | max_rank = offset_nf + 53 + n_nf * 53; 612 | 613 | std::cout << "\n\nMAX_RANK = " << max_rank << "\n"; 614 | std::vector(max_rank, 0).swap(hand_ranks); 615 | 616 | hand_ranks[0] = offset_nf; 617 | hand_ranks[1] = offset_fr4; 618 | 619 | std::cout << "\n\nEvaluating flush suits...\n"; 620 | std::tr1::unordered_map map_fs; 621 | map_fs.insert(std::tr1::unordered_map::value_type(-1, 0)); 622 | process_ids(id_fs, offset_fs, offset_nf, hand_ranks, 623 | add_card_to_id_flush_suits, eval_flush_suits, 0, 0, map_fs); 624 | 625 | std::cout << "\n\nEvaluating flush ranks (suit #4 + dummies)...\n"; 626 | std::tr1::unordered_map map_fr4; 627 | map_fr4.insert(std::tr1::unordered_map::value_type(-1, offset_fr4)); 628 | process_ids(id_fr4, offset_fr4, 0, hand_ranks, 629 | add_card_to_id_flush_ranks_4, eval_flush_ranks, 3, 1, map_fr4); 630 | 631 | std::cout << "\n\nEvaluating non-flush hands...\n"; 632 | std::tr1::unordered_map map_nf; 633 | map_nf.insert(std::tr1::unordered_map::value_type(-1, offset_nf)); 634 | process_ids(id_nf, offset_nf, 0, hand_ranks, 635 | add_card_to_id_no_flush, eval_no_flush, 0, 0, map_nf); 636 | 637 | // hand_ranks.resize(max_rank); 638 | 639 | std::cout << "\n\nDone.\n"; 640 | 641 | return max_rank; 642 | } 643 | 644 | int test_all_handranks(const char *filename, const char *filename7) 645 | { 646 | int *HR_new = smart_load(filename); 647 | int *HR_old = smart_load(filename7); 648 | 649 | int c[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 650 | int64_t n = 0; 651 | int64_t N[3] = {133784560LL, 752538150LL, 652 | 3679075400LL}; // C(52, 7), C(52, 8), C(52, 9) 653 | int min0[3] = {0, 0, 1}; 654 | int max0[3] = {0, 0, 52}; 655 | int min1[3] = {0, 1, 1}; 656 | int max1[3] = {0, 52, 52}; 657 | int n_board_perms[3] = {1, 4, 10}; 658 | int board_paths[10]; 659 | 660 | // int no_flush_offset = HR_new[0], 661 | // flush_offset = HR_new[1]; 662 | 663 | for (int k = 0; k < 3; k++) 664 | { 665 | std::cout << "\nChecking all " << (7 + k) << "-card sorted combinations...\n"; 666 | n = 0; 667 | 668 | for (c[0] = min0[k]; c[0] <= max0[k]; c[0]++) 669 | { 670 | int fs0 = HR_new[106 + c[0]]; // flush suit 671 | int snf0 = HR_new[HR_new[0] + 53 + c[0]]; // score no flush 672 | for (c[1] = (min1[k] == 0) ? 0 : (c[0] + 1); c[1] <= max1[k]; c[1]++) 673 | { 674 | int fs1 = HR_new[fs0 + c[1]]; 675 | int snf1 = HR_new[snf0 + c[1]]; 676 | for (c[2] = c[1] + 1; c[2] <= 52; c[2]++) 677 | { 678 | int fs2 = HR_new[fs1 + c[2]]; 679 | int snf2 = HR_new[snf1 + c[2]]; 680 | for (c[3] = c[2] + 1; c[3] <= 52; c[3]++) 681 | { 682 | int fs3 = HR_new[fs2 + c[3]]; 683 | int snf3 = HR_new[snf2 + c[3]]; 684 | for (c[4] = c[3] + 1; c[4] <= 52; c[4]++) 685 | { 686 | int fs4 = HR_new[fs3 + c[4]]; 687 | int snf4 = HR_new[snf3 + c[4]]; 688 | 689 | for (int nb = 0; nb < n_board_perms[k]; nb++) 690 | board_paths[nb] = HR_old[HR_old[HR_old[53 + 691 | c[(2 - k) + board_perms[nb][0]]] + 692 | c[(2 - k) + board_perms[nb][1]]] + 693 | c[(2 - k) + board_perms[nb][2]]]; 694 | 695 | for (c[5] = c[4] + 1; c[5] <= 52; c[5]++) 696 | { 697 | int fs5 = HR_new[fs4 + c[5]]; 698 | int snf5 = HR_new[snf4 + c[5]]; 699 | for (c[6] = c[5] + 1; c[6] <= 52; c[6]++) 700 | { 701 | int fs6 = HR_new[fs5 + c[6]]; 702 | int snf6 = HR_new[snf5 + c[6]]; 703 | for (c[7] = c[6] + 1; c[7] <= 52; c[7]++) 704 | { 705 | int fs7 = HR_new[fs6 + c[7]]; 706 | int snf7 = HR_new[snf6 + c[7]]; 707 | for (c[8] = c[7] + 1; c[8] <= 52; c[8]++) 708 | { 709 | int fs = HR_new[fs7 + c[8]], 710 | score_new = HR_new[snf7 + c[8]]; 711 | if (fs != 0) 712 | { 713 | int *HR_flush = HR_new + (4 - fs); 714 | int score_flush = HR_new[1] + 56; 715 | for (int i = 0; i < 9; i++) 716 | score_flush = HR_flush[score_flush + c[i]]; 717 | score_new = MAX(score_new, score_flush); 718 | } 719 | 720 | int score_old = 0; 721 | for (int np = 0; np < n_pocket_perms; np++) 722 | for (int nb = 0; nb < n_board_perms[k]; nb++) 723 | score_old = MAX(score_old, 724 | (HR_old[HR_old[HR_old[board_paths[nb] + 725 | c[5 + pocket_perms[np][0]]] + 726 | c[5 + pocket_perms[np][1]]]])); 727 | 728 | n++; 729 | if (score_new != score_old) 730 | { 731 | std::cout << "\n" << "(" << c[0]; 732 | for (int i = 1; i < 9; i++) 733 | std::cout << ", " << c[i]; 734 | std::cout << "): old = " << score_old << 735 | ", new = " << score_new << "\n"; 736 | goto fail; 737 | } 738 | else if ((n % 1000LL) == 0) 739 | { 740 | std::cout << "\r\t" << n << " / " << N[k] << 741 | " combinations verified"; 742 | } 743 | } 744 | } 745 | } 746 | } 747 | } 748 | } 749 | } 750 | } 751 | } 752 | std::cout << "\r\t" << n << " / " << N[k] << " combinations verified"; 753 | } 754 | 755 | std::cout << "\n\nAll combinations verified successfully.\n\n"; 756 | std::cout << "\nGreat success.\n"; 757 | free(HR_new); free(HR_old); HR_new = 0; HR_old = 0; 758 | return 0; 759 | 760 | fail: 761 | std::cout << "\nEpic fail.\n"; 762 | free(HR_new); free(HR_old); HR_new = 0; HR_old = 0; 763 | return 1; 764 | } 765 | 766 | struct commas_locale : std::numpunct 767 | { 768 | char do_thousands_sep() const { return ','; } 769 | std::string do_grouping() const { return "\3"; } 770 | }; 771 | 772 | int raygen9(const char *filename, const char *filename7, bool test=true) 773 | { 774 | std::vector hand_ranks; 775 | std::cout.imbue(std::locale(std::locale(), new commas_locale)); 776 | generate_handranks(hand_ranks); 777 | smart_save(&hand_ranks[0], (int) hand_ranks.size(), filename); 778 | if (test && filename7) 779 | return test_all_handranks(filename, filename7); 780 | return 0; 781 | } 782 | -------------------------------------------------------------------------------- /src/raygen9.h: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Copyright (c) 2013-2015, Whatmatters Inc. 3 | ** 4 | ** It is free software, and may be redistributed under the terms specified in the 5 | ** GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 6 | ** 7 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 8 | ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 9 | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 10 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 11 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 12 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 13 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | ***/ 15 | 16 | int raygen9(const char *filename, const char *filename7, bool test=true); -------------------------------------------------------------------------------- /src/rayutils.cpp: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Copyright (c) 2013-2015, Whatmatters Inc. 3 | ** Copyright (c) Ray Wooten 4 | ** Copyright (c) Kevin L. Suffecool 5 | ** 6 | ** It is free software, and may be redistributed under the terms specified in the 7 | ** GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 8 | ** 9 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 10 | ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 11 | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 12 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 13 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 14 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 15 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | ***/ 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "rayutils.h" 32 | #include "arrays.h" 33 | 34 | // this function is required to read binary files larger than 2GB 35 | int load_file(char* dest, size_t size, size_t nitems, FILE* stream) 36 | { 37 | int ret = 0; 38 | size_t bytes_to_load = nitems * size; 39 | size_t block_size = 0; 40 | int offset = 0; 41 | 42 | while (bytes_to_load > 0) 43 | { 44 | block_size = bytes_to_load > READ_BLOCK_SIZE ? READ_BLOCK_SIZE : bytes_to_load; 45 | ret += fread(dest + offset, block_size, 1, stream); 46 | offset += block_size; 47 | bytes_to_load -= block_size; 48 | } 49 | 50 | return ret; 51 | } 52 | 53 | // binary search in the cactus array 54 | int cactus_findit(int key) 55 | { 56 | int low = 0, high = 4887, mid; 57 | while (low <= high) 58 | { 59 | mid = (high + low) >> 1; 60 | if (key < products[mid]) 61 | high = mid - 1; 62 | else if (key > products[mid]) 63 | low = mid + 1; 64 | else 65 | return mid; 66 | } 67 | std::cout << "\ncactus_findit(): no match found; key = " << key << "\n"; 68 | return -1; 69 | } 70 | 71 | // convert cactus handrank to the new format 72 | int cactus_to_ray(int holdrank) 73 | { 74 | // hhhhrrrrrrrrrrrr hhhh = 1 high card -> 9 straight flush 75 | // r..r = rank within the above 1 to max of 2861 76 | 77 | int handrank = 7463 - holdrank; // now the worst hand = 1 78 | 79 | if (handrank < 1278) handrank = handrank - 0 + 4096 * 1; // 1277 high card 80 | else if (handrank < 4138) handrank = handrank - 1277 + 4096 * 2; // 2860 one pair 81 | else if (handrank < 4996) handrank = handrank - 4137 + 4096 * 3; // 858 two pair 82 | else if (handrank < 5854) handrank = handrank - 4995 + 4096 * 4; // 858 three-kind 83 | else if (handrank < 5864) handrank = handrank - 5853 + 4096 * 5; // 10 straights 84 | else if (handrank < 7141) handrank = handrank - 5863 + 4096 * 6; // 1277 flushes 85 | else if (handrank < 7297) handrank = handrank - 7140 + 4096 * 7; // 156 full house 86 | else if (handrank < 7453) handrank = handrank - 7296 + 4096 * 8; // 156 four-kind 87 | else handrank = handrank - 7452 + 4096 * 9; // 10 straight-flushes 88 | 89 | return handrank; 90 | } 91 | 92 | const char *hand_rank_str(int handrank_num) 93 | { 94 | const char *_hand_rank_str[] = 95 | { 96 | "n/a", 97 | "straight flush", 98 | "four of a kind", 99 | "full house", 100 | "flush", 101 | "straight", 102 | "three of a kind", 103 | "two pairs", 104 | "one pair", 105 | "high card" 106 | }; 107 | return _hand_rank_str[handrank_num]; 108 | } 109 | 110 | const char *get_hand_rank(int handrank) 111 | { 112 | return hand_rank_str(handrank >> 12); 113 | } 114 | 115 | 116 | unsigned long mix(unsigned long a, unsigned long b, unsigned long c) 117 | { 118 | a = a - b; a = a - c; a = a^(c >> 13); 119 | b = b - c; b = b - a; b = b^(a << 8); 120 | c = c - a; c = c - b; c = c^(b >> 13); 121 | a = a - b; a = a - c; a = a^(c >> 12); 122 | b = b - c; b = b - a; b = b^(a << 16); 123 | c = c - a; c = c - b; c = c^(b >> 5); 124 | a = a - b; a = a - c; a = a^(c >> 3); 125 | b = b - c; b = b - a; b = b^(a << 10); 126 | c = c - a; c = c - b; c = c^(b >> 15); 127 | return c; 128 | } 129 | 130 | static int RAND_MAX_DIV_52[53]; 131 | 132 | void init_random_int_52() 133 | { 134 | int i; 135 | for (i = 1; i <= 52; i++) 136 | RAND_MAX_DIV_52[i] = RAND_MAX / (i + 1); 137 | srand((unsigned int) mix(clock(), time(NULL), getpid())); 138 | } 139 | 140 | int random_int_52(int k) 141 | { 142 | int r; 143 | if (k == 0) 144 | return 0; // prevent potential segfault 145 | do { r = rand() / RAND_MAX_DIV_52[k]; } while (r > k); 146 | return r; // 0 to k 147 | } 148 | 149 | inline void swap(int *x, int *y) 150 | { 151 | int z = *x; 152 | *x = *y; 153 | *y = z; 154 | } 155 | 156 | void random_sample_52_ross(int n, int k, int *out) 157 | { 158 | // Ross algorithm modified to work in-place (C) Aldanor 159 | const int DECK_52[52] = { 160 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 161 | 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 162 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 163 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; 164 | memcpy(out, DECK_52, sizeof(DECK_52)); 165 | int i; 166 | for (i = 0; i < k; i++) 167 | swap(out + i, out + i + random_int_52(n - 1 - i)); 168 | } 169 | 170 | int *smart_load(const char *filename) 171 | { 172 | // the size of the file is contained in the header 173 | FILE *f = fopen(filename, "rb"); 174 | if (f) 175 | { 176 | int size = 0; 177 | fread(&size, sizeof(int), 1, f); 178 | int *data = (int *) malloc(size * sizeof(int)); 179 | load_file((char *)data, sizeof(int), size, f); 180 | fclose(f); 181 | return data; 182 | } 183 | else 184 | return NULL; 185 | } 186 | 187 | key_t generate_random_shm_key(void) 188 | { 189 | /* 190 | Referecnce: 191 | ----------- 192 | http://www.c-faq.com/lib/randrange.html 193 | 194 | NOTICE: 195 | ------- 196 | We don't know what type key_t is, so we follow a 197 | conservative approach and generate only keys where 198 | 0 <= key <= SHRT_MAX. 199 | 200 | Such values will work if key_t is typedef-ed as a short, int, uint, 201 | long or ulong. 202 | */ 203 | int key; 204 | do { 205 | key = ((int)((double)rand() / ((double)RAND_MAX + 1) * SHRT_MAX)) + 1; 206 | } while (key == IPC_PRIVATE); 207 | 208 | return (key_t)key; 209 | } 210 | 211 | int *smart_load_to_shm(const char *filename, key_t key) 212 | { 213 | int shmid; 214 | // the size of the file is contained in the header 215 | FILE *f = fopen(filename, "rb"); 216 | int *shm, *hr; 217 | int size = 0; 218 | if (f) 219 | { 220 | fread(&size, sizeof(int), 1, f); 221 | shmid = shmget(key, size*sizeof(int) + sizeof(int), IPC_CREAT | 0600); 222 | if (shmid == -1) 223 | { 224 | perror("shmget"); 225 | return NULL; 226 | } 227 | 228 | if ((shm = (int *)shmat(shmid, NULL, 0)) == (int *) -1) 229 | { 230 | perror("shmat"); 231 | return NULL; 232 | } 233 | *shm = size; 234 | hr = shm + 1; 235 | load_file((char *)hr, sizeof(int), size, f); 236 | fclose(f); 237 | return shm; 238 | } 239 | return NULL; 240 | } 241 | 242 | int *attach_hr(key_t key) 243 | { 244 | int hr_size; 245 | hr_size = *attach_shm(key, 1); 246 | return attach_shm(key, hr_size + 1) + 1; 247 | } 248 | 249 | int *attach_shm(key_t key, int size) 250 | { 251 | int* shm; 252 | int shmid; 253 | 254 | // locating the segment 255 | if ((shmid = shmget(key, size * sizeof(int), 0600)) < 0) { 256 | perror("shmget"); 257 | return NULL; 258 | } 259 | // attaching the segment 260 | if ((shm = (int *)shmat(shmid, NULL, 0)) == (int *) -1) { 261 | perror("shmat"); 262 | return NULL; 263 | } 264 | return shm; 265 | } 266 | 267 | int del_shm(key_t key) 268 | { 269 | int shmid; 270 | int res; 271 | // locating the segment 272 | if ((shmid = shmget(key, 0, 0600)) < 0) { 273 | perror("shmget"); 274 | return -1; 275 | } 276 | // deleting the segment 277 | if ((res = shmctl(shmid, IPC_RMID, (struct shmid_ds *) NULL)) == -1) 278 | { 279 | perror("shmctl"); 280 | return -1; 281 | } 282 | return res; 283 | } 284 | 285 | 286 | int smart_save(int *x, int size, const char *filename) 287 | { 288 | std::cout << "\nSaving the data to \"" << filename << "\"..."; 289 | std::ofstream out(filename, std::ios::out | std::ios::binary); 290 | if (!out) 291 | { 292 | std::cout << "\tError opening file.\n"; 293 | return 1; 294 | } 295 | int header[1]; 296 | header[0] = size; // store the size in the first 4 bytes 297 | out.write(reinterpret_cast(header), sizeof(int)); 298 | out.write(reinterpret_cast((int *) x), 299 | sizeof(int) * header[0]); 300 | return 0; 301 | } 302 | 303 | -------------------------------------------------------------------------------- /src/rayutils.h: -------------------------------------------------------------------------------- 1 | /*** 2 | ** Copyright (c) 2013-2015, Whatmatters Inc. 3 | ** Copyright (c) Ray Wooten 4 | ** Copyright (c) Kevin L. Suffecool 5 | ** 6 | ** It is free software, and may be redistributed under the terms specified in the 7 | ** GNU GENERAL PUBLIC LICENSE Version 2 or higher (see LICENSE.txt file). 8 | ** 9 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 10 | ** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 11 | ** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 12 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 13 | ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 14 | ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 15 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 | ***/ 17 | 18 | #define MAX_PLAYERS 10 19 | 20 | #define READ_BLOCK_SIZE 2000000000 21 | 22 | #define MAX(x, y) ((x) > (y) ? (x) : (y)) 23 | #define MIN(x, y) ((x) < (y) ? (x) : (y)) 24 | 25 | #define STRAIGHT_FLUSH 1 26 | #define FOUR_OF_A_KIND 2 27 | #define FULL_HOUSE 3 28 | #define FLUSH 4 29 | #define STRAIGHT 5 30 | #define THREE_OF_A_KIND 6 31 | #define TWO_PAIR 7 32 | #define ONE_PAIR 8 33 | #define HIGH_CARD 9 34 | 35 | #define CLUB 0x8000 36 | #define DIAMOND 0x4000 37 | #define HEART 0x2000 38 | #define SPADE 0x1000 39 | 40 | #include 41 | 42 | int load_file(char* dest, size_t size, size_t nitems, FILE* stream); 43 | int cactus_findit(int key); 44 | int cactus_to_ray(int holdrank); 45 | const char *hand_rank_str(int handrank_num); 46 | const char *get_hand_rank(int handrank); 47 | void init_random_int_52(); 48 | void swap(int *x, int *y); 49 | void random_sample_52_ross(int n, int k, int *out); 50 | int *smart_load(const char *filename); 51 | int smart_save(int *x, int size, const char *filename); 52 | key_t generate_random_shm_key(void); 53 | int *smart_load_to_shm(const char *filename, key_t key); 54 | int *attach_hr(key_t key); 55 | int *attach_shm(key_t key, int size); 56 | int del_shm(key_t key); 57 | --------------------------------------------------------------------------------