Package org.cspoker.common.handeval.spears2p2

Source Code of org.cspoker.common.handeval.spears2p2.StateTableEvaluator

/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/**
* I have no problem at all for the code to be used in a GPL program.
* - Ray Wotton
*/
package org.cspoker.common.handeval.spears2p2;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.log4j.Logger;
import org.cspoker.common.elements.cards.Card;

/**
*
* http://forumserver.twoplustwo.com/showthreaded.php?Cat=0&Number=9765615&page=0&vc=1
* http://forumserver.twoplustwo.com/showthreaded.php?Cat=0&Number=9774228&page=0&vc=1
*
* To evaluate 6 card hand make the last card a zero.
*
*/
public class StateTableEvaluator {
 
  private final static Logger logger = Logger.getLogger(StateTableEvaluator.class);

  private final int HAND_RANKS_SIZE = 32487834;
  /*
   * Card to integer conversions: 2c = 1 2d = 2 2h = 3 2s = 4 3c = 5 3d = 6 3h
   * = 7 3s = 8 4c = 9 4d = 10 4h = 11 4s = 12 5c = 13 5d = 14 5h = 15 5s = 16
   * 6c = 17 6d = 18 6h = 19 6s = 20 7c = 21 7d = 22 7h = 23 7s = 24 8c = 25
   * 8d = 26 8h = 27 8s = 28 9c = 29 9d = 30 9h = 31 9s = 32 Tc = 33 Td = 34
   * Th = 35 Ts = 36 Jc = 37 Jd = 38 Jh = 39 Js = 40 Qc = 41 Qd = 42 Qh = 43
   * Qs = 44 Kc = 45 Kd = 46 Kh = 47 Ks = 48 Ac = 49 Ad = 50 Ah = 51 As = 52
   */

  private final int NUM_SUITS = 4;
  private final int NUM_RANKS = 13;

  public int[] handRanks = new int[HAND_RANKS_SIZE]; // array to hold hand
                            // rank lookup table
  private final boolean verbose = true; // toggles verbose mode

  private int[] hand; // re-usable array to hold cards in a hand
  private final int size = 612978; // lookup table size
  private final long[] keys = new long[size]; // array to hold key lookup
                        // table
  private int numKeys = 1; // counter for number of defined keys in key array
  private long maxKey = 0; // holds current maximum key value
  private int numCards = 0; // re-usable counter for number of cards in a hand
  private int cardIndex = 0; // re-usable index for cards in a hands
  private int maxHandRankIndex = 0;

  private long startTimer;
  private long stopTimer;

  private final String HAND_RANKS_FILE = "handRanks.zip";

  private StateTableEvaluator() throws FileNotFoundException, IOException,
      ClassNotFoundException {
    logger.info("Loading evaluation tables ...");
    File f = new File(HAND_RANKS_FILE);
    if (!f.exists()) {
      logger.info("Evaluation tables do not exist, this is first time run. Generating them ...");
      handRanks = new int[HAND_RANKS_SIZE];
      generateTables();
      saveTables();
      logger.info("Loading evaluation tables (again) ...");
    }
    long t = System.currentTimeMillis();
    ZipInputStream zipStream = new ZipInputStream(new FileInputStream(
        HAND_RANKS_FILE));
    zipStream.getNextEntry();
    ObjectInputStream s = new ObjectInputStream(zipStream);
    handRanks = (int[]) s.readObject();
    t = System.currentTimeMillis() - t;
    logger.info("Evaluation tables loaded in " + t / 1000.0
        + " seconds");
  }

  private static StateTableEvaluator instance = null;

  public synchronized static StateTableEvaluator getInstance() {
    if (instance == null) {
      try {
        instance = new StateTableEvaluator();
      } catch (FileNotFoundException e) {
        throw new IllegalStateException(e);
      } catch (IOException e) {
        throw new IllegalStateException(e);
      } catch (ClassNotFoundException e) {
        throw new IllegalStateException(e);
      }
    }
    return instance;
  }

  // Inserts a key into the key array and returns the insertion index.
  private int insertKey(long key) {

    // check to see if key is valid
    if (key == 0) {
      return 0;
    }

    // short circuit insertion for most common cases
    if (key >= maxKey) {
      if (key > maxKey) {
        keys[numKeys++] = key; // appends the new key to the key array
        maxKey = key;
      }
      return numKeys - 1;
    }

    // use binary search to find insertion point for new key
    int low = -1;
    int high = numKeys;
    int pivot;
    long difference;

    while (high - low > 1) {
      pivot = low + high >>> 1;
      difference = keys[pivot] - key;
      if (difference > 0) {
        high = pivot;
      } else if (difference < 0) {
        low = pivot;
      } else {
        return pivot; // key already exists
      }
    }

    // key does not exist so must be inserted
    System.arraycopy(keys, high, keys, high + 1, numKeys - high);
    keys[high] = key;

    numKeys++;
    return high;

  } // END insertKey method

