summaryrefslogtreecommitdiff
path: root/Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua')
-rw-r--r--Data/DefaultContent/Libraries/addons/addons/boxdestroyer/boxdestroyer.lua466
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)