using System; using System.Linq; using System.Collections.Generic; public static class Consts { public const double eps = 0.001; } public class MyRotation: PaladinRotation { public MyRotation (bool hasDp, bool has4pc, double executionTime, double crit, double haste, int efGay) :base (hasDp, has4pc, executionTime, crit, haste, efGay) { } public override void Step() { // buff expires soon if (FreeEF && FreeEF.Time <= Hasted(3)) { CastEF(); } else if (DP && DP.Time <= Hasted(3)) { CastEF(); } else if (FreeLoD && FreeLoD.Time <= Hasted(3)) { CastLoD(); } //capped else if (HolyPower == 5) { CastEF(); } //HS else if (HS) { CastHS(); } // buff expires soon if we wait 0.5s for HS else if (DP && DP.Time <= Hasted(3) + 0.5) { CastEF(); } else if (FreeEF && FreeEF.Time <= Hasted(3) + 0.5) { CastEF(); } else if (FreeLoD && FreeLoD.Time <= Hasted(3) + 0.5) { CastLoD(); } //wait 0.5s for HS else if (HS.Time < 0.5) { CastNothing(HS.Time); } //free casts else if (DP && DP.Time >= Hasted(1.5)) { CastEF(); } else if (FreeEF && FreeEF.Time >= Hasted(1.5)) { CastEF(); } else if (FreeLoD && FreeLoD.Time >= Hasted(1.5)) { CastLoD(); } //3 hp LoD else if (HolyPower >= 3) { CastEF(); } //IoL HL else if (IoL) { CastHL(); } //1-2hp EF else if (EFGay > 0 && HolyPower >= EFGay) { CastEF(); } //wait 1s for HS else if (HS.Time < 1) { CastNothing(HS.Time); } //HL filler least priority else { CastHL(); } } } public class Timer { public Timer(List timers, double defaultTime) { _defaultTime = defaultTime; _active = false; _time = 0; timers.Add(this); } private bool _active; private double _time; private double _defaultTime; public double Time { get { return _time; } } protected bool Active { get { return _active; } } public void Set() { _time = _defaultTime; _active = true; } public void Reset() { _active = false; _time = 0; } public void Update(double time) { if(!_active) return; _time -= time; if(_time < Consts.eps) Reset(); if (TimeoutHandler!=null) TimeoutHandler(); } public Action TimeoutHandler; } public class Buff: Timer { public Buff(List timers, double time): base(timers, time) { } public static implicit operator bool(Buff b) { return b.Active; } } public class Cooldown: Timer { public Cooldown(List timers, double time): base(timers, time) { } public static implicit operator bool(Cooldown cd) { return !cd.Active; } } public class PaladinRotation { private List _timers = new List(); private double _time = 0; private readonly double _maxTime; protected readonly Buff FreeEF; protected readonly Buff FreeLoD; protected readonly Buff DoubleHS; protected readonly Buff DP; protected readonly Buff IoL; protected readonly Cooldown HS; protected int HolyPower = 0; protected readonly bool Has4pc; protected readonly bool HasDP; protected readonly int EFGay; protected readonly double Crit; protected readonly double Haste; protected readonly double _hasteMult; private readonly Result _result = new Result(); public void CastHS() { if (!HS) throw new Exception("hs cd"); if (DoubleHS) { DoubleHS.Reset(); } else { HS.Set(); } if (HolyPower < 5) HolyPower++; if (Roll(Crit * 2)) { _result.HSCrits++; IoL.Set(); } AddTime(Hasted(1.5)); _result.HSCount++; } public void CastHL() { if(IoL) { IoL.Reset(); AddTime(Hasted(1)); if (Roll(10)) DoubleHS.Set(); AddTime(1 - Hasted(1)); _result.IoLHL++; } else { if (Roll(10)) DoubleHS.Set(); AddTime(Hasted(2.5)); } _result.HLCount++; } public void CastEF() { if (FreeEF && FreeEF.Time >= Hasted(1.5)) { FreeEF.Reset(); _result.EF3++; if (HasDP && Roll(25)) DP.Set(); if (Has4pc && Roll(20)) FreeLoD.Set(); } else if (DP && DP.Time >= Hasted(1.5)) { DP.Reset(); _result.EF3++; if (HasDP && Roll(25)) DP.Set(); if (Has4pc && Roll(20)) FreeLoD.Set(); } else if (HolyPower >= 3) { HolyPower -= 3; _result.EF3++; if (HasDP && Roll(25)) DP.Set(); if (Has4pc && Roll(20)) FreeLoD.Set(); } else if (EFGay > 0 && HolyPower >= EFGay) { switch(HolyPower) { case 1: _result.EF1++; if (HasDP && Roll(25/3.0)) DP.Set(); if (Has4pc && Roll(20)) FreeLoD.Set(); break; case 2: _result.EF2++; if (HasDP && Roll(25*2/3.0)) DP.Set(); if (Has4pc && Roll(20)) FreeLoD.Set(); break; default: throw new Exception("failed 1HP EF gay attempt"); break; } HolyPower = 0; } else { throw new Exception("1HP EF gay attempt"); } AddTime(Hasted(1.5)); _result.EFCount++; } public void CastLoD() { if (FreeLoD && FreeLoD.Time >= Hasted(1.5)) { FreeLoD.Reset(); } else if (DP && DP.Time >= Hasted(1.5)) { DP.Reset(); } else if (HolyPower >= 3) { HolyPower -= 3; } else { throw new Exception("1HP LoD gay attempt"); } AddTime(Hasted(1.5)); if (HasDP && Roll(25)) DP.Set(); if (Has4pc && Roll(25)) FreeEF.Set(); _result.LoDCount++; } public void CastNothing(double time) { AddTime(time); _result.WaitTime+=time; } public bool Roll(double chance) { return RandomHelper.R.NextDouble() < chance / 100; } public double Hasted(double time) { return time * _hasteMult; } public PaladinRotation(bool hasDp, bool has4pc, double executionTime, double crit, double haste, int efGay) { HasDP = hasDp; Has4pc = has4pc; EFGay = efGay; _maxTime = executionTime; Crit = crit; Haste = haste; _hasteMult = 1 / (1 + Haste / 100); FreeEF = new Buff(_timers, 10); FreeLoD = new Buff(_timers, 10); DP = new Buff(_timers, 8); DoubleHS = new Buff(_timers, 15); IoL = new Buff(_timers, 15); HS = new Cooldown(_timers, Hasted(6)); } public void AddTime(double time) { if (time <= Consts.eps) throw new Exception("time interval " + time.ToString()); foreach (var timer in _timers) { timer.Update(time); } _time += time; _result.TotalTime = _time; } public virtual void Step() { } public Result Execute() { while(_time < _maxTime) Step(); return _result; } } public static class RandomHelper { public static void Init() { R = new Random(); } public static Random R; } public class Result { public int LoDCount; public int EFCount; public int HSCount; public int HLCount; public double TotalTime; public double WaitTime; public int IoLHL; public int HSCrits; public int EF1; public int EF2; public int EF3; public override string ToString() { return string.Format("LoD: {0}, EF: {1}, HS: {2}, HL: {3}", LoDCount, EFCount, HSCount, HLCount); } } public class Test { public static void WriteResults(List results, double crit, double haste, double mastery) { var lods = results.Average(u=>u.LoDCount); var ef1 = results.Average(u=>u.EF1); var ef2 = results.Average(u=>u.EF2); var ef3 = results.Average(u=>u.EF3); var efs = ef3 + ef2*2/3.0 + ef1/3.0; var hss = results.Average(u=>u.HSCount); var hls = results.Average(u=>u.HLCount); var hscrits = results.Average(u=>u.HSCrits); var time = results.Average(u=>u.TotalTime); Console.WriteLine(string.Format("iter: {0} LoD: {1:F1}, EF: {2:F1}, HS: {3:F1}, HL: {4:F1}, IoLHL: {5:F1}, Time: {6:F2}s, Wait: {7:F2}s", results.Count, lods, efs, hss, hls, results.Average(u=>u.IoLHL), time, results.Average(u=>u.WaitTime) )); var mana = hss * 2352 + hls * 3300; Console.WriteLine(string.Format("EF1: {0:F1}, EF2: {1:F1}, EF3: {2:F1}, mana: {3:F0}", ef1, ef2, ef3, mana)); var critmult = 1 + crit / 100; var lodheal = 7398*6; var hlheal = 25150; var hsheal = 11736; var efheal = 26619 + 1008*15*(1 + haste/100); var masterymult = mastery / 100; var healing = lods * (1 + 0.3*11/12.0 + masterymult) * critmult * lodheal + (hss + hscrits) * (1 + 1 + masterymult) * hsheal + hls * (1 + 1 + masterymult) * critmult * hlheal + efs * (1 + 1 + masterymult) * critmult * efheal; var hps = healing / time; var hpm = healing / mana; Console.WriteLine(string.Format("hps: {0:F0}, hpm: {1:F2}", hps, hpm)); } public static void Main() { try { RandomHelper.Init(); List results1 = new List(); var crit = 25; var haste = 25; var mastery = 25; Console.WriteLine ("10k iter, no DP, 4pc, 600s, 25% crit, 25% haste, no gay"); for (int i=0; i<10000; i++) { var rotation = new MyRotation(false, true, 600, crit, haste, 0); var result = rotation.Execute(); results1.Add(result); } WriteResults(results1, crit, haste, mastery); results1 = new List(); Console.WriteLine ("10k iter, no DP, 4pc, 600s, 25% crit, 25% haste, 1hp gay"); for (int i=0; i<10000; i++) { var rotation = new MyRotation(false, true, 600, crit, haste, 1); var result = rotation.Execute(); results1.Add(result); } WriteResults(results1, crit, haste, mastery); results1 = new List(); Console.WriteLine ("10k iter, no DP, 4pc, 600s, 25% crit, 25% haste, 2hp gay"); for (int i=0; i<10000; i++) { var rotation = new MyRotation(false, true, 600, crit, haste, 2); var result = rotation.Execute(); results1.Add(result); } WriteResults(results1, crit, haste, mastery); } catch (Exception ex) { Console.WriteLine(ex); } } }