-
Notifications
You must be signed in to change notification settings - Fork 3
/
BARRewards.cs
140 lines (114 loc) · 3.69 KB
/
BARRewards.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
using System;
using System.Collections.Generic;
using System.Text;
namespace bagpipe {
class BARRewards {
private const string ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
private const uint XOR_KEY = 0x9A3652D9;
private const int EXPECTED_VALUES = 14;
private static double ApplyDiminishingReturns(int points) => Math.Pow(points, 0.75);
private static int ReverseDiminishingReturns(double bonus) => (int)Math.Round(Math.Pow(bonus, 1.0 / 0.75));
public static readonly double MAX_REWARD = ApplyDiminishingReturns(int.MaxValue);
private readonly List<int> pointValues;
public bool InUpdate { get; private set; } = false;
private readonly ProfileEntryViewModel entry;
public BARRewards(ProfileEntryViewModel entry) {
this.entry = entry;
pointValues = new List<int>();
if (entry != null) {
entry.PropertyChanged += (sender, e) => {
if (e.PropertyName == nameof(ProfileEntryViewModel.Value) && !InUpdate) {
Decode();
}
};
Decode();
}
}
private void Decode() {
pointValues.Clear();
string encoded = (string)entry?.Value;
if (encoded == null) {
return;
}
uint workingInt = 0;
int offset = 0;
foreach (char c in encoded) {
uint idx = (uint)ALPHABET.IndexOf(c);
workingInt |= idx << offset;
offset += 5;
if (offset >= 32) {
pointValues.Add((int)(workingInt ^ XOR_KEY));
offset -= 32;
workingInt = idx >> (5 - offset);
}
}
for (int i = pointValues.Count; i < EXPECTED_VALUES; i++) {
pointValues.Add(0);
}
}
private void Encode() {
if (entry == null) {
return;
}
StringBuilder encoded = new StringBuilder();
int offset = 0;
uint overflow = 0;
foreach (int pointVal in pointValues) {
uint workingVal = ((uint)Math.Max(0, pointVal)) ^ XOR_KEY;
if (offset != 0) {
uint idx = (overflow | (workingVal << (5 - offset))) & 0b11111;
encoded.Append(ALPHABET[(int)idx]);
}
while (offset <= 32 - 5) {
uint idx = (workingVal >> offset) & 0b11111;
encoded.Append(ALPHABET[(int)idx]);
offset += 5;
}
if (offset == 32) {
offset = 0;
overflow = 0;
} else {
overflow = workingVal >> offset;
offset -= 32 - 5;
}
}
if (offset != 0) {
encoded.Append(ALPHABET[(int)(overflow & 0b11111)]);
}
InUpdate = true;
entry.Value = encoded.ToString();
InUpdate = false;
}
public double? this[BARRewardStat stat] {
get {
int idx = (int)stat;
if (entry == null || idx < 0 || idx >= EXPECTED_VALUES) {
return null;
}
return ApplyDiminishingReturns(pointValues[idx]);
}
set {
int idx = (int)stat;
if (entry == null || idx < 0 || idx >= EXPECTED_VALUES || value == null) {
return;
}
pointValues[idx] = ReverseDiminishingReturns((double)value);
Encode();
}
}
public double? GetInterval(BARRewardStat stat) {
int idx = (int)stat;
if (entry == null || idx < 0 || idx >= EXPECTED_VALUES) {
return null;
}
int points = pointValues[idx];
if (points <= 0) {
return ApplyDiminishingReturns(points + 1) - ApplyDiminishingReturns(points);
} else if (points == int.MaxValue) {
return ApplyDiminishingReturns(points) - ApplyDiminishingReturns(points - 1);
} else {
return (ApplyDiminishingReturns(points + 1) - ApplyDiminishingReturns(points - 1)) / 2;
}
}
}
}