Exemple #1
0
def get_suggestions(cards,
                    n_best_splits,
                    unique_code_levels,
                    strategyID=1,
                    ret_card_strs=True):
    deck = Deck()
    cards = deck.deal_custom_hand(cards)

    strategy = ChinesePokerPctileScoreStrategy.init_from_db(
        strategyID,
        split_gen_filter_by_score=True,
        sort_set_cards_by_group=True,
    )

    best_splits, _, _ = strategy.pick_n_best_splits(
        cards, n_best_splits, unique_code_levels=unique_code_levels)

    # Insert processing - sort cards, add desciprions of groups, scores

    #for split in best_splits:
    #  # inds = split[0]
    #  cards = split[1]
    #  codes = split[2]
    #  scores = split[3]
    #  weighted_score = split[4]
    split_code_scores = [split.StratSplitScore for split in best_splits]

    if ret_card_strs is True:
        return [[[str(c) for c in s] for s in split.Cards]
                for split in best_splits], split_code_scores
    else:
        return best_splits, split_code_scores
def gen_best_split_data(
  n_hands,
  strategy=None,
  output_progress_every=None,
  split_gen_strategy=None,
):
  
  if strategy is None:
    strategy = ChinesePokerPctileScoreStrategy(split_gen_filter_by_score = True)
  start = timer()
  deck = Deck()
  
  split_codes = []
  split_scores = []
  split_sup_scores = []
  split_cards = []

  split_gen_filter_scores = []
  split_gen_filter_sup_scores = []
  for hI in range(n_hands):
    cards = deck.deal_cards()[0]
    if split_gen_strategy is not None:
      splits_pool = split_gen_strategy.gen_all_splits(cards)[0]
    else:
      splits_pool = None
    best_split, _, _ = strategy.pick_single_split(cards, generated_splits=splits_pool)
    
    split_codes.append(best_split.Codes)
    split_sup_scores.append(best_split.StratSupScores)
    split_scores.append(best_split.StratSplitScore)

    split_gen_filter_sup_scores.append(best_split.GenFilterSupScores)
    split_gen_filter_scores.append(best_split.GenFilterSplitScore)

    temp_cards = flattened_list(best_split.Cards)
    split_cards.append([str(card) for card in temp_cards])

    if output_progress_every is not None and (hI+1)%output_progress_every == 0:
      min_elapsed = (timer()-start)/60
      print (f'Processed {hI+1} of {n_hands} hands. {min_elapsed} min elasped.')
  data = {
    'Codes':split_codes,
    'Scores':split_scores,
    'SupScores':split_sup_scores,
    'Cards':split_cards,
    'GenFilterScores':split_gen_filter_scores,
    'GenFilterSupScores':split_gen_filter_sup_scores,
  }
  return data
Exemple #3
0
  def __init__(
    self, 
    game_mode='AUTO',
    players=None,
    strategies=None,
    random_seat=False,
    history=None,
  ):
    self.deck = Deck()
    self.n_players = len(ChinesePokerGame.player_seat_labels) # 4
    self.cur_dealer_ind = ChinesePokerGame.player_seat_labels.index(ChinesePokerGame.starting_dealer)
    self.cur_play_seq = None

    self.games_played = 0
    #self.games_history = ChinesePokerGameHistory()

    self.game_mode = game_mode
    self.players = {}
    if strategies is None:
      strategies = [ChinesePokerPctileScoreStrategy() for pI in range(self.n_players)]
    elif not isinstance(strategies, (list, tuple)):
      strategies = [strategies for pI in range(self.n_players)]
    

    self.reset_cur_game_info()
    
    if game_mode == 'AUTO':
      if not random_seat:
        for i,label in enumerate(self.player_seat_labels):
          if players is None or players[i] is None:
            self.players[label] = ComputerChinesePokerPlayer(strategy=strategies[i])
          else:
            self.players[label] = players[i]
      else:    
        print('Need to implement random_seating')
    else:
      raise InvalidGameModeError(f'game_mode {game_mode} not valid.')

    if history is None:
      history = ChinesePokerGameHistory()
    
    self.history = history

    return
Exemple #4
0
    def yield_dealt_hands_from_text_dump(self,
                                         text_file=None,
                                         deck_obj=Deck(),
                                         load_from=None,
                                         load_to=None):
        if text_file is None:
            text_file = self.get_latest_dealt_hands_dump()

        with open(text_file) as f:
            for line_no, line in enumerate(f):
                if load_from and line_no + 1 < load_from:
                    continue

                yield line_no, self._convert_rep_str_to_dealt_cards(
                    line, deck_obj)
                if load_to and line_no + 1 > load_to:
                    break

        return
def get_splits_data_for_single_game_and_seat_from_db(
    game_id,
    seat_id,
    cards=None,
):
    """[summary]

  Args:
      game_id (int): GameID
      seat_id (int): Between 1 and 4
      cards (List): List of Card objects or card strings. 
  """

    if cards is None:
        hands = next(yield_dealt_hands_from_db(game_id, game_id))[1]
        cards = hands[seat_id - 1]
    elif isinstance(cards[0], str):
        deck = Deck()
        cards = deck.deal_custom_hand(cards)

    splits_table = GameC.CHINESE_POKER_db_consts['splits_table']
    codes_table = GameC.CHINESE_POKER_db_consts['split_codes_table']
    query = f'SELECT SplitSeqNo, SplitStr, + ' \
            f'c1.L1Code AS S1L1Code, c1.L2Code AS S1L2Code, c1.L3Code AS S1L3Code, c1.L4Code AS S1L4Code, c1.L5Code AS S1L5Code, c1.L6Code AS S1L6Code, ' + \
            f'c2.L1Code AS S2L1Code, c2.L2Code AS S2L2Code, c2.L3Code AS S2L3Code, c2.L4Code AS S2L4Code, c2.L5Code AS S2L5Code, c2.L6Code AS S2L6Code, ' + \
            f'c3.L1Code AS S3L1Code, c3.L2Code AS S3L2Code, c3.L3Code AS S3L3Code, c3.L4Code AS S3L4Code, c3.L5Code AS S3L5Code, c3.L6Code AS S3L6Code ' + \
            f'FROM {splits_table} s ' + \
            f'JOIN {codes_table} c1 ON s.SplitID=c1.SplitID ' + \
            f'JOIN {codes_table} c2 ON s.SplitID=c2.SplitID ' + \
            f'JOIN {codes_table} c3 ON s.SplitID=c3.SplitID ' + \
            f'WHERE s.GameID={game_id} AND s.SeatID={seat_id} ' + \
            f'AND c1.SetNo=1 AND c2.SetNo=2 AND c3.SetNo=3'
    db_output, _ = DBF.select_query(query)
    hand_splits = []
    for row in db_output:
        split_seq_no, split_str, s1c1, s1c2, s1c3, s1c4, s1c5, s1c6, s2c1, s2c2, s2c3, s2c4, s2c5, s2c6, s3c1, s3c2, s3c3, s3c4, s3c5, s3c6 = row

        split_cards, split_inds = _convert_split_str_to_split_cards(
            cards, split_str)
        s1code = [s1c1, s1c2, s1c3, s1c4, s1c5, s1c6]
        s2code = [s2c1, s2c2, s2c3, s2c4, s2c5, s2c6]
        s3code = [s3c1, s3c2, s3c3, s3c4, s3c5, s3c6]

        s1code = CardGroupCode([code for code in s1code if code is not None])
        s2code = CardGroupCode([code for code in s2code if code is not None])
        s3code = CardGroupCode([code for code in s3code if code is not None])

        split_info_factory = ChinesePokerStrategy.ranked_split_info_factory

        split_info = split_info_factory(
            split_inds,
            split_cards,
            (s1code, s2code, s3code),
            None,
            None,
            None,
            None,
            None,
            split_seq_no,
        )
        hand_splits.append(split_info)

    hand_splits = sorted(hand_splits, key=lambda x: x.SeqNo)
    return hand_splits
