WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Lua/XML Help (https://www.wowinterface.com/forums/forumdisplay.php?f=16)
-   -   math logic bomb (https://www.wowinterface.com/forums/showthread.php?t=44203)

myrroddin 09-07-12 01:34 PM

math logic bomb
 
I am working with random numbers, with the intent to attach them to a frame that moves. Never mind the code issues of moving the frame, I am struggling with the math. The average of all the numbers is what moves the pointer frame. So far so good.

The trouble is the end number has to be between 1 and 21, inclusive. Here is a hard-coded example.
Lua Code:
  1. local pointer = 97846.7851 -- this would actually be random
  2. if pointer < 0 then
  3.     pointer = pointer * -1 -- can't have neg numbers because frames are numbered in the positive
  4. end
  5. pointer = mod(pointer, 21) -- this won't work, because 0 is a possibility
  6. pointer = round() -- local function that rounds better than floor or ceil
  7. -- attach pointer to frame
Is it possible to do something like the following? I've tried Googling for a hint, but haven't found anything. Also, I'm not sure if mod(pointer, 21) is correct either.
Lua Code:
  1. pointer = mod(pointer, 21) > 0

Haleth 09-07-12 01:47 PM

You can't have a mathematically correct average of a set of numbers if you're going to limit the average below the highest number of the set.

Why don't you limit your random numbers (e.g. random(21)) then divide the end result by the amount of numbers?

myrroddin 09-07-12 02:22 PM

The random numbers aren't generated from math.random. They are the average of all the mana deltas within x amount of time. For example, say you gain or lose the following amount of mana:
  • 100
  • -500
  • 450
  • 800
Which sums up to 850. The average is 212.5. I need to convert 212.5 into a spread from 1 to 21 plot points. Because there is no way to determine ahead of time mana deltas, I have to add them as I get them, then average them.

Dridzt 09-07-12 02:38 PM

Is it a sliding window that always contains 21 datapoints?
(ie starts plotting when you have at least 21 datapoints and from that point on displays the last 21)

myrroddin 09-07-12 02:56 PM

If I understand you correctly, then yes, sort of. Think of the PvP capture bar, where it swings between Horde and Alliance. The vertical slider is my plot point, and moves the slider based on the mana returns. Obviously, a mana delta value measured in hundreds, or thousands, isn't any good; the graph would have to be huge, which is why I am making it 21 segments. The main frame behind has a width of 210, so breaking it up into 21 chunks makes setting a width of each segment easy.

That is why I am trying to figure out the math to convert the average deltas into somewhere on a 21 segment frame. I have the average, deltas, frame segments, all that built. It is just the math that converts I am having a challenge with.

Does that make more sense? Oh, in fact, I AM using the PvP capture bar texture.

Dridzt 09-07-12 03:50 PM

Actually the math would be simple if I could understand what the bar represents.
I'm not sure you have decided that yet yourself :p

I mean ok it's 21 segments, let's say 10 'negative' segments (losing mana) 1 'zero' segment (not losing or gaining), 10 'positive' segments (gaining mana)
|----------|-|----------|

What amount does the total width of the bar represent (splitting it in 21 segments aside, there must be something to divide to 21 parts)
What is a meaningful number for the user to be at the left end of the bar (highest rate of losing mana) to the right end of the bar (highest rate of gaining mana)?

Edit: What I posted above should get you an average MPS over 21 seconds if your deltas are 1/sec.
There's no way to place -215 MPS (or +450 MPS) on a bar if you can't know what's the range of the bar,
that's not a math problem it's a logic problem.
Tell me what the full range of the bar corresponds to and I can help with the rest.

Jamash 09-07-12 07:35 PM

You can't scale your sliding window average to your display range until you figure out what your display range is. What are the minimum and maximum values you intend to display? What will you do with values outside that range? Does the minimum = (-the maximum)?

Is the range fixed value? Is it determined by the values in the sliding window? Does it depend on values that have passed out of the sliding window?

Once you have answered that, scaling the input value (the sliding window average) to an output value (an integer between -10 and +10, or equivalently 0 to 20, inclusive) is easy.

Dridzt 09-07-12 09:28 PM

Hrrmph... proof of concept.

Tried to think as a caster :p

<<Center>> = "Rest"
<< Left = "How fast to OOM at current mana expenditure" (the more left the slider the faster we're going oom)
Right >> = "How fast to mana-full at current mana-gain" (the more right, the faster we're gaining full mana)

This makes it so we don't need a static MPS range, rather the user can set a "warning time" before the slider will start moving.
In example code warning time = 30, 10 segments for each side, slider moves 1 notch at 30sec to OOM, another notch at 27sec to OOM and so on until full left at 3sec to OOM.
Similar for right side with slider closer to center for slow mana-regen, further right for mana-tide/innervate etc.
Code:

local frame = CreateFrame("Frame")
frame:SetScript("OnEvent",
function(self,event,...)
        return self[event] and self[event](...)
end)
frame:RegisterEvent("PLAYER_LOGIN")

-- These would be options
local averageOver = 20 -- delta segments to average
local polling = 0.5 -- changing this changes the averaging timeframe (10*1sec segments = 10sec, 10*2sec segments = 20sec to get rolling average etc)
local time_to_either_end = 30 -- middle of bar to either end in seconds (leftmost = oom, rightmost = full mana)
local bar_segments = {10,1,10} -- 10 negative, 1 steady, 10 positive

-- function upvalues
local tinsert, tremove, ipairs, floor, abs, max = tinsert, tremove, ipairs, floor, abs, max
local UnitPower, UnitPowerMax = UnitPower, UnitPowerMax

local mDeltas = {} -- temp table
local lastMana
local function getRollingMPS()
        local mps_per_sec
        local mana = UnitPower("player",SPELL_POWER_MANA)
        local now = GetTime()
        local delta = {mana-lastMana, now}
        lastMana = mana
        table.insert(mDeltas, 1, delta) -- always insert at first position
        if mDeltas[averageOver+1] then mDeltas[averageOver+1] = nil end -- remove the tail to keep the size down
        local sum, startt, endt = 0
        for i,delta in ipairs(mDeltas) do
                if i==1 then
                        endt = delta[2]
                else
                        startt = delta[2]
                end
                sum = sum + delta[1]
                if i==averageOver then break end
        end
        mps_per_sec = (startt and endt and startt~=endt) and sum/(endt-startt) or 0
        return mps_per_sec
end

local function Update(self, elapsed)
        self.lastTime = self.lastTime + elapsed
        if self.lastTime >= polling then
                local barsegment, time_per_segment -- -10.....-1,0,1.....10
                local rollingMPS = getRollingMPS()
                local manaMax = UnitPowerMax("player",SPELL_POWER_MANA)
                local mana = UnitPower("player",SPELL_POWER_MANA)
                -- edge cases first: oom, full mana, 0 mps
                if rollingMPS == 0 then
                        if mana==manaMax then
                                barsegment = 0
                        elseif mana == 0 then
                                barsegment = -bar_segments[1]
                        end
                else
                        local mana_to_full = manaMax - mana
                        if rollingMPS > 0 then -- gaining mana
                                if mana_to_full > 0 then
                                        local time_to_full = mana_to_full/rollingMPS
                                        time_per_segment = time_to_either_end/bar_segments[3]
                                        barsegment = floor((bar_segments[3]+1)-(min(max(time_to_full,time_per_segment),time_to_either_end)/time_per_segment)+0.5)
                                else
                                        barsegment = 0
                                end
                        else -- losing mana
                                if mana == 0 then
                                        barsegment = -bar_segments[1]
                                else
                                        local time_to_empty = mana/abs(rollingMPS)
                                        time_per_segment = time_to_either_end/bar_segments[1]
                                        barsegment = -floor((bar_segments[1]+1)-(min(max(time_to_empty,time_per_segment),time_to_either_end)/time_per_segment)+0.5)
                                end
                        end
                end
                barsegment = barsegment or 0
                frame.slider:SetValue(barsegment)
                _G[frame.slider:GetName().."Text"]:SetText(barsegment)
                self.lastTime = 0
        end
end

local updateEngine = CreateFrame("Frame")
function frame.PLAYER_LOGIN()
        local slider = CreateFrame("Slider","TestRollingManaSlider",UIParent,"OptionsSliderTemplate")
        slider:SetPoint("TOP",0,-50)
        slider:SetSize(200,30)
        slider:SetMinMaxValues(-10,10)
        slider:SetValueStep(1)
        slider:SetValue(0)
        slider:EnableMouse(false)
        _G[slider:GetName().."Low"]:SetText("-10")
        _G[slider:GetName().."Text"]:SetText("0")
        _G[slider:GetName().."High"]:SetText("10")
        frame.slider = slider
        updateEngine.lastTime = 0
        lastMana = UnitPower("player",SPELL_POWER_MANA)
        updateEngine:SetScript("OnUpdate", Update)
end


myrroddin 09-08-12 03:24 AM

Proof of concept is fun! :eek: Anyway, I thought I would post my code, before attempting to add Dridzt's modifications, just to see where I am at. The first pastebin is my entire code, complete with unnecessary, sometimes commented out parts. The second is pared down to include just the relevant stuff.

http://pastebin.com/A3w443DG -- full code. math breaks at line 476
http://pastebin.com/12RqSrk1 -- slimmed down. math breaks down at line 120

myrroddin 09-08-12 03:35 AM

Quote:

Originally Posted by Dridzt (Post 262734)
I mean ok it's 21 segments, let's say 10 'negative' segments (losing mana) 1 'zero' segment (not losing or gaining), 10 'positive' segments (gaining mana)
|----------|-|----------|

That is what I was thinking, yes.

Quote:

Originally Posted by Dridzt (Post 262734)
What is a meaningful number for the user to be at the left end of the bar (highest rate of losing mana) to the right end of the bar (highest rate of gaining mana)?

I was wondering about that back when I started this project. Given that in Pandaria, I am seeing values of +/-1000 right now, I would suggest that is the limit; however, the values spike even higher during Innervate, ShadowFiend, potions, etc, and might go the opposite way if a mob drains mana. This needs to be future-proofed. Any suggestions on safe upper and lower limits?

Quote:

Originally Posted by Dridzt (Post 262734)
Edit: What I posted above should get you an average MPS over 21 seconds if your deltas are 1/sec.
There's no way to place -215 MPS (or +450 MPS) on a bar if you can't know what's the range of the bar,
that's not a math problem it's a logic problem.
Tell me what the full range of the bar corresponds to and I can help with the rest.

Whoa, are you telling me that LOGIC has something to do with MATH? UNPOSSIBLE! LOL see above paragraph.

Dridzt 09-08-12 01:40 PM

ACM Slim modified
http://pastebin.com/fGJRa9Xg

myrroddin 09-08-12 02:33 PM

Thank you Dridzt. Will drop it in and see what happens. I have some other bugs that need squashing, and they are preventing the addon from working in the first place, but one way or the other, I will post back and let you know. :D:banana::cool:

SDPhantom 09-08-12 02:33 PM

Quote:

Originally Posted by myrroddin (Post 262784)
I was wondering about that back when I started this project. Given that in Pandaria, I am seeing values of +/-1000 right now, I would suggest that is the limit; however, the values spike even higher during Innervate, ShadowFiend, potions, etc, and might go the opposite way if a mob drains mana. This needs to be future-proofed. Any suggestions on safe upper and lower limits?

If you want a static limit, I'd say the entire mana pool as a base since you can never lose or gain more than the entire pool at a time. Otherwise, you could try to keep track of a peak value and adjust accordingly to that.

myrroddin 09-08-12 03:09 PM

Quote:

Originally Posted by SDPhantom (Post 262902)
If you want a static limit, I'd say the entire mana pool as a base since you can never lose or gain more than the entire pool at a time. Otherwise, you could try to keep track of a peak value and adjust accordingly to that.

I was thinking of that originally, but that seemed extreme. Besides, I am not dealing with mana gains or losses; I am dealing with the difference between your current mana and the mana quantity you had during last update, or mana delta. This is a much smaller amount.

The idea for ACM came from a guild healer that wanted to know how effective he was at conserving mana partway though a fight. He can see his current mana on his unit frame. "Am I actually conserving mana, and how effectively am I conserving it?" was his question.

SDPhantom, if 1100 is too low, I can either hard code it up, or take Dridzt's suggestion and make it a user setting.

SDPhantom 09-08-12 08:38 PM

Quote:

Originally Posted by myrroddin (Post 262911)
I was thinking of that originally, but that seemed extreme. Besides, I am not dealing with mana gains or losses; I am dealing with the difference between your current mana and the mana quantity you had during last update, or mana delta. This is a much smaller amount.

Any way you look at it, by definition, delta and gain/loss is the exact same thing. The size of the input values makes no difference. That, however, was not the focus of my post.

The first suggestion was rather extreme, but I have yet to see a better one for a static value. I was leaning more toward the dynamic "peak" value though, having the addon display out of the largest value it currently has come across (taking the absolute value of all deltas).



Quote:

Originally Posted by myrroddin (Post 262911)
The idea for ACM came from a guild healer that wanted to know how effective he was at conserving mana partway though a fight. He can see his current mana on his unit frame. "Am I actually conserving mana, and how effectively am I conserving it?" was his question.

A couple years ago, I had an idea of an addon like this that would take mana consumption and apply it to damage done. This would result in an efficiency rating I called DPM (Damage per Mana). The higher DPM you have, the more efficient you are at casting. To make sure the value was truly honest and serve as a baseline to smarter casting, it would disregard mana gains and on the healing side, overheals would not count either.

What lead to scrapping this idea was the complexity of all the abilities I would have to support that applies buffs/debuffs that increase damage dealt from you and other players or decreases the target's armor/resistance rating. This would be so the associated ability would get proper credit for its own efficiency rating.


All times are GMT -6. The time now is 05:29 PM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI