gotchi-battler-game-logic 1.0.0 → 2.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 (39) hide show
  1. package/.env.example +1 -0
  2. package/.vscode/settings.json +4 -4
  3. package/Dockerfile +10 -0
  4. package/README.md +49 -49
  5. package/cloudbuild.yaml +27 -0
  6. package/constants/tournamentManagerAbi.json +208 -208
  7. package/game-logic/index.js +6 -5
  8. package/game-logic/v1.4/constants.js +120 -120
  9. package/game-logic/v1.4/index.js +1366 -1353
  10. package/game-logic/v1.5/index.js +8 -8
  11. package/game-logic/v1.6/constants.js +129 -129
  12. package/game-logic/v1.6/index.js +1406 -1402
  13. package/game-logic/v1.7/constants.js +147 -0
  14. package/game-logic/v1.7/index.js +1389 -0
  15. package/index.js +13 -6
  16. package/package.json +26 -22
  17. package/schemas/team.json +208 -203
  18. package/scripts/balancing/createCSV.js +126 -0
  19. package/scripts/balancing/fixTrainingGotchis.js +260 -0
  20. package/scripts/balancing/processSims.js +230 -0
  21. package/scripts/balancing/sims.js +278 -0
  22. package/scripts/balancing/v1.7/class_combos.js +44 -0
  23. package/scripts/balancing/v1.7/setTeamPositions.js +105 -0
  24. package/scripts/balancing/v1.7/training_gotchis.json +20162 -0
  25. package/scripts/balancing/v1.7/trait_combos.json +10 -0
  26. package/scripts/balancing/v1.7.1/class_combos.js +44 -0
  27. package/scripts/balancing/v1.7.1/setTeamPositions.js +122 -0
  28. package/scripts/balancing/v1.7.1/training_gotchis.json +22402 -0
  29. package/scripts/balancing/v1.7.1/trait_combos.json +10 -0
  30. package/scripts/data/team1.json +200 -200
  31. package/scripts/data/team2.json +200 -200
  32. package/scripts/data/tournaments.json +66 -66
  33. package/scripts/runBattle.js +15 -15
  34. package/scripts/validateBattle.js +70 -64
  35. package/scripts/validateTournament.js +101 -101
  36. package/utils/contracts.js +12 -12
  37. package/utils/errors.js +29 -29
  38. package/utils/transforms.js +88 -47
  39. package/utils/validations.js +39 -39