  // Returns a key for the hand created by adding a new card to the hand
  // represented by the given key. Returns 0 if new card already appears in
  // hand.
  private long makeKey(long baseKey, int newCard) {

    int[] suitCount = new int[NUM_SUITS + 1]; // number of times a suit
                          // appears in a hand
    int[] rankCount = new int[NUM_RANKS + 1]; // number of times a rank
                          // appears in a hand
    hand = new int[8];

    // extract the hand represented by the key value
    for (cardIndex = 0; cardIndex < 6; cardIndex++) {

      // hand[0] is used to hold the new card
      hand[cardIndex + 1] = (int) (baseKey >>> 8 * cardIndex & 0xFF);
    }

    hand[0] = formatCard8bit(newCard);

    // examine the hand to determine number of cards and rank/suit counts
    for (numCards = 0; hand[numCards] != 0; numCards++) {
      suitCount[hand[numCards] & 0xF]++;
      rankCount[hand[numCards] >>> 4 & 0xF]++;

      // check to see if new card is already contained in hand (rank and
      // suit considered)
      if (numCards != 0 && hand[0] == hand[numCards]) {
        return 0;
      }
    }

    // check to see if we already have four of a particular rank
    if (numCards > 4) {
      for (int rank = 1; rank < 14; rank++) {
        if (rankCount[rank] > 4) {
          return 0;
        }
      }
    }

    // determine the minimum number of suits required for a flush to be
    // possible
    int minSuitCount = numCards - 2;

    // check to see if suit is significant
    if (minSuitCount > 1) {
      // examine each card in the hand
      for (cardIndex = 0; cardIndex < numCards; cardIndex++) {
        // if the suit is not significant then strip it from the card
        if (suitCount[hand[cardIndex] & 0xF] < minSuitCount) {
          hand[cardIndex] &= 0xF0;
        }
      }
    }

    sortHand();

    long key = 0;
    for (int i = 0; i < 7; i++) {
      key += (long) hand[i] << i * 8;
    }

    return key;

  } // END makeKey method

  // Formats and returns a card in 8-bit packed representation.
  private int formatCard8bit(int card) {

    // 8-Bit Packed Card Representation
    // +--------+
    // |rrrr--ss|
    // +--------+
    // r = rank of card (deuce = 1, trey = 2, four = 3, five = 4,..., ace =
    // 13)
    // s = suit of card (suits are arbitrary, can take value from 0 to 3)

    card--;
    return ((card >>> 2) + 1 << 4) + (card & 3) + 1;

  } // END formatCard8bit method

  // Sorts the hand using Bose-Nelson Sorting Algorithm (N = 7).
  private void sortHand() {
    swapCard(0, 4);
    swapCard(1, 5);
    swapCard(2, 6);
    swapCard(0, 2);
    swapCard(1, 3);
    swapCard(4, 6);
    swapCard(2, 4);
    swapCard(3, 5);
    swapCard(0, 1);
    swapCard(2, 3);
    swapCard(4, 5);
    swapCard(1, 4);
    swapCard(3, 6);
    swapCard(1, 2);
    swapCard(3, 4);
    swapCard(5, 6);
  } // End sortHand method

  // Swaps card i with card j.
  private void swapCard(int i, int j) {
    if (hand[i] < hand[j]) {
      hand[i] ^= hand[j];
      hand[j] ^= hand[i];
      hand[i] ^= hand[j];
    }
  } // END swapCard method

  // Determines the relative strength of a hand (the hand is given by its
  // unique key value).
  private int getHandRank(long key) {

    // The following method implements a modified version of "Cactus Kev's
    // Five-Card
    // Poker Hand Evaluator" to determine the relative strength of two
    // five-card hands.
    // Reference: http://www.suffecool.net/poker/evaluator.html

    hand = new int[8];
    int currentCard;
    int rank;
    int handRank = 9999;
    int holdrank = 9999;
    int suit = 0;
    int numCards = 0;

    final int[] primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41 };