import glob
import json
from deprecated import deprecated
import random

from ChinesePokerLib.classes.Deck import Deck
from ChinesePokerLib.classes.Card import Card
import ChinesePokerLib.modules.DBFunctions as DBF
from ChinesePokerLib.classes.CardGroup import CardGroupCode
from ChinesePokerLib.classes.Strategy import ChinesePokerStrategy

import ChinesePokerLib.vars.GameConstants as GameC
import ChinesePokerLib.vars.GlobalConstants as GlobC

deck_type = 'STANDARD'
deck_obj = Deck(deck_type)


def get_latest_dealt_hands_dump(min_n_games=10000):
    files = glob.glob(str(GlobC.DEALT_HANDS_DUMP_DIR / '*.txt'))

    p = re.compile('.*?/DealtCardsDump_([0-9]*?)Games_([0-9]{8}).txt')
    matches = [
        (p.match(filename).group(0), p.match(filename).group(1),
         p.match(filename).group(2)) for filename in files
        if p.match(filename) and int(p.match(filename).group(1)) >= min_n_games
    ]

    datetimes = [datetime.strptime(item[2], '%Y%m%d') for item in matches]
    max_date_ind = datetimes.index(max(datetimes))
Exemple #7
0
class ChinesePokerGame:

  # Class attributes
  game_type = GConst.ChinesePokerKey
  player_seat_labels = GConst.GEN_seat_labels[game_type] # ('S', 'W', 'N', 'E')
  starting_dealer = GConst.GEN_default_starting_dealer[game_type] # 'S'
  hands_split_into = GConst.GEN_hands_split_into[game_type] # (3,5,5)
  splits_force_ascending = GConst.GEN_hands_split_ascending_required[game_type] # True

  hand_class = ChinesePokerHand

  def __init__(
    self, 
    game_mode='AUTO',
    players=None,
    strategies=None,
    random_seat=False,
    history=None,
  ):
    self.deck = Deck()
    self.n_players = len(ChinesePokerGame.player_seat_labels) # 4
    self.cur_dealer_ind = ChinesePokerGame.player_seat_labels.index(ChinesePokerGame.starting_dealer)
    self.cur_play_seq = None

    self.games_played = 0
    #self.games_history = ChinesePokerGameHistory()

    self.game_mode = game_mode
    self.players = {}
    if strategies is None:
      strategies = [ChinesePokerPctileScoreStrategy() for pI in range(self.n_players)]
    elif not isinstance(strategies, (list, tuple)):
      strategies = [strategies for pI in range(self.n_players)]
    

    self.reset_cur_game_info()
    
    if game_mode == 'AUTO':
      if not random_seat:
        for i,label in enumerate(self.player_seat_labels):
          if players is None or players[i] is None:
            self.players[label] = ComputerChinesePokerPlayer(strategy=strategies[i])
          else:
            self.players[label] = players[i]
      else:    
        print('Need to implement random_seating')
    else:
      raise InvalidGameModeError(f'game_mode {game_mode} not valid.')

    if history is None:
      history = ChinesePokerGameHistory()
    
    self.history = history

    return

  def reset_cur_game_info(self):
    self.cur_scores = None
    self.cur_score_details = None
    self.cur_split_ranks = None
    self.cur_round_time = None
    self.cur_base_scores = None
    return

  def _create_computer_player(self):
    
    return

  def play_game(self, cards=None, deal_cards_from_dealer=False, splits_data=None):
    """Play single game of ChinesePoker
    """
    self._pregame_processes()

    start = timer()
    if self.game_mode == 'AUTO':
      self.play_auto_game(cards, deal_cards_from_dealer, splits_data)
    self.cur_round_time = timer()-start

    self._postgame_processes()
      
    return

  def play_auto_game(
    self,
    cards=None,
    deal_cards_from_dealer=False,
    splits_data=None, # Container of 4 dicts
  ):
    """Play single auto-game of Chinese Poker
    """
    if splits_data is not None:
      self._dist_splits_data(splits_data)
    elif splits_data is None:  
      if cards is not None:
        self._deal_specific_cards(cards, deal_cards_from_dealer)
      elif cards is None:
        _ = self._deal_random_cards()
    
    # Choose hands
    for player_ind in self.cur_play_seq:
      seat_label = self.player_seat_labels[player_ind]
      self.players[seat_label].play_hand()

    # Compare splits
    comparison_results, self.cur_split_ranks = self._compare_splits()
    self.cur_scores, self.cur_score_details, self.cur_base_scores = self._score_comparison(comparison_results)
    return 
  
  def _score_comparison(self, comparison_results):
    tot_scores = {}
    det_scores = {}
    base_scores = {}
    for seat in self.player_seat_labels:
      player_score = 0
      player_base_score = 0
      det_scores[seat] = {}
      for opp_seat in self.player_seat_labels:
        if opp_seat != seat:
          comp_score = sum(comparison_results[seat][opp_seat])

          player_base_score += comp_score

          if abs(comp_score) == len(comparison_results[seat][opp_seat]):
            comp_score = 2*comp_score
          
          player_score += comp_score
          det_scores[seat][opp_seat] = comp_score
      tot_scores[seat] = player_score
      base_scores[seat] = player_base_score
    return tot_scores, det_scores, base_scores

  def _compare_splits(self):
    seat_pairs = list(combinations(self.player_seat_labels,2))
    
    comparison_results = {}
    for seat in self.player_seat_labels:
      comparison_results[seat] = {}

    all_ranks = {label:[] for label in self.player_seat_labels}

    # Do it by ranks instead 
    for set_i in range(len(self.hands_split_into)):
      set_codes = [self.players[label].split_info['Codes'][set_i] for label in self.player_seat_labels]
      ranks = ss.rankdata(set_codes)
      for sI, label in enumerate(self.player_seat_labels):
        all_ranks[label].append(ranks[sI])
    
    for pair in seat_pairs:
      pair_comparison_results = self._compare_pair_of_ranks(
        all_ranks[pair[0]],
        all_ranks[pair[1]],
      )

      #pair_comparison_results = self._compare_pair_of_splits(
      #  self.players[pair[0]],
      #  self.players[pair[1]],
      #)
      #  classifier,

      comparison_results[pair[0]][pair[1]] = pair_comparison_results
      comparison_results[pair[1]][pair[0]] = [-result for result in pair_comparison_results]
    return comparison_results, all_ranks

