Skip to content

Commit 9844637

Browse files
committed
Fraction
1 parent a089cec commit 9844637

File tree

4 files changed

+137
-65
lines changed

4 files changed

+137
-65
lines changed

Common/BiomeExtractionSystem.cs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ public bool GetPoolEntry(string name, out PoolEntry pool)
771771
/// <param name="name">The name of the new PoolEntry</param>
772772
/// <param name="priority">The Priority value of this pool entry. Higher priorities are evaluated first.<br/>
773773
/// Upon finding a valid PoolEntry, the priority system will finish checking other pools with the same <br/>priority, ignoring everything else.</param>
774-
/// <param name="tier">The minimum Extractor tier required to access this pool.</param>
774+
/// <param name="requirements">A list of <see cref="Predicate{T}"/> objexts that the scan must pass to access this pool.</param>
775775
/// <param name="localizationKey">The localization key that is used by this pool. </param>
776776
/// <returns><see langword="true"/> if the PoolEntry didn't exist already, <see langword="false"/> otherwise</returns>
777777
public bool AddPool(string name, int priority, Predicate<ScanData>[] requirements, string localizationKey) => AddPool(new PoolEntry(name, requirements, localizationKey), priority);
@@ -806,7 +806,7 @@ public bool GetPoolEntry(string name, out PoolEntry pool)
806806
/// <param name="name">The name of the new PoolEntry</param>
807807
/// <param name="priority">The Priority value of this pool entry. Higher priorities are evaluated first.<br/>
808808
/// Upon finding a valid PoolEntry, the priority system will finish checking other pools with the same <br/>priority, ignoring everything else.</param>
809-
/// <param name="tier">The minimum Extractor tier required to access this pool.</param>
809+
/// <param name="requirements">A list of <see cref="Predicate{T}"/> objexts that the scan must pass to access this pool.</param>
810810
/// <param name="nonBlocking">If this is true, the priority system will keep checking for pools with lower <br/>priority even if this pool is valid.</param>
811811
/// <param name="localizationKey">The localization key that is used by this pool. </param>
812812
/// <returns><see langword="true"/> if the PoolEntry didn't exist already, <see langword="false"/> otherwise</returns>
@@ -898,6 +898,14 @@ public bool FlushPoolRequirements(string poolName)
898898
/// <returns><see langword="true"/> if the method found a pool to add the item to, <see langword="false"/> otherwise</returns>
899899
public bool AddItemInPool(string poolName, short itemId, int weight) => AddItemInPool(poolName, new ItemEntry(itemId, 1), weight);
900900
/// <summary>
901+
/// Adds a new ItemEntry to a pool.<br/>The item will be registered with a count equal to 1.
902+
/// </summary>
903+
/// <param name="poolName">The name of the pool to add the item to.</param>
904+
/// <param name="itemId">The id of the item to add</param>
905+
/// <param name="weight">A fraction that corresponds to the weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
906+
/// <returns><see langword="true"/> if the method found a pool to add the item to, <see langword="false"/> otherwise</returns>
907+
public bool AddItemInPool(string poolName, short itemId, Fraction weight) => AddItemInPool(poolName, new ItemEntry(itemId, 1), weight);
908+
/// <summary>
901909
/// Adds a new ItemEntry to a pool.
902910
/// </summary>
903911
/// <param name="poolName">The name of the pool to add the item to.</param>
@@ -914,31 +922,29 @@ public bool FlushPoolRequirements(string poolName)
914922
/// <param name="itemId">The id of the item to add</param>
915923
/// <param name="count">The amount of the given item this entry will contain.<br/>
916924
/// Use <see cref="AddItemInPool(string, ItemEntry, int, int)"/> if you need to make it a range.</param>
917-
/// <param name="numerator">The numerator of the weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
918-
/// <param name="denominator">The denominator of the weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
925+
/// <param name="numerator">A fraction that corresponds to the weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
919926
/// <returns><see langword="true"/> if the method found a pool to add the item to, <see langword="false"/> otherwise</returns>
920-
public bool AddItemInPool(string poolName, short itemId, int count, int numerator, int denominator) => AddItemInPool(poolName, new ItemEntry(itemId, count), numerator, denominator);
927+
public bool AddItemInPool(string poolName, short itemId, int count, Fraction weight) => AddItemInPool(poolName, new ItemEntry(itemId, count), weight);
921928
/// <summary>
922929
/// Adds a new ItemEntry to a pool.
923930
/// </summary>
924931
/// <param name="poolName">The name of the pool to add the item to.</param>
925932
/// <param name="item">The ItemEntry to add</param>
926933
/// <param name="weight">The weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
927934
/// <returns><see langword="true"/> if the method found a pool to add the item to, <see langword="false"/> otherwise</returns>
928-
public bool AddItemInPool(string poolName, ItemEntry item, int weight) => AddItemInPool(poolName, item, weight, 1);
935+
public bool AddItemInPool(string poolName, ItemEntry item, int weight) => AddItemInPool(poolName, item, new Fraction(weight));
929936
/// <summary>
930937
/// Adds a new ItemEntry to a pool.
931938
/// </summary>
932939
/// <param name="poolName">The name of the pool to add the item to.</param>
933940
/// <param name="item">The ItemEntry to add</param>
934-
/// <param name="numerator">The numerator of the weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
935-
/// <param name="denominator">The denominator of the weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
941+
/// <param name="weight">A fraction that corresponds to the weight of probability associated to this ItemEntry.<br/>The higher the weight, the more common the item is.</param>
936942
/// <returns><see langword="true"/> if the method found a pool to add the item to, <see langword="false"/> otherwise</returns>
937-
public bool AddItemInPool(string poolName, ItemEntry item, int numerator, int denominator)
943+
public bool AddItemInPool(string poolName, ItemEntry item, Fraction weight)
938944
{
939945
PoolEntry pool = GetPoolEntry(poolName);
940946
if (pool == null) return false;
941-
_itemPools[poolName].Add(item, numerator, denominator);
947+
_itemPools[poolName].Add(item, weight.Num, weight.Den);
942948
return true;
943949
}
944950

@@ -1019,18 +1025,18 @@ internal Item RollItem(List<PoolEntry> pools)
10191025
return new(entry.Id, entry.Roll);
10201026
}
10211027

1022-
int totalWeight = 0;
1028+
Fraction totalWeight = Fraction.Zero;
10231029
foreach (PoolEntry pool in pools)
10241030
totalWeight += _itemPools[pool.Name].TotalWeight;
1025-
int roll = Main.rand.Next(totalWeight);
1026-
int current = 0;
1031+
Fraction roll = new(Main.rand.Next(totalWeight.Num), totalWeight.Den);
1032+
Fraction current = Fraction.Zero;
10271033
ItemEntry result = new(ItemID.None, 1);
10281034
foreach (PoolEntry pool in pools)
10291035
{
10301036
current += _itemPools[pool.Name].TotalWeight;
10311037
if (current > roll)
10321038
{
1033-
int weight = roll - (current - _itemPools[pool.Name].TotalWeight);
1039+
Fraction weight = roll - (current - _itemPools[pool.Name].TotalWeight);
10341040
result = _itemPools[pool.Name].FromWeight(weight);
10351041
break;
10361042
}
@@ -1049,7 +1055,7 @@ internal WeightedList<ItemEntry> JoinPools(List<PoolEntry> pools)
10491055
foreach (PoolEntry pool in pools)
10501056
{
10511057
WeightedList<ItemEntry> items = _itemPools[pool.Name];
1052-
foreach (KeyValuePair<ItemEntry, int> entry in items)
1058+
foreach (KeyValuePair<ItemEntry, Fraction> entry in items)
10531059
joinedPool.Add(entry);
10541060
}
10551061
}

Common/Collections/WeightedList.cs

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,19 @@
55

66
namespace BiomeExtractorsMod.Common.Collections
77
{
8-
public class WeightedList<T> : IDictionary<T, int>
8+
public class WeightedList<T> : IDictionary<T, Fraction>
99
{
10-
private readonly Dictionary<T, int> dictionary;
11-
public int TotalWeight { get; private set; }
12-
/// <summary>
13-
/// How much the weights have been multiplied compared to this list's original state.
14-
/// </summary>
15-
public int Scale { get; private set; }
10+
private readonly Dictionary<T, Fraction> dictionary;
11+
public Fraction TotalWeight { get; private set; }
1612
public int Count => dictionary.Count;
1713

1814
public bool IsReadOnly => false;
1915

2016
public ICollection<T> Keys => dictionary.Keys;
2117

22-
public ICollection<int> Values => dictionary.Values;
18+
public ICollection<Fraction> Values => dictionary.Values;
2319

24-
public int this[T element]
20+
public Fraction this[T element]
2521
{
2622
get => dictionary[element];
2723
set => dictionary[element] = value;
@@ -30,17 +26,13 @@ public int this[T element]
3026
public WeightedList()
3127
{
3228
dictionary = [];
33-
TotalWeight = 0;
34-
Scale = 1;
29+
TotalWeight = Fraction.Zero;
3530
}
3631

37-
public void Add(T element) => Add(element, 1);
38-
public void Add(T element, int weight) => Add(element, weight, 1);
39-
public void Add(T element, int weight_num, int weight_den)
32+
public void Add(T element) => Add(element, Fraction.One);
33+
public void Add(T element, int weight_num, int weight_den) => Add(element, new Fraction(weight_num, weight_den));
34+
public void Add(T element, Fraction weight)
4035
{
41-
if (weight_num <= 0 || weight_den <= 0) return;
42-
int scale = ApplyScale(weight_den);
43-
int weight = weight_num * scale;
4436
if (dictionary.ContainsKey(element))
4537
dictionary[element]+=weight;
4638
else
@@ -51,7 +43,7 @@ public void Add(T element, int weight_num, int weight_den)
5143
public void Clear()
5244
{
5345
dictionary.Clear();
54-
TotalWeight = 0;
46+
TotalWeight = Fraction.Zero;
5547
}
5648

5749
public bool ContainsKey(T element)
@@ -65,31 +57,31 @@ public bool Remove(T element)
6557
return true;
6658
}
6759

68-
public IEnumerator<KeyValuePair<T, int>> GetEnumerator()
60+
public IEnumerator<KeyValuePair<T, Fraction>> GetEnumerator()
6961
=> dictionary.GetEnumerator();
7062

7163
IEnumerator IEnumerable.GetEnumerator()
7264
=> dictionary.GetEnumerator();
7365

74-
public bool TryGetValue(T key, [MaybeNullWhen(false)] out int value)
66+
public bool TryGetValue(T key, [MaybeNullWhen(false)] out Fraction value)
7567
=> dictionary.TryGetValue(key, out value);
7668

7769
public T Roll()
7870
{
7971
if (dictionary.Count == 0) return default;
80-
int roll = Main.rand.Next(TotalWeight);
72+
Fraction roll = new(Main.rand.Next(TotalWeight.Num), TotalWeight.Den);
8173

82-
T ret = FromWeight (roll);
74+
T ret = FromWeight(roll);
8375

8476
if (ret.Equals(default(T))) return Roll();
8577
return ret;
8678
}
8779

88-
public T FromWeight(int weight)
80+
public T FromWeight(Fraction weight)
8981
{
9082
if (weight < 0) return default;
9183

92-
int current = 0;
84+
Fraction current = new(0);
9385
T def = default;
9486
T ret = default;
9587
foreach (T key in Keys)
@@ -101,49 +93,29 @@ public T FromWeight(int weight)
10193
return ret;
10294
}
10395

104-
public void Add(KeyValuePair<T, int> item)
96+
public void Add(KeyValuePair<T, Fraction> item)
10597
=> Add(item.Key, item.Value);
10698

107-
public bool Contains(KeyValuePair<T, int> item)
99+
public bool Contains(KeyValuePair<T, Fraction> item)
108100
{
109101
return ContainsKey(item.Key) && this[item.Key] == item.Value;
110102
}
111103

112-
public void CopyTo(KeyValuePair<T, int>[] array, int arrayIndex)
104+
public void CopyTo(KeyValuePair<T, Fraction>[] array, int arrayIndex)
113105
{
114106
int i = arrayIndex;
115-
foreach (KeyValuePair<T, int> element in dictionary)
107+
foreach (KeyValuePair<T, Fraction> element in dictionary)
116108
{
117109
array[i] = element;
118110
i++;
119111
}
120112
}
121113

122-
public bool Remove(KeyValuePair<T, int> item)
114+
public bool Remove(KeyValuePair<T, Fraction> item)
123115
{
124116
if (Contains(item))
125117
return dictionary.Remove(item.Key);
126118
return false;
127119
}
128-
129-
public int ApplyScale(int scale)
130-
{
131-
if(scale <= 0) return -1;
132-
int s = scale;
133-
int p = Scale;
134-
while (s > 0 && p > 0)
135-
{
136-
if (s > p) s %= p;
137-
else p %= s;
138-
}
139-
int mcm = Scale * scale / (p | s);
140-
int newscaling = mcm / scale;
141-
foreach(T key in dictionary.Keys)
142-
{
143-
dictionary[key] = dictionary[key] * newscaling;
144-
}
145-
TotalWeight *= newscaling;
146-
return newscaling;
147-
}
148120
}
149121
}

Common/Fraction.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+

2+
using System;
3+
4+
namespace BiomeExtractorsMod.Common
5+
{
6+
public struct Fraction(int num, int den = 1) : IComparable, IComparable<Fraction>
7+
{
8+
public static readonly Fraction Zero = new();
9+
public static readonly Fraction One = new(1);
10+
11+
public Fraction() : this(0) { }
12+
private readonly double Value => (double)this;
13+
public int Num = num;
14+
public int Den = den;
15+
public readonly Fraction ToCanonical()
16+
{
17+
int n = Num;
18+
int d = Den;
19+
while (n > 0 && d > 0)
20+
{
21+
if(n>d) n%=d;
22+
else d%=n;
23+
}
24+
int gcd = n | d;
25+
int num = Num / gcd;
26+
int den = Den / gcd;
27+
if (num < 0 && den < 0) { num = -num; den = -den; }
28+
return new Fraction(num, den);
29+
}
30+
31+
public static explicit operator float(Fraction a) => a.Den == 0 ? float.NaN : (float)a.Num / (float)a.Den;
32+
public static explicit operator double(Fraction a) => a.Den == 0 ? double.NaN : (double)a.Num / (double)a.Den;
33+
public static explicit operator decimal(Fraction a) => a.Den == 0 ? decimal.MaxValue : (decimal)a.Num / (decimal)a.Den;
34+
public static Fraction operator +(Fraction a) => a;
35+
public static Fraction operator -(Fraction a) => new(-a.Num, a.Den);
36+
public static Fraction operator +(Fraction a, Fraction b)
37+
{
38+
int da = a.Den;
39+
int db = b.Den;
40+
while (da > 0 && db > 0)
41+
{
42+
if (da > db) da %= db;
43+
else db %= da;
44+
}
45+
int gcd = da | db;
46+
int mcm = a.Den * b.Den / gcd;
47+
int num = (b.Num * a.Den + a.Num * b.Den) / gcd;
48+
return new Fraction(num, mcm);
49+
}
50+
public static Fraction operator -(Fraction a, Fraction b) => a+(-b);
51+
public static Fraction operator +(Fraction a, int b) => a + new Fraction(b);
52+
public static Fraction operator -(Fraction a, int b) => a+(-b);
53+
public static Fraction operator ++(Fraction a) => a+1;
54+
public static Fraction operator --(Fraction a) => a-1;
55+
public static Fraction operator *(Fraction a, Fraction b) => new(a.Num * b.Num, a.Den * b.Den);
56+
public static Fraction operator /(Fraction a, Fraction b) => new(a.Num * b.Den, a.Den * b.Num);
57+
public static Fraction operator *(Fraction a, int b) => a * new Fraction(b);
58+
public static Fraction operator /(Fraction a, int b) => a * new Fraction(1, b);
59+
public static Fraction operator /(int a, Fraction b) => new(a * b.Den, b.Num);
60+
public static Fraction operator ^(Fraction a, int b) => new(a.Num ^ b, a.Den ^ b);
61+
public static bool operator ==(Fraction a, Fraction b) => (double)a == (double)b;
62+
public static bool operator ==(Fraction a, int b) => (double)a == b;
63+
public static bool operator !=(Fraction a, Fraction b) => (double)a != (double)b;
64+
public static bool operator !=(Fraction a, int b) => (double)a != b;
65+
public static bool operator >(Fraction a, Fraction b) => (double)a > (double)b;
66+
public static bool operator >(Fraction a, int b) => (double)a > b;
67+
public static bool operator <(Fraction a, Fraction b) => (double)a < (double)b;
68+
public static bool operator <(Fraction a, int b) => (double)a < b;
69+
public static bool operator >=(Fraction a, Fraction b) => (double)a >= (double)b;
70+
public static bool operator >=(Fraction a, int b) => (double)a >= b;
71+
public static bool operator <=(Fraction a, Fraction b) => (double)a <= (double)b;
72+
public static bool operator <=(Fraction a, int b) => (double)a <= b;
73+
public override readonly bool Equals(object obj)
74+
{
75+
if (obj is Fraction frc) return this == frc;
76+
return Value.Equals(obj);
77+
}
78+
public override readonly int GetHashCode() => Value.GetHashCode();
79+
80+
public override readonly string ToString() => Den == 1 ? Num.ToString() : $"{Num}/{Den}";
81+
82+
public readonly int CompareTo(object? obj)
83+
{
84+
if (obj is Fraction other) return CompareTo(other);
85+
return Value.CompareTo(obj);
86+
}
87+
public readonly int CompareTo(Fraction other)
88+
{
89+
if (this > other) return 1;
90+
if (this < other) return -1;
91+
return 0;
92+
}
93+
}
94+
}

Common/UI/UISlotArea.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public void InitElements(WeightedList<ItemEntry> pool)
8181
{
8282
ItemEntry entry = entries[n];
8383
Item item = new(entry.Id);
84-
decimal chance = pool[entry] * 100 / (decimal)pool.TotalWeight;
84+
decimal chance = (decimal)(pool[entry] * 100 / pool.TotalWeight);
8585
SlotData data = new(item, entry.Min, entry.Max, chance);
8686
SlotData[n] = data;
8787
}

0 commit comments

Comments
 (0)