Added Ellie.Econ

This commit is contained in:
Emotion 2023-07-12 00:17:25 +12:00
parent 7eb4be66ec
commit 48f6686146
No known key found for this signature in database
GPG key ID: D7D3E4C27A98C37B
26 changed files with 950 additions and 0 deletions

View file

@ -32,6 +32,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ellie.Marmalade", "src\Elli
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellise.Common", "src\Ellise.Common\Ellise.Common.csproj", "{227F78CC-633E-4B1F-A12B-DF8BFF30549C}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellise.Common", "src\Ellise.Common\Ellise.Common.csproj", "{227F78CC-633E-4B1F-A12B-DF8BFF30549C}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ellie.Econ", "src\Ellie.Econ\Ellie.Econ.csproj", "{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -70,6 +72,10 @@ Global
{227F78CC-633E-4B1F-A12B-DF8BFF30549C}.Debug|Any CPU.Build.0 = Debug|Any CPU {227F78CC-633E-4B1F-A12B-DF8BFF30549C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{227F78CC-633E-4B1F-A12B-DF8BFF30549C}.Release|Any CPU.ActiveCfg = Release|Any CPU {227F78CC-633E-4B1F-A12B-DF8BFF30549C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{227F78CC-633E-4B1F-A12B-DF8BFF30549C}.Release|Any CPU.Build.0 = Release|Any CPU {227F78CC-633E-4B1F-A12B-DF8BFF30549C}.Release|Any CPU.Build.0 = Release|Any CPU
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -84,6 +90,7 @@ Global
{8D996036-52D1-4B11-B7D7-6F853A907EDD} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67} {8D996036-52D1-4B11-B7D7-6F853A907EDD} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
{D6CF9ABE-205E-4699-90CA-0F18ED236490} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67} {D6CF9ABE-205E-4699-90CA-0F18ED236490} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
{227F78CC-633E-4B1F-A12B-DF8BFF30549C} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67} {227F78CC-633E-4B1F-A12B-DF8BFF30549C} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
{5E69237D-37C5-4EDF-9037-9C8FDF23FD86} = {C5E3EF2E-72CF-41BB-B0C5-EB4C08403E67}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {878761F1-C7B5-4D38-A00D-3377D703EBBA} SolutionGuid = {878761F1-C7B5-4D38-A00D-3377D703EBBA}

309
src/Ellie.Econ/Deck/Deck.cs Normal file
View file

@ -0,0 +1,309 @@
#nullable disable
namespace Ellie.Econ;
public class Deck
{
public enum CardSuit
{
Spades = 1,
Hearts = 2,
Diamonds = 3,
Clubs = 4
}
private static readonly Dictionary<int, string> _cardNames = new()
{
{ 1, "Ace" },
{ 2, "Two" },
{ 3, "Three" },
{ 4, "Four" },
{ 5, "Five" },
{ 6, "Six" },
{ 7, "Seven" },
{ 8, "Eight" },
{ 9, "Nine" },
{ 10, "Ten" },
{ 11, "Jack" },
{ 12, "Queen" },
{ 13, "King" }
};
private static Dictionary<string, Func<List<Card>, bool>> handValues;
public List<Card> CardPool { get; set; }
private readonly Random _r = new EllieRandom();
static Deck()
=> InitHandValues();
/// <summary>
/// Creates a new instance of the BlackJackGame, this allows you to create multiple games running at one time.
/// </summary>
public Deck()
=> RefillPool();
/// <summary>
/// Restart the game of blackjack. It will only refill the pool for now. Probably wont be used, unless you want to have
/// only 1 bjg running at one time,
/// then you will restart the same game every time.
/// </summary>
public void Restart()
=> RefillPool();
/// <summary>
/// Removes all cards from the pool and refills the pool with all of the possible cards. NOTE: I think this is too
/// expensive.
/// We should probably make it so it copies another premade list with all the cards, or something.
/// </summary>
protected virtual void RefillPool()
{
CardPool = new(52);
//foreach suit
for (var j = 1; j < 14; j++)
// and number
for (var i = 1; i < 5; i++)
//generate a card of that suit and number and add it to the pool
// the pool will go from ace of spades,hears,diamonds,clubs all the way to the king of spades. hearts, ...
CardPool.Add(new((CardSuit)i, j));
}
/// <summary>
/// Take a card from the pool, you either take it from the top if the deck is shuffled, or from a random place if the
/// deck is in the default order.
/// </summary>
/// <returns>A card from the pool</returns>
public Card Draw()
{
if (CardPool.Count == 0)
Restart();
//you can either do this if your deck is not shuffled
var num = _r.Next(0, CardPool.Count);
var c = CardPool[num];
CardPool.RemoveAt(num);
return c;
// if you want to shuffle when you fill, then take the first one
/*
Card c = cardPool[0];
cardPool.RemoveAt(0);
return c;
*/
}
/// <summary>
/// Shuffles the deck. Use this if you want to take cards from the top of the deck, instead of randomly. See DrawACard
/// method.
/// </summary>
private void Shuffle()
{
if (CardPool.Count <= 1)
return;
var orderedPool = CardPool.Shuffle();
CardPool ??= orderedPool.ToList();
}
public override string ToString()
=> string.Concat(CardPool.Select(c => c.ToString())) + Environment.NewLine;
private static void InitHandValues()
{
bool HasPair(List<Card> cards)
{
return cards.GroupBy(card => card.Number).Count(group => group.Count() == 2) == 1;
}
bool IsPair(List<Card> cards)
{
return cards.GroupBy(card => card.Number).Count(group => group.Count() == 3) == 0 && HasPair(cards);
}
bool IsTwoPair(List<Card> cards)
{
return cards.GroupBy(card => card.Number).Count(group => group.Count() == 2) == 2;
}
bool IsStraight(List<Card> cards)
{
if (cards.GroupBy(card => card.Number).Count() != cards.Count())
return false;
var toReturn = cards.Max(card => card.Number) - cards.Min(card => card.Number) == 4;
if (toReturn || cards.All(c => c.Number != 1))
return toReturn;
var newCards = cards.Select(c => c.Number == 1 ? new(c.Suit, 14) : c).ToArray();
return newCards.Max(card => card.Number) - newCards.Min(card => card.Number) == 4;
}
bool HasThreeOfKind(List<Card> cards)
{
return cards.GroupBy(card => card.Number).Any(group => group.Count() == 3);
}
bool IsThreeOfKind(List<Card> cards)
{
return HasThreeOfKind(cards) && !HasPair(cards);
}
bool IsFlush(List<Card> cards)
{
return cards.GroupBy(card => card.Suit).Count() == 1;
}
bool IsFourOfKind(List<Card> cards)
{
return cards.GroupBy(card => card.Number).Any(group => group.Count() == 4);
}
bool IsFullHouse(List<Card> cards)
{
return HasPair(cards) && HasThreeOfKind(cards);
}
bool HasStraightFlush(List<Card> cards)
{
return IsFlush(cards) && IsStraight(cards);
}
bool IsRoyalFlush(List<Card> cards)
{
return cards.Min(card => card.Number) == 1
&& cards.Max(card => card.Number) == 13
&& HasStraightFlush(cards);
}
bool IsStraightFlush(List<Card> cards)
{
return HasStraightFlush(cards) && !IsRoyalFlush(cards);
}
handValues = new()
{
{ "Royal Flush", IsRoyalFlush },
{ "Straight Flush", IsStraightFlush },
{ "Four Of A Kind", IsFourOfKind },
{ "Full House", IsFullHouse },
{ "Flush", IsFlush },
{ "Straight", IsStraight },
{ "Three Of A Kind", IsThreeOfKind },
{ "Two Pairs", IsTwoPair },
{ "A Pair", IsPair }
};
}
public static string GetHandValue(List<Card> cards)
{
if (handValues is null)
InitHandValues();
foreach (var kvp in handValues.Where(x => x.Value(cards)))
return kvp.Key;
return "High card " + (cards.FirstOrDefault(c => c.Number == 1)?.GetValueText() ?? cards.Max().GetValueText());
}
public class Card : IComparable
{
private static readonly IReadOnlyDictionary<CardSuit, string> _suitToSuitChar = new Dictionary<CardSuit, string>
{
{ CardSuit.Diamonds, "♦" },
{ CardSuit.Clubs, "♣" },
{ CardSuit.Spades, "♠" },
{ CardSuit.Hearts, "♥" }
};
private static readonly IReadOnlyDictionary<string, CardSuit> _suitCharToSuit = new Dictionary<string, CardSuit>
{
{ "♦", CardSuit.Diamonds },
{ "d", CardSuit.Diamonds },
{ "♣", CardSuit.Clubs },
{ "c", CardSuit.Clubs },
{ "♠", CardSuit.Spades },
{ "s", CardSuit.Spades },
{ "♥", CardSuit.Hearts },
{ "h", CardSuit.Hearts }
};
private static readonly IReadOnlyDictionary<char, int> _numberCharToNumber = new Dictionary<char, int>
{
{ 'a', 1 },
{ '2', 2 },
{ '3', 3 },
{ '4', 4 },
{ '5', 5 },
{ '6', 6 },
{ '7', 7 },
{ '8', 8 },
{ '9', 9 },
{ 't', 10 },
{ 'j', 11 },
{ 'q', 12 },
{ 'k', 13 }
};
public CardSuit Suit { get; }
public int Number { get; }
public string FullName
{
get
{
var str = string.Empty;
if (Number is <= 10 and > 1)
str += "_" + Number;
else
str += GetValueText().ToLowerInvariant();
return str + "_of_" + Suit.ToString().ToLowerInvariant();
}
}
private readonly string[] _regIndicators =
{
"🇦", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":keycap_ten:",
"🇯", "🇶", "🇰"
};
public Card(CardSuit s, int cardNum)
{
Suit = s;
Number = cardNum;
}
public string GetValueText()
=> _cardNames[Number];
public override string ToString()
=> _cardNames[Number] + " Of " + Suit;
public int CompareTo(object obj)
{
if (obj is not Card card)
return 0;
return Number - card.Number;
}
public static Card Parse(string input)
{
if (string.IsNullOrWhiteSpace(input))
throw new ArgumentNullException(nameof(input));
if (input.Length != 2
|| !_numberCharToNumber.TryGetValue(input[0], out var n)
|| !_suitCharToSuit.TryGetValue(input[1].ToString(), out var s))
throw new ArgumentException("Invalid input", nameof(input));
return new(s, n);
}
public string GetEmojiString()
{
var str = string.Empty;
str += _regIndicators[Number - 1];
str += _suitToSuitChar[Suit];
return str;
}
}
}

