gotchi-battler-game-logic 2.0.7 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.vscode/settings.json +4 -4
  2. package/Dockerfile +9 -9
  3. package/README.md +49 -49
  4. package/cloudbuild.yaml +27 -27
  5. package/constants/tournamentManagerAbi.json +208 -208
  6. package/game-logic/index.js +6 -6
  7. package/game-logic/v1.4/constants.js +114 -114
  8. package/game-logic/v1.4/index.js +1366 -1366
  9. package/game-logic/v1.6/constants.js +123 -123
  10. package/game-logic/v1.6/index.js +1406 -1406
  11. package/game-logic/v1.7/constants.js +142 -140
  12. package/game-logic/v1.7/helpers.js +595 -593
  13. package/game-logic/v1.7/index.js +802 -795
  14. package/index.js +12 -12
  15. package/package.json +26 -26
  16. package/schemas/team.json +349 -343
  17. package/scripts/balancing/createCSV.js +126 -126
  18. package/scripts/balancing/fixTrainingGotchis.js +155 -259
  19. package/scripts/balancing/processSims.js +229 -229
  20. package/scripts/balancing/sims.js +278 -278
  21. package/scripts/balancing/v1.7/class_combos.js +43 -43
  22. package/scripts/balancing/v1.7/setTeamPositions.js +105 -105
  23. package/scripts/balancing/v1.7/training_gotchis.json +20161 -20161
  24. package/scripts/balancing/v1.7/trait_combos.json +9 -9
  25. package/scripts/balancing/v1.7.1/class_combos.js +43 -43
  26. package/scripts/balancing/v1.7.1/setTeamPositions.js +122 -122
  27. package/scripts/balancing/v1.7.1/training_gotchis.json +22401 -22401
  28. package/scripts/balancing/v1.7.1/trait_combos.json +9 -9
  29. package/scripts/balancing/v1.7.2/class_combos.js +44 -0
  30. package/scripts/balancing/v1.7.2/setTeamPositions.js +122 -0
  31. package/scripts/balancing/v1.7.2/training_gotchis.json +22402 -0
  32. package/scripts/balancing/v1.7.2/trait_combos.json +10 -0
  33. package/scripts/data/team1.json +213 -213
  34. package/scripts/data/team2.json +200 -200
  35. package/scripts/data/tournaments.json +66 -66
  36. package/scripts/{runBattle.js → runLocalBattle.js} +18 -18
  37. package/scripts/runRealBattle.js +52 -0
  38. package/scripts/simRealBattle.js +121 -0
  39. package/scripts/validateBattle.js +74 -70
  40. package/scripts/validateTournament.js +101 -101
  41. package/utils/contracts.js +12 -12
  42. package/utils/errors.js +29 -29
  43. package/utils/mapGotchi.js +119 -0
  44. package/utils/transforms.js +89 -88
  45. package/utils/validations.js +39 -39
  46. package/debug.log +0 -2
  47. package/game-logic/v1.6/debug.log +0 -1
  48. package/game-logic/v1.7/debug.log +0 -3
  49. package/scripts/data/debug.log +0 -2
  50. package/scripts/debug.log +0 -1