#        first_set = classifier._find_n_card_set_codes(
#          first_set_cards,
#          set_size=self.split_into[0],
#          max_code = second_set_code,
#        )
#
#        if len(first_set) == 0:
#          continue
#        else:
#          first_set_code = first_set[0][0]  

  def _get_all_codes(self):
    return

  def _compare_pair_of_ranks(self, ranks1, ranks2):
    n_splits = len(self.hands_split_into)
    comparison_results = []

    for sI in range(n_splits):
      if ranks1[sI] > ranks2[sI]:
        comparison_results.append(1)
      elif ranks1[sI] < ranks2[sI]:
        comparison_results.append(-1)
      else:
        comparison_results.append(0)
    return comparison_results

  def _compare_pair_of_splits(self, player1, player2):
    if classifier is None:
      classifier = CardGroupClassifier()
    
    n_splits = len(self.hands_split_into)

    comparison_results = []
    
    for sI in range(n_splits):
      p1_code = player1.cur_hand.split_codes[sI]
      p2_code = player2.cur_hand.split_codes[sI]

      if p1_code > p2_code:
        comparison_results.append(1)
      elif p1_code < p2_code:
        comparison_results.append(-1)
      else:
        comparison_results.append(0)
    return comparison_results

  def _update_history(self):
    # Prepare data
    history_data = {'Players': {},
      'Dealer': None,
      'RoundTime': None, 
    }

    for seat_label in self.player_seat_labels:
      this_player = self.players[seat_label]
      history_data['Players'][seat_label] = {}
      history_data['Players'][seat_label]['PlayerID'] = this_player.id
      history_data['Players'][seat_label]['SplitCards'] = this_player.split_info['Cards']
      history_data['Players'][seat_label]['SplitCodes'] = this_player.split_info['Codes']
      history_data['Players'][seat_label]['SplitCodeSupScores'] = this_player.split_info['SupScores']
      history_data['Players'][seat_label]['SplitRanks'] = self.cur_split_ranks[seat_label]
      history_data['Players'][seat_label]['SplitCodeScore'] = this_player.split_info['SplitScore']
      history_data['Players'][seat_label]['GameScoreDetails'] = self.cur_score_details[seat_label]
      history_data['Players'][seat_label]['TotGameScore'] = self.cur_scores[seat_label]
      history_data['Players'][seat_label]['SplitSelectionTime'] = this_player.split_info['SelectionTime']
      history_data['Players'][seat_label]['SplitsGenerated'] = this_player.split_info['SplitsGenerated']
      history_data['Players'][seat_label]['BaseGameScore'] = self.cur_base_scores[seat_label]
    history_data['Dealer'] = self.player_seat_labels[self.cur_dealer_ind]
    history_data['RoundTime'] = self.cur_round_time

    self.history.add_round(history_data)
    # 
    return 

  def _pregame_processes(self):
    # Play seqeuence (start with one after dealer)
    self.cur_play_seq = [(self.cur_dealer_ind + 1 + i) % self.n_players for i in range(self.n_players)]
    return

  def _postgame_processes(self):
    
    self.games_played += 1
    
    # Update history/stats
    self._update_history()

    # Change dealer
    self._next_dealer()
    
    # Clear variables
    self.reset_cur_game_info()

    for seat_label in self.player_seat_labels:
      #self.players[seat_label].cur_hand.reset_split_info()
      self.players[seat_label].reset_cur_game_data()
    return
  
  def _next_dealer(self):
    #cur_dealer_ind = self.player_labels.index(self.cur_dealer)
    self.cur_dealer_ind = (self.cur_dealer_ind + 1) % self.n_players
    
    return
  
  def _deal_random_cards(self):
    """Deal random sets of hands

    Returns:
        [type]: [description]
    """
    
    dealt_cards = self.deck.deal_cards()

    self._update_player_hands(dealt_cards)

    return dealt_cards
  
  def _deal_specific_cards(self, cards_to_deal, deal_cards_from_dealer):
    """Deal specific hands

    Args:
        card_to_deal ([type]): Hands to deal
    """
    self._update_player_hands(cards_to_deal, deal_cards_from_dealer)
    return
  
  def _update_player_hands(self, dealt_cards, deal_cards_from_dealer):
    
    if deal_cards_from_dealer:
      for i, player_ind in enumerate(self.cur_play_seq):
        seat_label = self.player_seat_labels[player_ind]
        self.players[seat_label].cur_hand = self.hand_class(dealt_cards[i])
    else:
      for i, seat_label in enumerate(self.__class__.player_seat_labels):
        self.players[seat_label].cur_hand = self.hand_class(dealt_cards[i])
    return

  def _dist_splits_data(self, splits_data):
    for i, seat_label in enumerate(self.__class__.player_seat_labels):
      if 'P' in list(splits_data.keys())[0]:
        self.players[seat_label].cur_splits_data = splits_data[f'P{i}']
      else:
        self.players[seat_label].cur_splits_data = splits_data[i]
    return