View file

@ -0,0 +1,5 @@
namespace Ellie.Econ;
public abstract record class NewCard<TSuit, TValue>(TSuit Suit, TValue Value)
where TSuit : struct, Enum
where TValue : struct, Enum;

View file

@ -0,0 +1,54 @@
namespace Ellie.Econ;
public abstract class NewDeck<TCard, TSuit, TValue>
where TCard : NewCard<TSuit, TValue>
where TSuit : struct, Enum
where TValue : struct, Enum
{
protected static readonly TSuit[] _suits = Enum.GetValues<TSuit>();
protected static readonly TValue[] _values = Enum.GetValues<TValue>();
public virtual int CurrentCount
=> _cards.Count;
public virtual int TotalCount { get; }
protected readonly LinkedList<TCard> _cards = new();
public NewDeck()
{
TotalCount = _suits.Length * _values.Length;
}
public virtual TCard? Draw()
{
var first = _cards.First;
if (first is not null)
{
_cards.RemoveFirst();
return first.Value;
}
return null;
}
public virtual TCard? Peek(int x = 0)
{
var card = _cards.First;
for (var i = 0; i < x; i++)
{
card = card?.Next;
}
return card?.Value;
}
public virtual void Shuffle()
{
var cards = _cards.ToList();
var newCards = cards.Shuffle();
_cards.Clear();
foreach (var card in newCards)
_cards.AddFirst(card);
}
}

View file

@ -0,0 +1,28 @@
namespace Ellie.Econ;
public class MultipleRegularDeck : NewDeck<RegularCard, RegularSuit, RegularValue>
{
private int Decks { get; }
public override int TotalCount { get; }
public MultipleRegularDeck(int decks = 1)
{
if (decks < 1)
throw new ArgumentOutOfRangeException(nameof(decks), "Has to be more than 0");
Decks = decks;
TotalCount = base.TotalCount * decks;
for (var i = 0; i < Decks; i++)
{
foreach (var suit in _suits)
{
foreach (var val in _values)
{
_cards.AddLast((RegularCard)Activator.CreateInstance(typeof(RegularCard), suit, val)!);
}
}
}
}
}

