import random
import json

# ---------- PARAMETERS (change these) ----------
NUM_TRIALS = 10000                      # how many times to repeat the experiment per shuffle-count
SHUFFLE_COUNTS = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # test these strip-shuffle counts

MIN_PACKETS = 4                        # min number of packets pulled off per strip shuffle
MAX_PACKETS = 8                        # max number of packets pulled off per strip shuffle

# deck definitions: name -> (ordered "new deck" list, expected rising sequences if truly random)
DECKS = {
    "Standard": {
        "deck": list(range(52)),               # 52 cards, all unique, ace..king order
        "target_random_avg": 26.5,             # (n+1)/2 for n=52
    },
    "Skibbo": {
        "deck": [v for v in range(1, 13) for _ in range(5)],  # 60 cards, values 1-12, 5 copies each
        "target_random_avg": 30.5,              # (n+1)/2 for n=60
    },
}
# one output json per deck, named "<DeckName>_strip_shuffle_results.json"
# ------------------------------------------------


def split_pile(pile_size, num_segments):
    """Break pile_size cards into num_segments non-negative chunks (some can be 0),
    using random cut points so any distribution (incl. lumpy or empty) is possible."""
    if num_segments <= 1:
        return [pile_size]
    cuts = sorted(random.uniform(0, pile_size) for _ in range(num_segments - 1))
    cuts = [0] + cuts + [pile_size]
    return [round(cuts[i + 1]) - round(cuts[i]) for i in range(num_segments)]


def strip_shuffle(deck):
    """Pull packets off the top one at a time, each new packet dropped on top of the
    growing pile in the other hand. Net effect: packet order gets reversed, but the
    card order *within* each packet is unchanged."""
    n = len(deck)
    num_packets = random.randint(MIN_PACKETS, MAX_PACKETS)
    sizes = split_pile(n, num_packets)

    packets = []
    idx = 0
    for size in sizes:
        packets.append(deck[idx:idx + size])
        idx += size

    result = []
    for packet in reversed(packets):
        result.extend(packet)
    return result


def count_rising_sequences(deck):
    # counts runs of cards still in ascending original order
    count = 1
    for i in range(1, len(deck)):
        if deck[i] < deck[i - 1]:
            count += 1
    return count


def run_experiment_for_deck(deck_name, deck_template, target_random_avg):
    all_results = {}

    for num_shuffles in SHUFFLE_COUNTS:
        rising_seq_counts = []
        for _ in range(NUM_TRIALS):
            deck = list(deck_template)
            for _ in range(num_shuffles):
                deck = strip_shuffle(deck)
            rising_seq_counts.append(count_rising_sequences(deck))

        avg = sum(rising_seq_counts) / len(rising_seq_counts)
        all_results[str(num_shuffles)] = {
            "num_shuffles": num_shuffles,
            "avg_rising_sequences": avg,
            "target_random_avg": target_random_avg,
            "raw_counts": rising_seq_counts,
        }

    settings = {
        "METHOD": "Strip Shuffle",
        "NUM_TRIALS": NUM_TRIALS,
        "MIN_PACKETS": MIN_PACKETS,
        "MAX_PACKETS": MAX_PACKETS,
        "DECK": deck_name,
        "DECK_SIZE": len(deck_template),
    }

    output_file = f"{deck_name}_strip_shuffle_results.json"
    with open(output_file, "w") as f:
        json.dump({"settings": settings, "results": all_results}, f, indent=2)

    print(f"Done. Results written to {output_file}\n")
    for k, v in all_results.items():
        print(f"{v['num_shuffles']} shuffles -> avg rising sequences: {v['avg_rising_sequences']:.2f} "
              f"(target: {target_random_avg})")
    print()


def run_experiment():
    for deck_name, deck_info in DECKS.items():
        run_experiment_for_deck(deck_name, deck_info["deck"], deck_info["target_random_avg"])


if __name__ == "__main__":
    run_experiment()