@@ -1,64 +1,70 @@
1
- const axios = require('axios')
2
- const { GameError, ValidationError } = require('../utils/errors')
3
- const { logToInGameTeams } = require('../utils/transforms')
4
- const { compareLogs } = require('../utils/validations')
5
- const gameVersions = require('../game-logic')
6
-
7
- const main = async (battleId, seed, gameLogicVersion) => {
8
-
9
- if (!gameLogicVersion) gameLogicVersion = gameVersions.current
10
- if (!gameVersions[gameLogicVersion]) throw new Error('Invalid game logic version')
11
-
12
- const gameLoop = gameVersions[gameLogicVersion].gameLoop
13
-
14
- const res = await axios.get(`https://storage.googleapis.com/gotchi-battler-live_battles/v1/${battleId}.json`)
15
-
16
- if (!res || !res.data || !res.data.layout) {
17
- console.error('Battle not found')
18
- return
19
- }
20
-
21
- // Transform the logs to in-game teams
22
- const teams = logToInGameTeams(res.data)
23
-
24
- try {
25
- // Run the game loop
26
- const logs = await gameLoop(teams[0], teams[1], seed)
27
-
28
- // Validate the results
29
- compareLogs(res.data, logs)
30
-
31
- return logs
32
- } catch (error) {
33
- if (error instanceof GameError) {
34
- console.error('Errored game logs: ', error.logs)
35
- }
36
-
37
- throw error
38
- }
39
- }
40
-
41
- module.exports = main
42
-
43
- // node scripts/validateBattle.js 4d0f3c5c-08a0-42db-bd34-dee44300685a 82807311112923564712218359337695919195403960526804010606215202651499586140469
44
- if (require.main === module) {
45
- const battleId = process.argv[2]
46
- const seed = process.argv[3]
47
- const gameLogicVersion = process.argv[4]
48
-
49
- main(battleId, seed, gameLogicVersion)
50
- .then(() => {
51
- console.log('Results from game logic match the logs ✅')
52
- console.log('Done')
53
- process.exit(0)
54
- })
55
- .catch((error) => {
56
- if (error instanceof ValidationError) {
57
- console.error('Results from game logic do not match the logs ')
58
- }
59
-
60
- console.error('Error: ', error.message)
61
-
62
- process.exit(1)
63
- })
64
- }
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 { GameError, ValidationError } = require('../utils/errors')
9
+ const { logToInGameTeams } = require('../utils/transforms')
10
+ const { compareLogs } = require('../utils/validations')
11
+ const gameVersions = require('../game-logic')
12
+
13
+ const main = async (battleId, seed, gameLogicVersion) => {
14
+
15
+ if (!gameLogicVersion) gameLogicVersion = gameVersions.current
16
+ if (!gameVersions[gameLogicVersion]) throw new Error('Invalid game logic version')
17
+
18
+ const gameLoop = gameVersions[gameLogicVersion].gameLoop
19
+
20
+ const res = await axios.get(`https://storage.googleapis.com/gotchi-battler-live_battles/v1/${battleId}.json`)
21
+
22
+ if (!res || !res.data || !res.data.layout) {
23
+ console.error('Battle not found')
24
+ return
25
+ }
26
+
27
+ // Transform the logs to in-game teams
28
+ const teams = logToInGameTeams(res.data)
29
+
30
+ try {
31
+ // Run the game loop
32
+ const logs = await gameLoop(teams[0], teams[1], seed)
33
+
34
+ // Validate the results
35
+ compareLogs(res.data, logs)
36
+
37
+ return logs
38
+ } catch (error) {
39
+ if (error instanceof GameError) {
40
+ console.error('Errored game logs: ', error.logs)
41
+ }
42
+
43
+ throw error
44
+ }
45
+ }
46
+
47
+ module.exports = main
48
+
49
+ // node scripts/validateBattle.js 4d0f3c5c-08a0-42db-bd34-dee44300685a 82807311112923564712218359337695919195403960526804010606215202651499586140469
50
+ if (require.main === module) {
51
+ const battleId = process.argv[2]
52
+ const seed = process.argv[3]
53
+ const gameLogicVersion = process.argv[4]
54
+
55
+ main(battleId, seed, gameLogicVersion)
56
+ .then(() => {
57
+ console.log('Results from game logic match the logs ')
58
+ console.log('Done')
59
+ process.exit(0)
60
+ })
61
+ .catch((error) => {
62
+ if (error instanceof ValidationError) {
63
+ console.error('Results from game logic do not match the logs ❌')
64
+ }
65
+
66
+ console.error('Error: ', error.message)
67
+
68
+ process.exit(1)
69
+ })
70
+ }
@@ -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
+ }
@@ -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.providers.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.providers.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
  }
