Pelinor Posted August 2, 2023 Share Posted August 2, 2023 So many changes to the combat system have me confused. What is the new combat formula with the addition of girl equipment, loss of harmony, and girl "skills." 1 Link to comment Share on other sites More sharing options...
Horsting Posted August 3, 2023 Share Posted August 3, 2023 This is not a serious question but a (not so) subtile criticism about the raising complexity of the game, is it? What do you mean by "loss of harmony"? Link to comment Share on other sites More sharing options...
Pelinor Posted August 3, 2023 Author Share Posted August 3, 2023 Back when classes were a thing, which girls you went after mattered since harmony was related. Now the harmony stat is nothing more than a critical hit chance indicator (with RNG a poor one). The scripts have a combat simulator, but now the simulation results require a constant back and forth between the League overview page and the combat screen. I asked what the current algorithm is so I could add it to my spreadsheet. 1 Link to comment Share on other sites More sharing options...
Horsting Posted August 3, 2023 Share Posted August 3, 2023 1 hour ago, Pelinor said: I asked what the current algorithm is so I could add it to my spreadsheet. Was there ever a single formula possible? I mean the scripts do an iterative simulation for a reason, and do not try to calculate odds with a single analytic formula. I actually thought about develop some, which at least come close to the actual odds, but as far as I can think of it is impossible to get precise odds for a single opponent with an analytic formula, but rough averages which do not consider a specific number of rounds, of where you need to give a best guess number of rounds as input. Just to get a better idea what you are looking for: A spreadsheet where you can enter your's and opponent's stats (AP, ego, defence, harmony, crit bonus) to get some rough win chance and/or expected points? I also find it annoying that one must navigate back and forth to see the script results. But maybe Zoo and/or Tom will add them to the overview page/table as well. The update is pretty new and they just stated to tackle all the needed script updates to deal with the changes. What I currently do is using the new "Power" value as an idea in which area the odds are. This works quite well: Two opponents with the same power also have very similar simulation results. What I am missing is the team colour, to know whether I can raise my odds with a good counter team or not. Link to comment Share on other sites More sharing options...
Master-17 Posted August 4, 2023 Share Posted August 4, 2023 1 hour ago, Horsting said: Was there ever a single formula possible? I mean the scripts do an iterative simulation for a reason And you did not think that this formula was actually used in this script? Because it was the script that performed the full calculations for the simulation of battles. It doesn't send thousands of requests to the server, to which it gives thousands of answers so that the script brings them together. It takes the stats of both opponents (you and your enemy) and performs thousands of calculations using a specific formula and gives you the processed results. 4 Link to comment Share on other sites More sharing options...
Pelinor Posted August 4, 2023 Author Share Posted August 4, 2023 54 minutes ago, Master-17 said: It takes the stats of both opponents (you and your enemy) and performs thousands of calculations using a specific formula and gives you the processed results. Exactly (I could not have said this better) so. I don't want to reinvent the wheel, but I would like a "ballpark" formula for my spreadsheet so I don't always go for the default "attack strength" guideline on combat. Link to comment Share on other sites More sharing options...
Horsting Posted August 4, 2023 Share Posted August 4, 2023 There is a difference between a simulation, in this case one which step by steps loops through every round of every possible outcome of the fight, varying because you and your opponent do or do not crit in every round, and a single analytic formula which you could enter into a hand calculator after entering a number of variables and get a result. Excel (and alike) can deal with analytic formulas, but not with simulations. A "program" like the HH++ JavaScript tool can do simulations. Theoretically it is possible to create analytic formulas from simulations, but those are usually too complex for anyone to deal with. Best you can practically do is to approximate simulation results with an analytic formula, but it is not trivial to find one which does this well. Do you still have the spreadsheet you used for the old battle system, so I can get an idea how it worked? Link to comment Share on other sites More sharing options...
Moderator Ravi-Sama Posted August 4, 2023 Moderator Share Posted August 4, 2023 (edited) class Simulator { constructor({player, opponent, logging, preSim}) { this.player = player this.opponent = opponent this.logging = logging this.preSim = preSim } run () { if (this.logging) { console.log('Running simulation against', this.opponent.name) console.groupCollapsed('Player') console.dir({...this.player}) console.groupEnd() console.groupCollapsed(this.opponent.name) console.dir({...this.opponent}) console.groupEnd() } const setup = x => { x.critMultiplier = 2 + x.bonuses.critDamage x.dmg = Math.max(0, x.dmg) x.baseAttack = { probability: 1 - x.critchance, damageAmount: Math.ceil(x.dmg), healAmount: Math.ceil(x.dmg * x.bonuses.healOnHit) } x.critAttack = { probability: x.critchance, damageAmount: Math.ceil(x.dmg * x.critMultiplier), healAmount: Math.ceil(x.dmg * x.critMultiplier * x.bonuses.healOnHit) } x.hp = Math.ceil(x.hp) } setup(this.player) setup(this.opponent) this.cache = {} this.runs = 0 let ret try { // start simulation from player's turn ret = this.playerTurn(this.player.hp, this.opponent.hp, 0) } catch (error) { if (this.logging) console.log(`An error occurred during the simulation against ${this.opponent.name}`, error) return { points: [], win: Number.NaN, loss: Number.NaN, avgTurns: Number.NaN, scoreClass: 'minus' } } const sum = ret.win + ret.loss ret.win /= sum ret.loss /= sum ret.scoreClass = ret.win>0.9?'plus':ret.win<0.5?'minus':'close' if (this.logging) {console.log(`Ran ${this.runs} simulations against ${this.opponent.name}; aggregated win chance: ${ret.win * 100}%, average turns: ${ret.avgTurns}`)} if (this.preSim) { if (ret.win <= 0) ret.impossible = true if (ret.loss <= 0) ret.guaranteed = true } return ret } mergeResult(x, xProbability, y, yProbability) { const points = {} Object.entries(x.points).map(([point, probability]) => [point, probability * xProbability]) .concat(Object.entries(y.points).map(([point, probability]) => [point, probability * yProbability])) .forEach(([point, probability]) => { points[point] = (points[point] || 0) + probability }) const merge = (x, y) => x * xProbability + y * yProbability const win = merge(x.win, y.win) const loss = merge(x.loss, y.loss) const avgTurns = merge(x.avgTurns, y.avgTurns) return { points, win, loss, avgTurns } } playerTurn(playerHP, opponentHP, turns) { turns += 1 // avoid a stack overflow const maxAllowedTurns = 50 if (turns > maxAllowedTurns) throw new Error() // read cache const cachedResult = this.cache?.[playerHP]?.[opponentHP] if (cachedResult) return cachedResult // simulate base attack and critical attack const baseAtk = this.player.baseAttack const baseAtkResult = this.playerAttack(playerHP, opponentHP, baseAtk, turns) const critAtk = this.player.critAttack const critAtkResult = this.playerAttack(playerHP, opponentHP, critAtk, turns) // merge result const mergedResult = this.mergeResult(baseAtkResult, baseAtk.probability, critAtkResult, critAtk.probability) // count player's turn mergedResult.avgTurns += 1 // write cache if (!this.cache[playerHP]) this.cache[playerHP] = {} if (!this.cache[playerHP][opponentHP]) this.cache[playerHP][opponentHP] = {} this.cache[playerHP][opponentHP] = mergedResult return mergedResult } playerAttack(playerHP, opponentHP, attack, turns) { // damage opponentHP -= attack.damageAmount // heal on hit playerHP += attack.healAmount playerHP = Math.min(playerHP, this.player.hp) // check win if (opponentHP <= 0) { const point = 15 + Math.ceil(10 * playerHP / this.player.hp) this.runs += 1 return { points: { [point]: 1 }, win: 1, loss: 0, avgTurns: 0 } } // next turn return this.opponentTurn(playerHP, opponentHP, turns) } opponentTurn(playerHP, opponentHP, turns) { // simulate base attack and critical attack const baseAtk = this.opponent.baseAttack const baseAtkResult = this.opponentAttack(playerHP, opponentHP, baseAtk, turns) const critAtk = this.opponent.critAttack const critAtkResult = this.opponentAttack(playerHP, opponentHP, critAtk, turns) // merge result return this.mergeResult(baseAtkResult, baseAtk.probability, critAtkResult, critAtk.probability) } opponentAttack(playerHP, opponentHP, attack, turns) { // damage playerHP -= attack.damageAmount // heal on hit opponentHP += attack.healAmount opponentHP = Math.min(opponentHP, this.opponent.hp) // check loss if (playerHP <= 0) { const point = 3 + Math.ceil(10 * (this.opponent.hp - opponentHP) / this.opponent.hp) this.runs += 1 return { points: { [point]: 1 }, win: 0, loss: 1, avgTurns: 0 } } // next turn return this.playerTurn(playerHP, opponentHP, turns) } } export default Simulator window.HHPlusPlus.Simulator = Simulator Edited August 4, 2023 by Ravi-Sama Copy/Pasted correct Sim code. 1 Link to comment Share on other sites More sharing options...
Horsting Posted August 4, 2023 Share Posted August 4, 2023 1 hour ago, Ravi-Sama said: I think this is what the sim does: This is only the code for collecting player and opponent data and calculate the final crit chance for both, not the simulation itself. Here is the simulator of Zoo's BDSM version: https://github.com/zoop0kemon/hh-plus-plus/blob/main/src/modules/BattleSimulatorModule/Simulator.js Core part is those nested playerTurn => playerAttack (once with, once without crit, splitting the path) => opponentTurn => opponentAttack (once with, once without crit, splitting the path) iterations which build up a tree of all possible outcomes and aggregate the results along with their individual chance to happen. 2 Link to comment Share on other sites More sharing options...
Moderator Ravi-Sama Posted August 4, 2023 Moderator Share Posted August 4, 2023 11 minutes ago, Horsting said: This is only the code for collecting player and opponent data and calculate the final crit chance for both, not the simulation itself. Here is the simulator of Zoo's BDSM version: https://github.com/zoop0kemon/hh-plus-plus/blob/main/src/modules/BattleSimulatorModule/Simulator.js Core part is those nested playerTurn => playerAttack (once with, once without crit, splitting the path) => opponentTurn => opponentAttack (once with, once without crit, splitting the path) iterations which build up a tree of all possible outcomes and aggregate the results along with their individual chance to happen. Cool, makes sense. Link to comment Share on other sites More sharing options...
Recommended Posts
Please sign in to comment
You will be able to leave a comment after signing in
Sign In Now