Updated Ellie.Econ
This commit is contained in:
parent
b7f1a0a3db
commit
2a4730a1fd
25 changed files with 963 additions and 0 deletions
309
src/Ellie.Econ/Deck/Deck.cs
Normal file
309
src/Ellie.Econ/Deck/Deck.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/Ellie.Econ/Deck/NewCard.cs
Normal file
5
src/Ellie.Econ/Deck/NewCard.cs
Normal 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;
|
54
src/Ellie.Econ/Deck/NewDeck.cs
Normal file
54
src/Ellie.Econ/Deck/NewDeck.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
src/Ellie.Econ/Deck/Regular/RegularCard.cs
Normal file
4
src/Ellie.Econ/Deck/Regular/RegularCard.cs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
namespace Ellie.Econ;
|
||||||
|
|
||||||
|
public sealed record class RegularCard(RegularSuit Suit, RegularValue Value)
|
||||||
|
: NewCard<RegularSuit, RegularValue>(Suit, Value);
|
15
src/Ellie.Econ/Deck/Regular/RegularDeck.cs
Normal file
15
src/Ellie.Econ/Deck/Regular/RegularDeck.cs
Normal 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)!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
src/Ellie.Econ/Deck/Regular/RegularDeckExtensions.cs
Normal file
56
src/Ellie.Econ/Deck/Regular/RegularDeckExtensions.cs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
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()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
9
src/Ellie.Econ/Deck/Regular/RegularSuit.cs
Normal file
9
src/Ellie.Econ/Deck/Regular/RegularSuit.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ellie.Econ;
|
||||||
|
|
||||||
|
public enum RegularSuit
|
||||||
|
{
|
||||||
|
Hearts,
|
||||||
|
Diamonds,
|
||||||
|
Clubs,
|
||||||
|
Spades
|
||||||
|
}
|
18
src/Ellie.Econ/Deck/Regular/RegularValue.cs
Normal file
18
src/Ellie.Econ/Deck/Regular/RegularValue.cs
Normal 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,
|
||||||
|
}
|
13
src/Ellie.Econ/Ellie.Econ.csproj
Normal file
13
src/Ellie.Econ/Ellie.Econ.csproj
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ellie.Common\Ellie.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
7
src/Ellie.Econ/Gambling/Betdraw/BetdrawColorGuess.cs
Normal file
7
src/Ellie.Econ/Gambling/Betdraw/BetdrawColorGuess.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ellie.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public enum BetdrawColorGuess
|
||||||
|
{
|
||||||
|
Red,
|
||||||
|
Black
|
||||||
|
}
|
86
src/Ellie.Econ/Gambling/Betdraw/BetdrawGame.cs
Normal file
86
src/Ellie.Econ/Gambling/Betdraw/BetdrawGame.cs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
9
src/Ellie.Econ/Gambling/Betdraw/BetdrawResult.cs
Normal file
9
src/Ellie.Econ/Gambling/Betdraw/BetdrawResult.cs
Normal 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; }
|
||||||
|
}
|
7
src/Ellie.Econ/Gambling/Betdraw/BetdrawResultType.cs
Normal file
7
src/Ellie.Econ/Gambling/Betdraw/BetdrawResultType.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ellie.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public enum BetdrawResultType
|
||||||
|
{
|
||||||
|
Win,
|
||||||
|
Lose
|
||||||
|
}
|
7
src/Ellie.Econ/Gambling/Betdraw/BetdrawValueGuess.cs
Normal file
7
src/Ellie.Econ/Gambling/Betdraw/BetdrawValueGuess.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Ellie.Econ.Gambling.Betdraw;
|
||||||
|
|
||||||
|
public enum BetdrawValueGuess
|
||||||
|
{
|
||||||
|
High,
|
||||||
|
Low,
|
||||||
|
}
|
33
src/Ellie.Econ/Gambling/Betflip/BetflipGame.cs
Normal file
33
src/Ellie.Econ/Gambling/Betflip/BetflipGame.cs
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
8
src/Ellie.Econ/Gambling/Betflip/BetflipResult.cs
Normal file
8
src/Ellie.Econ/Gambling/Betflip/BetflipResult.cs
Normal 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; }
|
||||||
|
}
|
42
src/Ellie.Econ/Gambling/Betroll/BetrollGame.cs
Normal file
42
src/Ellie.Econ/Gambling/Betroll/BetrollGame.cs
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
9
src/Ellie.Econ/Gambling/Betroll/BetrollResult.cs
Normal file
9
src/Ellie.Econ/Gambling/Betroll/BetrollResult.cs
Normal 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; }
|
||||||
|
}
|
75
src/Ellie.Econ/Gambling/Rps/RpsGame.cs
Normal file
75
src/Ellie.Econ/Gambling/Rps/RpsGame.cs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
namespace Ellie.Econ.Gambling.Rps;
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
116
src/Ellie.Econ/Gambling/Slot/SlotGame.cs
Normal file
116
src/Ellie.Econ/Gambling/Slot/SlotGame.cs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
namespace Ellie.Econ.Gambling;
|
||||||
|
|
||||||
|
//here is a payout chart
|
||||||
|
//https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
|
||||||
|
//thanks to judge for helping me with this
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
*/
|
9
src/Ellie.Econ/Gambling/Slot/SlotResult.cs
Normal file
9
src/Ellie.Econ/Gambling/Slot/SlotResult.cs
Normal 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; }
|
||||||
|
}
|
9
src/Ellie.Econ/Gambling/Wof/LuLaResult.cs
Normal file
9
src/Ellie.Econ/Gambling/Wof/LuLaResult.cs
Normal 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; }
|
||||||
|
}
|
34
src/Ellie.Econ/Gambling/Wof/WofGame.cs
Normal file
34
src/Ellie.Econ/Gambling/Wof/WofGame.cs
Normal 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(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
1
src/Ellie.Econ/GlobalUsings.cs
Normal file
1
src/Ellie.Econ/GlobalUsings.cs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
global using Ellie.Common;
|
Loading…
Reference in a new issue