View file

@ -0,0 +1,4 @@
namespace Ellie.Econ;
public sealed record class RegularCard(RegularSuit Suit, RegularValue Value)
: NewCard<RegularSuit, RegularValue>(Suit, Value);

View file

@ -0,0 +1,15 @@
namespace Ellie.Econ;
public sealed class RegularDeck : NewDeck<RegularCard, RegularSuit, RegularValue>
{
public RegularDeck()
{
foreach (var suit in _suits)
{
foreach (var val in _values)
{
_cards.AddLast((RegularCard)Activator.CreateInstance(typeof(RegularCard), suit, val)!);
}
}
}
}

View file

@ -0,0 +1,43 @@
namespace Ellie.Econ;
public static class RegularDeckExtensions
{
public static string GetEmoji(this RegularSuit suit)
=> suit switch
{
RegularSuit.Hearts => "♥️",
RegularSuit.Spades => "♠️",
RegularSuit.Diamonds => "♦️",
_ => "♣️",
};
public static string GetEmoji(this RegularValue value)
=> value switch
{
RegularValue.Ace => "🇦",
RegularValue.Two => "2⃣",
RegularValue.Three => "3⃣",
RegularValue.Four => "4⃣",
RegularValue.Five => "5⃣",
RegularValue.Six => "6⃣",
RegularValue.Seven => "7⃣",
RegularValue.Eight => "8⃣",
RegularValue.Nine => "9⃣",
RegularValue.Ten => "🔟",
RegularValue.Jack => "🇯",
RegularValue.Queen => "🇶",
_ => "🇰",
};
public static string GetEmoji(this RegularCard card)
=> $"{card.Value.GetEmoji()} {card.Suit.GetEmoji()}";
public static string GetName(this RegularValue value)
=> value.ToString();
public static string GetName(this RegularSuit suit)
=> suit.ToString();
public static string GetName(this RegularCard card)
=> $"{card.Value.ToString()} of {card.Suit.GetName()}";
}

View file

@ -0,0 +1,9 @@
namespace Ellie.Econ;
public enum RegularSuit
{
Hearts,
Diamonds,
Clubs,
Spades
}

View file

@ -0,0 +1,18 @@
namespace Ellie.Econ;
public enum RegularValue
{
Ace = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Jack = 12,
Queen = 13,
King = 14,
}

View file

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ellise.Common\Ellise.Common.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,7 @@
namespace Ellie.Econ.Gambling.Betdraw;
public enum BetdrawColorGuess
{
Red,
Black
}

View file

@ -0,0 +1,84 @@
namespace Ellie.Econ.Gambling.Betdraw;
public sealed class BetdrawGame
{
private static readonly EllieRandom _rng = new();
private readonly RegularDeck _deck;
private const decimal SINGLE_GUESS_MULTI = 2.075M;
private const decimal DOUBLE_GUESS_MULTI = 4.15M;
public BetdrawGame()
{
_deck = new RegularDeck();
}
public BetdrawResult Draw(BetdrawValueGuess? val, BetdrawColorGuess? col, decimal amount)
{
if (val is null && col is null)
throw new ArgumentNullException(nameof(val));
var card = _deck.Peek(_rng.Next(0, 52))!;
var realVal = (int)card.Value < 7
? BetdrawValueGuess.Low
: BetdrawValueGuess.High;
var realCol = card.Suit is RegularSuit.Diamonds or RegularSuit.Hearts
? BetdrawColorGuess.Red
: BetdrawColorGuess.Black;
// if card is 7, autoloss
if (card.Value == RegularValue.Seven)
{
return new()
{
Won = 0M,
Multiplier = 0M,
ResultType = BetdrawResultType.Lose,
Card = card,
};
}
byte win = 0;
if (val is BetdrawValueGuess valGuess)
{
if (realVal != valGuess)
return new()
{
Won = 0M,
Multiplier = 0M,
ResultType = BetdrawResultType.Lose,
Card = card
};
++win;
}
if (col is BetdrawColorGuess colGuess)
{
if (realCol != colGuess)
return new()
{
Won = 0M,
Multiplier = 0M,
ResultType = BetdrawResultType.Lose,
Card = card
};
++win;
}
var multi = win == 1
? SINGLE_GUESS_MULTI
: DOUBLE_GUESS_MULTI;
return new()
{
Won = amount * multi,
Multiplier = multi,
ResultType = BetdrawResultType.Win,
Card = card
};
}
}

