gotchi-battler-game-logic 2.0.7 → 3.0.0
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.
- package/.vscode/settings.json +4 -4
- package/Dockerfile +9 -9
- package/README.md +49 -49
- package/cloudbuild.yaml +27 -27
- package/constants/tournamentManagerAbi.json +208 -208
- package/game-logic/index.js +7 -6
- package/game-logic/v1.4/constants.js +114 -114
- package/game-logic/v1.4/index.js +1366 -1366
- package/game-logic/v1.6/constants.js +123 -123
- package/game-logic/v1.6/index.js +1406 -1406
- package/game-logic/v1.7/constants.js +142 -140
- package/game-logic/v1.7/helpers.js +610 -593
- package/game-logic/v1.7/index.js +802 -795
- package/game-logic/v1.8/constants.js +135 -0
- package/game-logic/v1.8/helpers.js +628 -0
- package/game-logic/v1.8/index.js +826 -0
- package/index.js +12 -12
- package/package.json +26 -26
- package/schemas/team.json +349 -343
- package/scripts/balancing/createCSV.js +126 -126
- package/scripts/balancing/createTrainingGotchis.js +267 -0
- package/scripts/balancing/extractOnchainTraits.js +61 -0
- package/scripts/balancing/fixTrainingGotchis.js +155 -259
- package/scripts/balancing/processSims.js +229 -229
- package/scripts/balancing/sims.js +278 -278
- package/scripts/balancing/v1.7/class_combos.js +43 -43
- package/scripts/balancing/v1.7/mapGotchi.js +119 -0
- package/scripts/balancing/v1.7/setTeamPositions.js +105 -105
- package/scripts/balancing/v1.7/training_gotchis.json +20161 -20161
- package/scripts/balancing/v1.7/training_gotchis_traits.json +520 -0
- package/scripts/balancing/v1.7/trait_combos.json +9 -9
- package/scripts/balancing/v1.7.1/class_combos.js +43 -43
- package/scripts/balancing/v1.7.1/mapGotchi.js +119 -0
- package/scripts/balancing/v1.7.1/setTeamPositions.js +122 -122
- package/scripts/balancing/v1.7.1/training_gotchis.json +22401 -22401
- package/scripts/balancing/v1.7.1/training_gotchis_traits.json +520 -0
- package/scripts/balancing/v1.7.1/trait_combos.json +9 -9
- package/scripts/balancing/v1.7.2/class_combos.js +44 -0
- package/scripts/balancing/v1.7.2/mapGotchi.js +157 -0
- package/scripts/balancing/v1.7.2/setTeamPositions.js +122 -0
- package/scripts/balancing/v1.7.2/training_gotchis.json +22402 -0
- package/scripts/balancing/v1.7.2/training_gotchis_traits.json +520 -0
- package/scripts/balancing/v1.7.2/trait_combos.json +10 -0
- package/scripts/balancing/v1.7.3/class_combos.js +44 -0
- package/scripts/balancing/v1.7.3/mapGotchi.js +164 -0
- package/scripts/balancing/v1.7.3/setTeamPositions.js +122 -0
- package/scripts/balancing/v1.7.3/training_gotchis.json +22402 -0
- package/scripts/balancing/v1.7.3/training_gotchis_traits.json +37 -0
- package/scripts/balancing/v1.7.3/trait_combos.json +10 -0
- package/scripts/data/team1.json +213 -213
- package/scripts/data/team2.json +200 -200
- package/scripts/data/tournaments.json +71 -66
- package/scripts/{runBattle.js → runLocalBattle.js} +18 -18
- package/scripts/runRealBattle.js +52 -0
- package/scripts/simRealBattle.js +121 -0
- package/scripts/validateBattle.js +83 -70
- package/scripts/validateTournament.js +101 -101
- package/utils/contracts.js +12 -12
- package/utils/errors.js +29 -29
- package/utils/mapGotchi.js +119 -0
- package/utils/transforms.js +89 -88
- package/utils/validations.js +39 -39
- package/debug.log +0 -2
- package/game-logic/v1.6/debug.log +0 -1
- package/game-logic/v1.7/debug.log +0 -3
- package/scripts/data/debug.log +0 -2
- 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
|
|
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-8jzd7
|
|
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
|
+
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
|
|
3
|
+
const specials = [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"class": "Ninja",
|
|
7
|
+
"name": "Spectral strike",
|
|
8
|
+
"cooldown": 0,
|
|
9
|
+
"leaderPassive": "Sharpen blades"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": 2,
|
|
13
|
+
"class": "Enlightened",
|
|
14
|
+
"name": "Meditate",
|
|
15
|
+
"cooldown": 0,
|
|
16
|
+
"leaderPassive": "Cloud of Zen"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": 3,
|
|
20
|
+
"class": "Cleaver",
|
|
21
|
+
"name": "Cleave",
|
|
22
|
+
"cooldown": 2,
|
|
23
|
+
"leaderPassive": "Frenzy"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": 4,
|
|
27
|
+
"class": "Tank",
|
|
28
|
+
"name": "Taunt",
|
|
29
|
+
"cooldown": 0,
|
|
30
|
+
"leaderPassive": "Fortify"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": 5,
|
|
34
|
+
"class": "Cursed",
|
|
35
|
+
"name": "Curse",
|
|
36
|
+
"cooldown": 0,
|
|
37
|
+
"leaderPassive": "Spread the fear"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": 6,
|
|
41
|
+
"class": "Healer",
|
|
42
|
+
"name": "Blessing",
|
|
43
|
+
"cooldown": 0,
|
|
44
|
+
"leaderPassive": "Cleansing Aura"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": 7,
|
|
48
|
+
"class": "Mage",
|
|
49
|
+
"name": "Thunder",
|
|
50
|
+
"cooldown": 2,
|
|
51
|
+
"leaderPassive": "Channel the coven"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"id": 8,
|
|
55
|
+
"class": "Troll",
|
|
56
|
+
"name": "Devestating Smash",
|
|
57
|
+
"cooldown": 2,
|
|
58
|
+
"leaderPassive": "Clan momentum"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const powerLevels = ['Godlike', 'Mythical', 'Legendary', 'Rare', 'Uncommon', 'Common', 'Garbage']
|
|
64
|
+
const traitCombos = [
|
|
65
|
+
'++++', '+++-', '++-+', '++--', '+-++', '+-+-', '+--+', '+---',
|
|
66
|
+
'-+++', '-++-', '-+-+', '-+--', '--++', '--+-', '---+', '----'
|
|
67
|
+
]
|
|
68
|
+
const classes = ['Ninja','Enlightened','Cleaver','Tank','Cursed','Healer', 'Mage', 'Troll']
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// Create training gotchi objects like below for all combinations of powerLevels, traitCombos and classes
|
|
72
|
+
// {
|
|
73
|
+
// "id": 1,
|
|
74
|
+
// "snapshotBlock": 0,
|
|
75
|
+
// "onchainId": 1000001,
|
|
76
|
+
// "name": "Godlike ++++ Ninja",
|
|
77
|
+
// "brs": 783,
|
|
78
|
+
// "nrg": 110,
|
|
79
|
+
// "agg": 98,
|
|
80
|
+
// "spk": 101,
|
|
81
|
+
// "brn": 107,
|
|
82
|
+
// "eyc": 94,
|
|
83
|
+
// "eys": 94,
|
|
84
|
+
// "kinship": 0,
|
|
85
|
+
// "xp": 0,
|
|
86
|
+
// "speed": 161,
|
|
87
|
+
// "health": 587,
|
|
88
|
+
// "crit": 25,
|
|
89
|
+
// "armor": 0,
|
|
90
|
+
// "evade": 16,
|
|
91
|
+
// "resist": 0,
|
|
92
|
+
// "magic": 486,
|
|
93
|
+
// "physical": 196,
|
|
94
|
+
// "accuracy": 95,
|
|
95
|
+
// "attack": "magic",
|
|
96
|
+
// "actionDelay": 0.617,
|
|
97
|
+
// "specialId": 1,
|
|
98
|
+
// "svgFront": "https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/ninja_godlike_front.png",
|
|
99
|
+
// "svgBack": "https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/ninja_godlike_back.png",
|
|
100
|
+
// "svgLeft": "https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/ninja_godlike_left.png",
|
|
101
|
+
// "svgRight": "https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/ninja_godlike_right.png",
|
|
102
|
+
// "createdAt": "2023-09-09 12:10:01",
|
|
103
|
+
// "updatedAt": "2024-06-05 16:34:00",
|
|
104
|
+
// "special": {
|
|
105
|
+
// "id": 1,
|
|
106
|
+
// "class": "Ninja",
|
|
107
|
+
// "name": "Spectral strike",
|
|
108
|
+
// "cooldown": 0,
|
|
109
|
+
// "leaderPassive": "Sharpen blades"
|
|
110
|
+
// }
|
|
111
|
+
// }
|
|
112
|
+
|
|
113
|
+
const main = (version) => {
|
|
114
|
+
const trainingGotchisTraits = require(`./v${version}/training_gotchis_traits.json`)
|
|
115
|
+
const { mapGotchi } = require(`./v${version}/mapGotchi.js`)
|
|
116
|
+
|
|
117
|
+
const trainingGotchis = []
|
|
118
|
+
|
|
119
|
+
let id = 1
|
|
120
|
+
traitCombos.forEach((t) => {
|
|
121
|
+
powerLevels.forEach((p) => {
|
|
122
|
+
for (let index = 0; index < t.length; index ++) {
|
|
123
|
+
const c = t[index]
|
|
124
|
+
|
|
125
|
+
const gotchi = {
|
|
126
|
+
id,
|
|
127
|
+
snapshotBlock: 0,
|
|
128
|
+
onchainId: 1000000 + id,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Based on the position of the trait in the traitCombos array, we can determine the class of the gotchi
|
|
132
|
+
// e.g. if the first trait is positive then add ninja, if it's negative then add enlightened
|
|
133
|
+
const className = classes[index * 2 + (c === '+' ? 0 : 1)]
|
|
134
|
+
|
|
135
|
+
// e.g Godlike ++++ Mage
|
|
136
|
+
gotchi.name = `${p} ${t} ${className}`
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* This is for versions <= 1.7.2
|
|
140
|
+
*/
|
|
141
|
+
// const trainingGotchiTraits = trainingGotchisTraits[p.toLowerCase()][className.toLowerCase()]
|
|
142
|
+
|
|
143
|
+
// if (!trainingGotchiTraits) {
|
|
144
|
+
// throw new Error(`No traits found for ${p} ${className}`)
|
|
145
|
+
// }
|
|
146
|
+
|
|
147
|
+
// gotchi.brs = trainingGotchiTraits.brs
|
|
148
|
+
// gotchi.nrg = trainingGotchiTraits.nrg[t[0] === '-' ? 0 : 1]
|
|
149
|
+
// gotchi.agg = trainingGotchiTraits.agg[t[1] === '-' ? 0 : 1]
|
|
150
|
+
// gotchi.spk = trainingGotchiTraits.spk[t[2] === '-' ? 0 : 1]
|
|
151
|
+
// gotchi.brn = trainingGotchiTraits.brn[t[3] === '-' ? 0 : 1]
|
|
152
|
+
// gotchi.eys = trainingGotchiTraits.eys[1] // These are always in index 1
|
|
153
|
+
// gotchi.eyc = trainingGotchiTraits.eyc[1] // These are always in index 1
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* This is for versions >= 1.7.3
|
|
157
|
+
*/
|
|
158
|
+
const trainingGotchiTraits = trainingGotchisTraits[p.toLowerCase()]
|
|
159
|
+
|
|
160
|
+
if (!trainingGotchiTraits) {
|
|
161
|
+
throw new Error(`No traits found for ${p}`)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
gotchi.brs = trainingGotchiTraits.brs
|
|
165
|
+
gotchi.nrg = trainingGotchiTraits.traits[t[0] === '-' ? 0 : 1]
|
|
166
|
+
gotchi.agg = trainingGotchiTraits.traits[t[1] === '-' ? 0 : 1]
|
|
167
|
+
gotchi.spk = trainingGotchiTraits.traits[t[2] === '-' ? 0 : 1]
|
|
168
|
+
gotchi.brn = trainingGotchiTraits.traits[t[3] === '-' ? 0 : 1]
|
|
169
|
+
gotchi.eys = trainingGotchiTraits.eyes
|
|
170
|
+
gotchi.eyc = trainingGotchiTraits.eyes
|
|
171
|
+
|
|
172
|
+
gotchi.kinship = 0
|
|
173
|
+
gotchi.xp = 0
|
|
174
|
+
|
|
175
|
+
// Map the in game stats to the gotchi
|
|
176
|
+
mapGotchi(gotchi)
|
|
177
|
+
gotchi.attack = t[3] === '-' ? 'physical' : 'magic'
|
|
178
|
+
gotchi.actionDelay = Math.round(((100 / gotchi.speed) + Number.EPSILON) * 1000) / 1000
|
|
179
|
+
|
|
180
|
+
gotchi.specialId = classes.indexOf(className) + 1
|
|
181
|
+
gotchi.svgFront = `https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/${className.toLowerCase()}_${p.toLowerCase()}_front.png`
|
|
182
|
+
gotchi.svgBack = `https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/${className.toLowerCase()}_${p.toLowerCase()}_back.png`
|
|
183
|
+
gotchi.svgLeft = `https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/${className.toLowerCase()}_${p.toLowerCase()}_left.png`
|
|
184
|
+
gotchi.svgRight = `https://storage.googleapis.com/gotchi-battler-qa_gotchis/training/${className.toLowerCase()}_${p.toLowerCase()}_right.png`
|
|
185
|
+
gotchi.createdAt = "2023-09-09 12:10:01"
|
|
186
|
+
gotchi.updatedAt = "2024-06-05 16:34:00"
|
|
187
|
+
gotchi.special = specials.find(s => s.id === gotchi.specialId)
|
|
188
|
+
|
|
189
|
+
trainingGotchis.push(gotchi)
|
|
190
|
+
|
|
191
|
+
id++
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const avgGotchis = createAvgGotchs(trainingGotchis)
|
|
197
|
+
|
|
198
|
+
trainingGotchis.push(...avgGotchis)
|
|
199
|
+
|
|
200
|
+
fs.writeFileSync(`./training_gotchis.json`, JSON.stringify(trainingGotchis, null, 2))
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const createAvgGotchs = (trainingGotchis) => {
|
|
204
|
+
const statsToOverwrite = ["speed", "health", "crit", "armor", "evade", "resist", "accuracy"]
|
|
205
|
+
|
|
206
|
+
const avgGotchis = [];
|
|
207
|
+
|
|
208
|
+
// Add magic gotchis
|
|
209
|
+
['magic', 'physical'].forEach((brainValue) => {
|
|
210
|
+
powerLevels.forEach((powerLevel) => {
|
|
211
|
+
classes.forEach((className) => {
|
|
212
|
+
// Find all gotchis with the same class and power level
|
|
213
|
+
// Names are in the format "Godlike ++++ Ninja"
|
|
214
|
+
const gotchis = trainingGotchis.filter(gotchi => gotchi.name.includes(powerLevel) && gotchi.name.includes(className))
|
|
215
|
+
|
|
216
|
+
// Copy over one of the gotchis
|
|
217
|
+
const avgGotchi = JSON.parse(JSON.stringify(gotchis[0]))
|
|
218
|
+
|
|
219
|
+
// Add an id
|
|
220
|
+
avgGotchi.id = trainingGotchis.length + avgGotchis.length + 1
|
|
221
|
+
|
|
222
|
+
// Overwrite the name
|
|
223
|
+
avgGotchi.name = `${powerLevel} avg-${brainValue} ${className}`
|
|
224
|
+
|
|
225
|
+
// Overwrite the stats
|
|
226
|
+
statsToOverwrite.forEach((stat) => {
|
|
227
|
+
const total = gotchis.reduce((acc, gotchi) => acc + gotchi[stat], 0)
|
|
228
|
+
|
|
229
|
+
if (['speed', 'health', 'armor', 'resist', 'magic', 'physical', 'accuracy'].includes(stat)) {
|
|
230
|
+
avgGotchi[stat] = Math.round(total / gotchis.length)
|
|
231
|
+
} else {
|
|
232
|
+
// Round to 2 decimal places
|
|
233
|
+
avgGotchi[stat] = Math.round((total / gotchis.length) * 100) / 100
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
// Get the highest/lowest magic value
|
|
238
|
+
const magicValues = gotchis.map(gotchi => gotchi.magic)
|
|
239
|
+
const highestMagic = Math.max(...magicValues)
|
|
240
|
+
const lowestMagic = Math.min(...magicValues)
|
|
241
|
+
|
|
242
|
+
// Get the highest/lowest physical value
|
|
243
|
+
const physicalValues = gotchis.map(gotchi => gotchi.physical)
|
|
244
|
+
const highestPhysical = Math.max(...physicalValues)
|
|
245
|
+
const lowestPhysical = Math.min(...physicalValues)
|
|
246
|
+
|
|
247
|
+
// If this is a magic gotchi then set the magic value to the highest
|
|
248
|
+
if (brainValue === 'magic') {
|
|
249
|
+
avgGotchi.magic = highestMagic
|
|
250
|
+
avgGotchi.physical = lowestPhysical
|
|
251
|
+
avgGotchi.attack = 'magic'
|
|
252
|
+
} else {
|
|
253
|
+
avgGotchi.magic = lowestMagic
|
|
254
|
+
avgGotchi.physical = highestPhysical
|
|
255
|
+
avgGotchi.attack = 'physical'
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
avgGotchis.push(avgGotchi)
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
return avgGotchis
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// node scripts/balancing/createTrainingGotchis.js
|
|
267
|
+
main('1.7.3')
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const trainingGotchis = require('./v1.7/training_gotchis.json')
|
|
3
|
+
|
|
4
|
+
const onchainTraitNames = [
|
|
5
|
+
'nrg',
|
|
6
|
+
'agg',
|
|
7
|
+
'spk',
|
|
8
|
+
'brn',
|
|
9
|
+
'eys',
|
|
10
|
+
'eyc'
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
const main = () => {
|
|
14
|
+
const onchainTraits = {}
|
|
15
|
+
|
|
16
|
+
// Loop through all training gotchis ids 1-448 and extract the onchain traits
|
|
17
|
+
for (let i = 1; i < 449; i++) {
|
|
18
|
+
const gotchi = trainingGotchis[i]
|
|
19
|
+
|
|
20
|
+
const powerLevel = gotchi.name.split(' ')[0].toLowerCase()
|
|
21
|
+
const gotchiClass = gotchi.name.split(' ')[2].toLowerCase()
|
|
22
|
+
|
|
23
|
+
if (!onchainTraits[powerLevel]) {
|
|
24
|
+
onchainTraits[powerLevel] = {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!onchainTraits[powerLevel][gotchiClass]) {
|
|
28
|
+
onchainTraits[powerLevel][gotchiClass] = {}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!onchainTraits[powerLevel][gotchiClass].brs) {
|
|
32
|
+
onchainTraits[powerLevel][gotchiClass].brs = gotchi.brs
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
onchainTraitNames.forEach((trait) => {
|
|
36
|
+
if (!onchainTraits[powerLevel][gotchiClass][trait]) {
|
|
37
|
+
onchainTraits[powerLevel][gotchiClass][trait] = []
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (gotchi[trait] < 50) {
|
|
41
|
+
if (!onchainTraits[powerLevel][gotchiClass][trait][0]) {
|
|
42
|
+
onchainTraits[powerLevel][gotchiClass][trait][0] = gotchi[trait]
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
if (!onchainTraits[powerLevel][gotchiClass][trait][1]) {
|
|
46
|
+
onchainTraits[powerLevel][gotchiClass][trait][1] = gotchi[trait]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fs.writeFileSync('./training_gotchis_traits.json', JSON.stringify(onchainTraits, (key, value) => {
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
return JSON.stringify(value); // Convert array to a string
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
}, 2).replace(/"\[(.*?)\]"/g, "[$1]"))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// node scripts/balancing/extractOnchainTraits.js
|
|
61
|
+
main()
|