Exemple #8
0
    def __init__(self, deck_type='STANDARD'):
        self.deck_type = deck_type
        self.deck = Deck(deck_type)

        return
Exemple #9
0
class Data:
    def __init__(self, deck_type='STANDARD'):
        self.deck_type = deck_type
        self.deck = Deck(deck_type)

        return

    """
  def _find_latest_score_file(n_cards, set_size):
    base_filename = f'best{set_size}CardSetOf{n_cards}CardHand_'
    files = glob.glob(op.join(DATADIR, base_filename + '*.csv'))
    
    p = re.compile('.*?Hand_([0-9]*?)\.csv')
    dates = [p.match(filename).group(1) for filename in files if p.match(filename)]
    datetimes = [datetime.strptime(date_str, '%Y%m%d') for date_str in dates]
    max_date = max(datetimes)

    latest_file = base_filename + datetime.strftime(max_date, '%Y%m%d') + '.csv'
    
    latest_file = op.join(DATADIR, latest_file)
    
    return latest_file
    
  """

    def get_latest_dealt_hands_dump(self, min_n_games=10000):
        files = glob.glob(str(GlobC.DEALT_HANDS_DUMP_DIR / '*.txt'))

        p = re.compile('.*?/DealtCardsDump_([0-9]*?)Games_([0-9]{8}).txt')
        matches = [(p.match(filename).group(0), p.match(filename).group(1),
                    p.match(filename).group(2)) for filename in files
                   if p.match(filename)
                   and int(p.match(filename).group(1)) >= min_n_games]

        datetimes = [datetime.strptime(item[2], '%Y%m%d') for item in matches]
        max_date_ind = datetimes.index(max(datetimes))

        latest_file = matches[max_date_ind][0]
        return latest_file

    def gen_dealt_hands_and_add_to_db(
            self,
            n_games,
            table=GameC.CHINESE_POKER_db_consts['dealt_hands_table'],
            write_every=1000):

        conn = DBF.connect_to_db()
        cursor = conn.cursor()
        start = timer()
        to_be_inserted = []
        base_query = f'INSERT INTO {table} (DealtHandsStr) VALUES '
        for i in range(1, n_games + 1):
            dealt_cards = self.deck.deal_cards()
            dealt_cards_str = self._convert_dealt_cards_to_rep_str(dealt_cards)
            to_be_inserted.append(dealt_cards_str)
            if i % write_every == 0:
                query = base_query
                for dealt_cards_str in to_be_inserted:
                    query += f'({dealt_cards_str}),'
                #insert_query = """INSERT INTO random_dealt_hands (DealtHandsStr) VALUES (%s)"""
                #cursor.executemany(insert_query, to_be_inserted)
                query = query[:-1]
                cursor.execute(query)
                conn.commit()
                min_elapsed = (timer() - start) / 60
                print(
                    f'Inserted {i} of {n_games} DealtHandsStr rows into {table} - {min_elapsed} min elasped.'
                )
                to_be_inserted = []

    def gen_dealt_hands_text_dump(self,
                                  n_games,
                                  output_file=None,
                                  output_progress_every=1000):

        if output_file is None:
            today_str = datetime.today().strftime('%Y%m%d')
            output_file = pathlib.Path(
                GlobC.DEALT_HANDS_DUMP_DIR
            ) / f'DealtCardsDump_{n_games}Games_{today_str}.txt'

        #undealt_deck_rep = ['0' for card in self.deck.deck]

        with open(output_file, 'w') as f:
            for gI in range(n_games):
                if output_progress_every and (gI +
                                              1) % output_progress_every == 0:
                    print(f'Dealt {gI+1} of {n_games}')
                #dealt_deck_rep = undealt_deck_rep.copy()
                dealt_cards = self.deck.deal_cards()
                dealt_cards_str = self._convert_dealt_cards_to_rep_str(
                    dealt_cards)
                f.write(dealt_cards_str + '\n')
        return

    def yield_dealt_hands_from_text_dump(self,
                                         text_file=None,
                                         deck_obj=Deck(),
                                         load_from=None,
                                         load_to=None):
        if text_file is None:
            text_file = self.get_latest_dealt_hands_dump()

        with open(text_file) as f:
            for line_no, line in enumerate(f):
                if load_from and line_no + 1 < load_from:
                    continue

                yield line_no, self._convert_rep_str_to_dealt_cards(
                    line, deck_obj)
                if load_to and line_no + 1 > load_to:
                    break

        return

    def yield_dealt_hands_from_db(
        self,
        start_game_no,
        end_game_no,
        db_connector=None,
        db_load_batch_size=1000,
        cards_as_str=False,
    ):
        dealt_hands_table = GameC.CHINESE_POKER_db_consts["dealt_hands_table"]
        base_query = f'SELECT GameID, DealtHandsStr FROM {dealt_hands_table} WHERE GameID BETWEEN %s AND %s'
        deck_obj = self.deck

        if start_game_no is None:
            start_game_no = 1

        temp_start_game_no = start_game_no

        while 1:
            temp_end_game_no = min(end_game_no,
                                   temp_start_game_no + db_load_batch_size - 1)
            query = base_query % (temp_start_game_no, temp_end_game_no)

            db_output, _ = DBF.select_query(query)
            for row in db_output:
                dealt_cards = self._convert_rep_str_to_dealt_cards(
                    row[1], deck_obj)
                if cards_as_str:
                    dealt_cards = [[str(card) for card in hand]
                                   for hand in hands]
                yield row[0], dealt_cards
            if temp_end_game_no >= end_game_no:
                break

            temp_start_game_no += db_load_batch_size
        yield None, None

    def _convert_dealt_cards_to_rep_str(self, dealt_cards):
        dealt_deck_rep = ['0' for card in self.deck.deck]
        for pI, player_cards in enumerate(dealt_cards):
            for card in player_cards:
                dealt_deck_rep[card.deck_card_ind] = str(pI + 1)
                dealt_cards_str = ''.join(dealt_deck_rep)
        return dealt_cards_str

    def _convert_rep_str_to_dealt_cards(self, rep_str, deck_obj, n_players=4):

        player_inds = [int(char) for char in rep_str.strip()]
        if not n_players:
            n_players = max(player_inds)

        dealt_cards = [[] for _ in range(n_players)]

        for card_strength_ind, player_ind in enumerate(player_inds):
            card_deck_ind = deck_obj.inds_of_deck_card_order[card_strength_ind]
            dealt_cards[player_ind - 1].append(deck_obj.deck[card_deck_ind])

        return dealt_cards

    def yield_splits_from_dealt_hands(
        self,
        strategy,
        start_deal_no=None,
        end_deal_no=None,
        verbose=False,
        from_db=True,
        dealt_hands_text_file=None,
    ):
        # VC TODO: verbose -> progress_every_n_splits

        if from_db:
            dealt_cards_generator = self.yield_dealt_hands_from_db(
                start_deal_no, end_deal_no)
        else:
            if dealt_hands_text_file is None:
                dealt_hands_text_file = self.get_latest_dealt_hands_dump()
            dealt_cards_generator = self.yield_dealt_hands_from_text_dump(
                dealt_hands_text_file)

        while 1:
            deal_no, dealt_cards = next(dealt_cards_generator)

            if deal_no is None:
                break

            if start_deal_no is not None and deal_no < start_deal_no:
                continue

            if end_deal_no is not None and deal_no > end_deal_no:
                break

            data = {}
            for player_ind in range(4):
                #_, splits, _ = strategy.pick_single_split(dealt_cards[player_ind])
                splits, sec_elapsed = strategy.gen_all_splits(
                    dealt_cards[player_ind])

                cards = []
                codes = []
                #scores = []
                card_inds = []
                split_inds_strs = []
                #weighted_scores = []
                for split in splits:
                    cards.append([
                        str(card) for split_set in split[1]
                        for card in split_set
                    ])
                    card_inds.append(
                        [ind for split_set in split[0] for ind in split_set])
                    codes.append([code.code for code in split[2]])
                    #scores.append(split[1][1])
                    #weighted_scores.append(split[1][0])
                    split_inds_strs.append(
                        self._convert_card_inds_to_set_inds_str(split[0]))

                data[f'P{player_ind+1}'] = {
                    'CardInds': card_inds,
                    'SplitIndsStrs': split_inds_strs,
                    'Cards': cards,
                    'Codes': codes,
                    'DealtCards': dealt_cards[player_ind],
                    'SecElapsed': sec_elapsed,
                }

            yield deal_no, data

        yield None, None
        return

    def write_splits_data_to_db(self, game_id, splits_data):
        """Function for writing splits data generated by 
    yield_splits_from_dealt_hands to db.
    
    Writes to: feasible_hand_splits - GameID, SeatID, SplitSeqNo, SplitStr, DateGen
               split_set_codes - SplitID, SeatID, SetNo, L1Code, L2Code, L3Code, L4Code, L5Code, L6Code

    Args:
        game_id (int): Game ID - Should match what is in
                                 random_dealt_hands
        splits_data ([type]): Dict with following structure:
          ('P{PLAYERNO}':player_splits_data) where PLAYERNO between 1 and 4 inclusive
          player_splits_data itself is a dict with the following structure:
            'DealtCards': Dealt cards in original order
            'CardInds': List of indices lists.
                        Each outer list corresponds to a possible split.
                        The indices refer to the indices of the DealtCards list. 
                        The order combined with the split_into attribute of the strategy object infers the split card sets.
            'Codes': List of list of tuples.
                     Each outer list corresponds to a possible split.
                     Each inner list consists of tuples representing the card group codes of the sets.
            'Scores': List of score lists.
                      Each outer list corresponds to a possible split.
                      Each inner list contains the set scores.  
            'WeightedScores': List of weighted scores.
                              One score for each possible split.    
            'SplitIndsStrs': List of strings, each of length 13, the characters correspond to the dealt cards.
                             Each string corresponds to a possible split.
                             The characters refer to the set number where each card is split into.
                             E.g. '1112222233333' means the first 3 cards (of DealtCards) are in the first set, 
                               the next 5 cards are in the second set and the last 5 cards are in the third set. 
    """

        date_gen = datetime.today().strftime('%Y-%m-%d')
        splits_table = GameC.CHINESE_POKER_db_consts['splits_table']

        codes_table = GameC.CHINESE_POKER_db_consts['split_codes_table']

        db_connector = DBF.connect_to_db()
        # First check that there are no existing splits in the DB. If there are, delete them.
        check_query = f"SELECT COUNT(*) FROM {splits_table} WHERE GameID={game_id} AND DateGenerated ='{date_gen}'"
        db_output, db_connector = DBF.select_query(check_query, db_connector)
        if db_output[0][0] > 0:
            delete_query = f"DELETE FROM {splits_table} WHERE GameID={game_id} AND DataGenerated='{date_gen}"
            db_connector, rows_deleted = DBF.delete_query(
                delete_query, db_connector)
            print(
                f'***Deleted {rows_deleted} rows with identical GameID ({game_id}) and DateGenerated ({date_gen}).***'
            )

        for seat_ID in range(1, 5):

            player_splits_data = splits_data[f'P{seat_ID}']
            #n_splits = len(player_splits_data['SplitIndsStrs']
            for seqI, split_str in enumerate(
                    player_splits_data['SplitIndsStrs']):
                splits_query = f'INSERT INTO {splits_table} (GameID, SeatID, SplitSeqNo, SplitStr, DateGenerated) VALUES (%s, %s, %s, %s, %s)'
                splits_query = splits_query % (game_id, seat_ID, seqI + 1,
                                               split_str, f"'{date_gen}'")

                db_connector, split_id = DBF.insert_query(
                    splits_query, db_connector, False, True)

                base_codes_query = f'INSERT INTO {codes_table} ' + \
                                    '(SplitID, SetNo, L1Code, L2Code, L3Code, L4Code, L5Code, L6Code) VALUES ' + \
                                    '(%s, %s, %s, %s, %s, %s, %s, %s)'

                values_list = []
                for setI, set_code in enumerate(
                        player_splits_data['Codes'][seqI]):
                    base_val = [None for i in range(8)]
                    base_val[0] = split_id
                    base_val[1] = setI + 1
                    for levelI, level_code in enumerate(set_code):
                        base_val[2 + levelI] = level_code
                    values_list.append(tuple(base_val))

                db_connector = DBF.insert_many_query(base_codes_query,
                                                     values_list, db_connector,
                                                     True)

                db_connector = DBF.try_commit(db_connector)

        return

    def _convert_card_inds_to_set_inds_str(self, card_inds, n_cards=13):
        if not n_cards:
            n_cards = sum([len(card_set) for card_set in card_inds])

        char_list = ['' for i in range(n_cards)]

        for set_i, card_set_inds in enumerate(card_inds):
            for ind in card_set_inds:
                char_list[ind] = str(set_i + 1)

        set_inds_str = ''.join(char_list)

        return set_inds_str

    def _convert_split_str_to_split_cards(
        self,
        cards,
        split_str,
    ):
        card_set_inds = [int(c) - 1 for c in list(split_str)]
        n_sets = max(card_set_inds) + 1

        card_sets = [[] for _ in range(n_sets)]

        for cI, card in enumerate(cards):
            card_sets[card_set_inds[cI]].append(card)

        card_sets = [tuple(card_set) for card_set in card_sets]

        return card_sets

    def yield_splits_data_from_db(
        self,
        start_game_id=None,
        end_game_id=None,
    ):

        #db_connector = DBF.connect_to_db()
        splits_table = GameC.CHINESE_POKER_db_consts['splits_table']
        codes_table = GameC.CHINESE_POKER_db_consts['split_codes_table']
        base_query = f'SELECT SplitSeqNo, SplitStr, + ' \
                     f'c1.L1Code AS S1L1Code, c1.L2Code AS S1L2Code, c1.L3Code AS S1L3Code, c1.L4Code AS S1L4Code, c1.L5Code AS S1L5Code, c1.L6Code AS S1L6Code, ' + \
                     f'c2.L1Code AS S2L1Code, c2.L2Code AS S2L2Code, c2.L3Code AS S2L3Code, c2.L4Code AS S2L4Code, c2.L5Code AS S2L5Code, c2.L6Code AS S2L6Code, ' + \
                     f'c3.L1Code AS S3L1Code, c3.L2Code AS S3L2Code, c3.L3Code AS S3L3Code, c3.L4Code AS S3L4Code, c3.L5Code AS S3L5Code, c3.L6Code AS S3L6Code ' + \
                     f'FROM {splits_table} s ' + \
                     f'JOIN {codes_table} c1 ON s.SplitID=c1.SplitID ' + \
                     f'JOIN {codes_table} c2 ON s.SplitID=c2.SplitID ' + \
                     f'JOIN {codes_table} c3 ON s.SplitID=c3.SplitID ' + \
                     f'WHERE s.GameID=%s AND s.SeatID=%s ' + \
                     f'AND c1.SetNo=1 AND c2.SetNo=2 AND c3.SetNo=3'
        for game_id in range(start_game_id, end_game_id + 1):
            hands = next(self.yield_dealt_hands_from_db(game_id, game_id))[1]
            game_splits = []

            for seat_id in range(1, 5):
                query = base_query % (game_id, seat_id)
                cards = hands[seat_id - 1]
                db_output, _ = DBF.select_query(query)

                hand_splits = []
                for row in db_output:
                    split_seq_no, split_str, s1c1, s1c2, s1c3, s1c4, s1c5, s1c6, s2c1, s2c2, s2c3, s2c4, s2c5, s2c6, s3c1, s3c2, s3c3, s3c4, s3c5, s3c6 = row

                    split_cards = self._convert_split_str_to_split_cards(
                        cards, split_str)
                    s1code = [s1c1, s1c2, s1c3, s1c4, s1c5, s1c6]
                    s2code = [s2c1, s2c2, s2c3, s2c4, s2c5, s2c6]
                    s3code = [s3c1, s3c2, s3c3, s3c4, s3c5, s3c6]

                    s1code = CardGroupCode(
                        [code for code in s1code if code is not None])
                    s2code = CardGroupCode(
                        [code for code in s2code if code is not None])
                    s3code = CardGroupCode(
                        [code for code in s3code if code is not None])

                    split_info_factory = ChinesePokerStrategy.ranked_split_info_factory

                    split_info = split_info_factory(
                        None,
                        split_cards,
                        (s1code, s2code, s3code),
                        None,
                        None,
                        None,
                        None,
                        None,
                        split_seq_no,
                    )
                    hand_splits.append(split_info)

                game_splits.append(hand_splits)

            yield game_id, game_splits

            # TODO Convert each row to RankedSplitInfo named tuple
            # (Inds, Cards, Codes, )
        return

    @deprecated
    def yield_splits_data_from_json(self,
                                    json_file=None,
                                    start_deal_no=None,
                                    end_deal_no=None):
        """Generator that yields dictionary of splits data, one dealt hand at a time
    """
        if json_file is None:
            json_file = GlobC.DATADIR / 'SplitsFromDealtHands' / 'splits_json_data_0_to_999.txt'

        p = re.compile('^Deal ([0-9]*?) (.*?)$')
        with open(json_file) as f:
            for line in f:
                deal_no = int(p.match(line).group(1))

                if start_deal_no is not None and deal_no < start_deal_no:
                    continue

                if end_deal_no is not None and deal_no > end_deal_no:
                    break

                json_str = p.match(line).group(2)
                splits_data_dict = json.loads(json_str)
                yield deal_no, splits_data_dict

        yield None, None
        return

    """
  def agg_splits_data_from_json_by_player(
    self,
    json_file=None,
    start_deal_no=None,
    end_deal_no=None,
    players=['P0','P1','P2','P3']
  ):

    if json_file is None:
      json_file = GlobC.DATADIR / 'SplitsFromDealtHands' / 'splits_json_data_0_to_999.txt'
    
    if isinstance(players, str):
      players = [players,]

    player_splits_data = {player: [] for player in players}
    splits_data_gen = self.yield_splits_data_from_json(json_file, start_deal_no, end_deal_no)

    while 1:
      deal_no, splits_data = next(splits_data_gen)
      if deal_no is None:
        break
      
      for player in players:
        player_splits_data[player].append(splits_data[player])
    
    return player_splits_data
  """

    @classmethod
    def random_game_setup_from_db(cls):
        max_game_id = cls.max_game_id_in_splits_table()
        random_game_id = random.randint(1, max_game_id - 1)
        hand_no = random.randind(1, 4)
        game_setup = load_game_setup(random_game_id, hand_no)

        return random_game_id, hand_no, game_setup

    @classmethod
    def load_game_setups(cls, game_id, hand_no):
        dealt_hands_gen = yield_dealt_hands_from_db()
        return

    ##########################################
    ### START Useful query functions START ###
    ##########################################

    @classmethod
    def max_game_id_in_splits_table(cls):
        splits_table = GameC.CHINESE_POKER_db_consts['splits_table']
        query = f'SELECT MAX(GameID) FROM {splits_table}'
        db_output, _ = DBF.select_query(query)

        return db_output[0][0]

    ######################################
    ### END Useful query functions END ###
    ######################################