View file

@ -0,0 +1,9 @@
namespace Ellie.Econ.Gambling.Betdraw;
public readonly struct BetdrawResult
{
public decimal Won { get; init; }
public decimal Multiplier { get; init; }
public BetdrawResultType ResultType { get; init; }
public RegularCard Card { get; init; }
}

View file

@ -0,0 +1,7 @@
namespace Ellie.Econ.Gambling.Betdraw;
public enum BetdrawResultType
{
Win,
Lose
}

View file

@ -0,0 +1,7 @@
namespace Ellie.Econ.Gambling.Betdraw;
public enum BetdrawValueGuess
{
High,
Low,
}

View file

@ -0,0 +1,33 @@
namespace Ellie.Econ.Gambling;
public sealed class BetflipGame
{
private readonly decimal _winMulti;
private static readonly EllieRandom _rng = new EllieRandom();
public BetflipGame(decimal winMulti)
{
_winMulti = winMulti;
}
public BetflipResult Flip(byte guess, decimal amount)
{
var side = (byte)_rng.Next(0, 2);
if (side == guess)
{
return new BetflipResult()
{
Side = side,
Won = amount * _winMulti,
Multiplier = _winMulti
};
}
return new BetflipResult()
{
Side = side,
Won = 0,
Multiplier = 0,
};
}
}

View file

@ -0,0 +1,8 @@
namespace Ellie.Econ.Gambling;
public readonly struct BetflipResult
{
public decimal Won { get; init; }
public byte Side { get; init; }
public decimal Multiplier { get; init; }
}

View file

@ -0,0 +1,42 @@
namespace Ellie.Econ.Gambling;
public sealed class BetrollGame
{
private readonly (int WhenAbove, decimal MultiplyBy)[] _thresholdPairs;
private readonly EllieRandom _rng;
public BetrollGame(IReadOnlyList<(int WhenAbove, decimal MultiplyBy)> pairs)
{
_thresholdPairs = pairs.OrderByDescending(x => x.WhenAbove).ToArray();
_rng = new();
}
public BetrollResult Roll(decimal amount = 0)
{
var roll = _rng.Next(1, 101);
for (var i = 0; i < _thresholdPairs.Length; i++)
{
ref var pair = ref _thresholdPairs[i];
if (pair.WhenAbove < roll)
{
return new()
{
Multiplier = pair.MultiplyBy,
Roll = roll,
Threshold = pair.WhenAbove,
Won = amount * pair.MultiplyBy
};
}
}
return new()
{
Multiplier = 0,
Roll = roll,
Threshold = -1,
Won = 0,
};
}
}

View file

@ -0,0 +1,9 @@
namespace Ellie.Econ.Gambling;
public readonly struct BetrollResult
{
public int Roll { get; init; }
public decimal Multiplier { get; init; }
public decimal Threshold { get; init; }
public decimal Won { get; init; }
}

View file

@ -0,0 +1,73 @@
public sealed class RpsGame
{
private static readonly EllieRandom _rng = new EllieRandom();
const decimal WIN_MULTI = 1.95m;
const decimal DRAW_MULTI = 1m;
const decimal LOSE_MULTI = 0m;
public RpsGame()
{
}
public RpsResult Play(RpsPick pick, decimal amount)
{
var compPick = (RpsPick)_rng.Next(0, 3);
if (compPick == pick)
{
return new()
{
Won = amount * DRAW_MULTI,
Multiplier = DRAW_MULTI,
ComputerPick = compPick,
Result = RpsResultType.Draw,
};
}
if ((compPick == RpsPick.Paper && pick == RpsPick.Rock)
|| (compPick == RpsPick.Rock && pick == RpsPick.Scissors)
|| (compPick == RpsPick.Scissors && pick == RpsPick.Paper))
{
return new()
{
Won = amount * LOSE_MULTI,
Multiplier = LOSE_MULTI,
Result = RpsResultType.Lose,
ComputerPick = compPick,
};
}
return new()
{
Won = amount * WIN_MULTI,
Multiplier = WIN_MULTI,
Result = RpsResultType.Win,
ComputerPick = compPick,
};
}
}
public enum RpsPick : byte
{
Rock = 0,
Paper = 1,
Scissors = 2,
}
public enum RpsResultType : byte
{
Win,
Draw,
Lose
}
public readonly struct RpsResult
{
public decimal Won { get; init; }
public decimal Multiplier { get; init; }
public RpsResultType Result { get; init; }
public RpsPick ComputerPick { get; init; }
}

