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.
Files changed (74) hide show
  1. package/.cursor/rules/cursor-rules.mdc +67 -0
  2. package/.cursor/rules/directory-structure.mdc +63 -0
  3. package/.cursor/rules/self-improvement.mdc +64 -0
  4. package/.cursor/rules/tech-stack.mdc +99 -0
  5. package/README.md +7 -3
  6. package/eslint.config.js +31 -0
  7. package/game-logic/index.js +2 -5
  8. package/game-logic/v1.4/constants.js +0 -23
  9. package/game-logic/v1.4/index.js +64 -56
  10. package/game-logic/v1.5/constants.js +0 -23
  11. package/game-logic/v1.5/index.js +27 -21
  12. package/game-logic/v1.6/constants.js +0 -23
  13. package/game-logic/v1.6/index.js +27 -21
  14. package/game-logic/v1.7/constants.js +0 -23
  15. package/game-logic/v1.7/helpers.js +18 -3
  16. package/game-logic/v1.7/index.js +24 -18
  17. package/game-logic/v1.8/constants.js +112 -0
  18. package/game-logic/v1.8/helpers.js +628 -0
  19. package/game-logic/v1.8/index.js +832 -0
  20. package/game-logic/v2.0/constants.js +112 -0
  21. package/game-logic/v2.0/helpers.js +713 -0
  22. package/game-logic/v2.0/index.js +782 -0
  23. package/game-logic/v2.0/statuses.json +439 -0
  24. package/package.json +11 -4
  25. package/schemas/crystal.js +14 -0
  26. package/schemas/effect.js +25 -0
  27. package/schemas/gotchi.js +53 -0
  28. package/schemas/ingameteam.js +14 -0
  29. package/schemas/item.js +13 -0
  30. package/schemas/leaderskill.js +15 -0
  31. package/schemas/leaderskillstatus.js +12 -0
  32. package/schemas/special.js +22 -0
  33. package/schemas/team.js +24 -0
  34. package/schemas/team.json +254 -116
  35. package/scripts/balancing/createCSV.js +1 -1
  36. package/scripts/balancing/createTrainingGotchis.js +267 -0
  37. package/scripts/balancing/extractOnchainTraits.js +61 -0
  38. package/scripts/balancing/fixTrainingGotchis.js +41 -41
  39. package/scripts/balancing/processSims.js +6 -6
  40. package/scripts/balancing/sims.js +10 -17
  41. package/scripts/balancing/v1.7/mapGotchi.js +119 -0
  42. package/scripts/balancing/v1.7/setTeamPositions.js +2 -2
  43. package/scripts/balancing/v1.7/training_gotchis_traits.json +520 -0
  44. package/scripts/balancing/v1.7.1/mapGotchi.js +119 -0
  45. package/scripts/balancing/v1.7.1/setTeamPositions.js +2 -2
  46. package/scripts/balancing/v1.7.1/training_gotchis_traits.json +520 -0
  47. package/scripts/balancing/v1.7.2/mapGotchi.js +157 -0
  48. package/scripts/balancing/v1.7.2/setTeamPositions.js +2 -2
  49. package/scripts/balancing/v1.7.2/training_gotchis_traits.json +520 -0
  50. package/scripts/balancing/v1.7.3/class_combos.js +44 -0
  51. package/scripts/balancing/v1.7.3/mapGotchi.js +164 -0
  52. package/scripts/balancing/v1.7.3/setTeamPositions.js +122 -0
  53. package/scripts/balancing/v1.7.3/training_gotchis.json +22402 -0
  54. package/scripts/balancing/v1.7.3/training_gotchis_traits.json +37 -0
  55. package/scripts/balancing/v1.7.3/trait_combos.json +10 -0
  56. package/scripts/data/dungeon_mob_1.json +87 -0
  57. package/scripts/data/dungeon_mob_2.json +87 -0
  58. package/scripts/data/immaterialTeam1.json +374 -0
  59. package/scripts/data/immaterialTeam2.json +365 -0
  60. package/scripts/data/tournaments.json +5 -0
  61. package/scripts/generateAllSpecialsLogs.js +93 -0
  62. package/scripts/generateSpecialLogs.js +94 -0
  63. package/scripts/runCampaignBattles.js +41 -0
  64. package/scripts/runLocalBattle.js +6 -3
  65. package/scripts/runLocalDungeon.js +52 -0
  66. package/scripts/runPvPBattle.js +16 -0
  67. package/scripts/runRealBattle.js +8 -8
  68. package/scripts/simRealBattle.js +8 -8
  69. package/scripts/validateBattle.js +23 -16
  70. package/scripts/validateTournament.js +9 -9
  71. package/tests/getModifiedStats.test.js +78 -0
  72. package/utils/errors.js +13 -13
  73. package/utils/transforms.js +2 -8
  74. 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
@@ -41,12 +41,12 @@ if (require.main === module) {
41
41
  const battleId = process.argv[2]
42
42
 
43
43
  main(battleId)
44
- .then(() => {
45
- console.log('Done')
46
- process.exit(0)
47
- })
48
- .catch((error) => {
49
- console.error('Error: ', error)
50
- process.exit(1)
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
  }
@@ -110,12 +110,12 @@ if (require.main === module) {
110
110
  const battleId = process.argv[2]
111
111
 
112
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
- })
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
- .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
- }
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
- console.error('Error: ', error.message)
77
+ console.error('Error: ', error.message)
71
78
 
72
- process.exit(1)
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
- .then(() => {
93
- console.log('Done')
94
- process.exit(0)
95
- })
96
- .catch((error) => {
97
- console.error(error)
98
-
99
- process.exit(1)
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
- constructor(msg, logs) {
2
+ constructor(msg, logs) {
3
3
  super(msg)
4
4
  this.name = 'GameError'
5
5
 
6
- if(logs) {
7
- this.logs = logs
8
- }
9
- }
6
+ if(logs) {
7
+ this.logs = logs
8
+ }
9
+ }
10
10
  }
11
11
 
12
12
  class ValidationError extends Error {
13
- constructor(msg, originalLogs, newLogs) {
13
+ constructor(msg, originalLogs, newLogs) {
14
14
  super(msg)
15
15
  this.name = 'ValidationError'
16
16
 
17
- if(originalLogs) {
18
- this.originalLogs = originalLogs
19
- }
17
+ if(originalLogs) {
18
+ this.originalLogs = originalLogs
19
+ }
20
20
 
21
- if(newLogs) {
22
- this.newLogs = newLogs
23
- }
24
- }
21
+ if(newLogs) {
22
+ this.newLogs = newLogs
23
+ }
24
+ }
25
25
  }
26
26
 
27
27
  module.exports = {
@@ -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
 
File without changes