Exemple #10
0
def random_hand():

    deck = Deck()
    cards = [str(card) for card in deck.deal_cards()[0]]
    return cards
def compare_strategies_h2h_diff_hands(
  strats, # List of 2-4 strats
  n_games,
  hands_from_db=True,
  split_gen_strat=None
):
  all_best_splits = []
  n_gen_splits_data = []
  time_data = []
  #all_comp_res = []
  compare_timer_start = timer()
  
  n_strats = len(strats)

  if hands_from_db:
    max_game_id = Data.max_game_id_in_splits_table()
    game_ids = random.sample(range(1,max_game_id+1), n_games)
    data_obj = Data()
  else:
    deck = Deck()
  
  data_dict = {
    'GameSeqNo':[],
    'GameID':[],
    'Strat':[],
    'OppStrat':[],
    'Hand':[],
    'OppHand':[],
    'BaseScore':[],
    'BonusScore':[],
    'TotScore':[],
    'CompSet1':[],
    'CompSet2':[],
    'CompSet3':[],
  }
  for gI in range(n_games):
    print(f'Game {gI+1} of {n_games}:')
    hand_best_splits = []
    hand_n_gen_splits = []
    hand_times = []
     
    # Load or gen all splits 
    if hands_from_db is True:
      game_id = game_ids[gI]
      _, all_splits = next(data_obj.yield_splits_data_from_db(game_id, game_id))
      #all_splits = all_splits[:n_strats]
      cards = [None for _ in range(4)]
    else:
      cards = deck.deal_cards() #[0:n_strats]
      
      if split_gen_strat is not None:
        all_splits = []
        
        #for sI in range(n_strats):
        for hI, hand in enumerate(cards):
          all_splits.append(split_gen_strat.gen_all_splits(hand)[0])
          hand_n_gen_splits.append(len(all_splits[-1]))
          min_elapsed = (timer()-compare_timer_start)/60
          print(f'-->Generated {len(all_splits[-1])} feasible splits on hand {hI+1}. Cum. {min_elapsed} min elapsed.')
      else:
        all_splits = None
          
    
    game_strat_best_splits = []
    for sI, strat in enumerate(strats):
      strat_best_splits = []
      for hI in range(4):
        if hands_from_db or split_gen_strat is not None:
          strat_best_split, _, sec_elapsed = strat.pick_single_split(None, generated_splits=all_splits[hI])
          min_elapsed = (timer()-compare_timer_start)/60
          print(f'-->Picked best split on hand {hI+1} using strat {sI+1}. Cum. {min_elapsed} min elapsed.')
        
        else:
          strat_best_split, raw_splits_data, sec_elapsed = strat.pick_single_split(cards[hI])
          hand_n_gen_splits.append(len(raw_splits_data))
          min_elapsed = (timer()-compare_timer_start)/60
          print(f'-->Generated {len(raw_splits_data)} splits and picked best using strat {sI+1} on hand {hI+1}. Cum. {min_elapsed} min elapsed.')
        
        hand_times.append(sec_elapsed)
        strat_best_splits.append(strat_best_split)
      game_strat_best_splits.append(strat_best_splits)

    h2h_matchups = list(permutations(range(4), 2))
    
    for s1I in range(n_strats):
      for s2I in range(s1I+1, n_strats):
        for matchup in h2h_matchups:
          s1_split = game_strat_best_splits[s1I][matchup[0]]
          s2_split = game_strat_best_splits[s2I][matchup[1]]
          comp_res = _compare_codes(s1_split.Codes,s2_split.Codes)
          
          base_score = sum(comp_res)
          
          if abs(base_score) == 3:
            tot_score = base_score*2
            bonus_score = base_score
          else:
            tot_score = base_score
            bonus_score = 0
          data_dict['GameSeqNo'].append(gI+1)
          data_dict['GameSeqNo'].append(gI+1)
          
          if hands_from_db:
            data_dict['GameID'].append(game_id)
            data_dict['GameID'].append(game_id)
          else:
            data_dict['GameID'].append(np.nan)
            data_dict['GameID'].append(np.nan)

          data_dict['Strat'].append(s1I+1)
          data_dict['Strat'].append(s2I+1)

          data_dict['OppStrat'].append(s2I+1)
          data_dict['OppStrat'].append(s1I+1)

          data_dict['Hand'].append(matchup[0]+1)
          data_dict['Hand'].append(matchup[1]+1)

          data_dict['OppHand'].append(matchup[1]+1)
          data_dict['OppHand'].append(matchup[0]+1)

          data_dict['BaseScore'].append(base_score)
          data_dict['BaseScore'].append(-base_score)

          data_dict['BonusScore'].append(bonus_score)
          data_dict['BonusScore'].append(-bonus_score)

          data_dict['TotScore'].append(tot_score)
          data_dict['TotScore'].append(-tot_score)
          
          for sI in range(3):
            data_dict[f'CompSet{sI+1}'].append(comp_res[sI])
            data_dict[f'CompSet{sI+1}'].append(-comp_res[sI])

    """
    strat_perms = list(permutations(range(n_strats)))
    base_scores = np.full((n_strats,n_strats,len(strat_perms)), np.nan)
    tot_scores = base_scores.copy()
    comp_res_det = np.full((n_strats,n_strats,len(strat_perms),3), np.nan)
    for permI, strat_perm in enumerate(strat_perms):
      # Pick best split 
      for hI, stratI in enumerate(strat_perm):          
        if hands_from_db or split_gen_strat is not None:
          strat_best_split, _, sec_elapsed = strats[stratI].pick_single_split(cards[hI], generated_splits=all_splits[hI])
          min_elapsed = (timer()-compare_timer_start)/60
          print(f'-->Picked best split on hand {hI+1} using strat {stratI+1}. Cum. {min_elapsed} min elapsed.')
        
        else:
          strat_best_split, raw_splits_data, sec_elapsed = strats[stratI].pick_single_split(cards[hI])
          hand_n_gen_splits.append(len(raw_splits_data))
          min_elapsed = (timer()-compare_timer_start)/60
          print(f'-->Generated {len(raw_splits_data)} splits and picked best using strat {stratI+1} on hand {hI+1}. Cum. {min_elapsed} min elapsed.')
        
        hand_times.append(sec_elapsed)
        hand_best_splits.append(strat_best_split)
      
      # Compare best splits
      for s1I in range(n_strats):
        for s2I in range(s1I+1, n_strats):
          comp_res = _compare_codes(hand_best_splits[s1I].Codes,hand_best_splits[s2I].Codes)
          
          base_score = sum(comp_res)
          
          if abs(base_score) == 3:
            tot_score = base_score*2
          else:
            tot_score = base_score
          base_scores[strat_perm[s1I], strat_perm[s2I], permI] = base_score
          base_scores[strat_perm[s2I], strat_perm[s1I], permI] = -base_score
          tot_scores[strat_perm[s1I], strat_perm[s2I], permI] = tot_score
          tot_scores[strat_perm[s2I], strat_perm[s1I], permI] = -tot_score
          comp_res_det[strat_perm[s1I], strat_perm[s2I], permI, :] = comp_res
          comp_res_det[strat_perm[s2I], strat_perm[s1I], permI, :] = -comp_res_det[strat_perm[s1I], strat_perm[s2I], permI, :]

    
    hand_comp_res = (
      tot_scores,
      base_scores,
      comp_res_det,
    )
    
    """
    comp_data = pd.DataFrame(data_dict)
    #all_comp_res.append(hand_comp_res)
    all_best_splits.append(hand_best_splits)
    n_gen_splits_data.append(hand_n_gen_splits)
    time_data.append(hand_times)
    
    #if tot_score1 + tot_score2 != 0:
    #  print('***Difference in outcome detected***')
    #  print(f'S1H1vS2H2: {hand_best_splits[0].Codes} vs. {hand_best_splits[1].Codes}')
    #  print(f'S1H2vS2H1: {hand_best_splits[2].Codes} vs. {hand_best_splits[3].Codes}')
    #  print(hand_comp_res)
      
  return comp_data, all_best_splits, n_gen_splits_data, time_data


