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,126 +1,126 @@
1
- require('dotenv').config()
2
- const { writeFile } = require('node:fs/promises')
3
- const path = require('node:path')
4
-
5
- const { Storage } = require('@google-cloud/storage')
6
- const storage = new Storage()
7
- // Use key file locally
8
- process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(__dirname, '../../keyfile.json')
9
-
10
- const jsoncsv = require('json-csv')
11
-
12
- // Generate csv files for a Google Cloud run job
13
- const main = async () => {
14
- if (!process.argv[2]) {
15
- throw new Error('Execution ID required')
16
- }
17
- const executionId = process.argv[2]
18
- // const numOfTasks = 2640
19
- const numOfTasks = 5280
20
-
21
- const results = []
22
-
23
- // Download all the files in batches of 100
24
- for (let i = 0; i < numOfTasks; i += 100) {
25
- const batch = []
26
-
27
- for (let j = 0; j < 100; j++) {
28
- if (i + j < numOfTasks) {
29
- batch.push(storage.bucket(process.env.SIMS_BUCKET).file(`${executionId}_${i + j}.json`).download())
30
- }
31
- }
32
-
33
- const batchResults = await Promise.all(batch)
34
- results.push(...batchResults)
35
-
36
- console.log(`Downloaded ${i + batchResults.length} files`)
37
- }
38
-
39
- const combinedResults = []
40
-
41
- for (const result of results) {
42
- combinedResults.push(JSON.parse(result.toString()))
43
- }
44
-
45
- const fields = [
46
- {
47
- name: 'id',
48
- label: 'id'
49
- },
50
- {
51
- name: 'slot1',
52
- label: 'slot1'
53
- },
54
- {
55
- name: 'slot2',
56
- label: 'slot2'
57
- },
58
- {
59
- name: 'slot3',
60
- label: 'slot3'
61
- },
62
- {
63
- name: 'slot4',
64
- label: 'slot4'
65
- },
66
- {
67
- name: 'slot5',
68
- label: 'slot5'
69
- },
70
- {
71
- name: 'wins',
72
- label: 'wins'
73
- },
74
- {
75
- name: 'draws',
76
- label: 'draws'
77
- },
78
- {
79
- name: 'losses',
80
- label: 'losses'
81
- }
82
- ]
83
-
84
- // Check which powerLevels are present
85
- const powerLevels = ['Godlike', 'Mythical', 'Legendary', 'Rare', 'Uncommon', 'Common', 'Garbage']
86
- for (const powerLevel of powerLevels) {
87
- if (combinedResults[0][`wins${powerLevel}`]) {
88
- fields.push({
89
- name: `wins${powerLevel}`,
90
- label: `wins${powerLevel}`
91
- })
92
- fields.push({
93
- name: `draws${powerLevel}`,
94
- label: `draws${powerLevel}`
95
- })
96
- fields.push({
97
- name: `losses${powerLevel}`,
98
- label: `losses${powerLevel}`
99
- })
100
- }
101
- }
102
-
103
- const csv = await jsoncsv.buffered(combinedResults, {
104
- fields
105
- })
106
-
107
- await writeFile(path.join(__dirname, `/output/${executionId}.csv`), csv)
108
- console.log(`Wrote ${executionId}.csv`)
109
- }
110
-
111
- module.exports = main
112
-
113
- if (require.main === module) {
114
- // node scripts/balancing/createCSV.js balancing-sims-fgpsl
115
-
116
- main()
117
- .then(() => {
118
- console.log('Done')
119
- process.exit(0)
120
- })
121
- .catch((err) => {
122
- console.error(err)
123
- process.exit(1)
124
- })
125
- }
126
-
1
+ require('dotenv').config()
2
+ const { writeFile } = require('node:fs/promises')
3
+ const path = require('node:path')
4
+
5
+ const { Storage } = require('@google-cloud/storage')
6
+ const storage = new Storage()
7
+ // Use key file locally
8
+ process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(__dirname, '../../keyfile.json')
9
+
10
+ const jsoncsv = require('json-csv')
11
+
12
+ // Generate csv files for a Google Cloud run job
13
+ const main = async () => {
14
+ if (!process.argv[2]) {
15
+ throw new Error('Execution ID required')
16
+ }
17
+ const executionId = process.argv[2]
18
+ // const numOfTasks = 2640
19
+ const numOfTasks = 5280
20
+
21
+ const results = []
22
+
23
+ // Download all the files in batches of 100
24
+ for (let i = 0; i < numOfTasks; i += 100) {
25
+ const batch = []
26
+
27
+ for (let j = 0; j < 100; j++) {
28
+ if (i + j < numOfTasks) {
29
+ batch.push(storage.bucket(process.env.SIMS_BUCKET).file(`${executionId}_${i + j}.json`).download())
30
+ }
31
+ }
32
+
33
+ const batchResults = await Promise.all(batch)
34
+ results.push(...batchResults)
35
+
36
+ console.log(`Downloaded ${i + batchResults.length} files`)
37
+ }
38
+
39
+ const combinedResults = []
40
+
41
+ for (const result of results) {
42
+ combinedResults.push(JSON.parse(result.toString()))
43
+ }
44
+
45
+ const fields = [
46
+ {
47
+ name: 'id',
48
+ label: 'id'
49
+ },
50
+ {
51
+ name: 'slot1',
52
+ label: 'slot1'
53
+ },
54
+ {
55
+ name: 'slot2',
56
+ label: 'slot2'
57
+ },
58
+ {
59
+ name: 'slot3',
60
+ label: 'slot3'
61
+ },
62
+ {
63
+ name: 'slot4',
64
+ label: 'slot4'
65
+ },
66
+ {
67
+ name: 'slot5',
68
+ label: 'slot5'
69
+ },
70
+ {
71
+ name: 'wins',
72
+ label: 'wins'
73
+ },
74
+ {
75
+ name: 'draws',
76
+ label: 'draws'
77
+ },
78
+ {
79
+ name: 'losses',
80
+ label: 'losses'
81
+ }
82
+ ]
83
+
84
+ // Check which powerLevels are present
85
+ const powerLevels = ['Godlike', 'Mythical', 'Legendary', 'Rare', 'Uncommon', 'Common', 'Garbage']
86
+ for (const powerLevel of powerLevels) {
87
+ if (combinedResults[0][`wins${powerLevel}`]) {
88
+ fields.push({
89
+ name: `wins${powerLevel}`,
90
+ label: `wins${powerLevel}`
91
+ })
92
+ fields.push({
93
+ name: `draws${powerLevel}`,
94
+ label: `draws${powerLevel}`
95
+ })
96
+ fields.push({
97
+ name: `losses${powerLevel}`,
98
+ label: `losses${powerLevel}`
99
+ })
100
+ }
101
+ }
102
+
103
+ const csv = await jsoncsv.buffered(combinedResults, {
104
+ fields
105
+ })
106
+
107
+ await writeFile(path.join(__dirname, `/output/${executionId}.csv`), csv)
108
+ console.log(`Wrote ${executionId}.csv`)
109
+ }
110
+
111
+ module.exports = main
112
+
113
+ if (require.main === module) {
114
+ // node scripts/balancing/createCSV.js avg-sims-dfhn2
115
+
116
+ main()
117
+ .then(() => {
118
+ console.log('Done')
119
+ process.exit(0)
120
+ })
121
+ .catch((err) => {
122
+ console.error(err)
123
+ process.exit(1)
124
+ })
125
+ }
126
+
@@ -1,260 +1,156 @@
1
- const fs = require('fs')
2
- const trainingGotchis = require('./v1.7/training_gotchis.json')
3
-
4
- const specials = [
5
- {
6
- "id": 1,
7
- "class": "Ninja",
8
- "name": "Spectral strike",
9
- "cooldown": 0,
10
- "leaderPassive": "Sharpen blades"
11
- },
12
- {
13
- "id": 2,
14
- "class": "Enlightened",
15
- "name": "Meditate",
16
- "cooldown": 0,
17
- "leaderPassive": "Cloud of Zen"
18
- },
19
- {
20
- "id": 3,
21
- "class": "Cleaver",
22
- "name": "Cleave",
23
- "cooldown": 2,
24
- "leaderPassive": "Frenzy"
25
- },
26
- {
27
- "id": 4,
28
- "class": "Tank",
29
- "name": "Taunt",
30
- "cooldown": 0,
31
- "leaderPassive": "Fortify"
32
- },
33
- {
34
- "id": 5,
35
- "class": "Cursed",
36
- "name": "Curse",
37
- "cooldown": 0,
38
- "leaderPassive": "Spread the fear"
39
- },
40
- {
41
- "id": 6,
42
- "class": "Healer",
43
- "name": "Blessing",
44
- "cooldown": 0,
45
- "leaderPassive": "Cleansing Aura"
46
- },
47
- {
48
- "id": 7,
49
- "class": "Mage",
50
- "name": "Thunder",
51
- "cooldown": 2,
52
- "leaderPassive": "Channel the coven"
53
- },
54
- {
55
- "id": 8,
56
- "class": "Troll",
57
- "name": "Devestating Smash",
58
- "cooldown": 2,
59
- "leaderPassive": "Clan momentum"
60
- }
61
- ]
62
-
63
- const classes = ['Ninja','Enlightened','Cleaver','Tank','Cursed','Healer', 'Mage', 'Troll']
64
- const powerLevels = ['Godlike', 'Mythical', 'Legendary', 'Rare', 'Uncommon', 'Common', 'Garbage']
65
-
66
- // This is copied/hacked from the mapGotchi function in the backend
67
- const fixTraits = (gotchi) => {
68
- const traitMaps = {
69
- speed: {
70
- baseFormula: 100,
71
- multiplier: 1,
72
- traitKey: 0,
73
- isNegative: false
74
- },
75
- health: {
76
- baseFormula: 'brs*0.75',
77
- multiplier: 12,
78
- traitKey: 0,
79
- isNegative: true
80
- },
81
- crit: {
82
- baseFormula: 0,
83
- multiplier: 0.5,
84
- traitKey: 1,
85
- isNegative: false
86
- },
87
- armor: {
88
- baseFormula: 0,
89
- multiplier: 2,
90
- traitKey: 1,
91
- isNegative: true
92
- },
93
- evade: {
94
- baseFormula: 0,
95
- multiplier: 0.3,
96
- traitKey: 2,
97
- isNegative: false
98
- },
99
- resist: {
100
- baseFormula: 0,
101
- multiplier: 1,
102
- traitKey: 2,
103
- isNegative: true
104
- },
105
- magic: {
106
- baseFormula: 'brs*0.25',
107
- multiplier: 5,
108
- traitKey: 3,
109
- isNegative: false
110
- },
111
- physical: {
112
- baseFormula: 'brs*0.25',
113
- multiplier: 5,
114
- traitKey: 3,
115
- isNegative: true
116
- },
117
- accuracy: {
118
- baseFormula: 50,
119
- multiplier: 0.5,
120
- traitKey: 45,
121
- isNegative: false
122
- }
123
- }
124
-
125
- const traitValue = (trait) => {
126
- return trait < 50 ? 50 - trait : trait - 50 + 1
127
- }
128
-
129
- const onchainVals = [
130
- gotchi.nrg,
131
- gotchi.agg,
132
- gotchi.spk,
133
- gotchi.brn,
134
- gotchi.eyc,
135
- gotchi.eys
136
- ]
137
- // Convert trait value to in-game value
138
- const traitValues = onchainVals.map(x => { return traitValue(x) })
139
-
140
- // Map traits
141
- for(const trait in traitMaps) {
142
- const traitMap = traitMaps[trait]
143
- const onchainVal = onchainVals[traitMap.traitKey]
144
-
145
- let base = traitMap.baseFormula
146
-
147
- // If baseFormula is a string and contains a * then it is a formula
148
- if (typeof traitMap.baseFormula === 'string' && traitMap.baseFormula.includes('*')) {
149
- const formula = traitMap.baseFormula.split('*')
150
-
151
- if (!gotchi[formula[0]]) throw new Error('Trait not found: ', formula[0])
152
-
153
- base = Math.round(Number(gotchi[formula[0]]) * Number(formula[1]))
154
- }
155
-
156
- let newTrait
157
- if (trait !== 'accuracy') {
158
- if (traitMap.isNegative) {
159
- newTrait = onchainVal < 50 ? Math.round(base + (traitValues[traitMap.traitKey] * traitMap.multiplier)) : base
160
- } else {
161
- newTrait = onchainVal < 50 ? base : Math.round(base + (traitValues[traitMap.traitKey] * traitMap.multiplier))
162
- }
163
- } else {
164
- newTrait = base + ((traitValues[4] + traitValues[5]) * traitMap.multiplier)
165
- }
166
-
167
- if (newTrait !== gotchi[trait]) gotchi[trait] = newTrait
168
- }
169
- }
170
-
171
- const addAvgGotchs = () => {
172
- const statsToOverwrite = ["speed", "health", "crit", "armor", "evade", "resist", "accuracy"]
173
-
174
- const avgGotchis = [];
175
-
176
- // Add magic gotchis
177
- ['magic', 'physical'].forEach((brainValue) => {
178
- powerLevels.forEach((powerLevel) => {
179
- classes.forEach((className) => {
180
- // Find all gotchis with the same class and power level
181
- // Names are in the format "Godlike ++++ Ninja"
182
- const gotchis = trainingGotchis.filter(gotchi => gotchi.name.includes(powerLevel) && gotchi.name.includes(className))
183
-
184
- // Copy over one of the gotchis
185
- const avgGotchi = JSON.parse(JSON.stringify(gotchis[0]))
186
-
187
- // Add an id
188
- avgGotchi.id = trainingGotchis.length + avgGotchis.length + 1
189
-
190
- // Overwrite the name
191
- avgGotchi.name = `${powerLevel} avg-${brainValue} ${className}`
192
-
193
- // Overwrite the stats
194
- statsToOverwrite.forEach((stat) => {
195
- const total = gotchis.reduce((acc, gotchi) => acc + gotchi[stat], 0)
196
- avgGotchi[stat] = Math.round(total / gotchis.length)
197
- })
198
-
199
- // Get the highest/lowest magic value
200
- const magicValues = gotchis.map(gotchi => gotchi.magic)
201
- const highestMagic = Math.max(...magicValues)
202
- const lowestMagic = Math.min(...magicValues)
203
-
204
- // Get the highest/lowest physical value
205
- const physicalValues = gotchis.map(gotchi => gotchi.physical)
206
- const highestPhysical = Math.max(...physicalValues)
207
- const lowestPhysical = Math.min(...physicalValues)
208
-
209
- // If this is a magic gotchi then set the magic value to the highest
210
- if (brainValue === 'magic') {
211
- avgGotchi.magic = highestMagic
212
- avgGotchi.physical = lowestPhysical
213
- avgGotchi.attack = 'magic'
214
- } else {
215
- avgGotchi.magic = lowestMagic
216
- avgGotchi.physical = highestPhysical
217
- avgGotchi.attack = 'physical'
218
- }
219
-
220
- avgGotchis.push(avgGotchi)
221
- })
222
- })
223
- })
224
-
225
- trainingGotchis.push(...avgGotchis)
226
- }
227
-
228
- // Sort the gotchis by id
229
- trainingGotchis.sort((a, b) => a.id - b.id)
230
-
231
- trainingGotchis.forEach(gotchi => {
232
- // Add special to gotchi
233
- const special = specials.find(special => special.id === gotchi.specialId)
234
-
235
- if (!special) throw new Error(`Special not found for gotchi: "${gotchi.name}"`)
236
-
237
- gotchi.special = special
238
-
239
- // Fix names
240
- // If name ends with a + or - then add the class name at the end
241
- if (gotchi.name.endsWith('+') || gotchi.name.endsWith('-')) {
242
- gotchi.name = `${gotchi.name} ${classes[gotchi.specialId - 1]}`
243
- }
244
-
245
- // Remove extra fields
246
- delete gotchi.tier
247
- delete gotchi.class
248
- delete gotchi.stats_brs
249
-
250
- // Make sure the gotchi has the correct traits
251
- fixTraits(gotchi)
252
- })
253
-
254
- // Add the average gotchis
255
- addAvgGotchs()
256
-
257
- // Write the updated trainingGotchis to a new file
258
- fs.writeFileSync('./training_gotchis1.json', JSON.stringify(trainingGotchis, null, '\t'))
259
-
1
+ const fs = require('fs')
2
+ const trainingGotchis = require('./v1.7.2/training_gotchis.json')
3
+ const { mapGotchi } = require('../../utils/mapGotchi')
4
+
5
+ const specials = [
6
+ {
7
+ "id": 1,
8
+ "class": "Ninja",
9
+ "name": "Spectral strike",
10
+ "cooldown": 0,
11
+ "leaderPassive": "Sharpen blades"
12
+ },
13
+ {
14
+ "id": 2,
15
+ "class": "Enlightened",
16
+ "name": "Meditate",
17
+ "cooldown": 0,
18
+ "leaderPassive": "Cloud of Zen"
19
+ },
20
+ {
21
+ "id": 3,
22
+ "class": "Cleaver",
23
+ "name": "Cleave",
24
+ "cooldown": 2,
25
+ "leaderPassive": "Frenzy"
26
+ },
27
+ {
28
+ "id": 4,
29
+ "class": "Tank",
30
+ "name": "Taunt",
31
+ "cooldown": 0,
32
+ "leaderPassive": "Fortify"
33
+ },
34
+ {
35
+ "id": 5,
36
+ "class": "Cursed",
37
+ "name": "Curse",
38
+ "cooldown": 0,
39
+ "leaderPassive": "Spread the fear"
40
+ },
41
+ {
42
+ "id": 6,
43
+ "class": "Healer",
44
+ "name": "Blessing",
45
+ "cooldown": 0,
46
+ "leaderPassive": "Cleansing Aura"
47
+ },
48
+ {
49
+ "id": 7,
50
+ "class": "Mage",
51
+ "name": "Thunder",
52
+ "cooldown": 2,
53
+ "leaderPassive": "Channel the coven"
54
+ },
55
+ {
56
+ "id": 8,
57
+ "class": "Troll",
58
+ "name": "Devestating Smash",
59
+ "cooldown": 2,
60
+ "leaderPassive": "Clan momentum"
61
+ }
62
+ ]
63
+
64
+ const classes = ['Ninja','Enlightened','Cleaver','Tank','Cursed','Healer', 'Mage', 'Troll']
65
+ const powerLevels = ['Godlike', 'Mythical', 'Legendary', 'Rare', 'Uncommon', 'Common', 'Garbage']
66
+
67
+ const addAvgGotchs = () => {
68
+ const statsToOverwrite = ["speed", "health", "crit", "armor", "evade", "resist", "accuracy"]
69
+
70
+ const avgGotchis = [];
71
+
72
+ // Add magic gotchis
73
+ ['magic', 'physical'].forEach((brainValue) => {
74
+ powerLevels.forEach((powerLevel) => {
75
+ classes.forEach((className) => {
76
+ // Find all gotchis with the same class and power level
77
+ // Names are in the format "Godlike ++++ Ninja"
78
+ const gotchis = trainingGotchis.filter(gotchi => gotchi.name.includes(powerLevel) && gotchi.name.includes(className))
79
+
80
+ // Copy over one of the gotchis
81
+ const avgGotchi = JSON.parse(JSON.stringify(gotchis[0]))
82
+
83
+ // Add an id
84
+ avgGotchi.id = trainingGotchis.length + avgGotchis.length + 1
85
+
86
+ // Overwrite the name
87
+ avgGotchi.name = `${powerLevel} avg-${brainValue} ${className}`
88
+
89
+ // Overwrite the stats
90
+ statsToOverwrite.forEach((stat) => {
91
+ const total = gotchis.reduce((acc, gotchi) => acc + gotchi[stat], 0)
92
+ avgGotchi[stat] = Math.round(total / gotchis.length)
93
+ })
94
+
95
+ // Get the highest/lowest magic value
96
+ const magicValues = gotchis.map(gotchi => gotchi.magic)
97
+ const highestMagic = Math.max(...magicValues)
98
+ const lowestMagic = Math.min(...magicValues)
99
+
100
+ // Get the highest/lowest physical value
101
+ const physicalValues = gotchis.map(gotchi => gotchi.physical)
102
+ const highestPhysical = Math.max(...physicalValues)
103
+ const lowestPhysical = Math.min(...physicalValues)
104
+
105
+ // If this is a magic gotchi then set the magic value to the highest
106
+ if (brainValue === 'magic') {
107
+ avgGotchi.magic = highestMagic
108
+ avgGotchi.physical = lowestPhysical
109
+ avgGotchi.attack = 'magic'
110
+ } else {
111
+ avgGotchi.magic = lowestMagic
112
+ avgGotchi.physical = highestPhysical
113
+ avgGotchi.attack = 'physical'
114
+ }
115
+
116
+ avgGotchis.push(avgGotchi)
117
+ })
118
+ })
119
+ })
120
+
121
+ trainingGotchis.push(...avgGotchis)
122
+ }
123
+
124
+ // Sort the gotchis by id
125
+ trainingGotchis.sort((a, b) => a.id - b.id)
126
+
127
+ trainingGotchis.forEach(gotchi => {
128
+ // Add special to gotchi
129
+ const special = specials.find(special => special.id === gotchi.specialId)
130
+
131
+ if (!special) throw new Error(`Special not found for gotchi: "${gotchi.name}"`)
132
+
133
+ gotchi.special = special
134
+
135
+ // Fix names
136
+ // If name ends with a + or - then add the class name at the end
137
+ if (gotchi.name.endsWith('+') || gotchi.name.endsWith('-')) {
138
+ gotchi.name = `${gotchi.name} ${classes[gotchi.specialId - 1]}`
139
+ }
140
+
141
+ // Remove extra fields
142
+ delete gotchi.tier
143
+ delete gotchi.class
144
+ delete gotchi.stats_brs
145
+
146
+ // Make sure the gotchi has the correct traits
147
+ mapGotchi(gotchi)
148
+ })
149
+
150
+ // Add the average gotchis
151
+ addAvgGotchs()
152
+
153
+ // Write the updated trainingGotchis to a new file
154
+ fs.writeFileSync('./training_gotchis1.json', JSON.stringify(trainingGotchis, null, '\t'))
155
+
260
156
  // node scripts/balancing/fixTrainingGotchis.js