Notze

MTG/RiffleShuffle Simulation

Jul 26th, 2025
456
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.58 KB | None | 0 0
  1. # Script to simulate a riffle shuffle (adapted to fit Magic The Gathering: Commander decks)
  2.  
  3. import random
  4.  
  5. # the maximum difference of stack sizes (in number of cards) when splitting the deck
  6. splitting_deviation_in_cards = 5
  7.  
  8. # the probability of mistakes during the merging of the two card stacks
  9. # range 0 to 1 where 0 means the shuffle is perfectly executed, always taking exactly one card from each stack
  10. # and 1 resulting in a 50% chance to get a card from the same stack as before
  11. merging_randomness = 0.05
  12.  
  13. # similar to 'merging_randomness' but only applies to the first card when merging two stacks and is either random or not
  14. first_card_is_always_from_left_stack = False
  15.  
  16. # how many time the deck shall be shuffled
  17. number_of_shuffles = 1000
  18.  
  19. # verbosity of the script (1 = print finished shuffle, 2 = print every iteration, 3 = print everything)
  20. log_level = 1
  21.  
  22. def main():
  23.     stack_of_cards = ["c" for x in range(0, 30)] + ["s" for x in range(0, 10)] + ["i" for x in range(0, 10)] + ["a" for x in range(0, 11)] + ["l" for x in range(0, 38)]
  24.     print("Unshuffled deck: " + ''.join(stack_of_cards))
  25.    
  26.     for i in range(1, number_of_shuffles):
  27.         stack_of_cards = riffleShuffle(stack_of_cards)
  28.         if (log_level >= 2):
  29.             print("Shuffled deck " + str(i) + " times: [" + str(landScore(stack_of_cards)) + "] " + ''.join(stack_of_cards))
  30.     stack_of_cards = riffleShuffle(stack_of_cards)
  31.     print("Shuffled deck " + str(number_of_shuffles) + " times: [" + str(landScore(stack_of_cards)) + "] " + ''.join(stack_of_cards))
  32.    
  33. def riffleShuffle(stack_of_cards):
  34.     split_stack_of_cards = splitStack(stack_of_cards)
  35.     shuffled_stack = mergeStacks(split_stack_of_cards[0], split_stack_of_cards[1])
  36.     return (shuffled_stack)
  37.  
  38. def splitStack(stack_of_cards):
  39.     size_of_stack = len(stack_of_cards)
  40.     deviation_in_cards = round(random.uniform(0, 1) * splitting_deviation_in_cards)
  41.     deviation_is_positive = coinFlip()
  42.     deviation_from_middle_of_stack = deviation_in_cards if deviation_is_positive else -deviation_in_cards
  43.     middle_of_stack = round(size_of_stack / 2) + deviation_from_middle_of_stack
  44.    
  45.     left_half = [stack_of_cards[i] for i in range(0,middle_of_stack)]
  46.     right_half = [stack_of_cards[i] for i in range(middle_of_stack,size_of_stack)]
  47.     split_stack_of_cards = [left_half, right_half]
  48.    
  49.     if (log_level >= 3):
  50.         print(" missed the center of the deck by " + str(deviation_from_middle_of_stack) + " cards while splitting: " + str(split_stack_of_cards))
  51.     return split_stack_of_cards
  52.  
  53. def mergeStacks(lhs, rhs):
  54.     next_card_comes_from_from_lhs = True if first_card_is_always_from_left_stack else coinFlip()
  55.     probability_that_next_card_comes_from_opposite_site = (1 - merging_randomness/2)
  56.    
  57.     merged_stack = [lhs.pop(0)] if next_card_comes_from_from_lhs else [rhs.pop(0)]
  58.     while (len(lhs) > 0 and len(rhs) > 0):
  59.         random_number = random.uniform(0, 1)
  60.         if (random_number <= probability_that_next_card_comes_from_opposite_site):
  61.             next_card_comes_from_from_lhs = not next_card_comes_from_from_lhs
  62.         merged_stack.append(lhs.pop(0) if next_card_comes_from_from_lhs else rhs.pop(0))
  63.    
  64.     for card in lhs:
  65.         merged_stack.append(card)
  66.     for card in rhs:
  67.         merged_stack.append(card)
  68.    
  69.     return merged_stack
  70.    
  71. def landScore(stack_of_cards):
  72.     consecutive_lands_list, consecutive_non_lands_list = landAnalysis(stack_of_cards)
  73.     consecutive_lands_score = sum([pow(2, x-2) for x in consecutive_lands_list if x >= 2])
  74.     consecutive_non_lands_score = sum([pow(2, x-3) for x in consecutive_non_lands_list if x >= 3])
  75.  
  76.     return consecutive_lands_score + consecutive_non_lands_score
  77.  
  78. def landAnalysis(stack_of_cards):
  79.     consecutive_lands_list = []
  80.     consecutive_non_lands_list = []
  81.     land_streak = 0
  82.     non_land_streak = 0
  83.     for card in stack_of_cards:
  84.         if (card == 'l'):
  85.             land_streak = land_streak + 1
  86.             if (non_land_streak > 0):
  87.                 consecutive_non_lands_list.append(non_land_streak)
  88.             non_land_streak = 0
  89.         else:
  90.             non_land_streak = non_land_streak + 1
  91.             if (land_streak > 0):
  92.                 consecutive_lands_list.append(land_streak)
  93.             land_streak = 0
  94.     if (land_streak > 0):
  95.             consecutive_lands_list.append(land_streak)
  96.     if (non_land_streak > 0):
  97.         consecutive_non_lands_list.append(non_land_streak)
  98.     return (consecutive_lands_list, consecutive_non_lands_list)
  99.    
  100. def coinFlip():
  101.     return round(random.uniform(0, 1))
  102.    
  103.  
  104. main()
Advertisement
Add Comment
Please, Sign In to add comment