@@ -1,48 +1,89 @@
1
- const logToInGameTeams = (originalLog) => {
2
- // Deep copy the log to avoid modifying the original log
3
- const log = JSON.parse(JSON.stringify(originalLog))
4
-
5
- const teams = [];
6
-
7
- [0, 1].forEach((teamIndex) => {
8
- teams.push({
9
- formation: {
10
- front: log.layout.teams[teamIndex].rows[0].slots.map((slot) => {
11
- if (slot.isActive) {
12
- const gotchi = log.gotchis.find((gotchi) => gotchi.id === slot.id)
13
-
14
- if (!gotchi) {
15
- throw new Error(`Gotchi not found: ${slot.id}`)
16
- }
17
-
18
- return gotchi
19
- } else {
20
- return null
21
- }
22
- }),
23
- back: log.layout.teams[teamIndex].rows[1].slots.map((slot) => {
24
- if (slot.isActive) {
25
- const gotchi = log.gotchis.find((gotchi) => gotchi.id === slot.id)
26
-
27
- if (!gotchi) {
28
- throw new Error(`Gotchi not found: ${slot.id}`)
29
- }
30
-
31
- return gotchi
32
- } else {
33
- return null
34
- }
35
- })
36
- },
37
- leader: log.layout.teams[teamIndex].leaderId,
38
- name: log.layout.teams[teamIndex].name,
39
- owner: log.layout.teams[teamIndex].owner
40
- })
41
- });
42
-
43
- return teams
44
- }
45
-
46
- module.exports = {
47
- logToInGameTeams
1
+ const logToInGameTeams = (originalLog) => {
2
+ // Deep copy the log to avoid modifying the original log
3
+ const log = JSON.parse(JSON.stringify(originalLog))
4
+
5
+ const teams = [];
6
+
7
+ [0, 1].forEach((teamIndex) => {
8
+ teams.push({
9
+ formation: {
10
+ front: log.layout.teams[teamIndex].rows[0].slots.map((slot) => {
11
+ if (slot.isActive) {
12
+ const gotchi = log.gotchis.find((gotchi) => gotchi.id === slot.id)
13
+
14
+ if (!gotchi) {
15
+ throw new Error(`Gotchi not found: ${slot.id}`)
16
+ }
17
+
18
+ return gotchi
19
+ } else {
20
+ return null
21
+ }
22
+ }),
23
+ back: log.layout.teams[teamIndex].rows[1].slots.map((slot) => {
24
+ if (slot.isActive) {
25
+ const gotchi = log.gotchis.find((gotchi) => gotchi.id === slot.id)
26
+
27
+ if (!gotchi) {
28
+ throw new Error(`Gotchi not found: ${slot.id}`)
29
+ }
30
+
31
+ return gotchi
32
+ } else {
33
+ return null
34
+ }
35
+ })
36
+ },
37
+ leader: log.layout.teams[teamIndex].leaderId,
38
+ name: log.layout.teams[teamIndex].name,
39
+ owner: log.layout.teams[teamIndex].owner
40
+ })
41
+ });
42
+
43
+ return teams
44
+ }
45
+
46
+ const webappTeamToInGameTeam = (webappTeam) => {
47
+ const inGameTeam = {
48
+ formation: {
49
+ front: [front1Gotchi, front2Gotchi, front3Gotchi, front4Gotchi, front5Gotchi],
50
+ back: [back1Gotchi, back2Gotchi, back3Gotchi, back4Gotchi, back5Gotchi],
51
+ },
52
+ leader: webappTeam.leader,
53
+ name: webappTeam.name,
54
+ owner: webappTeam.owner
55
+ }
56
+
57
+ inGameTeam.formation.front.forEach(gotchi => {
58
+ // remove availableSpecials
59
+ delete gotchi.availableSpecials
60
+ })
61
+
62
+ return inGameTeam
63
+ }
64
+
65
+ const inGameTeamToWebappTeam = (inGameTeam) => {
66
+ const webappTeam = {
67
+ front1Gotchi: inGameTeam.formation.front[0],
68
+ front2Gotchi: inGameTeam.formation.front[1],
69
+ front3Gotchi: inGameTeam.formation.front[2],
70
+ front4Gotchi: inGameTeam.formation.front[3],
71
+ front5Gotchi: inGameTeam.formation.front[4],
72
+ back1Gotchi: inGameTeam.formation.back[0],
73
+ back2Gotchi: inGameTeam.formation.back[1],
74
+ back3Gotchi: inGameTeam.formation.back[2],
75
+ back4Gotchi: inGameTeam.formation.back[3],
76
+ back5Gotchi: inGameTeam.formation.back[4],
77
+ leader: inGameTeam.leader,
78
+ name: inGameTeam.name,
79
+ owner: inGameTeam.owner
80
+ }
81
+
82
+ return webappTeam
83
+ }
84
+
85
+ module.exports = {
86
+ logToInGameTeams,
87
+ webappTeamToInGameTeam,
88
+ inGameTeamToWebappTeam
48
89
  }
@@ -1,40 +1,40 @@
1
- const { ValidationError } = require('./errors')
2
-
3
- const compareLogs = (originalLogs, newLogs) => {
4
- // Check winner, loser and numOfTurns properties
5
- if (originalLogs.result.winner !== newLogs.result.winner) {
6
- throw new ValidationError(`Winner mismatch: ${originalLogs.result.winner} !== ${newLogs.result.winner}`, originalLogs, newLogs)
7
- }
8
-
9
- if (originalLogs.result.loser !== newLogs.result.loser) {
10
- throw new ValidationError(`Loser mismatch: ${originalLogs.result.loser} !== ${newLogs.result.loser}`, originalLogs, newLogs)
11
- }
12
-
13
- if (originalLogs.result.numOfTurns !== newLogs.result.numOfTurns) {
14
- throw new ValidationError(`numOfTurns mismatch: ${originalLogs.result.numOfTurns} !== ${newLogs.result.numOfTurns}`, originalLogs, newLogs)
15
- }
16
-
17
- // Validate winningTeam array
18
- originalLogs.result.winningTeam.forEach((gotchi) => {
19
- // Check id, name and health properties
20
- const gotchi2 = newLogs.result.winningTeam.find((gotchi2) => gotchi2.id === gotchi.id)
21
-
22
- if (!gotchi2) {
23
- throw new ValidationError(`Gotchi not found in winningTeam: ${gotchi.id}`, originalLogs, newLogs)
24
- }
25
-
26
- if (gotchi.name !== gotchi2.name) {
27
- throw new ValidationError(`Gotchi name mismatch: ${gotchi.name} !== ${gotchi2.name}`, originalLogs, newLogs)
28
- }
29
-
30
- if (gotchi.health !== gotchi2.health) {
31
- throw new ValidationError(`Gotchi health mismatch: ${gotchi.health} !== ${gotchi2.health}`, originalLogs, newLogs)
32
- }
33
- })
34
-
35
- return true
36
- }
37
-
38
- module.exports = {
39
- compareLogs
1
+ const { ValidationError } = require('./errors')
2
+
3
+ const compareLogs = (originalLogs, newLogs) => {
4
+ // Check winner, loser and numOfTurns properties
5
+ if (originalLogs.result.winner !== newLogs.result.winner) {
6
+ throw new ValidationError(`Winner mismatch: ${originalLogs.result.winner} !== ${newLogs.result.winner}`, originalLogs, newLogs)
7
+ }
8
+
9
+ if (originalLogs.result.loser !== newLogs.result.loser) {
10
+ throw new ValidationError(`Loser mismatch: ${originalLogs.result.loser} !== ${newLogs.result.loser}`, originalLogs, newLogs)
11
+ }
12
+
13
+ if (originalLogs.result.numOfTurns !== newLogs.result.numOfTurns) {
14
+ throw new ValidationError(`numOfTurns mismatch: ${originalLogs.result.numOfTurns} !== ${newLogs.result.numOfTurns}`, originalLogs, newLogs)
15
+ }
16
+
17
+ // Validate winningTeam array
18
+ originalLogs.result.winningTeam.forEach((gotchi) => {
19
+ // Check id, name and health properties
20
+ const gotchi2 = newLogs.result.winningTeam.find((gotchi2) => gotchi2.id === gotchi.id)
21
+
22
+ if (!gotchi2) {
23
+ throw new ValidationError(`Gotchi not found in winningTeam: ${gotchi.id}`, originalLogs, newLogs)
24
+ }
25
+
26
+ if (gotchi.name !== gotchi2.name) {
27
+ throw new ValidationError(`Gotchi name mismatch: ${gotchi.name} !== ${gotchi2.name}`, originalLogs, newLogs)
28
+ }
29
+
30
+ if (gotchi.health !== gotchi2.health) {
31
+ throw new ValidationError(`Gotchi health mismatch: ${gotchi.health} !== ${gotchi2.health}`, originalLogs, newLogs)
32
+ }
33
+ })
34
+
35
+ return true
36
+ }
37
+
38
+ module.exports = {
39
+ compareLogs
40
40
  }