Last active
August 1, 2024 01:53
-
-
Save foolmoron/943426178f68726b62298e4dba2d9850 to your computer and use it in GitHub Desktop.
Card matching game monte carlo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Diagnostics; | |
const int N_SIMS = 100_000_000; | |
const int N_THREADS = 1_000; | |
const int N_PLAYERS = 6; | |
const int N_WINS = 3; | |
const int N_PAIRS = 24; | |
float[] playerAccuracy = [ 0.90f, 0.87f, 0.83f, 0.80f, 0.90f, 0.85f ]; | |
var tasks = Enumerable.Range(0, N_THREADS).Select(i => Task.Run(RunSims)).ToArray(); | |
Stopwatch sw = Stopwatch.StartNew(); | |
Task.WaitAll(tasks); | |
sw.Stop(); | |
int[] playerWinsTotal = new int[N_PLAYERS]; | |
foreach (var task in tasks) { | |
for (int p = 0; p < N_PLAYERS; p++) { | |
playerWinsTotal[p] += task.Result[p]; | |
} | |
} | |
Console.WriteLine($"{N_SIMS} runs in {sw.Elapsed.TotalSeconds:f3}s:"); | |
for (int p = 0; p < playerWinsTotal.Length; p++) { | |
Console.WriteLine($"\tPlayer {p+1} (acc {playerAccuracy[p]:f2}%): {(double)playerWinsTotal[p] / N_SIMS:p}"); | |
} | |
int[] RunSims() { | |
int[] playerWins = new int[N_PLAYERS]; | |
for (int i = 0; i < (N_SIMS / N_THREADS); i++) { | |
playerWins[RunSim(playerAccuracy)]++; | |
} | |
return playerWins; | |
} | |
static int RunSim(float[] playerAccuracy) { | |
Random r = new(); | |
int[] playerMatches = new int[N_PLAYERS]; | |
List<int> allCards = Enumerable.Range(0, N_PAIRS * 2).ToList(); | |
List<int> unknownCards = Enumerable.Range(0, N_PAIRS * 2).ToList(); | |
HashSet<int> knownHalfPairs = new(N_PAIRS * 2); | |
HashSet<(int, int)> knownFullPairs = new(N_PAIRS); | |
while (true) { | |
for (int p = 0; p < N_PLAYERS; p++) { | |
var picks = (-1, -1); | |
if (r.NextSingle() > playerAccuracy[p]) { // forgot | |
var firstIdx = r.Next(allCards.Count); | |
var secondIdx = r.Next(allCards.Count - 1); | |
if (secondIdx >= firstIdx) { | |
secondIdx++; | |
} | |
picks = (allCards[firstIdx], allCards[secondIdx]); | |
} else if (knownFullPairs.Count != 0) { | |
picks = knownFullPairs.First(); | |
} else { | |
var firstIdx = r.Next(unknownCards.Count); | |
picks.Item1 = unknownCards[firstIdx]; | |
var other = GetOther(picks.Item1); | |
if (knownHalfPairs.Contains(other)) { | |
picks.Item2 = other; | |
} else { | |
var secondIdx = r.Next(unknownCards.Count - 1); | |
if (secondIdx >= firstIdx) { | |
secondIdx++; | |
} | |
picks.Item2 = unknownCards[secondIdx]; | |
} | |
} | |
if (picks.Item1 == GetOther(picks.Item2)) { | |
playerMatches[p]++; | |
if (playerMatches[p] == N_WINS) { | |
return p; | |
} | |
allCards.Remove(picks.Item1); | |
allCards.Remove(picks.Item2); | |
unknownCards.Remove(picks.Item1); | |
unknownCards.Remove(picks.Item2); | |
knownHalfPairs.Remove(picks.Item1); | |
knownHalfPairs.Remove(picks.Item2); | |
knownFullPairs.Remove(picks); | |
knownFullPairs.Remove((picks.Item2, picks.Item1)); | |
} else { | |
unknownCards.Remove(picks.Item1); | |
unknownCards.Remove(picks.Item2); | |
var other1 = GetOther(picks.Item1); | |
if (knownHalfPairs.Remove(other1)) { | |
knownFullPairs.Add((picks.Item1, other1)); | |
} else { | |
knownHalfPairs.Add(picks.Item1); | |
} | |
var other2 = GetOther(picks.Item2); | |
if (knownHalfPairs.Remove(other2)) { | |
knownFullPairs.Add((picks.Item2, other2)); | |
} else { | |
knownHalfPairs.Add(picks.Item2); | |
} | |
} | |
} | |
} | |
} | |
static int GetOther(int index) { | |
return index % 2 == 0 ? index + 1 : index - 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment