gotchi-battler-game-logic 2.0.6 → 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.
- 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 +6 -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 +595 -593
- package/game-logic/v1.7/index.js +802 -795
- 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/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/setTeamPositions.js +105 -105
- package/scripts/balancing/v1.7/training_gotchis.json +20161 -20161
- 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/setTeamPositions.js +122 -122
- package/scripts/balancing/v1.7.1/training_gotchis.json +22401 -22401
- 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/setTeamPositions.js +122 -0
- package/scripts/balancing/v1.7.2/training_gotchis.json +22402 -0
- package/scripts/balancing/v1.7.2/trait_combos.json +10 -0
- package/scripts/data/team1.json +213 -213
- package/scripts/data/team2.json +200 -200
- package/scripts/data/tournaments.json +66 -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 +74 -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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const axiosRetry = require('axios-retry').default
|
|
3
|
+
axiosRetry(axios, {
|
|
4
|
+
retries: 3,
|
|
5
|
+
retryDelay: axiosRetry.exponentialDelay
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
const { webappTeamToInGameTeam } = require('../utils/transforms')
|
|
9
|
+
const { mapTeam } = require('../utils/mapGotchi')
|
|
10
|
+
const { battle } = require('..')
|
|
11
|
+
|
|
12
|
+
const main = async (battleId) => {
|
|
13
|
+
|
|
14
|
+
const battleRes = await axios.get(`https://gotchi-battler-backend-blmom6tkla-ew.a.run.app/api/v1/battles/${battleId}`)
|
|
15
|
+
|
|
16
|
+
if (!battleRes || !battleRes.data || !battleRes.data.team1 || !battleRes.data.team2) {
|
|
17
|
+
console.error('Battle not found')
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Transform the logs to in-game teams
|
|
22
|
+
const team1 = webappTeamToInGameTeam(battleRes.data.team1Snapshot || battleRes.data.team1)
|
|
23
|
+
const team2 = webappTeamToInGameTeam(battleRes.data.team2Snapshot || battleRes.data.team2)
|
|
24
|
+
|
|
25
|
+
console.log('Team 1', team1.formation.front.filter(g => g).map(g => {
|
|
26
|
+
return {
|
|
27
|
+
name: g.name,
|
|
28
|
+
health: g.health,
|
|
29
|
+
magic: g.magic,
|
|
30
|
+
physical: g.physical
|
|
31
|
+
}
|
|
32
|
+
}).concat(team1.formation.back.filter(g => g).map(g => {
|
|
33
|
+
return {
|
|
34
|
+
name: g.name,
|
|
35
|
+
health: g.health,
|
|
36
|
+
magic: g.magic,
|
|
37
|
+
physical: g.physical
|
|
38
|
+
}
|
|
39
|
+
})))
|
|
40
|
+
console.log('Team 2', team2.formation.front.filter(g => g).map(g => {
|
|
41
|
+
return {
|
|
42
|
+
name: g.name,
|
|
43
|
+
health: g.health,
|
|
44
|
+
magic: g.magic,
|
|
45
|
+
physical: g.physical
|
|
46
|
+
}
|
|
47
|
+
}).concat(team2.formation.back.filter(g => g).map(g => {
|
|
48
|
+
return {
|
|
49
|
+
name: g.name,
|
|
50
|
+
health: g.health,
|
|
51
|
+
magic: g.magic,
|
|
52
|
+
physical: g.physical
|
|
53
|
+
}
|
|
54
|
+
})))
|
|
55
|
+
|
|
56
|
+
mapTeam(team1)
|
|
57
|
+
mapTeam(team2)
|
|
58
|
+
|
|
59
|
+
console.log('Team 1', team1.formation.front.filter(g => g).map(g => {
|
|
60
|
+
return {
|
|
61
|
+
name: g.name,
|
|
62
|
+
health: g.health,
|
|
63
|
+
magic: g.magic,
|
|
64
|
+
physical: g.physical
|
|
65
|
+
}
|
|
66
|
+
}).concat(team1.formation.back.filter(g => g).map(g => {
|
|
67
|
+
return {
|
|
68
|
+
name: g.name,
|
|
69
|
+
health: g.health,
|
|
70
|
+
magic: g.magic,
|
|
71
|
+
physical: g.physical
|
|
72
|
+
}
|
|
73
|
+
})))
|
|
74
|
+
console.log('Team 2', team2.formation.front.filter(g => g).map(g => {
|
|
75
|
+
return {
|
|
76
|
+
name: g.name,
|
|
77
|
+
health: g.health,
|
|
78
|
+
magic: g.magic,
|
|
79
|
+
physical: g.physical
|
|
80
|
+
}
|
|
81
|
+
}).concat(team2.formation.back.filter(g => g).map(g => {
|
|
82
|
+
return {
|
|
83
|
+
name: g.name,
|
|
84
|
+
health: g.health,
|
|
85
|
+
magic: g.magic,
|
|
86
|
+
physical: g.physical
|
|
87
|
+
}
|
|
88
|
+
})))
|
|
89
|
+
|
|
90
|
+
// Run 1000 sims to get the average stats
|
|
91
|
+
let team1Wins = 0
|
|
92
|
+
let team2Wins = 0
|
|
93
|
+
for (let i = 0; i < 1000; i++) {
|
|
94
|
+
const logs = await battle(team1, team2, `${Math.random()}`, false)
|
|
95
|
+
if (logs.result.winner === 1) {
|
|
96
|
+
team1Wins++
|
|
97
|
+
} else {
|
|
98
|
+
team2Wins++
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(`Team 1 wins: ${team1Wins}`)
|
|
103
|
+
console.log(`Team 2 wins: ${team2Wins}`)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = main
|
|
107
|
+
|
|
108
|
+
// node scripts/simRealBattle.js 19566507-bafe-4a97-a5ee-0926ac9efcd8
|
|
109
|
+
if (require.main === module) {
|
|
110
|
+
const battleId = process.argv[2]
|
|
111
|
+
|
|
112
|
+
main(battleId)
|
|
113
|
+
.then(() => {
|
|
114
|
+
console.log('Done')
|
|
115
|
+
process.exit(0)
|
|
116
|
+
})
|
|
117
|
+
.catch((error) => {
|
|
118
|
+
console.error('Error: ', error)
|
|
119
|
+
process.exit(1)
|
|
120
|
+
})
|
|
121
|
+
}
|
|
@@ -1,70 +1,74 @@
|
|
|
1
|
-
const axios = require('axios')
|
|
2
|
-
const axiosRetry = require('axios-retry').default
|
|
3
|
-
axiosRetry(axios, {
|
|
4
|
-
retries: 3,
|
|
5
|
-
retryDelay: axiosRetry.exponentialDelay
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
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
|
-
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const axiosRetry = require('axios-retry').default
|
|
3
|
+
axiosRetry(axios, {
|
|
4
|
+
retries: 3,
|
|
5
|
+
retryDelay: axiosRetry.exponentialDelay
|
|
6
|
+
})
|
|
7
|
+
const fs = require('fs')
|
|
8
|
+
const path = require('path')
|
|
9
|
+
|
|
10
|
+
const { GameError, ValidationError } = require('../utils/errors')
|
|
11
|
+
const { logToInGameTeams } = require('../utils/transforms')
|
|
12
|
+
const { compareLogs } = require('../utils/validations')
|
|
13
|
+
const gameVersions = require('../game-logic')
|
|
14
|
+
|
|
15
|
+
const main = async (battleId, seed, gameLogicVersion) => {
|
|
16
|
+
|
|
17
|
+
if (!gameLogicVersion) gameLogicVersion = gameVersions.current
|
|
18
|
+
if (!gameVersions[gameLogicVersion]) throw new Error('Invalid game logic version')
|
|
19
|
+
|
|
20
|
+
const gameLoop = gameVersions[gameLogicVersion].gameLoop
|
|
21
|
+
|
|
22
|
+
const res = await axios.get(`https://storage.googleapis.com/gotchi-battler-live_battles/v1/${battleId}.json`)
|
|
23
|
+
|
|
24
|
+
if (!res || !res.data || !res.data.layout) {
|
|
25
|
+
console.error('Battle not found')
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Transform the logs to in-game teams
|
|
30
|
+
const teams = logToInGameTeams(res.data)
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Run the game loop
|
|
34
|
+
const logs = await gameLoop(teams[0], teams[1], seed, true)
|
|
35
|
+
|
|
36
|
+
fs.writeFileSync(path.join(__dirname, 'output', `${battleId}_${Date.now()}.json`), JSON.stringify(logs, null, '\t'))
|
|
37
|
+
|
|
38
|
+
// Validate the results
|
|
39
|
+
compareLogs(res.data, logs)
|
|
40
|
+
|
|
41
|
+
return logs
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof GameError) {
|
|
44
|
+
console.error('Errored game logs: ', error.logs)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
throw error
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = main
|
|
52
|
+
|
|
53
|
+
// node scripts/validateBattle.js 19566507-bafe-4a97-a5ee-0926ac9efcd8 86621513544048786694066938490119673052266855523096067323796322823978866460212
|
|
54
|
+
if (require.main === module) {
|
|
55
|
+
const battleId = process.argv[2]
|
|
56
|
+
const seed = process.argv[3]
|
|
57
|
+
const gameLogicVersion = process.argv[4]
|
|
58
|
+
|
|
59
|
+
main(battleId, seed, gameLogicVersion)
|
|
60
|
+
.then(() => {
|
|
61
|
+
console.log('Results from game logic match the logs ✅')
|
|
62
|
+
console.log('Done')
|
|
63
|
+
process.exit(0)
|
|
64
|
+
})
|
|
65
|
+
.catch((error) => {
|
|
66
|
+
if (error instanceof ValidationError) {
|
|
67
|
+
console.error('Results from game logic do not match the logs ❌')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.error('Error: ', error.message)
|
|
71
|
+
|
|
72
|
+
process.exit(1)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const axios = require('axios')
|
|
4
|
-
const { getTournamentContract } = require('../utils/contracts')
|
|
5
|
-
const { ValidationError } = require('../utils/errors')
|
|
6
|
-
const tournaments = require('./data/tournaments.json')
|
|
7
|
-
const validateBattle = require('./validateBattle')
|
|
8
|
-
|
|
9
|
-
const main = async (tournamentId) => {
|
|
10
|
-
|
|
11
|
-
console.log(`Getting tournament config for id ${tournamentId}...`)
|
|
12
|
-
|
|
13
|
-
const tournamentConfig = tournaments.find(t => `${t.id}` === `${tournamentId}`)
|
|
14
|
-
|
|
15
|
-
if (!tournamentConfig || !tournamentConfig.gameLogicVersion) {
|
|
16
|
-
console.error('Tournament config not found with id:', tournamentId)
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
console.log(`Found, using game logic version "${tournamentConfig.gameLogicVersion}" ✅`)
|
|
21
|
-
console.log(`Getting tournament data for id: ${tournamentId}...`)
|
|
22
|
-
|
|
23
|
-
const tournament = await axios.get(`https://gotchi-battler-backend-blmom6tkla-ew.a.run.app/api/v1/tournaments/${tournamentId}`)
|
|
24
|
-
|
|
25
|
-
if (!tournament || !tournament.data || !tournament.data.address) {
|
|
26
|
-
console.error('Tournament not found with id:', tournamentId)
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const onchainAddress = tournament.data.address
|
|
31
|
-
|
|
32
|
-
const brackets = await axios.get(`https://gotchi-battler-backend-blmom6tkla-ew.a.run.app/api/v1/tournaments/${tournamentId}/brackets`)
|
|
33
|
-
|
|
34
|
-
if (!brackets || !brackets.data || !brackets.data.length) {
|
|
35
|
-
console.error('Brackets not found')
|
|
36
|
-
return
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
console.log(`Tournament data for id ${tournamentId} found ✅`)
|
|
40
|
-
console.log(`Validating "${tournament.data.name}"...`)
|
|
41
|
-
console.log('(This process can take up to 20 minutes for large tournaments)')
|
|
42
|
-
for (const bracket of brackets.data) {
|
|
43
|
-
console.log(`Validating "${bracket.name}"...`)
|
|
44
|
-
for (const round of bracket.rounds) {
|
|
45
|
-
for (const battle of round.battles) {
|
|
46
|
-
// If battle is a BYE then skip
|
|
47
|
-
if (!battle.team1Id || !battle.team2Id) {
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Get seed for the battle
|
|
52
|
-
const tournamentContract = getTournamentContract(onchainAddress)
|
|
53
|
-
const seed = await tournamentContract.roundSeeds(round.roundStage)
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
await validateBattle(battle.id, seed.toString(), tournamentConfig.gameLogicVersion)
|
|
57
|
-
} catch (error) {
|
|
58
|
-
if (error instanceof ValidationError) {
|
|
59
|
-
console.error(`Battle ${battle.id} failed validation ❌`)
|
|
60
|
-
console.error(`Seed: "${seed.toString()}"`)
|
|
61
|
-
|
|
62
|
-
// Write original logs to file
|
|
63
|
-
const originalLogsFilename = `${battle.id}-originalLogs.json`
|
|
64
|
-
fs.writeFileSync(path.join(__dirname, 'output', originalLogsFilename), JSON.stringify(error.originalLogs, null, '\t'))
|
|
65
|
-
|
|
66
|
-
// Write new logs to file
|
|
67
|
-
const newLogsFilename = `${battle.id}-newLogs.json`
|
|
68
|
-
fs.writeFileSync(path.join(__dirname, 'output', newLogsFilename), JSON.stringify(error.newLogs, null, '\t'))
|
|
69
|
-
|
|
70
|
-
console.error(`Original logs written to ${path.join(__dirname, 'output', originalLogsFilename)}`)
|
|
71
|
-
console.error(`New logs written to ${path.join(__dirname, 'output', newLogsFilename)}`)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
throw error
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
console.log(`Round ${round.roundStage} validated ✅`)
|
|
79
|
-
}
|
|
80
|
-
console.log(`"${bracket.name}" validated ✅`)
|
|
81
|
-
}
|
|
82
|
-
console.log(`"${tournament.data.name}" validated ✅`)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
module.exports = main
|
|
86
|
-
|
|
87
|
-
// node scripts/validateTournament.js 15
|
|
88
|
-
if (require.main === module) {
|
|
89
|
-
const tournamentId = process.argv[2]
|
|
90
|
-
|
|
91
|
-
main(tournamentId)
|
|
92
|
-
.then(() => {
|
|
93
|
-
console.log('Done')
|
|
94
|
-
process.exit(0)
|
|
95
|
-
})
|
|
96
|
-
.catch((error) => {
|
|
97
|
-
console.error(error)
|
|
98
|
-
|
|
99
|
-
process.exit(1)
|
|
100
|
-
})
|
|
101
|
-
}
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const axios = require('axios')
|
|
4
|
+
const { getTournamentContract } = require('../utils/contracts')
|
|
5
|
+
const { ValidationError } = require('../utils/errors')
|
|
6
|
+
const tournaments = require('./data/tournaments.json')
|
|
7
|
+
const validateBattle = require('./validateBattle')
|
|
8
|
+
|
|
9
|
+
const main = async (tournamentId) => {
|
|
10
|
+
|
|
11
|
+
console.log(`Getting tournament config for id ${tournamentId}...`)
|
|
12
|
+
|
|
13
|
+
const tournamentConfig = tournaments.find(t => `${t.id}` === `${tournamentId}`)
|
|
14
|
+
|
|
15
|
+
if (!tournamentConfig || !tournamentConfig.gameLogicVersion) {
|
|
16
|
+
console.error('Tournament config not found with id:', tournamentId)
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log(`Found, using game logic version "${tournamentConfig.gameLogicVersion}" ✅`)
|
|
21
|
+
console.log(`Getting tournament data for id: ${tournamentId}...`)
|
|
22
|
+
|
|
23
|
+
const tournament = await axios.get(`https://gotchi-battler-backend-blmom6tkla-ew.a.run.app/api/v1/tournaments/${tournamentId}`)
|
|
24
|
+
|
|
25
|
+
if (!tournament || !tournament.data || !tournament.data.address) {
|
|
26
|
+
console.error('Tournament not found with id:', tournamentId)
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const onchainAddress = tournament.data.address
|
|
31
|
+
|
|
32
|
+
const brackets = await axios.get(`https://gotchi-battler-backend-blmom6tkla-ew.a.run.app/api/v1/tournaments/${tournamentId}/brackets`)
|
|
33
|
+
|
|
34
|
+
if (!brackets || !brackets.data || !brackets.data.length) {
|
|
35
|
+
console.error('Brackets not found')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log(`Tournament data for id ${tournamentId} found ✅`)
|
|
40
|
+
console.log(`Validating "${tournament.data.name}"...`)
|
|
41
|
+
console.log('(This process can take up to 20 minutes for large tournaments)')
|
|
42
|
+
for (const bracket of brackets.data) {
|
|
43
|
+
console.log(`Validating "${bracket.name}"...`)
|
|
44
|
+
for (const round of bracket.rounds) {
|
|
45
|
+
for (const battle of round.battles) {
|
|
46
|
+
// If battle is a BYE then skip
|
|
47
|
+
if (!battle.team1Id || !battle.team2Id) {
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Get seed for the battle
|
|
52
|
+
const tournamentContract = getTournamentContract(onchainAddress)
|
|
53
|
+
const seed = await tournamentContract.roundSeeds(round.roundStage)
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
await validateBattle(battle.id, seed.toString(), tournamentConfig.gameLogicVersion)
|
|
57
|
+
} catch (error) {
|
|
58
|
+
if (error instanceof ValidationError) {
|
|
59
|
+
console.error(`Battle ${battle.id} failed validation ❌`)
|
|
60
|
+
console.error(`Seed: "${seed.toString()}"`)
|
|
61
|
+
|
|
62
|
+
// Write original logs to file
|
|
63
|
+
const originalLogsFilename = `${battle.id}-originalLogs.json`
|
|
64
|
+
fs.writeFileSync(path.join(__dirname, 'output', originalLogsFilename), JSON.stringify(error.originalLogs, null, '\t'))
|
|
65
|
+
|
|
66
|
+
// Write new logs to file
|
|
67
|
+
const newLogsFilename = `${battle.id}-newLogs.json`
|
|
68
|
+
fs.writeFileSync(path.join(__dirname, 'output', newLogsFilename), JSON.stringify(error.newLogs, null, '\t'))
|
|
69
|
+
|
|
70
|
+
console.error(`Original logs written to ${path.join(__dirname, 'output', originalLogsFilename)}`)
|
|
71
|
+
console.error(`New logs written to ${path.join(__dirname, 'output', newLogsFilename)}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw error
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
console.log(`Round ${round.roundStage} validated ✅`)
|
|
79
|
+
}
|
|
80
|
+
console.log(`"${bracket.name}" validated ✅`)
|
|
81
|
+
}
|
|
82
|
+
console.log(`"${tournament.data.name}" validated ✅`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = main
|
|
86
|
+
|
|
87
|
+
// node scripts/validateTournament.js 15
|
|
88
|
+
if (require.main === module) {
|
|
89
|
+
const tournamentId = process.argv[2]
|
|
90
|
+
|
|
91
|
+
main(tournamentId)
|
|
92
|
+
.then(() => {
|
|
93
|
+
console.log('Done')
|
|
94
|
+
process.exit(0)
|
|
95
|
+
})
|
|
96
|
+
.catch((error) => {
|
|
97
|
+
console.error(error)
|
|
98
|
+
|
|
99
|
+
process.exit(1)
|
|
100
|
+
})
|
|
101
|
+
}
|
package/utils/contracts.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const { ethers } = require('ethers')
|
|
2
|
-
const tournamentAbi = require('../constants/tournamentManagerAbi.json')
|
|
3
|
-
|
|
4
|
-
const getTournamentContract = (address) => {
|
|
5
|
-
const provider = new ethers.JsonRpcProvider('https://polygon-rpc.com')
|
|
6
|
-
const contract = new ethers.Contract(address, tournamentAbi, provider)
|
|
7
|
-
|
|
8
|
-
return contract
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
module.exports = {
|
|
12
|
-
getTournamentContract
|
|
1
|
+
const { ethers } = require('ethers')
|
|
2
|
+
const tournamentAbi = require('../constants/tournamentManagerAbi.json')
|
|
3
|
+
|
|
4
|
+
const getTournamentContract = (address) => {
|
|
5
|
+
const provider = new ethers.JsonRpcProvider('https://polygon-rpc.com')
|
|
6
|
+
const contract = new ethers.Contract(address, tournamentAbi, provider)
|
|
7
|
+
|
|
8
|
+
return contract
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
getTournamentContract
|
|
13
13
|
}
|
package/utils/errors.js
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
class GameError extends Error {
|
|
2
|
-
constructor(msg, logs) {
|
|
3
|
-
super(msg)
|
|
4
|
-
this.name = 'GameError'
|
|
5
|
-
|
|
6
|
-
if(logs) {
|
|
7
|
-
this.logs = logs
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class ValidationError extends Error {
|
|
13
|
-
constructor(msg, originalLogs, newLogs) {
|
|
14
|
-
super(msg)
|
|
15
|
-
this.name = 'ValidationError'
|
|
16
|
-
|
|
17
|
-
if(originalLogs) {
|
|
18
|
-
this.originalLogs = originalLogs
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if(newLogs) {
|
|
22
|
-
this.newLogs = newLogs
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
module.exports = {
|
|
28
|
-
GameError,
|
|
29
|
-
ValidationError
|
|
1
|
+
class GameError extends Error {
|
|
2
|
+
constructor(msg, logs) {
|
|
3
|
+
super(msg)
|
|
4
|
+
this.name = 'GameError'
|
|
5
|
+
|
|
6
|
+
if(logs) {
|
|
7
|
+
this.logs = logs
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class ValidationError extends Error {
|
|
13
|
+
constructor(msg, originalLogs, newLogs) {
|
|
14
|
+
super(msg)
|
|
15
|
+
this.name = 'ValidationError'
|
|
16
|
+
|
|
17
|
+
if(originalLogs) {
|
|
18
|
+
this.originalLogs = originalLogs
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if(newLogs) {
|
|
22
|
+
this.newLogs = newLogs
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
GameError,
|
|
29
|
+
ValidationError
|
|
30
30
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const mapTeam = (team) => {
|
|
2
|
+
for(const gotchi of team.formation.front) {
|
|
3
|
+
if (gotchi) mapGotchi(gotchi)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
for(const gotchi of team.formation.back) {
|
|
7
|
+
if (gotchi) mapGotchi(gotchi)
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// This is copied/hacked from the mapGotchi function in the backend
|
|
12
|
+
const mapGotchi = (gotchi) => {
|
|
13
|
+
const traitMaps = {
|
|
14
|
+
speed: {
|
|
15
|
+
baseFormula: 100,
|
|
16
|
+
multiplier: 1,
|
|
17
|
+
traitKey: 0,
|
|
18
|
+
isNegative: false
|
|
19
|
+
},
|
|
20
|
+
health: {
|
|
21
|
+
baseFormula: 'brs*0.85',
|
|
22
|
+
multiplier: 12,
|
|
23
|
+
traitKey: 0,
|
|
24
|
+
isNegative: true
|
|
25
|
+
},
|
|
26
|
+
crit: {
|
|
27
|
+
baseFormula: 0,
|
|
28
|
+
multiplier: 0.5,
|
|
29
|
+
traitKey: 1,
|
|
30
|
+
isNegative: false
|
|
31
|
+
},
|
|
32
|
+
armor: {
|
|
33
|
+
baseFormula: 0,
|
|
34
|
+
multiplier: 2,
|
|
35
|
+
traitKey: 1,
|
|
36
|
+
isNegative: true
|
|
37
|
+
},
|
|
38
|
+
evade: {
|
|
39
|
+
baseFormula: 0,
|
|
40
|
+
multiplier: 0.3,
|
|
41
|
+
traitKey: 2,
|
|
42
|
+
isNegative: false
|
|
43
|
+
},
|
|
44
|
+
resist: {
|
|
45
|
+
baseFormula: 0,
|
|
46
|
+
multiplier: 1,
|
|
47
|
+
traitKey: 2,
|
|
48
|
+
isNegative: true
|
|
49
|
+
},
|
|
50
|
+
magic: {
|
|
51
|
+
baseFormula: 'brs*0.35',
|
|
52
|
+
multiplier: 5,
|
|
53
|
+
traitKey: 3,
|
|
54
|
+
isNegative: false
|
|
55
|
+
},
|
|
56
|
+
physical: {
|
|
57
|
+
baseFormula: 'brs*0.35',
|
|
58
|
+
multiplier: 5,
|
|
59
|
+
traitKey: 3,
|
|
60
|
+
isNegative: true
|
|
61
|
+
},
|
|
62
|
+
accuracy: {
|
|
63
|
+
baseFormula: 50,
|
|
64
|
+
multiplier: 0.5,
|
|
65
|
+
traitKey: 45,
|
|
66
|
+
isNegative: false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const traitValue = (trait) => {
|
|
71
|
+
return trait < 50 ? 50 - trait : trait - 50 + 1
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const onchainVals = [
|
|
75
|
+
gotchi.nrg,
|
|
76
|
+
gotchi.agg,
|
|
77
|
+
gotchi.spk,
|
|
78
|
+
gotchi.brn,
|
|
79
|
+
gotchi.eyc,
|
|
80
|
+
gotchi.eys
|
|
81
|
+
]
|
|
82
|
+
// Convert trait value to in-game value
|
|
83
|
+
const traitValues = onchainVals.map(x => { return traitValue(x) })
|
|
84
|
+
|
|
85
|
+
// Map traits
|
|
86
|
+
for(const trait in traitMaps) {
|
|
87
|
+
const traitMap = traitMaps[trait]
|
|
88
|
+
const onchainVal = onchainVals[traitMap.traitKey]
|
|
89
|
+
|
|
90
|
+
let base = traitMap.baseFormula
|
|
91
|
+
|
|
92
|
+
// If baseFormula is a string and contains a * then it is a formula
|
|
93
|
+
if (typeof traitMap.baseFormula === 'string' && traitMap.baseFormula.includes('*')) {
|
|
94
|
+
const formula = traitMap.baseFormula.split('*')
|
|
95
|
+
|
|
96
|
+
if (!gotchi[formula[0]]) throw new Error('Trait not found: ', formula[0])
|
|
97
|
+
|
|
98
|
+
base = Math.round(Number(gotchi[formula[0]]) * Number(formula[1]))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let newTrait
|
|
102
|
+
if (trait !== 'accuracy') {
|
|
103
|
+
if (traitMap.isNegative) {
|
|
104
|
+
newTrait = onchainVal < 50 ? Math.round(base + (traitValues[traitMap.traitKey] * traitMap.multiplier)) : base
|
|
105
|
+
} else {
|
|
106
|
+
newTrait = onchainVal < 50 ? base : Math.round(base + (traitValues[traitMap.traitKey] * traitMap.multiplier))
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
newTrait = base + ((traitValues[4] + traitValues[5]) * traitMap.multiplier)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (newTrait !== gotchi[trait]) gotchi[trait] = newTrait
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = {
|
|
117
|
+
mapGotchi,
|
|
118
|
+
mapTeam
|
|
119
|
+
}
|