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.
Files changed (67) hide show
  1. package/.vscode/settings.json +4 -4
  2. package/Dockerfile +9 -9
  3. package/README.md +49 -49
  4. package/cloudbuild.yaml +27 -27
  5. package/constants/tournamentManagerAbi.json +208 -208
  6. package/game-logic/index.js +7 -6
  7. package/game-logic/v1.4/constants.js +114 -114
  8. package/game-logic/v1.4/index.js +1366 -1366
  9. package/game-logic/v1.6/constants.js +123 -123
  10. package/game-logic/v1.6/index.js +1406 -1406
  11. package/game-logic/v1.7/constants.js +142 -140
  12. package/game-logic/v1.7/helpers.js +610 -593
  13. package/game-logic/v1.7/index.js +802 -795
  14. package/game-logic/v1.8/constants.js +135 -0
  15. package/game-logic/v1.8/helpers.js +628 -0
  16. package/game-logic/v1.8/index.js +826 -0
  17. package/index.js +12 -12
  18. package/package.json +26 -26
  19. package/schemas/team.json +349 -343
  20. package/scripts/balancing/createCSV.js +126 -126
  21. package/scripts/balancing/createTrainingGotchis.js +267 -0
  22. package/scripts/balancing/extractOnchainTraits.js +61 -0
  23. package/scripts/balancing/fixTrainingGotchis.js +155 -259
  24. package/scripts/balancing/processSims.js +229 -229
  25. package/scripts/balancing/sims.js +278 -278
  26. package/scripts/balancing/v1.7/class_combos.js +43 -43
  27. package/scripts/balancing/v1.7/mapGotchi.js +119 -0
  28. package/scripts/balancing/v1.7/setTeamPositions.js +105 -105
  29. package/scripts/balancing/v1.7/training_gotchis.json +20161 -20161
  30. package/scripts/balancing/v1.7/training_gotchis_traits.json +520 -0
  31. package/scripts/balancing/v1.7/trait_combos.json +9 -9
  32. package/scripts/balancing/v1.7.1/class_combos.js +43 -43
  33. package/scripts/balancing/v1.7.1/mapGotchi.js +119 -0
  34. package/scripts/balancing/v1.7.1/setTeamPositions.js +122 -122
  35. package/scripts/balancing/v1.7.1/training_gotchis.json +22401 -22401
  36. package/scripts/balancing/v1.7.1/training_gotchis_traits.json +520 -0
  37. package/scripts/balancing/v1.7.1/trait_combos.json +9 -9
  38. package/scripts/balancing/v1.7.2/class_combos.js +44 -0
  39. package/scripts/balancing/v1.7.2/mapGotchi.js +157 -0
  40. package/scripts/balancing/v1.7.2/setTeamPositions.js +122 -0
  41. package/scripts/balancing/v1.7.2/training_gotchis.json +22402 -0
  42. package/scripts/balancing/v1.7.2/training_gotchis_traits.json +520 -0
  43. package/scripts/balancing/v1.7.2/trait_combos.json +10 -0
  44. package/scripts/balancing/v1.7.3/class_combos.js +44 -0
  45. package/scripts/balancing/v1.7.3/mapGotchi.js +164 -0
  46. package/scripts/balancing/v1.7.3/setTeamPositions.js +122 -0
  47. package/scripts/balancing/v1.7.3/training_gotchis.json +22402 -0
  48. package/scripts/balancing/v1.7.3/training_gotchis_traits.json +37 -0
  49. package/scripts/balancing/v1.7.3/trait_combos.json +10 -0
  50. package/scripts/data/team1.json +213 -213
  51. package/scripts/data/team2.json +200 -200
  52. package/scripts/data/tournaments.json +71 -66
  53. package/scripts/{runBattle.js → runLocalBattle.js} +18 -18
  54. package/scripts/runRealBattle.js +52 -0
  55. package/scripts/simRealBattle.js +121 -0
  56. package/scripts/validateBattle.js +83 -70
  57. package/scripts/validateTournament.js +101 -101
  58. package/utils/contracts.js +12 -12
  59. package/utils/errors.js +29 -29
  60. package/utils/mapGotchi.js +119 -0
  61. package/utils/transforms.js +89 -88
  62. package/utils/validations.js +39 -39
  63. package/debug.log +0 -2
  64. package/game-logic/v1.6/debug.log +0 -1
  65. package/game-logic/v1.7/debug.log +0 -3
  66. package/scripts/data/debug.log +0 -2
  67. package/scripts/debug.log +0 -1