    if (key != 0) {

      for (cardIndex = 0; cardIndex < 7; cardIndex++) {

        currentCard = (int) (key >>> 8 * cardIndex & 0xFF);
        if (currentCard == 0) {
          break;
        }
        numCards++;

        // Cactus Kev Card Representation
        // +--------+--------+--------+--------+
        // |xxxbbbbb|bbbbbbbb|cdhsrrrr|xxpppppp|
        // +--------+--------+--------+--------+
        // p = prime number of rank (deuce = 2, trey = 3, four = 5, five
        // = 7,..., ace = 41)
        // r = rank of card (deuce = 0, trey = 1, four = 2, five =
        // 3,..., ace = 12)
        // cdhs = suit of card
        // b = bit turned on depending on rank of card

        // extract suit and rank from 8-bit packed representation
        rank = (currentCard >>> 4) - 1;
        suit = currentCard & 0xF;

        // change card representation to Cactus Kev Representation
        hand[cardIndex] = primes[rank] | rank << 8 | 1 << suit + 11
            | 1 << 16 + rank;
      }

      switch (numCards) {
      case 5:

        holdrank = eval_5hand(hand[0], hand[1], hand[2], hand[3],
            hand[4]);
        break;

      case 6:

        // Cactus Kev's Evaluator ranks hands from 1 (Royal Flush) to
        // 7462 (Seven High Card)
        holdrank = eval_5hand(hand[0], hand[1], hand[2], hand[3],
            hand[4]);
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[2], hand[3], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[2], hand[4], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[3], hand[4], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[2],
            hand[3], hand[4], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[1], hand[2],
            hand[3], hand[4], hand[5]));
        break;

      case 7:

        holdrank = eval_5hand(hand[0], hand[1], hand[2], hand[3],
            hand[4]);
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[2], hand[3], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[2], hand[3], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[2], hand[4], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[2], hand[4], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[2], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[3], hand[4], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[3], hand[4], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[3], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[1],
            hand[4], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[2],
            hand[3], hand[4], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[2],
            hand[3], hand[4], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[2],
            hand[3], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[2],
            hand[4], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[0], hand[3],
            hand[4], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[1], hand[2],
            hand[3], hand[4], hand[5]));
        holdrank = Math.min(holdrank, eval_5hand(hand[1], hand[2],
            hand[3], hand[4], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[1], hand[2],
            hand[3], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[1], hand[2],
            hand[4], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[1], hand[3],
            hand[4], hand[5], hand[6]));
        holdrank = Math.min(holdrank, eval_5hand(hand[2], hand[3],
            hand[4], hand[5], hand[6]));
        break;

      default:

        logger.error("ERROR: Invalid hand in GetRank method.");
        break;

      }

      // Hand Rank Representation
      // +--------+--------+
      // |hhhheeee|eeeeeeee|
      // +--------+--------+
      // h = poker hand (1 = High Card, 2 = One Pair, 3 = Two Pair,..., 9
      // = Straight Flush)
      // e = equivalency class (Rank of equivalency class relative to base
      // hand)

      // +-----------------------------------+----------------------------------+-----------------+
      // 5-Card Equivalency Classes 7-Card Equivalency Classes
      // +-----------------------------------+----------------------------------+-----------------+
      // 1277 407 High Card
      // 2860 1470 One Pair
      // 858 763 Two Pair
      // 858 575 Three of a Kind
      // 10 10 Straight
      // 1277 1277 Flush
      // 156 156 Full House
      // 156 156 Four of a Kind
      // 10 10 Straight Flush
      // +----------+------------------------+----------------------------------+-----------------+
      // Total: 7462 4824
      // +----------+------------------------+----------------------------------+-----------------+

      handRank = 7463 - holdrank; // Invert ranking metric (1 is now worst
                    // hand)

      if (handRank < 1278) {
        handRank = handRank - 0 + 4096 * 1; // High Card
      } else if (handRank < 4138) {
        handRank = handRank - 1277 + 4096 * 2; // One Pair
      } else if (handRank < 4996) {
        handRank = handRank - 4137 + 4096 * 3; // Two Pair
      } else if (handRank < 5854) {
        handRank = handRank - 4995 + 4096 * 4; // Three of a Kind
      } else if (handRank < 5864) {
        handRank = handRank - 5853 + 4096 * 5; // Straight
      } else if (handRank < 7141) {
        handRank = handRank - 5863 + 4096 * 6; // Flush
      } else if (handRank < 7297) {
        handRank = handRank - 7140 + 4096 * 7; // Full House
      } else if (handRank < 7453) {
        handRank = handRank - 7296 + 4096 * 8; // Four of a Kind
      } else {
        handRank = handRank - 7452 + 4096 * 9; // Straight Flush
      }

    }
    return handRank;

  } // END getHandRank method

  private final int[] offsets = new int[] { 0, 1277, 4137, 4995, 5853, 5863,
      7140, 7296, 7452 };

  private int getIndex(int key) {

    // use binary search to find key
    int low = -1;
    int high = 4888;
    int pivot;

    while (high - low > 1) {
      pivot = low + high >>> 1;
      if (Products.table[pivot] > key) {
        high = pivot;
      } else if (Products.table[pivot] < key) {
        low = pivot;
      } else {
        return pivot;
      }
    }
    return -1;

  } // END getIndex method

  private int eval_5hand(int c1, int c2, int c3, int c4, int c5) {
    int q = (c1 | c2 | c3 | c4 | c5) >> 16;
    short s;

    // check for Flushes and Straight Flushes
    if ((c1 & c2 & c3 & c4 & c5 & 0xF000) != 0) {
      return Flushes.table[q];
    }

    // check for Straights and High Card hands
    if ((s = Unique.table[q]) != 0) {
      return s;
    }

    q = (c1 & 0xFF) * (c2 & 0xFF) * (c3 & 0xFF) * (c4 & 0xFF) * (c5 & 0xFF);
    q = getIndex(q);

    return Values.table[q];

  } // END eval_5hand method

  private void generateTables() {

    int card;
    int handRank;
    int keyIndex;
    long key;

    if (verbose) {
      logger.info("Generating and sorting keys...");
      startTimer = System.currentTimeMillis();
    }

    for (keyIndex = 0; keys[keyIndex] != 0 || keyIndex == 0; keyIndex++) {
      if (keyIndex % (size / 100) == 0) {
        logger.info(keyIndex / (size / 100) + "% generated.");
      }

      for (card = 1; card < 53; card++) { // add a card to each previously
                        // calculated key
        key = makeKey(keys[keyIndex], card); // create the new key

        if (numCards < 7) {
          insertKey(key); // insert the new key into the key lookup
                  // table
        }
      }
    }

    if (verbose) {
      stopTimer = System.currentTimeMillis();
      logger.info("Generating hand ranks...");
      startTimer = System.currentTimeMillis();
    }

    for (keyIndex = 0; keys[keyIndex] != 0 || keyIndex == 0; keyIndex++) {
      if (keyIndex % (size / 100) == 0) {
        logger.info(keyIndex / (size / 100) + "% generated.");
      }

      for (card = 1; card < 53; card++) {
        key = makeKey(keys[keyIndex], card);

        if (numCards < 7) {
          handRank = insertKey(key) * 53 + 53; // if number of cards
                              // is < 7 insert key
        } else {
          handRank = getHandRank(key); // if number of cards is 7
                          // insert hand rank
        }

        maxHandRankIndex = keyIndex * 53 + card + 53; // calculate hand
                                // rank
                                // insertion
                                // index
        handRanks[maxHandRankIndex] = handRank; // populate hand rank
                            // lookup table with
                            // appropriate value
      }

      if (numCards == 6 || numCards == 7) {
        // insert the hand rank into the hand rank lookup table
        handRanks[keyIndex * 53 + 53] = getHandRank(keys[keyIndex]);
      }
    }

  } // END generateTables method

  private void saveTables() throws FileNotFoundException, IOException {
    ZipOutputStream zipStream = new ZipOutputStream(new FileOutputStream(
        HAND_RANKS_FILE));
    zipStream.setLevel(Deflater.BEST_COMPRESSION);
    ZipEntry entry = new ZipEntry("handRanks.ser");
    zipStream.putNextEntry(entry);
    ObjectOutputStream s = new ObjectOutputStream(zipStream);
    s.writeObject(handRanks);
    s.close();
  }

  public int getRank(Card[] cards) {
    // System.out.println("Getting rank..");
    // Card[] cards = sevenCardHand.toCards();
    int rank = 53;
    for (Card card : cards) {
      int c = card.ordinal() + 1;
      rank = handRanks[c + rank];
    }
    // System.out.println("Got rank");
    int type = (rank >>> 12) - 1;
    rank = rank & 0xFFF;

    return offsets[type] + rank - 1;
  }

}
TOP

Related Classes of org.cspoker.common.handeval.spears2p2.StateTableEvaluator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.