gotchi-battler-game-logic 2.0.8 → 4.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/.cursor/rules/cursor-rules.mdc +67 -0
- package/.cursor/rules/directory-structure.mdc +63 -0
- package/.cursor/rules/self-improvement.mdc +64 -0
- package/.cursor/rules/tech-stack.mdc +99 -0
- package/README.md +7 -3
- package/eslint.config.js +31 -0
- package/game-logic/index.js +2 -5
- package/game-logic/v1.4/constants.js +0 -23
- package/game-logic/v1.4/index.js +64 -56
- package/game-logic/v1.5/constants.js +0 -23
- package/game-logic/v1.5/index.js +27 -21
- package/game-logic/v1.6/constants.js +0 -23
- package/game-logic/v1.6/index.js +27 -21
- package/game-logic/v1.7/constants.js +0 -23
- package/game-logic/v1.7/helpers.js +18 -3
- package/game-logic/v1.7/index.js +24 -18
- package/game-logic/v1.8/constants.js +112 -0
- package/game-logic/v1.8/helpers.js +628 -0
- package/game-logic/v1.8/index.js +832 -0
- package/game-logic/v2.0/constants.js +112 -0
- package/game-logic/v2.0/helpers.js +713 -0
- package/game-logic/v2.0/index.js +782 -0
- package/game-logic/v2.0/statuses.json +439 -0
- package/package.json +11 -4
- package/schemas/crystal.js +14 -0
- package/schemas/effect.js +25 -0
- package/schemas/gotchi.js +53 -0
- package/schemas/ingameteam.js +14 -0
- package/schemas/item.js +13 -0
- package/schemas/leaderskill.js +15 -0
- package/schemas/leaderskillstatus.js +12 -0
- package/schemas/special.js +22 -0
- package/schemas/team.js +24 -0
- package/schemas/team.json +254 -116
- package/scripts/balancing/createCSV.js +1 -1
- package/scripts/balancing/createTrainingGotchis.js +267 -0
- package/scripts/balancing/extractOnchainTraits.js +61 -0
- package/scripts/balancing/fixTrainingGotchis.js +41 -41
- package/scripts/balancing/processSims.js +6 -6
- package/scripts/balancing/sims.js +10 -17
- package/scripts/balancing/v1.7/mapGotchi.js +119 -0
- package/scripts/balancing/v1.7/setTeamPositions.js +2 -2
- package/scripts/balancing/v1.7/training_gotchis_traits.json +520 -0
- package/scripts/balancing/v1.7.1/mapGotchi.js +119 -0
- package/scripts/balancing/v1.7.1/setTeamPositions.js +2 -2
- package/scripts/balancing/v1.7.1/training_gotchis_traits.json +520 -0
- package/scripts/balancing/v1.7.2/mapGotchi.js +157 -0
- package/scripts/balancing/v1.7.2/setTeamPositions.js +2 -2
- package/scripts/balancing/v1.7.2/training_gotchis_traits.json +520 -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/dungeon_mob_1.json +87 -0
- package/scripts/data/dungeon_mob_2.json +87 -0
- package/scripts/data/immaterialTeam1.json +374 -0
- package/scripts/data/immaterialTeam2.json +365 -0
- package/scripts/data/tournaments.json +5 -0
- package/scripts/generateAllSpecialsLogs.js +93 -0
- package/scripts/generateSpecialLogs.js +94 -0
- package/scripts/runCampaignBattles.js +41 -0
- package/scripts/runLocalBattle.js +6 -3
- package/scripts/runLocalDungeon.js +52 -0
- package/scripts/runPvPBattle.js +16 -0
- package/scripts/runRealBattle.js +8 -8
- package/scripts/simRealBattle.js +8 -8
- package/scripts/validateBattle.js +23 -16
- package/scripts/validateTournament.js +9 -9
- package/tests/getModifiedStats.test.js +78 -0
- package/utils/errors.js +13 -13
- package/utils/transforms.js +2 -8
- package/scripts/output/.gitkeep +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { battle } = require('..')
|
|
4
|
+
|
|
5
|
+
// Edit these json files to test different battles
|
|
6
|
+
const team1 = require('./data/immaterialTeam1.json')
|
|
7
|
+
const team2 = require('./data/immaterialTeam2.json')
|
|
8
|
+
|
|
9
|
+
const results = battle(team1, team2, 'randomseed', {
|
|
10
|
+
debug: false,
|
|
11
|
+
type: 'pvp'
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
fs.writeFileSync(path.join(__dirname, 'output', 'pvp-results.json'), JSON.stringify(results, null, '\t'))
|
|
15
|
+
|
|
16
|
+
// node scripts/runPvPBattle.js
|
package/scripts/runRealBattle.js
CHANGED
|
@@ -41,12 +41,12 @@ if (require.main === module) {
|
|
|
41
41
|
const battleId = process.argv[2]
|
|
42
42
|
|
|
43
43
|
main(battleId)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
.then(() => {
|
|
45
|
+
console.log('Done')
|
|
46
|
+
process.exit(0)
|
|
47
|
+
})
|
|
48
|
+
.catch((error) => {
|
|
49
|
+
console.error('Error: ', error)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
})
|
|
52
52
|
}
|
package/scripts/simRealBattle.js
CHANGED
|
@@ -110,12 +110,12 @@ if (require.main === module) {
|
|
|
110
110
|
const battleId = process.argv[2]
|
|
111
111
|
|
|
112
112
|
main(battleId)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
121
|
}
|
|
@@ -4,8 +4,6 @@ axiosRetry(axios, {
|
|
|
4
4
|
retries: 3,
|
|
5
5
|
retryDelay: axiosRetry.exponentialDelay
|
|
6
6
|
})
|
|
7
|
-
const fs = require('fs')
|
|
8
|
-
const path = require('path')
|
|
9
7
|
|
|
10
8
|
const { GameError, ValidationError } = require('../utils/errors')
|
|
11
9
|
const { logToInGameTeams } = require('../utils/transforms')
|
|
@@ -18,7 +16,7 @@ const main = async (battleId, seed, gameLogicVersion) => {
|
|
|
18
16
|
if (!gameVersions[gameLogicVersion]) throw new Error('Invalid game logic version')
|
|
19
17
|
|
|
20
18
|
const gameLoop = gameVersions[gameLogicVersion].gameLoop
|
|
21
|
-
|
|
19
|
+
|
|
22
20
|
const res = await axios.get(`https://storage.googleapis.com/gotchi-battler-live_battles/v1/${battleId}.json`)
|
|
23
21
|
|
|
24
22
|
if (!res || !res.data || !res.data.layout) {
|
|
@@ -29,11 +27,20 @@ const main = async (battleId, seed, gameLogicVersion) => {
|
|
|
29
27
|
// Transform the logs to in-game teams
|
|
30
28
|
const teams = logToInGameTeams(res.data)
|
|
31
29
|
|
|
30
|
+
// If the game logic has a removeStatItems function, call it
|
|
31
|
+
// This is so the item buffs don't get applied twice
|
|
32
|
+
const helpers = require(`../game-logic/${gameLogicVersion}/helpers`)
|
|
33
|
+
|
|
34
|
+
if (helpers.removeStatItems) {
|
|
35
|
+
helpers.removeStatItems(helpers.getAlive(teams[0]))
|
|
36
|
+
helpers.removeStatItems(helpers.getAlive(teams[1]))
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
try {
|
|
33
40
|
// Run the game loop
|
|
34
41
|
const logs = await gameLoop(teams[0], teams[1], seed, true)
|
|
35
42
|
|
|
36
|
-
fs.writeFileSync(path.join(__dirname, 'output', `${battleId}_${Date.now()}.json`), JSON.stringify(logs, null, '\t'))
|
|
43
|
+
// fs.writeFileSync(path.join(__dirname, 'output', `${battleId}_${Date.now()}.json`), JSON.stringify(logs, null, '\t'))
|
|
37
44
|
|
|
38
45
|
// Validate the results
|
|
39
46
|
compareLogs(res.data, logs)
|
|
@@ -57,18 +64,18 @@ if (require.main === module) {
|
|
|
57
64
|
const gameLogicVersion = process.argv[4]
|
|
58
65
|
|
|
59
66
|
main(battleId, seed, gameLogicVersion)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
.then(() => {
|
|
68
|
+
console.log('Results from game logic match the logs ✅')
|
|
69
|
+
console.log('Done')
|
|
70
|
+
process.exit(0)
|
|
71
|
+
})
|
|
72
|
+
.catch((error) => {
|
|
73
|
+
if (error instanceof ValidationError) {
|
|
74
|
+
console.error('Results from game logic do not match the logs ❌')
|
|
75
|
+
}
|
|
69
76
|
|
|
70
|
-
|
|
77
|
+
console.error('Error: ', error.message)
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
process.exit(1)
|
|
80
|
+
})
|
|
74
81
|
}
|
|
@@ -89,13 +89,13 @@ if (require.main === module) {
|
|
|
89
89
|
const tournamentId = process.argv[2]
|
|
90
90
|
|
|
91
91
|
main(tournamentId)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
101
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const { expect } = require('chai')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
// Import from helpers
|
|
5
|
+
const { getModifiedStats } = require(path.join('..', 'game-logic', 'v2.0', 'helpers'))
|
|
6
|
+
|
|
7
|
+
const makeGotchi = (overrides = {}) => ({
|
|
8
|
+
id: 1,
|
|
9
|
+
name: 'Test',
|
|
10
|
+
speed: 10,
|
|
11
|
+
attack: 5,
|
|
12
|
+
defense: 3,
|
|
13
|
+
resist: 0,
|
|
14
|
+
criticalRate: 0,
|
|
15
|
+
criticalDamage: 15,
|
|
16
|
+
special: { initialCooldown: 0 },
|
|
17
|
+
statuses: [],
|
|
18
|
+
...overrides,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('getModifiedStats', () => {
|
|
22
|
+
it('applies flat stat buffs from statuses', () => {
|
|
23
|
+
const g = makeGotchi({ statuses: ['spd_up', 'atk_up', 'def_up'] })
|
|
24
|
+
const m = getModifiedStats(g)
|
|
25
|
+
expect(m.speed).to.equal(11)
|
|
26
|
+
expect(m.attack).to.equal(6)
|
|
27
|
+
expect(m.defense).to.equal(4)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('stacks multiple statuses and nets against opposing debuffs', () => {
|
|
31
|
+
const g = makeGotchi({ statuses: ['spd_up', 'spd_up', 'spd_down'] })
|
|
32
|
+
const m = getModifiedStats(g)
|
|
33
|
+
expect(m.speed).to.equal(11) // 10 + 1 net
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('keeps speed at minimum 1 to avoid zero/negative speed', () => {
|
|
37
|
+
const g = makeGotchi({ speed: 2, statuses: ['spd_down', 'spd_down', 'spd_down'] })
|
|
38
|
+
const m = getModifiedStats(g)
|
|
39
|
+
expect(m.speed).to.equal(1)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('ignores non stat_modifier statuses', () => {
|
|
43
|
+
const g = makeGotchi({ statuses: ['taunt', 'bleed'] }) // custom, turn_effect
|
|
44
|
+
const m = getModifiedStats(g)
|
|
45
|
+
expect(m.speed).to.equal(10)
|
|
46
|
+
expect(m.attack).to.equal(5)
|
|
47
|
+
expect(m.defense).to.equal(3)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('keeps defense at minimum 1 to avoid zero/negative defense', () => {
|
|
51
|
+
const g = makeGotchi({ defense: 1, statuses: ['def_down'] })
|
|
52
|
+
const m = getModifiedStats(g)
|
|
53
|
+
expect(m.defense).to.equal(1)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('keeps defense at minimum 1 even if base defense is 0 and no statuses', () => {
|
|
57
|
+
const g = makeGotchi({ defense: 0, statuses: [] })
|
|
58
|
+
const m = getModifiedStats(g)
|
|
59
|
+
expect(m.defense).to.equal(1)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('does not mutate the original gotchi object', () => {
|
|
63
|
+
const g = makeGotchi({ statuses: ['spd_up'] })
|
|
64
|
+
const before = { speed: g.speed, attack: g.attack, defense: g.defense }
|
|
65
|
+
const m = getModifiedStats(g)
|
|
66
|
+
expect(g.speed).to.equal(before.speed)
|
|
67
|
+
expect(g.attack).to.equal(before.attack)
|
|
68
|
+
expect(g.defense).to.equal(before.defense)
|
|
69
|
+
expect(m).to.not.equal(g)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('throws if an unknown status code is provided', () => {
|
|
73
|
+
const g = makeGotchi({ statuses: ['this_is_not_real'] })
|
|
74
|
+
expect(() => getModifiedStats(g)).to.throw('Status with code this_is_not_real not found')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
|
package/utils/errors.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
class GameError extends Error {
|
|
2
|
-
|
|
2
|
+
constructor(msg, logs) {
|
|
3
3
|
super(msg)
|
|
4
4
|
this.name = 'GameError'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
if(logs) {
|
|
7
|
+
this.logs = logs
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
class ValidationError extends Error {
|
|
13
|
-
|
|
13
|
+
constructor(msg, originalLogs, newLogs) {
|
|
14
14
|
super(msg)
|
|
15
15
|
this.name = 'ValidationError'
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
if(originalLogs) {
|
|
18
|
+
this.originalLogs = originalLogs
|
|
19
|
+
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
if(newLogs) {
|
|
22
|
+
this.newLogs = newLogs
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
module.exports = {
|
package/utils/transforms.js
CHANGED
|
@@ -38,7 +38,7 @@ const logToInGameTeams = (originalLog) => {
|
|
|
38
38
|
name: log.layout.teams[teamIndex].name,
|
|
39
39
|
owner: log.layout.teams[teamIndex].owner
|
|
40
40
|
})
|
|
41
|
-
})
|
|
41
|
+
})
|
|
42
42
|
|
|
43
43
|
return teams
|
|
44
44
|
}
|
|
@@ -49,17 +49,11 @@ const webappTeamToInGameTeam = (webappTeam) => {
|
|
|
49
49
|
front: [webappTeam.front1Gotchi, webappTeam.front2Gotchi, webappTeam.front3Gotchi, webappTeam.front4Gotchi, webappTeam.front5Gotchi],
|
|
50
50
|
back: [webappTeam.back1Gotchi, webappTeam.back2Gotchi, webappTeam.back3Gotchi, webappTeam.back4Gotchi, webappTeam.back5Gotchi],
|
|
51
51
|
},
|
|
52
|
-
leader: webappTeam.leader,
|
|
52
|
+
leader: webappTeam[webappTeam.leader],
|
|
53
53
|
name: webappTeam.name,
|
|
54
54
|
owner: webappTeam.owner
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
inGameTeam.formation.front.forEach(gotchi => {
|
|
58
|
-
if (!gotchi) return
|
|
59
|
-
// remove availableSpecials
|
|
60
|
-
delete gotchi.availableSpecials
|
|
61
|
-
})
|
|
62
|
-
|
|
63
57
|
return inGameTeam
|
|
64
58
|
}
|
|
65
59
|
|
package/scripts/output/.gitkeep
DELETED
|
File without changes
|