#############################################
### END Strategy comparison functions END ###
#############################################
def compare_strategies_same_hand(strategy_list, n_hands, n_best_splits=1, include_split_gen=False, split_gen_strategy=None):
  all_best_splits = []
  n_gen_splits_data = []
  time_data = []

  compare_timer_start = timer()

  for hI in range(n_hands):
    print(f'Hand {hI+1} of {n_hands}:')
    hand_best_splits = []
    hand_n_gen_splits = []
    hand_times = []
    deck = Deck()
    cards = deck.deal_cards()[0]
    
    

    if include_split_gen is False:
      if split_gen_strategy is None:
        split_gen_strategy = ChinesePokerPctileScoreStrategy(
          pick_max_gen_splits=None,
          pick_max_elapsed_sec=None
        )
      
      start = timer()
      all_splits = split_gen_strategy.gen_all_splits(cards, True)[0]
      hand_n_gen_splits = len(all_splits)
      hand_times.append(timer()-start)
      
      min_elapsed = (timer()-compare_timer_start)/60
      print(f'-->{len(all_splits)} feasible hands generated. Cum. {min_elapsed} min elapsed.')

      for sI, strategy in enumerate(strategy_list):
        strat_best_splits, _, sec_elapsed = strategy.pick_n_best_splits(
          None, 
          n_best_splits, 
          max_elapsed_sec=None, 
          unique_code_levels=None, 
          verbose=False, 
          generated_splits=all_splits,
        )
        hand_best_splits.append(strat_best_splits)
        hand_times.append(sec_elapsed)
        
        min_elapsed = (timer()-compare_timer_start)/60
        print(f'-->Picked best split using strategy {sI+1}. Cum. {min_elapsed} min elapsed.')
    else:
      for sI, strategy in enumerate(strategy_list):
        strat_best_splits, raw_splits_data, sec_elapsed = strategy.pick_n_best_splits(
          cards,
          n_best_splits,
        )
        hand_n_gen_splits.append(len(raw_splits_data))
        hand_times.append(sec_elapsed)
        
        min_elapsed = (timer()-compare_timer_start)/60
        print(f'-->Generated splits and picked best split using strategy {sI+1}. Cum. {min_elapsed} min elapsed.')
    all_best_splits.append(hand_best_splits)
    n_gen_splits_data.append(hand_n_gen_splits)
    time_data.append(hand_times)
    
  return all_best_splits, n_gen_splits_data, time_data