View file

@ -0,0 +1,113 @@
namespace Ellie.Econ.Gambling;
public class SlotGame
{
private static readonly EllieRandom _rng = new EllieRandom();
public SlotResult Spin(decimal bet)
{
var rolls = new[]
{
(byte)_rng.Next(0, 6),
(byte)_rng.Next(0, 6),
(byte)_rng.Next(0, 6)
};
ref var a = ref rolls[0];
ref var b = ref rolls[1];
ref var c = ref rolls[2];
var multi = 0;
var winType = SlotWinType.None;
if (a == b && b == c)
{
if (a == 5)
{
winType = SlotWinType.TrippleJoker;
multi = 30;
}
else
{
winType = SlotWinType.TrippleNormal;
multi = 10;
}
}
else if (a == 5 && (b == 5 || c == 5)
|| (b == 5 && c == 5))
{
winType = SlotWinType.DoubleJoker;
multi = 4;
}
else if (a == 5 || b == 5 || c == 5)
{
winType = SlotWinType.SingleJoker;
multi = 1;
}
return new()
{
Won = bet * multi,
WinType = winType,
Multiplier = multi,
Rolls = rolls,
};
}
}
public enum SlotWinType : byte
{
None,
SingleJoker,
DoubleJoker,
TrippleNormal,
TrippleJoker,
}
/*
var rolls = new[]
{
_rng.Next(default(byte), 6),
_rng.Next(default(byte), 6),
_rng.Next(default(byte), 6)
};
var multi = 0;
var winType = SlotWinType.None;
ref var a = ref rolls[0];
ref var b = ref rolls[1];
ref var c = ref rolls[2];
if (a == b && b == c)
{
if (a == 5)
{
winType = SlotWinType.TrippleJoker;
multi = 30;
}
else
{
winType = SlotWinType.TrippleNormal;
multi = 10;
}
}
else if (a == 5 && (b == 5 || c == 5)
|| (b == 5 && c == 5))
{
winType = SlotWinType.DoubleJoker;
multi = 4;
}
else if (rolls.Any(x => x == 5))
{
winType = SlotWinType.SingleJoker;
multi = 1;
}
return new()
{
Won = bet * multi,
WinType = winType,
Multiplier = multi,
Rolls = rolls,
};
}
*/

View file

@ -0,0 +1,9 @@
namespace Ellie.Econ.Gambling;
public readonly struct SlotResult
{
public decimal Multiplier { get; init; }
public byte[] Rolls { get; init; }
public decimal Won { get; init; }
public SlotWinType WinType { get; init; }
}

View file

@ -0,0 +1,9 @@
namespace Ellie.Econ.Gambling;
public readonly struct LuLaResult
{
public int Index { get; init; }
public decimal Multiplier { get; init; }
public decimal Won { get; init; }
public IReadOnlyList<decimal> Multipliers { get; init; }
}

View file

@ -0,0 +1,34 @@
namespace Ellie.Econ.Gambling;
public sealed class LulaGame
{
private static readonly IReadOnlyList<decimal> DEFAULT_MULTIPLIERS = new[] { 1.7M, 1.5M, 0.2M, 0.1M, 0.3M, 0.5M, 1.2M, 2.4M };
private readonly IReadOnlyList<decimal> _multipliers;
private static readonly EllieRandom _rng = new();
public LulaGame(IReadOnlyList<decimal> multipliers)
{
_multipliers = multipliers;
}
public LulaGame() : this(DEFAULT_MULTIPLIERS)
{
}
public LuLaResult Spin(long bet)
{
var result = _rng.Next(0, _multipliers.Count);
var multi = _multipliers[result];
var amount = bet * multi;
return new()
{
Index = result,
Multiplier = multi,
Won = amount,
Multipliers = _multipliers.ToArray(),
};
}
}

View file

@ -0,0 +1 @@
global using Ellise.Common;