diff options
Diffstat (limited to 'Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua')
-rw-r--r-- | Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua | 466 |
1 files changed, 0 insertions, 466 deletions
diff --git a/Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua b/Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua deleted file mode 100644 index af70616..0000000 --- a/Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua +++ /dev/null @@ -1,466 +0,0 @@ ---[[ -Copyright (c) 2014, Seth VanHeulen -All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---]] - --- addon information - -_addon.name = 'boxdestroyer' -_addon.version = '1.0.5' -_addon.command = 'boxdestroyer' -_addon.author = 'Seth VanHeulen (Acacia@Odin)' - --- modules - -require('pack') -require('tables') -require('chat') - --- load message constants - -require('messages') - --- config - -config = require('config') -defaults = { - HighlightResult = false, - HighlightColor = 36, -} -settings = config.load(defaults) --- global constants - -default = { - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 -} - -observed_default = { - ['second_even_odd'] = false, - ['first_even_odd'] = false, - ['range'] = false, - ['equal'] = false, - ['second_multiple'] = false, - ['first_multiple'] = false, - ['thief_tools_active'] = false -} - -thief_tools = {[1022] = true} - --- global variables - -box = {} -observed = {} -zone_id = windower.ffxi.get_info().zone - --- filter helper functions - -function greater_less(id, greater, num) - local new = {} - for _,v in pairs(box[id]) do - if greater and v > num or not greater and v < num then - table.insert(new, v) - end - end - return new -end - -function even_odd(id, div, rem) - local new = {} - for _,v in pairs(box[id]) do - if (math.floor(v / div) % 2) == rem then - table.insert(new, v) - end - end - return new -end - -function equal(id, first, num) - local new = {} - for _,v in pairs(box[id]) do - if first and math.floor(v / 10) == num or not first and (v % 10) == num then - table.insert(new, v) - end - end - return new -end - -function exclusive_mean(counts) - total = 0 - for _,v in pairs(counts) do - total = total + v - end - weighted_mean = 0 - for _,v in pairs(counts) do - weighted_mean = weighted_mean + (total - v) * v / total - end - return weighted_mean -end - -function calculate_odds(id,chances) - local reductions = {} - if not observed[id].first_even_odd then - local counter = {0} - counter[0] = 0 - for _,v in pairs(box[id]) do - counter[math.floor(v / 10) % 2] = counter[math.floor(v / 10) % 2] + 1 - end - reductions[#reductions+1] = exclusive_mean(counter) - end - if not observed[id].second_even_odd then - local counter = {0} - counter[0] = 0 - for _,v in pairs(box[id]) do - counter[v % 2] = counter[v % 2] + 1 - end - reductions[#reductions+1] = exclusive_mean(counter) - end - if not observed[id].range then - local new = {} - local reduction = 0 - for i,v in pairs(box[id]) do - new[i] = 0 - for _,m in pairs(box[id]) do - if m-v > 16 then break end - new[i] = new[i] + math.max(16-math.abs(m - v),0)^2/256 - end - reduction = reduction + (#box[id] - new[i])/#box[id] - end - - reductions[#reductions+1] = reduction - end - if not observed[id].equal then - local counter = {0,0,0,0,0,0,0,0,0} - counter[0] = 0 - local eliminated = {0,0,0,0,0,0,0,0,0} - eliminated[0] = 0 - for _,v in pairs(box[id]) do - counter[math.floor(v / 10)] = counter[math.floor(v / 10)] + 1/2 - counter[v % 10] = counter[v % 10] + 1/2 - for i = 0,9 do - if i ~= v % 10 and i ~= math.floor(v / 10) then - eliminated[i] = eliminated[i] + 1 - end - end - end - - reduction = 0 - for i,v in pairs(counter) do - reduction = reduction + eliminated[i] * v / #box[id] - end - - reductions[#reductions+1] = reduction - end - if not observed[id].second_multiple then - local counter = {0,0,0,0,0,0,0,0,0} - counter[0] = 0 - for _,v in pairs(box[id]) do - counter[v % 10] = counter[v % 10] + 1 - end - - local weights = { - counter[0] + counter[1]/2 + counter[2]/3, - counter[1]/2 + counter[2]/3 + counter[3]/3, - counter[2]/3 + counter[3]/3 + counter[4]/3, - counter[3]/3 + counter[4]/3 + counter[5]/3, - counter[4]/3 + counter[5]/3 + counter[6]/3, - counter[5]/3 + counter[6]/3 + counter[7]/3, - counter[6]/3 + counter[7]/3 + counter[8]/2, - counter[7]/3 + counter[8]/2 + counter[9] - } - - local eliminated = { - counter[3] + counter[4] + counter[5] + counter[6] + counter[7] + counter[8] + counter[9], - counter[0] + counter[4] + counter[5] + counter[6] + counter[7] + counter[8] + counter[9], - counter[0] + counter[1] + counter[5] + counter[6] + counter[7] + counter[8] + counter[9], - counter[0] + counter[1] + counter[2] + counter[6] + counter[7] + counter[8] + counter[9], - counter[0] + counter[1] + counter[2] + counter[3] + counter[7] + counter[8] + counter[9], - counter[0] + counter[1] + counter[2] + counter[3] + counter[4] + counter[8] + counter[9], - counter[0] + counter[1] + counter[2] + counter[3] + counter[4] + counter[5] + counter[9], - counter[0] + counter[1] + counter[2] + counter[3] + counter[4] + counter[5] + counter[6] - } - - local reduction = 0 - for i,v in pairs(weights) do - reduction = reduction + eliminated[i] * v / #box[id] - end - - reductions[#reductions + 1] = reduction - end - if not observed[id].first_multiple then - local counter = {0,0,0,0,0,0,0,0,0} - for _,v in pairs(box[id]) do - counter[math.floor(v / 10)] = counter[math.floor(v / 10)] + 1 - end - - local weights = { - counter[1] + counter[2]/2 + counter[3]/3, - counter[2]/2 + counter[3]/3 + counter[4]/3, - counter[3]/3 + counter[4]/3 + counter[5]/3, - counter[4]/3 + counter[5]/3 + counter[6]/3, - counter[5]/3 + counter[6]/3 + counter[7]/3, - counter[6]/3 + counter[7]/3 + counter[8]/2, - counter[7]/3 + counter[8]/2 + counter[9] - } - - local eliminated = { - counter[4] + counter[5] + counter[6] + counter[7] + counter[8] + counter[9], - counter[1] + counter[5] + counter[6] + counter[7] + counter[8] + counter[9], - counter[1] + counter[2] + counter[6] + counter[7] + counter[8] + counter[9], - counter[1] + counter[2] + counter[3] + counter[7] + counter[8] + counter[9], - counter[1] + counter[2] + counter[3] + counter[4] + counter[8] + counter[9], - counter[1] + counter[2] + counter[3] + counter[4] + counter[5] + counter[9], - counter[1] + counter[2] + counter[3] + counter[4] + counter[5] + counter[6] - } - - local reduction = 0 - for i,v in pairs(weights) do - reduction = reduction + eliminated[i] * v / #box[id] - end - - reductions[#reductions + 1] = reduction - end - - local expected_examine_value = 0 - for _,v in pairs(reductions) do - expected_examine_value = expected_examine_value + v/#reductions - end - - local optimal_guess = math.ceil(#box[id] / 2) - - local expected_guess_value = 2*optimal_guess - 2*optimal_guess^2 / #box[id] + 2*optimal_guess/#box[id] - 1 / #box[id] - - return expected_examine_value, expected_guess_value -end - --- display helper function - -function display(id, chances) - if #box[id] == 90 then - windower.add_to_chat(207, 'Possible combinations: 10~99') - else - windower.add_to_chat(207, 'Possible combinations: ' .. table.concat(box[id], ' ')) - end - local remaining = math.floor(#box[id] / math.pow(2, (chances - 1))) - if remaining == 0 then - remaining = 1 - end - - if chances == 1 and observed[id].equal then - -- The "equal" message (== "X") for X in 1..9 gives an unequal probability to the remaining options - -- because "XX" is twice as likely to be indicated by the "equal" message. - -- This is too annoying to propagate to the rest of the addon, although it should be some day. - local printed = false - for _,v in pairs(box[id]) do - if math.floor(v/10) == v%10 then - windower.add_to_chat(207, 'Best guess: %d (%d%%)':format(v, 1 / remaining * 100)) - printed = true - break - end - end - if not printed then - windower.add_to_chat(207, 'Best guess: %d (%d%%)':format(box[id][math.ceil(#box[id] / 2)], 1 / remaining * 100)) - end - else - windower.add_to_chat(207, 'best guess: %d (%d%%)':format(box[id][math.ceil(#box[id] / 2)], 1 / remaining * 100)) - local clue_value,guess_value = calculate_odds(id,chances) - local result = clue_value > guess_value and remaining ~= 1 and 'examining the chest' or 'guessing ' .. '%d':format(box[id][math.ceil(#box[id] / 2)]) - local formatted_result = settings.HighlightResult and result:color(settings.HighlightColor) or result - windower.add_to_chat(207, 'boxdestroyer recommends ' .. formatted_result .. '.') - end - -end - --- ID obtaining helper function -function get_id(zone_id,str) - return messages[zone_id] + offsets[str] -end - --- event callback functions - -function check_incoming_chunk(id, original, modified, injected, blocked) - if id == 0x0A then - zone_id = original:unpack('H', 49) - elseif messages[zone_id] then - if id == 0x0B then - box = {} - observed = {} - elseif id == 0x2A then - local box_id = original:unpack('I', 5) - local param0 = original:unpack('I', 9) - local param1 = original:unpack('I', 13) - local param2 = original:unpack('I', 17) - local message_id = original:unpack('H', 27) % 0x8000 - - if box[box_id] == nil then - box[box_id] = table.copy(default) - end - if observed[box_id] == nil then - observed[box_id] = table.copy(observed_default) - end - - if get_id(zone_id,'greater_less') == message_id then - box[box_id] = greater_less(box_id, param1 == 0, param0) - elseif get_id(zone_id,'second_even_odd') == message_id then - -- tells whether the second digit is even or odd - box[box_id] = even_odd(box_id, 1, param0) - observed[box_id].second_even_odd = true - elseif get_id(zone_id,'first_even_odd') == message_id then - -- tells whether the first digit is even or odd - box[box_id] = even_odd(box_id, 10, param0) - observed[box_id].first_even_odd = true - elseif get_id(zone_id,'range') == message_id then - if observed[box_id].thief_tools_active then - -- Thief tools are the same as normal ranges but with larger bounds. - -- lower bound (param0) = solution - RANDINT(8,32) - -- upper bound (param1) = solution + RANDINT(8,32) - -- param0 + 33 > solution > param0 + 7 - -- param1 - 7 > solution > param1 - 33 - -- if the bound is less than 11 or greater than 98, the message changes to "greater" or "less" respectively - box[box_id] = greater_less(box_id, true, math.max(param1-33,param0+7) ) - box[box_id] = greater_less(box_id, false, math.min(param0+33,param1-7) ) - observed[box_id].thief_tools_active = false - else - -- lower bound (param0) = solution - RANDINT(5,20) - -- upper bound (param1) = solution + RANDINT(5,20) - -- param0 + 21 > solution > param0 + 4 - -- param1 - 4 > solution > param1 - 21 - -- if the bound is less than 11 or greater than 98, the message changes to "greater" or "less" respectively - box[box_id] = greater_less(box_id, true, math.max(param1-21,param0+4) ) - box[box_id] = greater_less(box_id, false, math.min(param0+21,param1-4) ) - observed[box_id].range = true - end - elseif get_id(zone_id,'less') == message_id then - -- Less is a range with 9 as the lower bound - if observed[box_id].thief_tools_active then - box[box_id] = greater_less(box_id, true, math.max(9, param0-33) ) - box[box_id] = greater_less(box_id, false, math.min(10+33,param0-7) ) - observed[box_id].thief_tools_active = false - else - box[box_id] = greater_less(box_id, true, math.max(9, param0-21) ) - box[box_id] = greater_less(box_id, false, math.min(10+21,param0-4) ) - observed[box_id].range = true - end - elseif get_id(zone_id,'greater') == message_id then - -- Greater is a range with 100 as the upper bound - if observed[box_id].thief_tools_active then - box[box_id] = greater_less(box_id, true, math.max(99-33,param0+7) ) - box[box_id] = greater_less(box_id, false, math.min(100,param0+33) ) - observed[box_id].thief_tools_active = false - else - box[box_id] = greater_less(box_id, true, math.max(99-21,param0+4) ) - box[box_id] = greater_less(box_id, false, math.min(100,param0+21) ) - observed[box_id].range = true - end - elseif get_id(zone_id,'equal') == message_id then - -- single number that is either the first or second digit of the solution - local new = equal(box_id, true, param0) - local duplicate = param0 * 10 + param0 - for k,v in pairs(new) do - if v == duplicate then - table.remove(new, k) - end - end - for _,v in pairs(equal(box_id, false, param0)) do table.insert(new, v) end - table.sort(new) - box[box_id] = new - observed[box_id].equal = true - elseif get_id(zone_id,'second_multiple') == message_id then - -- three digit range including the second digit of the solution - local new = equal(box_id, false, param0) - for _,v in pairs(equal(box_id, false, param1)) do table.insert(new, v) end - for _,v in pairs(equal(box_id, false, param2)) do table.insert(new, v) end - table.sort(new) - box[box_id] = new - observed[box_id].second_multiple = true - elseif get_id(zone_id,'first_multiple') == message_id then - -- three digit range including the first digit of the solution - local new = equal(box_id, true, param0) - for _,v in pairs(equal(box_id, true, param1)) do table.insert(new, v) end - for _,v in pairs(equal(box_id, true, param2)) do table.insert(new, v) end - table.sort(new) - box[box_id] = new - observed[box_id].first_multiple = true - elseif get_id(zone_id,'success') == message_id or get_id(zone_id,'failure') == message_id then - box[box_id] = nil - elseif get_id(zone_id,'tool_failure') == message_id then - observed[box_id].thief_tools_active = false - end - elseif id == 0x34 then - local box_id = original:unpack('I', 5) - if windower.ffxi.get_mob_by_id(box_id).name == 'Treasure Casket' then - local chances = original:byte(9) - if box[box_id] == nil then - box[box_id] = table.copy(default) - observed[box_id] = table.copy(observed_default) - end - if chances > 0 and chances < 7 then - display(box_id, chances) - end - end - elseif id == 0x5B then - box[original:unpack('I', 17)] = nil - observed[original:unpack('I', 17)] = nil - end - end -end - -function watch_for_keys(id, original, modified, injected, blocked) - if blocked then - elseif (id == 0x036 or id == 0x037) and - windower.ffxi.get_mob_by_id(modified:unpack('I',0x05)).name == 'Treasure Casket' and - (windower.ffxi.get_player().main_job == 'THF' or windower.ffxi.get_player().sub_job == 'THF') then - - local box_id = modified:unpack('I',0x05) - if not box[box_id] then - box[box_id] = table.copy(default) - observed[box_id] = table.copy(observed_default) - end - - if id == 0x037 and thief_tools[windower.ffxi.get_items(modified:byte(0x11))[modified:byte(0xF)].id] then - observed[box_id].thief_tools_active = true - elseif id == 0x036 then - for i = 1,9 do - if thief_tools[windower.ffxi.get_items(0)[modified:byte(0x30+i)].id] then - observed[box_id].thief_tools_active = true - break - end - end - end - end -end - --- register event callbacks - -windower.register_event('incoming chunk', check_incoming_chunk) - -windower.register_event('outgoing chunk', watch_for_keys) |