def repeated_games_with_hand(
  cards, 
  n_games, 
  strategies=None, 
  test_n_best_splits=None, 
  verbose=True,
  unique_code_levels=None,
):
  start = timer()

  deck = Deck()

  if strategies is None:
    strategy = ChinesePokerPctileScoreStrategy(split_gen_filter_by_score = True)
    strategies = [strategy for _ in range(4)]
  elif not isinstance(strategies, list):
    strategies = [strategies for _ in range(4)]

  if test_n_best_splits is None:
    test_n_best_splits = 1

  cards = deck.deal_custom_hand(cards)
  
  if verbose:
    print(f'Cards: {cards}')
  
  best_splits_P0, _, _ = strategies[0].pick_n_best_splits(cards, test_n_best_splits, unique_code_levels=unique_code_levels)
  best_splits_P0_dict = {
    'Cards': [flattened_list(split[1]) for split in best_splits_P0],
    'Scores': [split[5] for split in best_splits_P0],
    'Codes': [[set_code.code for set_code in split[2]] for split in best_splits_P0],
  }

  game_obj = ChinesePokerGame(strategies=strategies)
  for gI in range(n_games):
    

    hands = deck.deal_partially_custom_cards(cards)
    
    #other_player_best_splits = []
    game_splits_data = {}
    for pI in range(1, 4):
      player_best_split, _, _ = strategies[pI].pick_single_split(hands[pI])
      
      player_split_dict = {
        'Cards': [flattened_list(player_best_split[1])],
        'Scores': [player_best_split[5]],
        'Codes': [[set_code.code for set_code in player_best_split[2]]],
      }
      
      game_splits_data[f'P{pI}'] = player_split_dict

    for splitI in range(len(best_splits_P0)):
      split_P0_dict = {
        'Cards': [best_splits_P0_dict['Cards'][splitI]],
        'Scores': [best_splits_P0_dict['Scores'][splitI]],
        'Codes': [best_splits_P0_dict['Codes'][splitI]],
      }    
      game_splits_data['P0'] = split_P0_dict
      game_obj.play_game(splits_data=game_splits_data)
    
    if verbose:
      min_elapsed = (timer()-start)/60
      print(f'-->Completed game {gI+1}/{n_games}. Time elapsed={min_elapsed} min.')
  return game_obj.history, best_splits_P0_dict