@@ -1,278 +1,278 @@
1
- require('dotenv').config()
2
- const path = require('path')
3
- const crypto = require('crypto')
4
- const { Storage } = require('@google-cloud/storage')
5
- const storage = new Storage()
6
- const classes = ['Ninja','Enlightened','Cleaver','Tank','Cursed','Healer', 'Mage', 'Troll']
7
-
8
- /**
9
- * Create teams from all the possible trait combinations from the class combination
10
- * @param {Array} classCombo The class combinations
11
- * @param {Array} classTraitCombos The trait combinations for each class
12
- * @param {Array} powerLevels The power levels
13
- * @param {Array} trainingGotchis The training gotchis
14
- * @param {Boolean} useTraitSets If false then exhaustive search is done, if true then only the trait sets are used
15
- * @returns {Object} A team object
16
- */
17
- const createTeamIndexes = (classCombos, classTraitCombos, powerLevels, trainingGotchis, useTraitSets) => {
18
-
19
- const teams = []
20
-
21
- if (!useTraitSets) {
22
- classCombos.forEach(classCombo => {
23
- powerLevels.forEach(powerLevel => {
24
- // Loop over each class in the classCombo
25
- for(let i = 0; i < classCombo.length; i++) {
26
- classTraitCombos[classCombo[i] - 1].forEach(traitSet1 => {
27
- for (let j = i + 1; j < classCombo.length; j++) {
28
- classTraitCombos[classCombo[j] - 1].forEach(traitSet2 => {
29
- for (let k = j + 1; k < classCombo.length; k++) {
30
- classTraitCombos[classCombo[k] - 1].forEach(traitSet3 => {
31
- for (let l = k + 1; l < classCombo.length; l++) {
32
- classTraitCombos[classCombo[l] - 1].forEach(traitSet4 => {
33
- for (let m = l + 1; m < classCombo.length; m++) {
34
- classTraitCombos[classCombo[m] - 1].forEach(traitSet5 => {
35
- const team = [];
36
-
37
- [traitSet1, traitSet2, traitSet3, traitSet4, traitSet5].forEach((traitSet, index) => {
38
- const gotchiName = `${powerLevel} ${traitSet} ${classes[classCombo[index] - 1]}`
39
- const gotchi = trainingGotchis.find(gotchi => {
40
- return gotchi.name === gotchiName
41
- })
42
-
43
- if (!gotchi) throw new Error(`Gotchi not found: "${gotchiName}"`)
44
-
45
- team.push(gotchi.id)
46
- })
47
-
48
- teams.push(team)
49
- })
50
- }
51
- })
52
- }
53
- })
54
- }
55
- })
56
- }
57
- })
58
- }
59
- })
60
- })
61
- } else {
62
- classCombos.forEach(classCombo => {
63
- powerLevels.forEach(powerLevel => {
64
- // Loop over how many trait sets there are in the classTraitCombos array
65
- classTraitCombos[0].forEach((x, i) => {
66
- const team = []
67
- classCombo.forEach(classIndex => {
68
- const gotchiName = `${powerLevel} ${classTraitCombos[classIndex - 1][i]} ${classes[classIndex - 1]}`
69
- const gotchi = trainingGotchis.find(gotchi => {
70
- return gotchi.name === gotchiName
71
- })
72
-
73
- if (!gotchi) throw new Error(`Gotchi not found: "${gotchiName}"`)
74
-
75
- team.push(gotchi.id)
76
- })
77
- teams.push(team)
78
- })
79
- })
80
- })
81
- }
82
-
83
-
84
-
85
- return teams
86
- }
87
-
88
- /**
89
- * Creates an in game team object from a team index
90
- * @param {Array} teamIndex An array of gotchi ids
91
- * @returns {Object} An in game team object
92
- */
93
- const createTeamFromTeamIndex = (teamIndex, trainingGotchis, setTeamPositions) => {
94
- const team = {
95
- formation: {
96
- front: [null, null, null, null, null],
97
- back: [null, null, null, null, null],
98
- },
99
- leader: null,
100
- name: null,
101
- owner: null
102
- }
103
-
104
- // Put all in the back row for now
105
- team.formation.back = teamIndex.map(gotchiId => {
106
- const gotchi = trainingGotchis.find(gotchi => gotchi.id === gotchiId)
107
-
108
- if (!gotchi) throw new Error(`Gotchi not found with id: "${gotchiId}"`)
109
-
110
- return gotchi
111
- })
112
-
113
- team.leader = team.formation.back[0].id
114
- team.name = `${team.formation.back[0].name[0]} ${teamIndex}` // e.g. "M 1,2,3,4,5"
115
- team.owner = '0x0000000000000000000000000000000000000000'
116
-
117
- // Set the team positions for each gotchi being in the front or back row
118
- setTeamPositions(team)
119
-
120
- return team
121
- }
122
-
123
- const getGotchisSimNameFromTeam = (team) => {
124
- const names = [];
125
-
126
- [0,1,2,3,4].forEach(i => {
127
- const gotchi = team.formation.back[i] || team.formation.front[i]
128
- const position = !!team.formation.back[i] ? 'B' : 'F'
129
-
130
- const nameParts = gotchi.name.split(' ')
131
- // Return e.g. "R|++++|1_B"
132
- names.push(`${nameParts[0][0]}|${nameParts[1]}|${classes.indexOf(nameParts[2]) + 1}_${position}`)
133
- })
134
- return names
135
- }
136
-
137
- const runSims = async (simsVersion, gameLogicVersion, simsPerMatchup) => {
138
- const trainingGotchis = require(`./${simsVersion}/training_gotchis.json`)
139
- const classCombos = require(`./${simsVersion}/class_combos.js`)
140
- const classTraitCombos = require(`./${simsVersion}/trait_combos.json`)
141
- const setTeamPositions = require(`./${simsVersion}/setTeamPositions`)
142
- const gameLogic = require("../../game-logic")[gameLogicVersion].gameLoop
143
-
144
- const attackingPowerLevels = ['Godlike']
145
- const defendingPowerLevels = ['Godlike', 'Mythical', 'Legendary']
146
-
147
- const attackingTeamIndexes = createTeamIndexes(classCombos, classTraitCombos, attackingPowerLevels, trainingGotchis, true)
148
-
149
- // console.log(`Running sims for ${attackingTeamIndexes.length} attacking teams`)
150
-
151
- const defendingTeamIndexes = createTeamIndexes(classCombos, classTraitCombos, defendingPowerLevels, trainingGotchis, true)
152
-
153
- // console.log(`Against ${defendingTeamIndexes.length} defending teams`)
154
-
155
- // Which attacking team are we running the sims on?
156
- // If running on Cloud Run, use the task index
157
- // If running locally, use the command line argument or default to 0
158
- const attackingTeamIndex = process.env.CLOUD_RUN_TASK_INDEX ? parseInt(process.env.CLOUD_RUN_TASK_INDEX) : parseInt(process.argv[2]) || 0
159
-
160
- // Get the attacking team
161
- const attackingTeam = createTeamFromTeamIndex(attackingTeamIndexes[attackingTeamIndex], trainingGotchis, setTeamPositions)
162
- const gotchiSimNames = getGotchisSimNameFromTeam(attackingTeam)
163
- // Run the sims for each defending team
164
- const results = {
165
- id: attackingTeamIndex,
166
- slot1: gotchiSimNames[0],
167
- slot2: gotchiSimNames[1],
168
- slot3: gotchiSimNames[2],
169
- slot4: gotchiSimNames[3],
170
- slot5: gotchiSimNames[4],
171
- wins: 0,
172
- draws: 0,
173
- losses: 0
174
- }
175
-
176
- defendingPowerLevels.forEach(powerLevel => {
177
- results[`wins${powerLevel}`] = 0
178
- results[`draws${powerLevel}`] = 0
179
- results[`losses${powerLevel}`] = 0
180
- })
181
-
182
- console.time(`Sims for ${attackingTeam.name}`)
183
-
184
- defendingTeamIndexes.forEach((defendingTeamIndex, i) => {
185
- const defendingTeam = createTeamFromTeamIndex(defendingTeamIndex, trainingGotchis, setTeamPositions)
186
-
187
- let matchupWins = 0
188
- let matchupDraws = 0
189
- let matchupLosses = 0
190
-
191
- // Run the sims
192
- Array(simsPerMatchup).fill(null).forEach(() => {
193
- // Quit early if result is already determined
194
- if (matchupWins >= simsPerMatchup / 2 ||
195
- matchupLosses >= simsPerMatchup / 2 ||
196
- matchupDraws >= simsPerMatchup / 2) return
197
-
198
- const logs = gameLogic(attackingTeam, defendingTeam, crypto.randomBytes(32).toString('hex'))
199
-
200
- if (logs.result.winner === 1) matchupWins++
201
- if (logs.result.winner === 0) matchupDraws++
202
- if (logs.result.winner === 2) matchupLosses++
203
- })
204
-
205
- // Get first letter of oppeonent team name to determine power level
206
- const powerLevel = defendingPowerLevels.find(name => name[0] === defendingTeam.name[0])
207
-
208
- if (matchupWins >= simsPerMatchup / 2) {
209
- results.wins++
210
- results[`wins${powerLevel}`]++
211
- }
212
-
213
- if (matchupDraws >= simsPerMatchup / 2) {
214
- results.draws++
215
- results[`draws${powerLevel}`]++
216
- }
217
-
218
- if (matchupLosses >= simsPerMatchup / 2) {
219
- results.losses++
220
- results[`losses${powerLevel}`]++
221
- }
222
- })
223
-
224
- console.timeEnd(`Sims for ${attackingTeam.name}`)
225
-
226
- // Check total wins, draws, losses to make sure they add up to number of defending teams
227
- const totalMatchups = results.wins + results.draws + results.losses
228
- if (totalMatchups !== defendingTeamIndexes.length) {
229
- throw new Error(`Total matchups (${totalMatchups}) does not match number of defending teams (${defendingTeamIndexes.length})`)
230
- }
231
-
232
- if (process.env.CLOUD_RUN_JOB && process.env.SIMS_BUCKET) {
233
- // Save as JSON to GCS
234
- try {
235
- await storage.bucket(process.env.SIMS_BUCKET).file(`${process.env.CLOUD_RUN_EXECUTION}_${attackingTeamIndex}.json`).save(JSON.stringify(results))
236
- } catch (err) {
237
- throw err
238
- }
239
-
240
- } else {
241
- console.log('Results', results)
242
-
243
- // Test saving to GCS
244
- if (false) {
245
- process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(__dirname, '../../keyfile.json')
246
- try {
247
- await storage.bucket(process.env.SIMS_BUCKET).file(`avg-sims-znw68_${attackingTeamIndex}.json`).save(JSON.stringify(results))
248
- } catch (err) {
249
- throw err
250
- }
251
- }
252
- }
253
- }
254
-
255
- module.exports = runSims
256
-
257
- // If running from the command line, run the sims
258
- // 1st argument is the attacking team index
259
- // 2nd argument is the sims version
260
- // 3rd argument is the game logic version
261
- // 4th argument is the number of sims per matchup
262
- // node scripts/balancing/sims.js 0 v1.7.1 v1.7 3 true
263
- if (require.main === module) {
264
- const simsVersion = process.env.SIMS_VERSION || process.argv[3] || 'v1.7'
265
- const gameLogicVersion = process.env.GAME_LOGIC_VERSION || process.argv[4] || 'v1.7'
266
- const simsPerMatchup = parseInt(process.env.SIMS_PER_MATCHUP) || parseInt(process.argv[5]) || 3
267
- const useAvg = process.env.USE_AVG || process.argv[6] === 'true' || false
268
-
269
- runSims(simsVersion, gameLogicVersion, simsPerMatchup, useAvg)
270
- .then(() => {
271
- console.log('Done')
272
- process.exit(0)
273
- })
274
- .catch((err) => {
275
- console.error(err)
276
- process.exit(1)
277
- })
278
- }
1
+ require('dotenv').config()
2
+ const path = require('path')
3
+ const crypto = require('crypto')
4
+ const { Storage } = require('@google-cloud/storage')
5
+ const storage = new Storage()
6
+ const classes = ['Ninja','Enlightened','Cleaver','Tank','Cursed','Healer', 'Mage', 'Troll']
7
+
8
+ /**
9
+ * Create teams from all the possible trait combinations from the class combination
10
+ * @param {Array} classCombo The class combinations
11
+ * @param {Array} classTraitCombos The trait combinations for each class
12
+ * @param {Array} powerLevels The power levels
13
+ * @param {Array} trainingGotchis The training gotchis
14
+ * @param {Boolean} useTraitSets If false then exhaustive search is done, if true then only the trait sets are used
15
+ * @returns {Object} A team object
16
+ */
17
+ const createTeamIndexes = (classCombos, classTraitCombos, powerLevels, trainingGotchis, useTraitSets) => {
18
+
19
+ const teams = []
20
+
21
+ if (!useTraitSets) {
22
+ classCombos.forEach(classCombo => {
23
+ powerLevels.forEach(powerLevel => {
24
+ // Loop over each class in the classCombo
25
+ for(let i = 0; i < classCombo.length; i++) {
26
+ classTraitCombos[classCombo[i] - 1].forEach(traitSet1 => {
27
+ for (let j = i + 1; j < classCombo.length; j++) {
28
+ classTraitCombos[classCombo[j] - 1].forEach(traitSet2 => {
29
+ for (let k = j + 1; k < classCombo.length; k++) {
30
+ classTraitCombos[classCombo[k] - 1].forEach(traitSet3 => {
31
+ for (let l = k + 1; l < classCombo.length; l++) {
32
+ classTraitCombos[classCombo[l] - 1].forEach(traitSet4 => {
33
+ for (let m = l + 1; m < classCombo.length; m++) {
34
+ classTraitCombos[classCombo[m] - 1].forEach(traitSet5 => {
35
+ const team = [];
36
+
37
+ [traitSet1, traitSet2, traitSet3, traitSet4, traitSet5].forEach((traitSet, index) => {
38
+ const gotchiName = `${powerLevel} ${traitSet} ${classes[classCombo[index] - 1]}`
39
+ const gotchi = trainingGotchis.find(gotchi => {
40
+ return gotchi.name === gotchiName
41
+ })
42
+
43
+ if (!gotchi) throw new Error(`Gotchi not found: "${gotchiName}"`)
44
+
45
+ team.push(gotchi.id)
46
+ })
47
+
48
+ teams.push(team)
49
+ })
50
+ }
51
+ })
52
+ }
53
+ })
54
+ }
55
+ })
56
+ }
57
+ })
58
+ }
59
+ })
60
+ })
61
+ } else {
62
+ classCombos.forEach(classCombo => {
63
+ powerLevels.forEach(powerLevel => {
64
+ // Loop over how many trait sets there are in the classTraitCombos array
65
+ classTraitCombos[0].forEach((x, i) => {
66
+ const team = []
67
+ classCombo.forEach(classIndex => {
68
+ const gotchiName = `${powerLevel} ${classTraitCombos[classIndex - 1][i]} ${classes[classIndex - 1]}`
69
+ const gotchi = trainingGotchis.find(gotchi => {
70
+ return gotchi.name === gotchiName
71
+ })
72
+
73
+ if (!gotchi) throw new Error(`Gotchi not found: "${gotchiName}"`)
74
+
75
+ team.push(gotchi.id)
76
+ })
77
+ teams.push(team)
78
+ })
79
+ })
80
+ })
81
+ }
82
+
83
+
84
+
85
+ return teams
86
+ }
87
+
88
+ /**
89
+ * Creates an in game team object from a team index
90
+ * @param {Array} teamIndex An array of gotchi ids
91
+ * @returns {Object} An in game team object
92
+ */
93
+ const createTeamFromTeamIndex = (teamIndex, trainingGotchis, setTeamPositions) => {
94
+ const team = {
95
+ formation: {
96
+ front: [null, null, null, null, null],
97
+ back: [null, null, null, null, null],
98
+ },
99
+ leader: null,
100
+ name: null,
101
+ owner: null
102
+ }
103
+
104
+ // Put all in the back row for now
105
+ team.formation.back = teamIndex.map(gotchiId => {
106
+ const gotchi = trainingGotchis.find(gotchi => gotchi.id === gotchiId)
107
+
108
+ if (!gotchi) throw new Error(`Gotchi not found with id: "${gotchiId}"`)
109
+
110
+ return gotchi
111
+ })
112
+
113
+ team.leader = team.formation.back[0].id
114
+ team.name = `${team.formation.back[0].name[0]} ${teamIndex}` // e.g. "M 1,2,3,4,5"
115
+ team.owner = '0x0000000000000000000000000000000000000000'
116
+
117
+ // Set the team positions for each gotchi being in the front or back row
118
+ setTeamPositions(team)
119
+
120
+ return team
121
+ }
122
+
123
+ const getGotchisSimNameFromTeam = (team) => {
124
+ const names = [];
125
+
126
+ [0,1,2,3,4].forEach(i => {
127
+ const gotchi = team.formation.back[i] || team.formation.front[i]
128
+ const position = !!team.formation.back[i] ? 'B' : 'F'
129
+
130
+ const nameParts = gotchi.name.split(' ')
131
+ // Return e.g. "R|++++|1_B"
132
+ names.push(`${nameParts[0][0]}|${nameParts[1]}|${classes.indexOf(nameParts[2]) + 1}_${position}`)
133
+ })
134
+ return names
135
+ }
136
+
137
+ const runSims = async (simsVersion, gameLogicVersion, simsPerMatchup) => {
138
+ const trainingGotchis = require(`./${simsVersion}/training_gotchis.json`)
139
+ const classCombos = require(`./${simsVersion}/class_combos.js`)
140
+ const classTraitCombos = require(`./${simsVersion}/trait_combos.json`)
141
+ const setTeamPositions = require(`./${simsVersion}/setTeamPositions`)
142
+ const gameLogic = require("../../game-logic")[gameLogicVersion].gameLoop
143
+
144
+ const attackingPowerLevels = ['Godlike']
145
+ const defendingPowerLevels = ['Godlike', 'Mythical', 'Legendary']
146
+
147
+ const attackingTeamIndexes = createTeamIndexes(classCombos, classTraitCombos, attackingPowerLevels, trainingGotchis, true)
148
+
149
+ // console.log(`Running sims for ${attackingTeamIndexes.length} attacking teams`)
150
+
151
+ const defendingTeamIndexes = createTeamIndexes(classCombos, classTraitCombos, defendingPowerLevels, trainingGotchis, true)
152
+
153
+ // console.log(`Against ${defendingTeamIndexes.length} defending teams`)
154
+
155
+ // Which attacking team are we running the sims on?
156
+ // If running on Cloud Run, use the task index
157
+ // If running locally, use the command line argument or default to 0
158
+ const attackingTeamIndex = process.env.CLOUD_RUN_TASK_INDEX ? parseInt(process.env.CLOUD_RUN_TASK_INDEX) : parseInt(process.argv[2]) || 0
159
+
160
+ // Get the attacking team
161
+ const attackingTeam = createTeamFromTeamIndex(attackingTeamIndexes[attackingTeamIndex], trainingGotchis, setTeamPositions)
162
+ const gotchiSimNames = getGotchisSimNameFromTeam(attackingTeam)
163
+ // Run the sims for each defending team
164
+ const results = {
165
+ id: attackingTeamIndex,
166
+ slot1: gotchiSimNames[0],
167
+ slot2: gotchiSimNames[1],
168
+ slot3: gotchiSimNames[2],
169
+ slot4: gotchiSimNames[3],
170
+ slot5: gotchiSimNames[4],
171
+ wins: 0,
172
+ draws: 0,
173
+ losses: 0
174
+ }
175
+
176
+ defendingPowerLevels.forEach(powerLevel => {
177
+ results[`wins${powerLevel}`] = 0
178
+ results[`draws${powerLevel}`] = 0
179
+ results[`losses${powerLevel}`] = 0
180
+ })
181
+
182
+ console.time(`Sims for ${attackingTeam.name}`)
183
+
184
+ defendingTeamIndexes.forEach((defendingTeamIndex, i) => {
185
+ const defendingTeam = createTeamFromTeamIndex(defendingTeamIndex, trainingGotchis, setTeamPositions)
186
+
187
+ let matchupWins = 0
188
+ let matchupDraws = 0
189
+ let matchupLosses = 0
190
+
191
+ // Run the sims
192
+ Array(simsPerMatchup).fill(null).forEach(() => {
193
+ // Quit early if result is already determined
194
+ if (matchupWins >= simsPerMatchup / 2 ||
195
+ matchupLosses >= simsPerMatchup / 2 ||
196
+ matchupDraws >= simsPerMatchup / 2) return
197
+
198
+ const logs = gameLogic(attackingTeam, defendingTeam, crypto.randomBytes(32).toString('hex'))
199
+
200
+ if (logs.result.winner === 1) matchupWins++
201
+ if (logs.result.winner === 0) matchupDraws++
202
+ if (logs.result.winner === 2) matchupLosses++
203
+ })
204
+
205
+ // Get first letter of oppeonent team name to determine power level
206
+ const powerLevel = defendingPowerLevels.find(name => name[0] === defendingTeam.name[0])
207
+
208
+ if (matchupWins >= simsPerMatchup / 2) {
209
+ results.wins++
210
+ results[`wins${powerLevel}`]++
211
+ }
212
+
213
+ if (matchupDraws >= simsPerMatchup / 2) {
214
+ results.draws++
215
+ results[`draws${powerLevel}`]++
216
+ }
217
+
218
+ if (matchupLosses >= simsPerMatchup / 2) {
219
+ results.losses++
220
+ results[`losses${powerLevel}`]++
221
+ }
222
+ })
223
+
224
+ console.timeEnd(`Sims for ${attackingTeam.name}`)
225
+
226
+ // Check total wins, draws, losses to make sure they add up to number of defending teams
227
+ const totalMatchups = results.wins + results.draws + results.losses
228
+ if (totalMatchups !== defendingTeamIndexes.length) {
229
+ throw new Error(`Total matchups (${totalMatchups}) does not match number of defending teams (${defendingTeamIndexes.length})`)
230
+ }
231
+
232
+ if (process.env.CLOUD_RUN_JOB && process.env.SIMS_BUCKET) {
233
+ // Save as JSON to GCS
234
+ try {
235
+ await storage.bucket(process.env.SIMS_BUCKET).file(`${process.env.CLOUD_RUN_EXECUTION}_${attackingTeamIndex}.json`).save(JSON.stringify(results))
236
+ } catch (err) {
237
+ throw err
238
+ }
239
+
240
+ } else {
241
+ console.log('Results', results)
242
+
243
+ // Test saving to GCS
244
+ if (false) {
245
+ process.env.GOOGLE_APPLICATION_CREDENTIALS = path.join(__dirname, '../../keyfile.json')
246
+ try {
247
+ await storage.bucket(process.env.SIMS_BUCKET).file(`avg-sims-znw68_${attackingTeamIndex}.json`).save(JSON.stringify(results))
248
+ } catch (err) {
249
+ throw err
250
+ }
251
+ }
252
+ }
253
+ }
254
+
255
+ module.exports = runSims
256
+
257
+ // If running from the command line, run the sims
258
+ // 1st argument is the attacking team index
259
+ // 2nd argument is the sims version
260
+ // 3rd argument is the game logic version
261
+ // 4th argument is the number of sims per matchup
262
+ // node scripts/balancing/sims.js 0 v1.7.3 v1.7 3 true
263
+ if (require.main === module) {
264
+ const simsVersion = process.env.SIMS_VERSION || process.argv[3] || 'v1.7.3'
265
+ const gameLogicVersion = process.env.GAME_LOGIC_VERSION || process.argv[4] || 'v1.7'
266
+ const simsPerMatchup = parseInt(process.env.SIMS_PER_MATCHUP) || parseInt(process.argv[5]) || 3
267
+ const useAvg = process.env.USE_AVG || process.argv[6] === 'true' || false
268
+
269
+ runSims(simsVersion, gameLogicVersion, simsPerMatchup, useAvg)
270
+ .then(() => {
271
+ console.log('Done')
272
+ process.exit(0)
273
+ })
274
+ .catch((err) => {
275
+ console.error(err)
276
+ process.exit(1)
277
+ })
278
+ }
@@ -1,44 +1,44 @@
1
- const generateClassCombinations = () => {
2
- // Generate combinations of the 8 classes for the 4 non-leader spots
3
- const combinations = []
4
-
5
- for (let i = 1; i <= 8; i++) {
6
- for (let j = 1; j <= 8; j++) {
7
- for (let k = 1; k <= 8; k++) {
8
- for (let l = 1; l <= 8; l++) {
9
- combinations.push([i, j, k, l].sort())
10
- }
11
- }
12
- }
13
- }
14
-
15
- // Remove duplicate combinations, so [1,1,1,1] is allowed but [1,1,1,2] and [1,1,2,1] are duplicates
16
- // Keep as numbers for now, convert to strings to remove duplicates, then convert back to numbers
17
- const uniqueCombinations = [...new Set(combinations.map((combination) => combination.join('')))].map((combination) => combination.split('').map((number) => parseInt(number)))
18
-
19
- return uniqueCombinations
20
- }
21
-
22
- const getCombinationsForALeader = (leaderClass) => {
23
- const combinations = generateClassCombinations()
24
-
25
- const combinationsForALeader = combinations.map((combination) => {
26
- return [leaderClass, ...combination]
27
- })
28
-
29
- return combinationsForALeader
30
- }
31
-
32
- const getAllClassCombos = () => {
33
- const allClassCombos = []
34
-
35
- for (let i = 1; i <= 8; i++) {
36
- allClassCombos.push(...getCombinationsForALeader(i))
37
- }
38
-
39
- return allClassCombos
40
- }
41
-
42
- const allClassCombos = getAllClassCombos()
43
-
1
+ const generateClassCombinations = () => {
2
+ // Generate combinations of the 8 classes for the 4 non-leader spots
3
+ const combinations = []
4
+
5
+ for (let i = 1; i <= 8; i++) {
6
+ for (let j = 1; j <= 8; j++) {
7
+ for (let k = 1; k <= 8; k++) {
8
+ for (let l = 1; l <= 8; l++) {
9
+ combinations.push([i, j, k, l].sort())
10
+ }
11
+ }
12
+ }
13
+ }
14
+
15
+ // Remove duplicate combinations, so [1,1,1,1] is allowed but [1,1,1,2] and [1,1,2,1] are duplicates
16
+ // Keep as numbers for now, convert to strings to remove duplicates, then convert back to numbers
17
+ const uniqueCombinations = [...new Set(combinations.map((combination) => combination.join('')))].map((combination) => combination.split('').map((number) => parseInt(number)))
18
+
19
+ return uniqueCombinations
20
+ }
21
+
22
+ const getCombinationsForALeader = (leaderClass) => {
23
+ const combinations = generateClassCombinations()
24
+
25
+ const combinationsForALeader = combinations.map((combination) => {
26
+ return [leaderClass, ...combination]
27
+ })
28
+
29
+ return combinationsForALeader
30
+ }
31
+
32
+ const getAllClassCombos = () => {
33
+ const allClassCombos = []
34
+
35
+ for (let i = 1; i <= 8; i++) {
36
+ allClassCombos.push(...getCombinationsForALeader(i))
37
+ }
38
+
39
+ return allClassCombos
40
+ }
41
+
42
+ const allClassCombos = getAllClassCombos()
43
+
44
44
  module.exports = allClassCombos