@@ -1,230 +1,230 @@
1
- require('dotenv').config()
2
- const fs = require('fs')
3
- const path = require('path')
4
- const { Storage } = require('@google-cloud/storage')
5
- const storage = new Storage()
6
-
7
- if (!process.env.CLOUD_RUN_JOB) {
8
- // Use key file locally
9
- process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(__dirname, '../../keyfile.json')
10
- }
11
-
12
- const downloadAndCombineResults = async (executionId, numOfTasks) => {
13
- const results = []
14
-
15
- // Download all the files in batches of 100
16
- for (let i = 0; i < numOfTasks; i += 100) {
17
- const batch = []
18
-
19
- for (let j = 0; j < 100; j++) {
20
- if (i + j < numOfTasks) {
21
- batch.push(storage.bucket(process.env.SIMS_BUCKET).file(`${executionId}_${i + j}.json`).download())
22
- }
23
- }
24
-
25
- const batchResults = await Promise.all(batch)
26
- results.push(...batchResults)
27
-
28
- console.log(`Downloaded ${i + batchResults.length} files`)
29
- }
30
-
31
- const combinedResults = []
32
-
33
- for (const result of results) {
34
- combinedResults.push(JSON.parse(result.toString()))
35
- }
36
-
37
- return combinedResults
38
- }
39
-
40
- const classes = ['Ninja','Enlightened','Cleaver','Tank','Cursed','Healer', 'Mage', 'Troll']
41
-
42
- const calculateKpis = (data) => {
43
- // Extract wins values
44
- const wins = data.map(item => item.wins);
45
-
46
- // Calculate the mean of wins
47
- const winsMean = wins.reduce((sum, value) => sum + value, 0) / wins.length;
48
-
49
- // Calculate the wins per slot
50
- const winsPerSlot = winsMean / 5
51
-
52
- // Calculate the standard deviation of wins
53
- const winsVariance = wins.reduce((sum, value) => sum + Math.pow(value - winsMean, 2), 0) / (wins.length - 1);
54
- const winsStdev = Math.sqrt(winsVariance)
55
-
56
- // Calculate Interquartile Range (IQR)
57
- wins.sort((a, b) => a - b)
58
- const q1 = wins[Math.floor(wins.length / 4)]
59
- const q3 = wins[Math.floor(wins.length * 3 / 4)]
60
- const iqr = q3 - q1
61
-
62
- // Analyse top and bottom quartiles
63
- const topQuartile = data.filter(item => item.wins >= q3)
64
- const bottomQuartile = data.filter(item => item.wins <= q1)
65
- const topQuartileWins = topQuartile.map(item => item.wins)
66
- const bottomQuartileWins = bottomQuartile.map(item => item.wins)
67
- const topQuartileWinsMean = topQuartileWins.reduce((sum, value) => sum + value, 0) / topQuartileWins.length
68
- const bottomQuartileWinsMean = bottomQuartileWins.reduce((sum, value) => sum + value, 0) / bottomQuartileWins.length
69
-
70
- // Non leader global KPIs
71
-
72
-
73
- const results = {
74
- winsMean: winsMean.toFixed(2),
75
- winsPerSlot: winsPerSlot.toFixed(2),
76
- winsStdev: winsStdev.toFixed(2),
77
- iqr: iqr.toFixed(2),
78
- topQuartileWinsMean: topQuartileWinsMean.toFixed(2),
79
- bottomQuartileWinsMean: bottomQuartileWinsMean.toFixed(2),
80
- classes: {}
81
- }
82
-
83
-
84
- classes.forEach((className, i) => {
85
- /**
86
- * Get leader KPIs
87
- * Leaders are in slot 1
88
- */
89
-
90
- // Names are in format "G|avg|1_B"
91
- // G is power level, avg is trait combo, 1 is class number, B is formation position
92
- const leaderWins = data.filter(item => item.slot1.includes(`${i + 1}`)).map(item => item.wins)
93
-
94
- const leaderWinsMean = leaderWins.reduce((sum, value) => sum + value, 0) / leaderWins.length;
95
- const leaderWinsVariance = leaderWins.reduce((sum, value) => sum + Math.pow(value - leaderWinsMean, 2), 0) / (leaderWins.length - 1);
96
- const leaderWinsStdev = Math.sqrt(leaderWinsVariance)
97
-
98
- // Calculate Interquartile Range (IQR)
99
- leaderWins.sort((a, b) => a - b)
100
- const leaderQ1 = leaderWins[Math.floor(leaderWins.length / 4)]
101
- const leaderQ3 = leaderWins[Math.floor(leaderWins.length * 3 / 4)]
102
- const leaderIqr = leaderQ3 - leaderQ1
103
-
104
- // Analyse top and bottom quartiles
105
- const leaderTopQuartile = data.filter(item => item.slot1.includes(`${i + 1}`) && item.wins >= leaderQ3)
106
- const leaderBottomQuartile = data.filter(item => item.slot1.includes(`${i + 1}`) && item.wins <= leaderQ1)
107
- const leaderTopQuartileWins = leaderTopQuartile.map(item => item.wins)
108
- const leaderBottomQuartileWins = leaderBottomQuartile.map(item => item.wins)
109
- const leaderTopQuartileWinsMean = leaderTopQuartileWins.reduce((sum, value) => sum + value, 0) / leaderTopQuartileWins.length
110
- const leaderBottomQuartileWinsMean = leaderBottomQuartileWins.reduce((sum, value) => sum + value, 0) / leaderBottomQuartileWins.length
111
-
112
- /**
113
- * Get non-leader KPIs
114
- * Non leaders are in slots 2, 3, 4, 5
115
- */
116
-
117
- // Get the teams where the class is in the non-leader slots
118
- const nonLeaderTeams = data.filter(item => item.slot2.includes(`${i + 1}`) || item.slot3.includes(`${i + 1}`) || item.slot4.includes(`${i + 1}`) || item.slot5.includes(`${i + 1}`))
119
-
120
- const totalNonLeaderWins = nonLeaderTeams.reduce((sum, item) => {
121
- const team = [item.slot2, item.slot3, item.slot4, item.slot5]
122
- const occurrences = team.filter(slot => slot.includes(`${i + 1}`)).length
123
- return sum + item.wins / 5 * occurrences
124
- }, 0)
125
-
126
- // Get total non-leader occurrences
127
- const totalOccurrences = nonLeaderTeams.reduce((sum, item) => {
128
- const team = [item.slot2, item.slot3, item.slot4, item.slot5]
129
- return sum + team.filter(slot => slot.includes(`${i + 1}`)).length
130
- }, 0)
131
-
132
- // Calulate win per non-leader occurance
133
- const winsPerOccurance = totalNonLeaderWins / totalOccurrences
134
-
135
-
136
- results.classes[className] = {
137
- leader: {
138
- winsMean: leaderWinsMean.toFixed(2),
139
- winsMeanVsGlobal: (leaderWinsMean - winsMean).toFixed(2),
140
- winsMeanVsGlobalPercentage: ((leaderWinsMean - winsMean) / winsMean * 100).toFixed(2),
141
- winsStdev: leaderWinsStdev.toFixed(2),
142
- winsStdevVsGlobal: (leaderWinsStdev - winsStdev).toFixed(2),
143
- winsStdevVsGlobalPercentage: ((leaderWinsStdev - winsStdev) / winsStdev * 100).toFixed(2),
144
- iqr: leaderIqr.toFixed(2),
145
- iqrVsGlobal: (leaderIqr - iqr).toFixed(2),
146
- iqrVsGlobalPercentage: ((leaderIqr - iqr) / iqr * 100).toFixed(2),
147
- topQuartileWinsMean: leaderTopQuartileWinsMean.toFixed(2),
148
- topQuartileWinsMeanVsGlobal: (leaderTopQuartileWinsMean - topQuartileWinsMean).toFixed(2),
149
- topQuartileWinsMeanVsGlobalPercentage: ((leaderTopQuartileWinsMean - topQuartileWinsMean) / topQuartileWinsMean * 100).toFixed(2),
150
- bottomQuartileWinsMean: leaderBottomQuartileWinsMean.toFixed(2),
151
- bottomQuartileWinsMeanVsGlobal: (leaderBottomQuartileWinsMean - bottomQuartileWinsMean).toFixed(2),
152
- bottomQuartileWinsMeanVsGlobalPercentage: ((leaderBottomQuartileWinsMean - bottomQuartileWinsMean) / bottomQuartileWinsMean * 100).toFixed(2)
153
- },
154
- nonLeader: {
155
- winsPerOccurance: winsPerOccurance.toFixed(2),
156
- winsPerOccuranceVsGlobal: (winsPerOccurance - winsPerSlot).toFixed(2),
157
- winsPerOccuranceVsGlobalPercentage: ((winsPerOccurance - winsPerSlot) / winsPerSlot * 100).toFixed(2)
158
- }
159
- }
160
- })
161
-
162
- return results
163
- }
164
-
165
- const main = async (executionId, numOfTasks, gameLogicVersion) => {
166
- const gameLogicConstants = require(`../../game-logic/${gameLogicVersion}/constants.js`)
167
-
168
- const data = await downloadAndCombineResults(executionId, numOfTasks)
169
-
170
- const availablePowerLevels = {
171
- 'G': 'Godlike',
172
- 'M': 'Mythical',
173
- 'L': 'Legendary',
174
- 'R': 'Rare',
175
- 'U': 'Uncommon',
176
- 'C': 'Common'
177
- }
178
-
179
- // Find power levels from data
180
- // slot1 is in the format "G|avg|1_B" where the first character is the power level
181
- // Find the unique first characters in slot1
182
- const powerLevels = Array.from(new Set(data.map(item => item.slot1[0])))
183
-
184
- // Calculate KPIs for each power level
185
- const results = {
186
- metadata: {
187
- gameLogicVersion: gameLogicVersion,
188
- gameLogicConstants: gameLogicConstants,
189
- powerLevels: powerLevels.map(powerLevel => availablePowerLevels[powerLevel])
190
- },
191
- results: {}
192
- }
193
- powerLevels.forEach((powerLevel) => {
194
- const powerLevelData = data.filter(item => item.slot1[0] === powerLevel)
195
- results.results[availablePowerLevels[powerLevel]] = calculateKpis(powerLevelData)
196
- })
197
-
198
- if(process.env.CLOUD_RUN_JOB) {
199
- const bucket = storage.bucket('gotchi-battler-sims-v1-7-1')
200
-
201
- // Get number of files in the bucket
202
- const [files] = await bucket.getFiles()
203
- const numOfFiles = files.length
204
-
205
- // Write the results to a file
206
- await bucket.file(`${numOfFiles + 1}.json`).save(JSON.stringify(results, null, 2))
207
- } else {
208
- // Write to /scripts/balancing/output/<executionId>.json
209
- fs.writeFileSync(path.join(__dirname, '/output/', `${executionId}.json`), JSON.stringify(results, null, 2))
210
- }
211
- }
212
-
213
- module.exports = main
214
-
215
- // node scripts/balancing/processSims.js avg-sims-99hzc 7920 v1.7
216
- if (require.main === module) {
217
- const executionId = process.env.EXECUTION_ID || process.argv[2]
218
- const numOfTasks = parseInt(process.env.NUM_OF_TASKS) || parseInt(process.argv[3])
219
- const gameLogicVersion = process.env.GAME_LOGIC_VERSION || process.argv[4]
220
-
221
- main(executionId, numOfTasks, gameLogicVersion)
222
- .then(() => {
223
- console.log('Done')
224
- process.exit(0)
225
- })
226
- .catch((error) => {
227
- console.error(error)
228
- process.exit(1)
229
- })
1
+ require('dotenv').config()
2
+ const fs = require('fs')
3
+ const path = require('path')
4
+ const { Storage } = require('@google-cloud/storage')
5
+ const storage = new Storage()
6
+
7
+ if (!process.env.CLOUD_RUN_JOB) {
8
+ // Use key file locally
9
+ process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(__dirname, '../../keyfile.json')
10
+ }
11
+
12
+ const downloadAndCombineResults = async (executionId, numOfTasks) => {
13
+ const results = []
14
+
15
+ // Download all the files in batches of 100
16
+ for (let i = 0; i < numOfTasks; i += 100) {
17
+ const batch = []
18
+
19
+ for (let j = 0; j < 100; j++) {
20
+ if (i + j < numOfTasks) {
21
+ batch.push(storage.bucket(process.env.SIMS_BUCKET).file(`${executionId}_${i + j}.json`).download())
22
+ }
23
+ }
24
+
25
+ const batchResults = await Promise.all(batch)
26
+ results.push(...batchResults)
27
+
28
+ console.log(`Downloaded ${i + batchResults.length} files`)
29
+ }
30
+
31
+ const combinedResults = []
32
+
33
+ for (const result of results) {
34
+ combinedResults.push(JSON.parse(result.toString()))
35
+ }
36
+
37
+ return combinedResults
38
+ }
39
+
40
+ const classes = ['Ninja','Enlightened','Cleaver','Tank','Cursed','Healer', 'Mage', 'Troll']
41
+
42
+ const calculateKpis = (data) => {
43
+ // Extract wins values
44
+ const wins = data.map(item => item.wins);
45
+
46
+ // Calculate the mean of wins
47
+ const winsMean = wins.reduce((sum, value) => sum + value, 0) / wins.length;
48
+
49
+ // Calculate the wins per slot
50
+ const winsPerSlot = winsMean / 5
51
+
52
+ // Calculate the standard deviation of wins
53
+ const winsVariance = wins.reduce((sum, value) => sum + Math.pow(value - winsMean, 2), 0) / (wins.length - 1);
54
+ const winsStdev = Math.sqrt(winsVariance)
55
+
56
+ // Calculate Interquartile Range (IQR)
57
+ wins.sort((a, b) => a - b)
58
+ const q1 = wins[Math.floor(wins.length / 4)]
59
+ const q3 = wins[Math.floor(wins.length * 3 / 4)]
60
+ const iqr = q3 - q1
61
+
62
+ // Analyse top and bottom quartiles
63
+ const topQuartile = data.filter(item => item.wins >= q3)
64
+ const bottomQuartile = data.filter(item => item.wins <= q1)
65
+ const topQuartileWins = topQuartile.map(item => item.wins)
66
+ const bottomQuartileWins = bottomQuartile.map(item => item.wins)
67
+ const topQuartileWinsMean = topQuartileWins.reduce((sum, value) => sum + value, 0) / topQuartileWins.length
68
+ const bottomQuartileWinsMean = bottomQuartileWins.reduce((sum, value) => sum + value, 0) / bottomQuartileWins.length
69
+
70
+ // Non leader global KPIs
71
+
72
+
73
+ const results = {
74
+ winsMean: winsMean.toFixed(2),
75
+ winsPerSlot: winsPerSlot.toFixed(2),
76
+ winsStdev: winsStdev.toFixed(2),
77
+ iqr: iqr.toFixed(2),
78
+ topQuartileWinsMean: topQuartileWinsMean.toFixed(2),
79
+ bottomQuartileWinsMean: bottomQuartileWinsMean.toFixed(2),
80
+ classes: {}
81
+ }
82
+
83
+
84
+ classes.forEach((className, i) => {
85
+ /**
86
+ * Get leader KPIs
87
+ * Leaders are in slot 1
88
+ */
89
+
90
+ // Names are in format "G|avg|1_B"
91
+ // G is power level, avg is trait combo, 1 is class number, B is formation position
92
+ const leaderWins = data.filter(item => item.slot1.includes(`${i + 1}`)).map(item => item.wins)
93
+
94
+ const leaderWinsMean = leaderWins.reduce((sum, value) => sum + value, 0) / leaderWins.length;
95
+ const leaderWinsVariance = leaderWins.reduce((sum, value) => sum + Math.pow(value - leaderWinsMean, 2), 0) / (leaderWins.length - 1);
96
+ const leaderWinsStdev = Math.sqrt(leaderWinsVariance)
97
+
98
+ // Calculate Interquartile Range (IQR)
99
+ leaderWins.sort((a, b) => a - b)
100
+ const leaderQ1 = leaderWins[Math.floor(leaderWins.length / 4)]
101
+ const leaderQ3 = leaderWins[Math.floor(leaderWins.length * 3 / 4)]
102
+ const leaderIqr = leaderQ3 - leaderQ1
103
+
104
+ // Analyse top and bottom quartiles
105
+ const leaderTopQuartile = data.filter(item => item.slot1.includes(`${i + 1}`) && item.wins >= leaderQ3)
106
+ const leaderBottomQuartile = data.filter(item => item.slot1.includes(`${i + 1}`) && item.wins <= leaderQ1)
107
+ const leaderTopQuartileWins = leaderTopQuartile.map(item => item.wins)
108
+ const leaderBottomQuartileWins = leaderBottomQuartile.map(item => item.wins)
109
+ const leaderTopQuartileWinsMean = leaderTopQuartileWins.reduce((sum, value) => sum + value, 0) / leaderTopQuartileWins.length
110
+ const leaderBottomQuartileWinsMean = leaderBottomQuartileWins.reduce((sum, value) => sum + value, 0) / leaderBottomQuartileWins.length
111
+
112
+ /**
113
+ * Get non-leader KPIs
114
+ * Non leaders are in slots 2, 3, 4, 5
115
+ */
116
+
117
+ // Get the teams where the class is in the non-leader slots
118
+ const nonLeaderTeams = data.filter(item => item.slot2.includes(`${i + 1}`) || item.slot3.includes(`${i + 1}`) || item.slot4.includes(`${i + 1}`) || item.slot5.includes(`${i + 1}`))
119
+
120
+ const totalNonLeaderWins = nonLeaderTeams.reduce((sum, item) => {
121
+ const team = [item.slot2, item.slot3, item.slot4, item.slot5]
122
+ const occurrences = team.filter(slot => slot.includes(`${i + 1}`)).length
123
+ return sum + item.wins / 5 * occurrences
124
+ }, 0)
125
+
126
+ // Get total non-leader occurrences
127
+ const totalOccurrences = nonLeaderTeams.reduce((sum, item) => {
128
+ const team = [item.slot2, item.slot3, item.slot4, item.slot5]
129
+ return sum + team.filter(slot => slot.includes(`${i + 1}`)).length
130
+ }, 0)
131
+
132
+ // Calulate win per non-leader occurance
133
+ const winsPerOccurance = totalNonLeaderWins / totalOccurrences
134
+
135
+
136
+ results.classes[className] = {
137
+ leader: {
138
+ winsMean: leaderWinsMean.toFixed(2),
139
+ winsMeanVsGlobal: (leaderWinsMean - winsMean).toFixed(2),
140
+ winsMeanVsGlobalPercentage: ((leaderWinsMean - winsMean) / winsMean * 100).toFixed(2),
141
+ winsStdev: leaderWinsStdev.toFixed(2),
142
+ winsStdevVsGlobal: (leaderWinsStdev - winsStdev).toFixed(2),
143
+ winsStdevVsGlobalPercentage: ((leaderWinsStdev - winsStdev) / winsStdev * 100).toFixed(2),
144
+ iqr: leaderIqr.toFixed(2),
145
+ iqrVsGlobal: (leaderIqr - iqr).toFixed(2),
146
+ iqrVsGlobalPercentage: ((leaderIqr - iqr) / iqr * 100).toFixed(2),
147
+ topQuartileWinsMean: leaderTopQuartileWinsMean.toFixed(2),
148
+ topQuartileWinsMeanVsGlobal: (leaderTopQuartileWinsMean - topQuartileWinsMean).toFixed(2),
149
+ topQuartileWinsMeanVsGlobalPercentage: ((leaderTopQuartileWinsMean - topQuartileWinsMean) / topQuartileWinsMean * 100).toFixed(2),
150
+ bottomQuartileWinsMean: leaderBottomQuartileWinsMean.toFixed(2),
151
+ bottomQuartileWinsMeanVsGlobal: (leaderBottomQuartileWinsMean - bottomQuartileWinsMean).toFixed(2),
152
+ bottomQuartileWinsMeanVsGlobalPercentage: ((leaderBottomQuartileWinsMean - bottomQuartileWinsMean) / bottomQuartileWinsMean * 100).toFixed(2)
153
+ },
154
+ nonLeader: {
155
+ winsPerOccurance: winsPerOccurance.toFixed(2),
156
+ winsPerOccuranceVsGlobal: (winsPerOccurance - winsPerSlot).toFixed(2),
157
+ winsPerOccuranceVsGlobalPercentage: ((winsPerOccurance - winsPerSlot) / winsPerSlot * 100).toFixed(2)
158
+ }
159
+ }
160
+ })
161
+
162
+ return results
163
+ }
164
+
165
+ const main = async (executionId, numOfTasks, gameLogicVersion) => {
166
+ const gameLogicConstants = require(`../../game-logic/${gameLogicVersion}/constants.js`)
167
+
168
+ const data = await downloadAndCombineResults(executionId, numOfTasks)
169
+
170
+ const availablePowerLevels = {
171
+ 'G': 'Godlike',
172
+ 'M': 'Mythical',
173
+ 'L': 'Legendary',
174
+ 'R': 'Rare',
175
+ 'U': 'Uncommon',
176
+ 'C': 'Common'
177
+ }
178
+
179
+ // Find power levels from data
180
+ // slot1 is in the format "G|avg|1_B" where the first character is the power level
181
+ // Find the unique first characters in slot1
182
+ const powerLevels = Array.from(new Set(data.map(item => item.slot1[0])))
183
+
184
+ // Calculate KPIs for each power level
185
+ const results = {
186
+ metadata: {
187
+ gameLogicVersion: gameLogicVersion,
188
+ gameLogicConstants: gameLogicConstants,
189
+ powerLevels: powerLevels.map(powerLevel => availablePowerLevels[powerLevel])
190
+ },
191
+ results: {}
192
+ }
193
+ powerLevels.forEach((powerLevel) => {
194
+ const powerLevelData = data.filter(item => item.slot1[0] === powerLevel)
195
+ results.results[availablePowerLevels[powerLevel]] = calculateKpis(powerLevelData)
196
+ })
197
+
198
+ if(process.env.CLOUD_RUN_JOB) {
199
+ const bucket = storage.bucket('gotchi-battler-sims-v1-7-2')
200
+
201
+ // Get number of files in the bucket
202
+ const [files] = await bucket.getFiles()
203
+ const numOfFiles = files.length
204
+
205
+ // Write the results to a file
206
+ await bucket.file(`${numOfFiles + 1}.json`).save(JSON.stringify(results, null, 2))
207
+ } else {
208
+ // Write to /scripts/balancing/output/<executionId>.json
209
+ fs.writeFileSync(path.join(__dirname, '/output/', `${executionId}.json`), JSON.stringify(results, null, 2))
210
+ }
211
+ }
212
+
213
+ module.exports = main
214
+
215
+ // node scripts/balancing/processSims.js avg-sims-99hzc 7920 v1.7
216
+ if (require.main === module) {
217
+ const executionId = process.env.EXECUTION_ID || process.argv[2]
218
+ const numOfTasks = parseInt(process.env.NUM_OF_TASKS) || parseInt(process.argv[3])
219
+ const gameLogicVersion = process.env.GAME_LOGIC_VERSION || process.argv[4]
220
+
221
+ main(executionId, numOfTasks, gameLogicVersion)
222
+ .then(() => {
223
+ console.log('Done')
224
+ process.exit(0)
225
+ })
226
+ .catch((error) => {
227
+ console.error(error)
228
+ process.exit(1)
229
+ })
230
230
  }