-
Notifications
You must be signed in to change notification settings - Fork 0
/
scores.gno
173 lines (147 loc) · 5.55 KB
/
scores.gno
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package shiken
import (
"std"
avl "gno.land/p/demo/avl"
leaderboard "gno.land/p/dev/shiken_leaderboards"
)
// Scores-specific errors
const (
ErrNoScoreRegistered = "score for address not found"
ErrNoLeaderboardForProblemId = "leaderboard for problem id not found or not existent"
ErrLeaderboardEmptyOrNotExistent = "leaderboard empty or not existent"
ErrSomethingFailed = "something failed"
)
func AddNewScore(address std.Address, problemId, score int, tests string) {
createNewAttemptByUser(address, problemId, score)
if validateTreeShouldAddScore(score, problemId) && compareNewScoreIsBetterThanOld(address, score, problemId) {
updateLeaderboardsAndAddressRegister(address, problemId, score, tests)
}
updateScoreCounter()
}
func validateTreeShouldAddScore(score, problemId int) bool {
tree := getProblemScoreTreeById(problemId)
if tree.IsEmpty() {
return true
}
worstScore := GetSlowestScore(problemId)
return worthToBeAdded(worstScore, score, tree)
}
func worthToBeAdded(worstScore, score int, tree *leaderboard.BTree) bool {
return worstScore > score || tree.GetSize() < 10000
}
func updateLeaderboardsAndAddressRegister(address std.Address, problemId, newScore int, tests string) {
scoreId := generateScoreId(newScore)
newScoreRecord := generateRecordForLeaderboard(address, newScore, scoreId, problemId, tests)
updateLeaderboard(problemId, newScoreRecord)
registerScoreIdToAddress(address, scoreId, problemId)
}
func generateScoreId(score int) int {
return score*LeaderboardFactor + scoreCounter
}
func generateRecordForLeaderboard(address std.Address, score, scoreId, problemId int, tests string) leaderboard.Content {
scoreStruct := generateNewScore(address, score, scoreId, problemId, tests)
newContent := leaderboard.Content{Value: &scoreStruct}
newContent.AddKey(scoreStruct.Key)
return newContent
}
func updateLeaderboard(problemId int, nodeForLeaderboard leaderboard.Content) {
bTreeScores := getLeaderboard(problemId)
bTreeScores.Insert(nodeForLeaderboard)
problemIdString := getStringFromInt(problemId)
leaderboardsKeyToScore.Set(problemIdString, bTreeScores)
}
func registerScoreIdToAddress(address std.Address, newId, problemId int) {
invertedLeaderboard := getInvertedLeaderboard(problemId)
if previousScoreExists(address, invertedLeaderboard) {
removePositionFromLeaderboard(address, problemId, invertedLeaderboard)
}
updateIdForAddress(address, problemId, newId, invertedLeaderboard)
}
func previousScoreExists(address std.Address, avlTree *avl.Tree) bool {
_, ok := avlTree.Get(address.String())
return ok
}
func removePositionFromLeaderboard(address std.Address, problemId int, leaderboardTree *avl.Tree) {
leaderboardTree.Remove(address.String())
leaderboardsAddressToKey.Set(getStringFromInt(problemId), leaderboardTree)
}
func updateIdForAddress(address std.Address, problemId, newId int, avlTree *avl.Tree) {
avlTree.Set(address.String(), newId)
leaderboardsAddressToKey.Set(getStringFromInt(problemId), avlTree)
}
func generateNewScore(address std.Address, score, scoreId, problemId int, tests string) Score {
newScore := Score{}
newScore.Key = scoreId
newScore.InputInformation(address, score, problemId, tests)
return newScore
}
func updateScoreCounter() {
scoreCounter++
}
func compareNewScoreIsBetterThanOld(address std.Address, newScore, problemId int) bool {
if !addressPreviousScoreExists(address, problemId) {
return true
}
lastScore := getAddressPosition(address, problemId)
// Case score exists
btree := getLeaderboard(problemId)
// Get the score for the given address
content, err := btree.Search(lastScore)
breakIfError(err, ErrSomethingFailed)
score := content.Value.(*Score)
// Check if the new score is better than the existing score
lastScore = lastScore / LeaderboardFactor
return newScore < score.Score
}
func getLeaderboard(problemId int) *leaderboard.BTree {
bTreeInterface, ok := leaderboardsKeyToScore.Get(getStringFromInt(problemId))
breakIfNotOk(ok, ErrNoLeaderboardForProblemId)
bTree := bTreeInterface.(*leaderboard.BTree)
return bTree
}
func GetFastestScore(problemId int) int {
tree := getProblemScoreTreeById(problemId)
bContent, err := tree.GetSmallestKey()
breakIfError(err, ErrLeaderboardEmptyOrNotExistent)
bScore := bContent.Value.(*Score)
return bScore.Score
}
func GetSlowestScore(problemId int) int {
tree := getProblemScoreTreeById(problemId)
scoreInterface, err := tree.GetGreatestKey()
breakIfError(err, ErrLeaderboardEmptyOrNotExistent)
scoreStruct := scoreInterface.Value.(*Score)
return scoreStruct.Score
}
func getInvertedLeaderboard(problemId int) *avl.Tree {
avlTreeInterface, ok := leaderboardsAddressToKey.Get(getStringFromInt(problemId))
breakIfNotOk(ok, ErrNoLeaderboardForProblemId)
return avlTreeInterface.(*avl.Tree)
}
func getAddressPosition(address std.Address, problemId int) int {
avlTree := getInvertedLeaderboard(problemId)
positionInterface, ok := avlTree.Get(address.String())
if !ok {
return -1
}
position := positionInterface.(int)
return position
}
func addressPreviousScoreExists(address std.Address, problemId int) bool {
position := getAddressPosition(address, problemId)
return position != -1
}
func readScores(tree *leaderboard.BTree) string {
interfaces := leaderboard.ValuesInOrder(tree) // Output is a slice of interface{}
output := generateScoresString(interfaces)
return output
}
func generateScoresString(interfaces []interface{}) string {
var output string
for _, v := range interfaces {
score := v.(*Score)
output += score.ReadScore() + ","
}
output = output[:len(output)-1] // Remove trailing comma
return output
}