Stravides

openbee

May 8th, 2014
214
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 27.39 KB | None | 0 0
  1. local version = {
  2.   ["major"] = 2,
  3.   ["minor"] = 1,
  4.   ["patch"] = 0
  5. }
  6.  
  7. function loadFile(fileName)
  8.   local f = fs.open(fileName, "r")
  9.   if f ~= nil then
  10.     local data = f.readAll()
  11.     f.close()
  12.     return textutils.unserialize(data)
  13.   end
  14. end
  15.  
  16. function saveFile(fileName, data)
  17.   local f = fs.open(fileName, "w")
  18.   f.write(textutils.serialize(data))
  19.   f.close()
  20. end
  21.  
  22. local config = loadFile("bee.config")
  23. if config == nil then
  24.   config = {
  25.     ["apiarySide"] = "left",
  26.     ["chestSide"] = "diamond_0",
  27.     ["chestDir"] = "up",
  28.     ["productDir"] = "down",
  29.     ["analyzerDir"] = "east"
  30.   }
  31.   saveFile("bee.config", config)
  32. end
  33.  
  34. local useAnalyzer = true
  35. local useReferenceBees = true
  36.  
  37. local traitPriority = {
  38.   "speciesChance",
  39.   "speed",
  40.   "fertility",
  41.   "nocturnal",
  42.   "tolerantFlyer",
  43.   "caveDwelling",
  44.   "temperatureTolerance",
  45.   "humidityTolerance",
  46.   "effect",
  47.   "flowering",
  48.   "flowerProvider",
  49.   "territory"
  50. }
  51.  
  52. function setPriorities(priority)
  53.   local species = nil
  54.   local priorityNum = 1
  55.   for traitNum, trait in ipairs(priority) do
  56.     local found = false
  57.     for traitPriorityNum = 1, #traitPriority do
  58.       if trait == traitPriority[traitPriorityNum] then
  59.         found = true
  60.         if priorityNum ~= traitPriorityNum then
  61.           table.remove(traitPriority, traitPriorityNum)
  62.           table.insert(traitPriority, priorityNum, trait)
  63.         end
  64.         priorityNum = priorityNum + 1
  65.         break
  66.       end
  67.     end
  68.     if not found then
  69.       species = trait
  70.     end
  71.   end
  72.   return species
  73. end
  74.  
  75. -- logging ----------------------------
  76.  
  77. local logFile
  78. function setupLog()
  79.   local logCount = 0
  80.   while fs.exists(string.format("bee.%d.log", logCount)) do
  81.     logCount = logCount + 1
  82.   end
  83.   logFile = fs.open(string.format("bee.%d.log", logCount), "w")
  84.   return string.format("bee.%d.log", logCount)
  85. end
  86.  
  87. function log(msg)
  88.   msg = msg or ""
  89.   logFile.write(tostring(msg))
  90.   logFile.flush()
  91.   io.write(msg)
  92. end
  93.  
  94. function logLine(...)
  95.   for i, msg in ipairs(arg) do
  96.     if msg == nil then
  97.       msg = ""
  98.     end
  99.     logFile.write(msg)
  100.     io.write(msg)
  101.   end
  102.   logFile.write("\n")
  103.   logFile.flush()
  104.   io.write("\n")
  105. end
  106.  
  107. function getPeripherals()
  108.   return peripheral.wrap(config.chestSide), peripheral.wrap(config.apiarySide)
  109. end
  110.  
  111. -- utility functions ------------------
  112.  
  113. function choose(list1, list2)
  114.   local newList = {}
  115.   if list2 then
  116.     for i = 1, #list2 do
  117.       for j = 1, #list1 do
  118.         if list1[j] ~= list2[i] then
  119.           table.insert(newList, {list1[j], list2[i]})
  120.         end
  121.       end
  122.     end
  123.   else
  124.     for i = 1, #list1 do
  125.       for j = i, #list1 do
  126.         if list1[i] ~= list1[j] then
  127.           table.insert(newList, {list1[i], list1[j]})
  128.         end
  129.       end
  130.     end
  131.   end
  132.   return newList
  133. end
  134.  
  135. -- fix for some versions returning bees.species.*
  136. local nameFix = {}
  137. function fixName(name)
  138.   if type(name) == "table" then
  139.     name = name.name
  140.   end
  141.   local newName = name:gsub("bees%.species%.",""):gsub("^.", string.upper)
  142.   if name ~= newName then
  143.     nameFix[newName] = name
  144.   end
  145.   return newName
  146. end
  147.  
  148. function fixBee(bee)
  149.   if bee.beeInfo ~= nil then
  150.     bee.beeInfo.displayName = fixName(bee.beeInfo.displayName)
  151.     if bee.beeInfo.isAnalyzed then
  152.       bee.beeInfo.active.species = fixName(bee.beeInfo.active.species)
  153.       bee.beeInfo.inactive.species = fixName(bee.beeInfo.inactive.species)
  154.     end
  155.   end
  156.   return bee
  157. end
  158.  
  159. function fixParents(parents)
  160.   parents.allele1 = fixName(parents.allele1)
  161.   parents.allele2 = fixName(parents.allele2)
  162.   if parents.result then
  163.     parents.result = fixName(parents.result)
  164.   end
  165.   return parents
  166. end
  167.  
  168. function beeName(bee)
  169.   if bee.beeInfo.active then
  170.     return bee.slot .. "=" .. bee.beeInfo.active.species:sub(1,3) .. "-" ..
  171.                               bee.beeInfo.inactive.species:sub(1,3)
  172.   else
  173.     return bee.slot .. "=" .. bee.beeInfo.displayName:sub(1,3)
  174.   end
  175. end
  176.  
  177. function printBee(bee)
  178.   if bee.beeInfo.isAnalyzed then
  179.     local active = bee.beeInfo.active
  180.     local inactive = bee.beeInfo.inactive
  181.     if active.species ~= inactive.species then
  182.       log(string.format("%s-%s", active.species, inactive.species))
  183.     else
  184.       log(active.species)
  185.     end
  186.     if bee.rawName == "item.beedronege" then
  187.       log(" Drone")
  188.     elseif bee.rawName == "item.beeprincessge" then
  189.       log(" Princess")
  190.     else
  191.       log(" Queen")
  192.     end
  193.     --log((active.nocturnal and " Nocturnal" or " "))
  194.     --log((active.tolerantFlyer and " Flyer" or " "))
  195.     --log((active.caveDwelling and " Cave" or " "))
  196.     logLine()
  197.     --logLine(string.format("Fert: %d  Speed: %d  Lifespan: %d", active.fertility, active.speed, active.lifespan))
  198.   else
  199.   end
  200. end
  201.  
  202. -- mutations and scoring --------------
  203.  
  204. -- build mutation graph
  205. function buildMutationGraph(apiary)
  206.   local mutations = {}
  207.   local beeNames = {}
  208.   function addMutateTo(parent1, parent2, offspring, chance)
  209.     beeNames[parent1] = true
  210.     beeNames[parent2] = true
  211.     beeNames[offspring] = true
  212.     if mutations[parent1] ~= nil then
  213.       if mutations[parent1].mutateTo[offspring] ~= nil then
  214.         mutations[parent1].mutateTo[offspring][parent2] = chance
  215.       else
  216.         mutations[parent1].mutateTo[offspring] = {[parent2] = chance}
  217.       end
  218.     else
  219.       mutations[parent1] = {
  220.         mutateTo = {[offspring]={[parent2] = chance}}
  221.       }
  222.     end
  223.   end
  224.   for _, parents in pairs(apiary.getBeeBreedingData()) do
  225.     fixParents(parents)
  226.     addMutateTo(parents.allele1, parents.allele2, parents.result, parents.chance)
  227.     addMutateTo(parents.allele2, parents.allele1, parents.result, parents.chance)
  228.   end
  229.   mutations.getBeeParents = function(name)
  230.     return apiary.getBeeParents((nameFix[name] or name))
  231.   end
  232.   return mutations, beeNames
  233. end
  234.  
  235. function buildTargetSpeciesList(catalog, apiary)
  236.   local targetSpeciesList = {}
  237.   local parentss = apiary.getBeeBreedingData()
  238.   for _, parents in pairs(parentss) do
  239.     if catalog.princessesBySpecies[parents.allele1] ~= nil and
  240.         catalog.princessesBySpecies[parents.allele2] ~= nil and
  241.         (
  242.           catalog.referencePrincessesBySpecies[parents.result] == nil or
  243.           catalog.referenceDronesBySpecies[parents.result] == nil
  244.         ) then
  245.       table.insert(targetSpeciesList, parents.result)
  246.     end
  247.   end
  248.   return targetSpeciesList
  249. end
  250.  
  251. -- percent chance of 2 species turning into a target species
  252. function mutateSpeciesChance(mutations, species1, species2, targetSpecies)
  253.   local chance = {}
  254.   if species1 == species2 then
  255.     chance[species1] = 100
  256.   else
  257.     chance[species1] = 50
  258.     chance[species2] = 50
  259.   end
  260.   if mutations[species1] ~= nil then
  261.     for species, mutates in pairs(mutations[species1].mutateTo) do
  262.       local mutateChance = mutates[species2]
  263.       if mutateChance ~= nil then
  264.         chance[species] = mutateChance
  265.         chance[species1] = chance[species1] - mutateChance / 2
  266.         chance[species2] = chance[species2] - mutateChance / 2
  267.       end
  268.     end
  269.   end
  270.   return chance[targetSpecies] or 0.0
  271. end
  272.  
  273. -- percent chance of 2 bees turning into target species
  274. function mutateBeeChance(mutations, princess, drone, targetSpecies)
  275.   if princess.beeInfo.isAnalyzed then
  276.     if drone.beeInfo.isAnalyzed then
  277.       return (mutateSpeciesChance(mutations, princess.beeInfo.active.species, drone.beeInfo.active.species, targetSpecies) / 4
  278.              +mutateSpeciesChance(mutations, princess.beeInfo.inactive.species, drone.beeInfo.active.species, targetSpecies) / 4
  279.              +mutateSpeciesChance(mutations, princess.beeInfo.active.species, drone.beeInfo.inactive.species, targetSpecies) / 4
  280.              +mutateSpeciesChance(mutations, princess.beeInfo.inactive.species, drone.beeInfo.inactive.species, targetSpecies) / 4)
  281.     end
  282.   elseif drone.beeInfo.isAnalyzed then
  283.   else
  284.     return mutateSpeciesChance(princess.beeInfo.displayName, drone.beeInfo.displayName, targetSpecies)
  285.   end
  286. end
  287.  
  288. function buildScoring()
  289.   function makeNumberScorer(trait, default)
  290.     local function scorer(bee)
  291.       if bee.beeInfo.isAnalyzed then
  292.         return (bee.beeInfo.active[trait] + bee.beeInfo.inactive[trait]) / 2
  293.       else
  294.         return default
  295.       end
  296.     end
  297.     return scorer
  298.   end
  299.  
  300.   function makeBooleanScorer(trait)
  301.     local function scorer(bee)
  302.       if bee.beeInfo.isAnalyzed then
  303.         return ((bee.beeInfo.active[trait] and 1 or 0) + (bee.beeInfo.inactive[trait] and 1 or 0)) / 2
  304.       else
  305.         return 0
  306.       end
  307.     end
  308.     return scorer
  309.   end
  310.  
  311.   function makeTableScorer(trait, default, lookup)
  312.     local function scorer(bee)
  313.       if bee.beeInfo.isAnalyzed then
  314.         return ((lookup[bee.beeInfo.active[trait]] or default) + (lookup[bee.beeInfo.inactive[trait]] or default)) / 2
  315.       else
  316.         return default
  317.       end
  318.     end
  319.     return scorer
  320.   end
  321.  
  322.   local scoresTolerance = {
  323.     ["None"]   = 0,
  324.     ["Up 1"]   = 1,
  325.     ["Up 2"]   = 2,
  326.     ["Up 3"]   = 3,
  327.     ["Up 4"]   = 4,
  328.     ["Up 5"]   = 5,
  329.     ["Down 1"] = 1,
  330.     ["Down 2"] = 2,
  331.     ["Down 3"] = 3,
  332.     ["Down 4"] = 4,
  333.     ["Down 5"] = 5,
  334.     ["Both 1"] = 2,
  335.     ["Both 2"] = 4,
  336.     ["Both 3"] = 6,
  337.     ["Both 4"] = 8,
  338.     ["Both 5"] = 10
  339.   }
  340.  
  341.   local scoresFlowerProvider = {
  342.     ["None"] = 5,
  343.     ["Rocks"] = 4,
  344.     ["Flowers"] = 3,
  345.     ["Mushroom"] = 2,
  346.     ["Cacti"] = 1,
  347.     ["Exotic Flowers"] = 0,
  348.     ["Jungle"] = 0
  349.   }
  350.  
  351.   return {
  352.     ["fertility"] = makeNumberScorer("fertility", 1),
  353.     ["flowering"] = makeNumberScorer("flowering", 1),
  354.     ["speed"] = makeNumberScorer("speed", 1),
  355.     ["lifespan"] = makeNumberScorer("lifespan", 1),
  356.     ["nocturnal"] = makeBooleanScorer("nocturnal"),
  357.     ["tolerantFlyer"] = makeBooleanScorer("tolerantFlyer"),
  358.     ["caveDwelling"] = makeBooleanScorer("caveDwelling"),
  359.     ["effect"] = makeBooleanScorer("effect"),
  360.     ["temperatureTolerance"] = makeTableScorer("temperatureTolerance", 0, scoresTolerance),
  361.     ["humidityTolerance"] = makeTableScorer("humidityTolerance", 0, scoresTolerance),
  362.     ["flowerProvider"] = makeTableScorer("flowerProvider", 0, scoresFlowerProvider),
  363.     ["territory"] = function(bee)
  364.       if bee.beeInfo.isAnalyzed then
  365.         return ((bee.beeInfo.active.territory[1] * bee.beeInfo.active.territory[2] * bee.beeInfo.active.territory[3]) +
  366.                      (bee.beeInfo.inactive.territory[1] * bee.beeInfo.inactive.territory[2] * bee.beeInfo.inactive.territory[3])) / 2
  367.       else
  368.         return 0
  369.       end
  370.     end
  371.   }
  372. end
  373.  
  374. function compareBees(scorers, a, b)
  375.   for _, trait in ipairs(traitPriority) do
  376.     local scorer = scorers[trait]
  377.     if scorer ~= nil then
  378.       local aScore = scorer(a)
  379.       local bScore = scorer(b)
  380.       if aScore ~= bScore then
  381.         return aScore > bScore
  382.       end
  383.     end
  384.   end
  385.   return true
  386. end
  387.  
  388. function compareMates(a, b)
  389.   for i, trait in ipairs(traitPriority) do
  390.     if a[trait] ~= b[trait] then
  391.       return a[trait] > b[trait]
  392.     end
  393.   end
  394.   return true
  395. end
  396.  
  397. function betterTraits(scorers, a, b)
  398.   local traits = {}
  399.   for _, trait in ipairs(traitPriority) do
  400.     local scorer = scorers[trait]
  401.     if scorer ~= nil then
  402.       local aScore = scorer(a)
  403.       local bScore = scorer(b)
  404.       if bScore > aScore then
  405.         table.insert(traits, trait)
  406.       end
  407.     end
  408.   end
  409.   return traits
  410. end
  411.  
  412. -- cataloging functions ---------------
  413.  
  414. function addBySpecies(beesBySpecies, bee)
  415.   if bee.beeInfo.isAnalyzed then
  416.     if beesBySpecies[bee.beeInfo.active.species] == nil then
  417.       beesBySpecies[bee.beeInfo.active.species] = {bee}
  418.     else
  419.       table.insert(beesBySpecies[bee.beeInfo.active.species], bee)
  420.     end
  421.     if bee.beeInfo.inactive.species ~= bee.beeInfo.active.species then
  422.       if beesBySpecies[bee.beeInfo.inactive.species] == nil then
  423.         beesBySpecies[bee.beeInfo.inactive.species] = {bee}
  424.       else
  425.         table.insert(beesBySpecies[bee.beeInfo.inactive.species], bee)
  426.       end
  427.     end
  428.   else
  429.     if beesBySpecies[bee.beeInfo.displayName] == nil then
  430.       beesBySpecies[bee.beeInfo.displayName] = {bee}
  431.     else
  432.       table.insert(beesBySpecies[bee.beeInfo.displayName], bee)
  433.     end
  434.   end
  435. end
  436.  
  437. function catalogBees(inv, scorers)
  438.   catalog = {}
  439.   catalog.princesses = {}
  440.   catalog.princessesBySpecies = {}
  441.   catalog.drones = {}
  442.   catalog.dronesBySpecies = {}
  443.   catalog.queens = {}
  444.   catalog.referenceDronesBySpecies = {}
  445.   catalog.referencePrincessesBySpecies = {}
  446.  
  447.   -- phase 1 -- analyze bees and mark reference bees
  448.   inv.condenseItems()
  449.   logLine(string.format("scanning %d slots", inv.size))
  450.   local referenceBeeCount = 0
  451.   local freeSlot = 0
  452.   local bees = inv.getAllStacks()
  453.   for slot = 1, inv.size do
  454.     local bee = bees[slot]
  455.     if bee ~= nil then
  456.       if bee.beeInfo ~= nil then
  457.         if bee.beeInfo.isAnalyzed == false and useAnalyzer == true then
  458.           local newSlot = analyzeBee(inv, slot)
  459.           if newSlot ~= nil and newSlot ~= slot then
  460.             inv.swapStacks(slot, newSlot)
  461.           end
  462.           bees[slot] = inv.getStackInSlot(slot)
  463.           bee = bees[slot]
  464.         end
  465.         fixBee(bee)
  466.         if useReferenceBees then
  467.           local referenceBySpecies = nil
  468.           if bee.rawName == "item.beedronege" then -- drones
  469.             referenceBySpecies = catalog.referenceDronesBySpecies
  470.           elseif bee.rawName == "item.beeprincessge" then -- princess
  471.             referenceBySpecies = catalog.referencePrincessesBySpecies
  472.           end
  473.           if referenceBySpecies ~= nil and bee.beeInfo.isAnalyzed then
  474.             if bee.beeInfo.active.species == bee.beeInfo.inactive.species then
  475.               local species = bee.beeInfo.active.species
  476.               if referenceBySpecies[species] == nil or
  477.                   compareBees(scorers, bee, referenceBySpecies[species]) then
  478.                 if referenceBySpecies[species] == nil then
  479.                   referenceBeeCount = referenceBeeCount + 1
  480.                   if slot ~= referenceBeeCount then
  481.                     inv.swapStacks(slot, referenceBeeCount)
  482.                   end
  483.                   bee.slot = referenceBeeCount
  484.                 else
  485.                   inv.swapStacks(slot, referenceBySpecies[species].slot)
  486.                   bee.slot = referenceBySpecies[species].slot
  487.                 end
  488.                 referenceBySpecies[species] = bee
  489.               end
  490.             end
  491.           end
  492.         end
  493.       end
  494.     else
  495.       freeSlot = slot
  496.       break
  497.     end
  498.   end
  499.   logLine(string.format("found %d reference bees", referenceBeeCount))
  500.   -- phase 2 -- ditch product and obsolete drones
  501.   bees = inv.getAllStacks()
  502.   local ditchSlot = 1
  503.   for slot = 1 + referenceBeeCount, inv.size do
  504.     local bee = bees[slot]
  505.     if bee ~= nil then
  506.       fixBee(bee)
  507.       -- remove analyzed drones where both the active and inactive species have
  508.       --   a both reference princess and drone
  509.       if bee.beeInfo == nil then
  510.         while inv.pushItem(config.chestDir, slot, 64, ditchSlot) == 0 do
  511.           ditchSlot = ditchSlot + 1
  512.           if ditchSlot > 125 then -- max possible size of ditch chest
  513.             break
  514.           end
  515.         end
  516.       elseif (
  517.         bee.rawName == "item.beedronege" and
  518.         bee.beeInfo.isAnalyzed and (
  519.           catalog.referencePrincessesBySpecies[bee.beeInfo.active.species] ~= nil and
  520.           catalog.referenceDronesBySpecies[bee.beeInfo.active.species] ~= nil and
  521.           catalog.referencePrincessesBySpecies[bee.beeInfo.inactive.species] ~= nil and
  522.           catalog.referenceDronesBySpecies[bee.beeInfo.inactive.species] ~= nil
  523.         )
  524.       ) then
  525.         local activeDroneTraits = betterTraits(scorers, catalog.referenceDronesBySpecies[bee.beeInfo.active.species], bee)
  526.         local inactiveDroneTraits = betterTraits(scorers, catalog.referenceDronesBySpecies[bee.beeInfo.inactive.species], bee)
  527.         if #activeDroneTraits > 0 or #inactiveDroneTraits > 0 then
  528.           -- manipulate reference bee to have better yet less important attribute
  529.           -- this ditches more bees while keeping at least one with the attribute
  530.           -- the cataloging step will fix the manipulation
  531.           for i, trait in ipairs(activeDroneTraits) do
  532.             catalog.referenceDronesBySpecies[bee.beeInfo.active.species].beeInfo.active[trait] = bee.beeInfo.active[trait]
  533.             catalog.referenceDronesBySpecies[bee.beeInfo.active.species].beeInfo.inactive[trait] = bee.beeInfo.inactive[trait]
  534.           end
  535.           for i, trait in ipairs(inactiveDroneTraits) do
  536.             catalog.referenceDronesBySpecies[bee.beeInfo.inactive.species].beeInfo.active[trait] = bee.beeInfo.active[trait]
  537.             catalog.referenceDronesBySpecies[bee.beeInfo.inactive.species].beeInfo.inactive[trait] = bee.beeInfo.inactive[trait]
  538.           end
  539.         else
  540.           -- ditch drone
  541.           while inv.pushItem(config.chestDir, slot, 64, ditchSlot) == 0 do
  542.             ditchSlot = ditchSlot + 1
  543.             if ditchSlot > 108 then
  544.               break
  545.             end
  546.           end
  547.         end
  548.       end
  549.     else
  550.     end
  551.   end
  552.   -- phase 3 -- catalog bees
  553.   bees = inv.getAllStacks()
  554.   for slot, bee in pairs(bees) do
  555.     fixBee(bee)
  556.     bee.slot = slot
  557.     if slot > referenceBeeCount then
  558.       if bee.rawName == "item.beedronege" then -- drones
  559.         table.insert(catalog.drones, bee)
  560.         addBySpecies(catalog.dronesBySpecies, bee)
  561.       elseif bee.rawName == "item.beeprincessge" then -- princess
  562.         table.insert(catalog.princesses, bee)
  563.         addBySpecies(catalog.princessesBySpecies, bee)
  564.       elseif bee.id == 13339 then -- queens
  565.         table.insert(catalog.queens, bee)
  566.       end
  567.     else
  568.       if bee.rawName == "item.beedronege" and bee.qty > 1 then
  569.         table.insert(catalog.drones, bee)
  570.         addBySpecies(catalog.dronesBySpecies, bee)
  571.       end
  572.     end
  573.   end
  574.   logLine(string.format("found %d queens, %d princesses, %d drones",
  575.       #catalog.queens, #catalog.princesses, #catalog.drones))
  576.   return catalog
  577. end
  578.  
  579. -- interaction functions --------------
  580.  
  581. function clearApiary(inv, apiary)
  582.   local beeCount = 0
  583.   local freeSlot = 1
  584.   local productSlot = 0
  585.   local bees = inv.getAllStacks()
  586.   local outputs = apiary.getAllStacks()
  587.   for slot = 3, 9 do
  588.     local output = outputs[slot]
  589.     if output ~= nil then
  590.       while bees[freeSlot] ~= nil do
  591.         freeSlot = freeSlot + 1
  592.       end
  593.       if output.rawName == "item.beedronege" or output.rawName == "item.beeprincessge" then
  594.         if freeSlot > inv.size then
  595.           error("Chest is full")
  596.         end
  597.         beeCount = beeCount + 1
  598.         apiary.pushItem(config.chestDir, slot, 64, freeSlot)
  599.         bees[freeSlot] = inv.getStackInSlot(freeSlot)
  600.       else
  601.         if config.chestDir == config.productDir then
  602.           local found = false
  603.           for productSlot, item in ipairs(bees) do
  604.             if output.name == item.name and
  605.                 (item.maxSize - item.qty) >= output.qty then
  606.               apiary.pushItem(config.productDir, slot, 64, productSlot)
  607.               found = true
  608.               break
  609.             end
  610.           end
  611.           if not found then
  612.             if freeSlot > inv.size then
  613.               error("Chest is full")
  614.             end
  615.             apiary.pushItem(config.productDir, slot, 64, freeSlot)
  616.             bees[freeSlot] = inv.getStackInSlot(freeSlot)
  617.           end
  618.         else
  619.           local productSlot = 1
  620.           while apiary.pushItem(config.productDir, slot, 64, productSlot) == 0 do
  621.             productSlot = productSlot + 1
  622.             if productSlot > 108 then
  623.               break
  624.             end
  625.           end          
  626.         end
  627.       end
  628.     end
  629.   end
  630.   return beeCount
  631. end
  632.  
  633. function clearAnalyzer(inv)
  634.   local invSlot = 1
  635.   local bees = inv.getAllStacks()
  636.   for analyzerSlot = 9, 12 do
  637.     while bees[invSlot] ~= nil do
  638.       invSlot = invSlot + 1
  639.       if invSlot > inv.size then
  640.         error("chest is full")
  641.       end
  642.     end
  643.     inv.pullItem(config.analyzerDir, analyzerSlot, 64, invSlot)
  644.   end
  645. end
  646.  
  647. function analyzeBee(inv, slot)
  648.   clearAnalyzer(inv)
  649.   log("analyzing bee ")
  650.   log(slot)
  651.   log("...")
  652.   if inv.pushItem(config.analyzerDir, slot, 64, 3) > 0 then
  653.     while inv.pullItem(config.analyzerDir, 9, 64, slot) == 0 do
  654.       if inv.getStackInSlot(slot) ~= nil then
  655.         slot = slot + 1
  656.         if slot > inv.size then
  657.           error("chest is full")
  658.         end
  659.       end
  660.       sleep(1)
  661.     end
  662.   else
  663.     logLine("Missing Analyzer")
  664.     useAnalyzer = false
  665.     return nil
  666.   end
  667.   printBee(fixBee(inv.getStackInSlot(slot)))
  668.   return slot
  669. end
  670.  
  671. function waitApiary(inv, apiary)
  672.   log("waiting for apiary")
  673.   while apiary.getStackInSlot(1) ~= nil or apiary.getStackInSlot(2) ~= nil do
  674.     log(".")
  675.     sleep(5)
  676.     if clearApiary(inv, apiary) > 0 then
  677.       -- breeding cycle done
  678.       break
  679.     end
  680.   end
  681.   clearApiary(inv, apiary)
  682.   logLine()
  683. end
  684.  
  685. function breedBees(inv, apiary, princess, drone)
  686.   clearApiary(inv, apiary)
  687.   waitApiary(inv, apiary)
  688.   apiary.pullItem(config.chestDir, princess.slot, 1, 1)
  689.   apiary.pullItem(config.chestDir, drone.slot, 1, 2)
  690.   waitApiary(inv, apiary)
  691. end
  692.  
  693. -- selects best pair for target species
  694. --   or initiates breeding of lower species
  695. function selectPair(mutations, scorers, catalog, targetSpecies)
  696.   logLine("targetting "..targetSpecies)
  697.   local baseChance = 0
  698.   if #mutations.getBeeParents(targetSpecies) > 0 then
  699.     local parents = mutations.getBeeParents(targetSpecies)[1]
  700.     baseChance = parents.chance
  701.     for _, s in ipairs(parents.specialConditions) do
  702.       logLine("    ", s)
  703.     end
  704.   end
  705.   local mateCombos = choose(catalog.princesses, catalog.drones)
  706.   local mates = {}
  707.   local haveReference = (catalog.referencePrincessesBySpecies[targetSpecies] ~= nil and
  708.       catalog.referenceDronesBySpecies[targetSpecies] ~= nil)
  709.   for i, v in ipairs(mateCombos) do
  710.     local chance = mutateBeeChance(mutations, v[1], v[2], targetSpecies) or 0
  711.     if (not haveReference and chance >= baseChance / 2) or
  712.         (haveReference and chance > 25) then
  713.       local newMates = {
  714.         ["princess"] = v[1],
  715.         ["drone"] = v[2],
  716.         ["speciesChance"] = chance
  717.       }
  718.       for trait, scorer in pairs(scorers) do
  719.         newMates[trait] = (scorer(v[1]) + scorer(v[2])) / 2
  720.       end
  721.       table.insert(mates, newMates)
  722.     end
  723.   end
  724.   if #mates > 0 then
  725.     table.sort(mates, compareMates)
  726.     for i = math.min(#mates, 10), 1, -1 do
  727.       local parents = mates[i]
  728.       logLine(beeName(parents.princess), " ", beeName(parents.drone), " ", parents.speciesChance, " ", parents.fertility, " ",
  729.             parents.flowering, " ", parents.nocturnal, " ", parents.tolerantFlyer, " ", parents.caveDwelling, " ",
  730.             parents.lifespan, " ", parents.temperatureTolerance, " ", parents.humidityTolerance)
  731.     end
  732.     return mates[1]
  733.   else
  734.     -- check for reference bees and breed if drone count is 1
  735.     if catalog.referencePrincessesBySpecies[targetSpecies] ~= nil and
  736.         catalog.referenceDronesBySpecies[targetSpecies] ~= nil then
  737.       logLine("Breeding extra drone from reference bees")
  738.       return {
  739.         ["princess"] = catalog.referencePrincessesBySpecies[targetSpecies],
  740.         ["drone"] = catalog.referenceDronesBySpecies[targetSpecies]
  741.       }
  742.     end
  743.     -- attempt lower tier bee
  744.     local parentss = mutations.getBeeParents(targetSpecies)
  745.     if #parentss > 0 then
  746.       logLine("lower tier")
  747.       table.sort(parentss, function(a, b) return a.chance > b.chance end)
  748.       local trySpecies = {}
  749.       for i, parents in ipairs(parentss) do
  750.         fixParents(parents)
  751.         if catalog.referencePrincessesBySpecies[parents.allele2] == nil and trySpecies[parents.allele2] == nil then
  752.           table.insert(trySpecies, parents.allele2)
  753.           trySpecies[parents.allele2] = true
  754.         end
  755.         if catalog.referencePrincessesBySpecies[parents.allele1] == nil and trySpecies[parents.allele1] == nil then
  756.           table.insert(trySpecies, parents.allele1)
  757.           trySpecies[parents.allele1] = true
  758.         end
  759.       end
  760.       for _, species in ipairs(trySpecies) do
  761.         local mates = selectPair(mutations, scorers, catalog, species)
  762.         if mates ~= nil then
  763.           return mates
  764.         end
  765.       end
  766.     end
  767.     return nil
  768.   end
  769. end
  770.  
  771. function isPureBred(bee1, bee2, targetSpecies)
  772.   if bee1.beeInfo.isAnalyzed and bee2.beeInfo.isAnalyzed then
  773.     if bee1.beeInfo.active.species == bee1.beeInfo.inactive.species and
  774.         bee2.beeInfo.active.species == bee2.beeInfo.inactive.species and
  775.         bee1.beeInfo.active.species == bee2.beeInfo.active.species and
  776.         (targetSpecies == nil or bee1.beeInfo.active.species == targetSpecies) then
  777.       return true
  778.     end
  779.   elseif bee1.beeInfo.isAnalyzed == false and bee2.beeInfo.isAnalyzed == false then
  780.     if bee1.beeInfo.displayName == bee2.beeInfo.displayName then
  781.       return true
  782.     end
  783.   end
  784.   return false
  785. end
  786.  
  787. function breedTargetSpecies(mutations, inv, apiary, scorers, targetSpecies)
  788.   local catalog = catalogBees(inv, scorers)
  789.   while true do
  790.     if #catalog.princesses == 0 then
  791.       log("Please add more princesses and press [Enter]")
  792.       io.read("*l")
  793.       catalog = catalogBees(inv, scorers)
  794.     elseif #catalog.drones == 0 and next(catalog.referenceDronesBySpecies) == nil then
  795.       log("Please add more drones and press [Enter]")
  796.       io.read("*l")
  797.       catalog = catalogBees(inv, scorers)
  798.     else
  799.       local mates = selectPair(mutations, scorers, catalog, targetSpecies)
  800.       if mates ~= nil then
  801.         if isPureBred(mates.princess, mates.drone, targetSpecies) then
  802.           break
  803.         else
  804.           breedBees(inv, apiary, mates.princess, mates.drone)
  805.           catalog = catalogBees(inv, scorers)
  806.         end
  807.       else
  808.         log("Please add more bee species and press [Enter]")
  809.         io.read("*l")
  810.         catalog = catalogBees(inv, scorers)
  811.       end
  812.     end
  813.   end
  814.   logLine("Bees are purebred")
  815. end
  816.  
  817. function breedAllSpecies(mutations, inv, apiary, scorers, speciesList)
  818.   if #speciesList == 0 then
  819.     log("Please add more bee species and press [Enter]")
  820.     io.read("*l")
  821.   else
  822.     for i, targetSpecies in ipairs(speciesList) do
  823.       breedTargetSpecies(mutations, inv, apiary, scorers, targetSpecies)
  824.     end
  825.   end
  826. end
  827.  
  828. function main(tArgs)
  829.   logLine(string.format("openbee version %d.%d.%d", version.major, version.minor, version.patch))
  830.   local targetSpecies = setPriorities(tArgs)
  831.   log("priority:")
  832.   for _, priority in ipairs(traitPriority) do
  833.     log(" "..priority)
  834.   end
  835.   logLine("")
  836.   local inv, apiary = getPeripherals()
  837.   inv.size = inv.getInventorySize()
  838.   local mutations, beeNames = buildMutationGraph(apiary)
  839.   local scorers = buildScoring()
  840.   clearApiary(inv, apiary)
  841.   clearAnalyzer(inv)
  842.   local catalog = catalogBees(inv, scorers)
  843.  
  844.   if targetSpecies ~= nil then
  845.     targetSpecies = tArgs[1]:sub(1,1):upper()..tArgs[1]:sub(2):lower()
  846.     if beeNames[targetSpecies] == true then
  847.       breedTargetSpecies(mutations, inv, apiary, scorers, targetSpecies)
  848.     else
  849.       logLine(string.format("Species '%s' not found.", targetSpecies))
  850.     end
  851.   else
  852.     while true do
  853.       breedAllSpecies(mutations, inv, apiary, scorers, buildTargetSpeciesList(catalog, apiary))
  854.       catalog = catalogBees(inv, scorers)
  855.     end
  856.   end
  857. end
  858.  
  859. local logFileName = setupLog()
  860. local status, err = pcall(main, {...})
  861. if not status then
  862.   logLine(err)
  863. end
  864. print("Log file is "..logFileName)
Add Comment
Please, Sign In to add comment