Initial commit

This commit is contained in:
2026-09-19 14:39:03 +01:00
commit 3449f141fa
21 changed files with 3349 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<LangVersion>latest</LangVersion>
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
</PropertyGroup>
<PropertyGroup>
<BepInExDir>C:\Program Files (x86)\Steam\steamapps\common\Monopoly Poker\BepInEx\core</BepInExDir>
<InteropDir>C:\Program Files (x86)\Steam\steamapps\common\Monopoly Poker\BepInEx\interop</InteropDir>
</PropertyGroup>
<!-- BepInEx 6 core + Unity interop references -->
<ItemGroup>
<Reference Include="BepInEx.Core">
<HintPath>$(BepInExDir)\BepInEx.Core.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="BepInEx.Unity.IL2CPP">
<HintPath>$(BepInExDir)\BepInEx.Unity.IL2CPP.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="0Harmony">
<HintPath>$(BepInExDir)\0Harmony.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Il2CppInterop.Runtime">
<HintPath>$(BepInExDir)\Il2CppInterop.Runtime.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>$(InteropDir)\UnityEngine.CoreModule.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>$(InteropDir)\Assembly-CSharp.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Il2Cppmscorlib">
<HintPath>$(InteropDir)\Il2Cppmscorlib.dll</HintPath>
<Private>false</Private>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>$(InteropDir)\Il2CppSystem.Core.dll</HintPath>
<Private>false</Private>
</Reference>
</ItemGroup>
</Project>
+216
View File
@@ -0,0 +1,216 @@
using BepInEx;
using BepInEx.Unity.IL2CPP;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AutoBlackjack;
/// <summary>
/// Manages JSON database for tracking blackjack statistics.
/// Tracks: bet amount, date/time, chip balance, and round outcome.
///
/// Logger doesnt work
/// </summary>
public class BlackjackStatsJsonDatabase : IDisposable
{
private readonly string _jsonPath;
private List<BlackjackRoundRecord> _records;
private bool _disposed;
public BlackjackStatsJsonDatabase(string jsonPath)
{
_jsonPath = jsonPath;
Initialize();
}
private void Initialize()
{
try
{
// Ensure directory exists
string directory = Path.GetDirectoryName(_jsonPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// Load existing records or create new list
if (File.Exists(_jsonPath))
{
string jsonContent = File.ReadAllText(_jsonPath);
_records = JsonSerializer.Deserialize<List<BlackjackRoundRecord>>(jsonContent)
?? new List<BlackjackRoundRecord>();
//Log.LogInfo($"Loaded {_records.Count} records from JSON database");
}
else
{
_records = new List<BlackjackRoundRecord>();
SaveToFile();
//Log.LogInfo($"Created new JSON database at: {_jsonPath}");
}
}
catch (Exception ex)
{
//Log.Error($"Failed to initialize JSON database: {ex}");
_records = new List<BlackjackRoundRecord>();
}
}
/// <summary>
/// Log a blackjack round with statistics.
/// </summary>
public void LogRound(long betAmount, long chipBalance, string outcome = null,
int playerTotal = 0, int dealerUpcard = 0, string action = null, long profit = 0)
{
try
{
var record = new BlackjackRoundRecord
{
Id = _records.Count > 0 ? _records[^1].Id + 1 : 1,
Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
BetAmount = betAmount,
ChipBalance = chipBalance,
Outcome = outcome ?? "",
PlayerTotal = playerTotal,
DealerUpcard = dealerUpcard,
Action = action ?? "",
Profit = profit
};
_records.Add(record);
SaveToFile();
}
catch (Exception ex)
{
//Log.Error($"Failed to log round to JSON database: {ex}");
}
}
/// <summary>
/// Save records to JSON file.
/// </summary>
private void SaveToFile()
{
try
{
var options = new JsonSerializerOptions
{
WriteIndented = true
};
string jsonContent = JsonSerializer.Serialize(_records, options);
File.WriteAllText(_jsonPath, jsonContent);
}
catch (Exception ex)
{
//Log.Error($"Failed to save JSON database: {ex}");
}
}
/// <summary>
/// Get total number of rounds played.
/// </summary>
public long GetTotalRounds()
{
return _records?.Count ?? 0;
}
/// <summary>
/// Get total profit/loss across all rounds.
/// </summary>
public long GetTotalProfit()
{
try
{
if (_records == null || _records.Count == 0)
return 0;
long total = 0;
foreach (var record in _records)
{
total += record.Profit;
}
return total;
}
catch (Exception ex)
{
//Log.Error($"Failed to get total profit: {ex}");
return 0;
}
}
/// <summary>
/// Get statistics summary.
/// </summary>
public void LogStatsSummary()
{
try
{
long totalRounds = GetTotalRounds();
long totalProfit = GetTotalProfit();
//Log.Info("=== Blackjack Stats Summary (JSON) ===");
//Log.Info($"Total Rounds: {totalRounds}");
//Log.Info($"Total Profit: {totalProfit:+#;-#;0} chips");
//if (totalRounds > 0)
//Log.Info($"Average Profit per Round: {(double)totalProfit / totalRounds:F2} chips");
//Log.Info("=======================================");
}
catch (Exception ex)
{
//Log.Error($"Failed to log stats summary: {ex}");
}
}
public void Dispose()
{
if (_disposed) return;
try
{
SaveToFile();
}
catch (Exception ex)
{
//Log.Error($"Error disposing JSON database: {ex}");
}
_disposed = true;
}
}
/// <summary>
/// Represents a single blackjack round record.
/// </summary>
public class BlackjackRoundRecord
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("date")]
public string Date { get; set; }
[JsonPropertyName("betAmount")]
public long BetAmount { get; set; }
[JsonPropertyName("chipBalance")]
public long ChipBalance { get; set; }
[JsonPropertyName("outcome")]
public string Outcome { get; set; }
[JsonPropertyName("playerTotal")]
public int PlayerTotal { get; set; }
[JsonPropertyName("dealerUpcard")]
public int DealerUpcard { get; set; }
[JsonPropertyName("action")]
public string Action { get; set; }
[JsonPropertyName("profit")]
public long Profit { get; set; }
}
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
Strategy:
Bet sizing (Hi-Lo count-based):
True Count ≤ 1: minimum bet
True Count 2: 2× minimum
True Count 3: 4× minimum
True Count 4: 6× minimum
True Count ≥ 5: maximum bet
Card Counting (Hi-Lo):
Cards 2-6: +1
Cards 7-9: 0
Cards 10, J, Q, K, A: -1
True Count = Running Count / Estimated Decks Remaining
Assumes 6-deck shoe. Running count persists across rounds.
All visible cards (all players + dealer) are scanned at decision time.
Dealer reveal is also counted (Hook 4) for next-round accuracy.
Illustrious 18 Deviations (applied when True Count thresholds met):
16 vs 10: STAND when TC ≥ 0 (basic says HIT)
16 vs 9: STAND when TC ≥ 5 (basic says HIT)
15 vs 10: STAND when TC ≥ 4 (basic says HIT)
12 vs 3: STAND when TC ≥ 2 (basic says HIT)
12 vs 2: STAND when TC ≥ 3 (basic says HIT)
10 vs 10: DOUBLE when TC ≥ 4 (basic says HIT)
10 vs A: DOUBLE when TC ≥ 4 (basic says HIT)
9 vs 2: DOUBLE when TC ≥ 1 (basic says HIT)
9 vs 7: DOUBLE when TC ≥ 3 (basic says HIT)
Soft totals: A soft total is any hand that has an Ace as one of the first two cards, the ace counts as 11 to start.
Soft 20 (A,9) always stands.
Soft 19 (A,8) doubles against dealer 6, otherwise stand.
Soft 18 (A,7) doubles against dealer 2 through 6, and hits against 9 through Ace, otherwise stand.
Soft 17 (A,6) doubles against dealer 3 through 6, otherwise hit.
Soft 16 (A,5) doubles against dealer 4 through 6, otherwise hit.
Soft 15 (A,4) doubles against dealer 4 through 6, otherwise hit.
Soft 14 (A,3) doubles against dealer 5 through 6, otherwise hit.
Soft 13 (A,2) doubles against dealer 5 through 6, otherwise hit.
Hard totals: A hard total is any hand that does not start with an ace in it, or it has been dealt an ace that can only be counted as 1 instead of 11.
17 and up always stands.
16 stands against dealer 2 through 6, otherwise hit.
15 stands against dealer 2 through 6, otherwise hit.
14 stands against dealer 2 through 6, otherwise hit.
13 stands against dealer 2 through 6, otherwise hit.
12 stands against dealer 4 through 6, otherwise hit.
11 always doubles.
10 doubles against dealer 2 through 9 otherwise hit.
9 doubles against dealer 3 through 6 otherwise hit.
8 always hits.
Splits:
Aces : always split
10 : always stand
9 : split vs dealer 2-9 except 7, else stand
8 : always split
7 : split vs dealer 2-7, else hit
6 : split vs dealer 2-6, else hit
5 : double vs dealer 2-9, else hit
4 : split vs dealer 5-6, else hit
1-3 : split vs dealer 2-7, else hit