Guest User

Untitled

a guest
Apr 21st, 2019
110
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 38.70 KB | None | 0 0
  1. local version = {
  2. ["major"] = 2,
  3. ["minor"] = 2,
  4. ["patch"] = 1
  5. }
  6.  
  7. local textutils = require("serialization")
  8. local fs = require("filesystem")
  9. local component = require("component")
  10. local sides = require("sides")
  11.  
  12. function loadFile(fileName)
  13. local f = io.open(fileName, "r")
  14. if f ~= nil then
  15. local data = f:read("*a")
  16. f:close()
  17. return textutils.unserialize(data)
  18. end
  19. end
  20.  
  21. function saveFile(fileName, data)
  22. local f = io.open(fileName, "w")
  23. f:write(textutils.serialize(data))
  24. f:close()
  25. end
  26.  
  27. local config = loadFile("bee.config")
  28. if config == nil then
  29. config = {
  30. ["apiarySide"] = "left",
  31. ["chestSide"] = "top",
  32. ["chestDir"] = "up",
  33. ["productDir"] = "down",
  34. ["analyzerDir"] = "east",
  35. ["ignoreSpecies"] = {
  36. "Leporine"
  37. }
  38. }
  39. saveFile("bee.config", config)
  40. end
  41.  
  42. local useAnalyzer = true
  43. local useReferenceBees = true
  44.  
  45. local traitPriority = {
  46. "speciesChance",
  47. "speed",
  48. "fertility",
  49. "nocturnal",
  50. "tolerantFlyer",
  51. "caveDwelling",
  52. "temperatureTolerance",
  53. "humidityTolerance",
  54. "effect",
  55. "flowering",
  56. "flowerProvider",
  57. "territory"
  58. }
  59.  
  60. function setPriorities(priority)
  61. local species = nil
  62. local priorityNum = 1
  63. for traitNum, trait in ipairs(priority) do
  64. local found = false
  65. for traitPriorityNum = 1, #traitPriority do
  66. if trait == traitPriority[traitPriorityNum] then
  67. found = true
  68. if priorityNum ~= traitPriorityNum then
  69. table.remove(traitPriority, traitPriorityNum)
  70. table.insert(traitPriority, priorityNum, trait)
  71. end
  72. priorityNum = priorityNum + 1
  73. break
  74. end
  75. end
  76. if not found then
  77. species = trait
  78. end
  79. end
  80. return species
  81. end
  82.  
  83. -- logging ----------------------------
  84.  
  85. local logFile
  86. function setupLog()
  87. local logCount = 0
  88. while fs.exists(string.format("bee.%d.log", logCount)) do
  89. logCount = logCount + 1
  90. end
  91. logFile = io.open(string.format("bee.%d.log", logCount), "w")
  92. return string.format("bee.%d.log", logCount)
  93. end
  94.  
  95. function log(msg)
  96. msg = msg or ""
  97. logFile:write(tostring(msg))
  98. logFile:flush()
  99. io.write(msg)
  100. end
  101.  
  102. function logLine(...)
  103. local arg = {...}
  104. for i, msg in ipairs(arg) do
  105. if msg == nil then
  106. msg = ""
  107. end
  108. logFile:write(msg)
  109. io.write(msg)
  110. end
  111. logFile:write("\n")
  112. logFile:flush()
  113. io.write("\n")
  114. end
  115.  
  116. function getComponents()
  117. --local names = table.concat(peripheral.getNames(), ", ")
  118. --local chestComponent = peripheral.wrap(config.chestSide)
  119. --if chestComponent == nil then
  120. --error("Bee chest not found at " .. config.chestSide .. ". Valid config values are " .. names .. ".")
  121. --end
  122. --local apiaryComponent = peripheral.wrap(config.apiarySide)
  123. --if apiaryComponent == nil then
  124. --error("Apiary not found at " .. config.apiarySide .. ". Valid config values are " .. names .. ".")
  125. --end
  126. ---- check config directions
  127. --if not pcall(function () chestComponent.pullItem(config.analyzerDir, 9) end) then
  128. --logLine("Analyzer direction incorrect. Direction should be relative to bee chest.")
  129. --useAnalyzer = false
  130. --end
  131. local inv = {}
  132. inv.transposer = component.transposer
  133. inv.db = component.database
  134. inv.interface = component.me_interface
  135. for i=1,80 do inv.db.clear(i) end
  136. local apiary = component.bee_housing
  137. return inv, apiary
  138. end
  139.  
  140. -- utility functions ------------------
  141.  
  142. function choose(list1, list2)
  143. local newList = {}
  144. if list2 then
  145. for i = 1, #list2 do
  146. for j = 1, #list1 do
  147. if list1[j] ~= list2[i] then
  148. table.insert(newList, {list1[j], list2[i]})
  149. end
  150. end
  151. end
  152. else
  153. for i = 1, #list1 do
  154. for j = i, #list1 do
  155. if list1[i] ~= list1[j] then
  156. table.insert(newList, {list1[i], list1[j]})
  157. end
  158. end
  159. end
  160. end
  161. return newList
  162. end
  163.  
  164. -- fix for yet another API change from openp
  165. function getAllBees(inv)
  166. local notbees = inv.interface.getItemsInNetwork()
  167. local bees = {}
  168. for i, bee in ipairs(notbees) do
  169. if bee.individual and bee.individual.isAnalyzed then
  170. local tmp = bee.individual
  171. bee.individual = nil
  172. local maxCount = 0
  173. local matchingBees = inv.interface.getItemsInNetwork(bee)
  174. for i, v in ipairs(matchingBees) do
  175. if v.size > 10 then
  176. maxCount = 20
  177. logLine(string.format("Warning: large bee count for bee %s", beeName(v)))
  178. break
  179. end
  180. if maxCount > 80 then
  181. maxCount = 80
  182. logLine(string.format("Warning: too many bees! %s", beeName(v)))
  183. break
  184. end
  185. maxCount = maxCount + v.size
  186. end
  187. inv.interface.store(bee, inv.db.address, 1, maxCount)
  188. bee.individual = tmp
  189. for j=1,maxCount do
  190. local bee2 = inv.db.get(j)
  191. local match = bee2 and bee2.individual and bee2.individual.isAnalyzed and bee.name == bee2.name
  192. if match then
  193. for k, v in pairs(bee2.individual) do
  194. if type(v) ~= "table" then
  195. if bee.individual[k] ~= v then
  196. match = false
  197. break
  198. end
  199. end
  200. end
  201. end
  202. if match then
  203. for k, v in pairs(bee2.individual.active) do
  204. if bee.individual.active[k] ~= v then
  205. match = false
  206. break
  207. end
  208. end
  209. end
  210. if match then
  211. for k, v in pairs(bee2.individual.inactive) do
  212. if bee.individual.inactive[k] ~= v then
  213. match = false
  214. break
  215. end
  216. end
  217. end
  218. if match then
  219. bee.hash = inv.db.computeHash(j)
  220. break
  221. else
  222. end
  223. if not bee.hash then
  224. logLine(string.format("Warning: no hash found for bee %s after checking %d drones", beeName(bee), maxCount))
  225. else
  226. print(string.format("Found bee: %s", beeName(bee)))
  227. table.insert(bees, bee)
  228. end
  229. for j=1,maxCount do inv.db.clear(j) end
  230. end
  231. end
  232. io.write("\n")
  233. return bees
  234. end
  235.  
  236. function setInterfaceBee(inv, bee, slot)
  237. local tmp = bee.individual
  238. local hash = bee.hash
  239. bee.individual = nil
  240. bee.hash = nil
  241. for i=1,80 do inv.db.clear(i) end
  242. inv.interface.store(bee, inv.db.address, 1, 80)
  243. bee.individual = tmp
  244. bee.hash = hash
  245. local location = 0
  246. for i=1,80 do
  247. if bee.hash == inv.db.computeHash(i) then
  248. location = i
  249. break
  250. end
  251. end
  252. if location > 0 then
  253. inv.interface.setInterfaceConfiguration(slot, inv.db.address, location, 64)
  254. end
  255. end
  256.  
  257. --function getBeeInSlot(inv, slot)
  258. --return inv.getStackInSlot(slot)
  259. --end
  260.  
  261. -- fix for some versions returning bees.species.*
  262. local nameFix = {}
  263. function fixName(name)
  264. if type(name) == "table" then
  265. name = name.name
  266. end
  267. local newName = name:gsub("bees%.species%.",""):gsub("^.", string.upper)
  268. if name ~= newName then
  269. nameFix[newName] = name
  270. end
  271. return newName
  272. end
  273.  
  274. function fixBee(bee)
  275. return bee
  276. --if bee.individual ~= nil then
  277. --bee.individual.displayName = fixName(bee.individual.displayName)
  278. --if bee.individual.isAnalyzed then
  279. --bee.individual.active.species = fixName(bee.individual.active.species)
  280. --bee.individual.inactive.species = fixName(bee.individual.inactive.species)
  281. --end
  282. --end
  283. --return bee
  284. end
  285.  
  286. function fixParents(parents)
  287. parents.allele1 = fixName(parents.allele1)
  288. parents.allele2 = fixName(parents.allele2)
  289. if parents.result then
  290. parents.result = fixName(parents.result)
  291. end
  292. return parents
  293. end
  294.  
  295. function beeName(bee)
  296. local hash = bee.hash or "nil"
  297. if bee.individual.active then
  298. return hash .. "=" .. bee.individual.active.species:sub(1,3) .. "-" ..
  299. bee.individual.inactive.species:sub(1,3)
  300. else
  301. return hash .. "=" .. bee.individual.displayName:sub(1,3)
  302. end
  303. end
  304.  
  305. function printBee(bee)
  306. if bee.individual.isAnalyzed then
  307. local active = bee.individual.active
  308. local inactive = bee.individual.inactive
  309. if active.species ~= inactive.species then
  310. log(string.format("%s-%s", active.species, inactive.species))
  311. else
  312. log(active.species)
  313. end
  314. if bee.raw_name == "item.for.beedronege" then
  315. log(" Drone")
  316. elseif bee.raw_name == "item.for.beeprincessge" then
  317. log(" Princess")
  318. else
  319. log(" Queen")
  320. end
  321. --log((active.nocturnal and " Nocturnal" or " "))
  322. --log((active.tolerantFlyer and " Flyer" or " "))
  323. --log((active.caveDwelling and " Cave" or " "))
  324. logLine()
  325. --logLine(string.format("Fert: %d Speed: %d Lifespan: %d", active.fertility, active.speed, active.lifespan))
  326. else
  327. end
  328. end
  329.  
  330. -- mutations and scoring --------------
  331.  
  332. -- build mutation graph
  333. function buildMutationGraph(apiary)
  334. local mutations = {}
  335. local beeNames = {}
  336. function addMutateTo(parent1, parent2, offspring, chance)
  337. beeNames[parent1] = true
  338. beeNames[parent2] = true
  339. beeNames[offspring] = true
  340. if mutations[parent1] ~= nil then
  341. if mutations[parent1].mutateTo[offspring] ~= nil then
  342. mutations[parent1].mutateTo[offspring][parent2] = chance
  343. else
  344. mutations[parent1].mutateTo[offspring] = {[parent2] = chance}
  345. end
  346. else
  347. mutations[parent1] = {
  348. mutateTo = {[offspring]={[parent2] = chance}}
  349. }
  350. end
  351. end
  352. for _, parents in ipairs(apiary.getBeeBreedingData()) do
  353. fixParents(parents)
  354. addMutateTo(parents.allele1, parents.allele2, parents.result, parents.chance)
  355. addMutateTo(parents.allele2, parents.allele1, parents.result, parents.chance)
  356. end
  357. mutations.getBeeParents = function(name)
  358. return apiary.getBeeParents((nameFix[name] or name))
  359. end
  360. return mutations, beeNames
  361. end
  362.  
  363. function buildTargetSpeciesList(catalog, apiary)
  364. local targetSpeciesList = {}
  365. local parentss = apiary.getBeeBreedingData()
  366. for _, parents in ipairs(parentss) do
  367. local skip = false
  368. for i, ignoreSpecies in ipairs(config.ignoreSpecies) do
  369. if parents.result == ignoreSpecies then
  370. skip = true
  371. break
  372. end
  373. end
  374. if not skip and
  375. ( -- skip if reference pair exists
  376. catalog.referencePrincessesBySpecies[parents.result] == nil or
  377. catalog.referenceDronesBySpecies[parents.result] == nil
  378. ) and
  379. ( -- princess 1 and drone 2 available
  380. catalog.princessesBySpecies[parents.allele1] ~= nil and
  381. catalog.dronesBySpecies[parents.allele2] ~= nil
  382. ) or
  383. ( -- princess 2 and drone 1 available
  384. catalog.princessesBySpecies[parents.allele2] ~= nil and
  385. catalog.dronesBySpecies[parents.allele1] ~= nil
  386. ) then
  387. table.insert(targetSpeciesList, parents.result)
  388. end
  389. end
  390. return targetSpeciesList
  391. end
  392.  
  393. -- percent chance of 2 species turning into a target species
  394. function mutateSpeciesChance(mutations, species1, species2, targetSpecies)
  395. local chance = {}
  396. if species1 == species2 then
  397. chance[species1] = 100
  398. else
  399. chance[species1] = 50
  400. chance[species2] = 50
  401. end
  402. if mutations[species1] ~= nil then
  403. for species, mutates in pairs(mutations[species1].mutateTo) do
  404. local mutateChance = mutates[species2]
  405. if mutateChance ~= nil then
  406. chance[species] = mutateChance
  407. chance[species1] = chance[species1] - mutateChance / 2
  408. chance[species2] = chance[species2] - mutateChance / 2
  409. end
  410. end
  411. end
  412. return chance[targetSpecies] or 0.0
  413. end
  414.  
  415. -- percent chance of 2 bees turning into target species
  416. function mutateBeeChance(mutations, princess, drone, targetSpecies)
  417. if princess.individual.isAnalyzed then
  418. if drone.individual.isAnalyzed then
  419. return (mutateSpeciesChance(mutations, princess.individual.active.species, drone.individual.active.species, targetSpecies) / 4
  420. +mutateSpeciesChance(mutations, princess.individual.inactive.species, drone.individual.active.species, targetSpecies) / 4
  421. +mutateSpeciesChance(mutations, princess.individual.active.species, drone.individual.inactive.species, targetSpecies) / 4
  422. +mutateSpeciesChance(mutations, princess.individual.inactive.species, drone.individual.inactive.species, targetSpecies) / 4)
  423. end
  424. elseif drone.individual.isAnalyzed then
  425. else
  426. return mutateSpeciesChance(princess.individual.displayName, drone.individual.displayName, targetSpecies)
  427. end
  428. end
  429.  
  430. function buildScoring()
  431. function makeNumberScorer(trait, default)
  432. local function scorer(bee)
  433. if bee.individual.isAnalyzed then
  434. return (bee.individual.active[trait] + bee.individual.inactive[trait]) / 2
  435. else
  436. return default
  437. end
  438. end
  439. return scorer
  440. end
  441.  
  442. function makeBooleanScorer(trait)
  443. local function scorer(bee)
  444. if bee.individual.isAnalyzed then
  445. return ((bee.individual.active[trait] and 1 or 0) + (bee.individual.inactive[trait] and 1 or 0)) / 2
  446. else
  447. return 0
  448. end
  449. end
  450. return scorer
  451. end
  452.  
  453. function makeTableScorer(trait, default, lookup)
  454. local function scorer(bee)
  455. if bee.individual.isAnalyzed then
  456. return ((lookup[bee.individual.active[trait]] or default) + (lookup[bee.individual.inactive[trait]] or default)) / 2
  457. else
  458. return default
  459. end
  460. end
  461. return scorer
  462. end
  463.  
  464. local scoresTolerance = {
  465. ["None"] = 0,
  466. ["Up 1"] = 1,
  467. ["Up 2"] = 2,
  468. ["Up 3"] = 3,
  469. ["Up 4"] = 4,
  470. ["Up 5"] = 5,
  471. ["Down 1"] = 1,
  472. ["Down 2"] = 2,
  473. ["Down 3"] = 3,
  474. ["Down 4"] = 4,
  475. ["Down 5"] = 5,
  476. ["Both 1"] = 2,
  477. ["Both 2"] = 4,
  478. ["Both 3"] = 6,
  479. ["Both 4"] = 8,
  480. ["Both 5"] = 10
  481. }
  482.  
  483. local scoresFlowerProvider = {
  484. ["None"] = 5,
  485. ["Rocks"] = 4,
  486. ["Flowers"] = 3,
  487. ["Mushroom"] = 2,
  488. ["Cacti"] = 1,
  489. ["Exotic Flowers"] = 0,
  490. ["Jungle"] = 0
  491. }
  492.  
  493. return {
  494. ["fertility"] = makeNumberScorer("fertility", 1),
  495. ["flowering"] = makeNumberScorer("flowering", 1),
  496. ["speed"] = makeNumberScorer("speed", 1),
  497. ["lifespan"] = makeNumberScorer("lifespan", 1),
  498. ["nocturnal"] = makeBooleanScorer("nocturnal"),
  499. ["tolerantFlyer"] = makeBooleanScorer("tolerantFlyer"),
  500. ["caveDwelling"] = makeBooleanScorer("caveDwelling"),
  501. ["effect"] = makeBooleanScorer("effect"),
  502. ["temperatureTolerance"] = makeTableScorer("temperatureTolerance", 0, scoresTolerance),
  503. ["humidityTolerance"] = makeTableScorer("humidityTolerance", 0, scoresTolerance),
  504. ["flowerProvider"] = makeTableScorer("flowerProvider", 0, scoresFlowerProvider),
  505. ["territory"] = function(bee)
  506. if bee.individual.isAnalyzed then
  507. local activeTerritory = textutils.unserialize(bee.individual.active.territory:sub(6))
  508. local inactiveTerritory = textutils.unserialize(bee.individual.inactive.territory:sub(6))
  509. return ((activeTerritory.x * activeTerritory.y * activeTerritory.z) +
  510. (inactiveTerritory.x * inactiveTerritory.y * inactiveTerritory.z)) / 2
  511. --return ((bee.individual.active.territory[1] * bee.individual.active.territory[2] * bee.individual.active.territory[3]) +
  512. --(bee.individual.inactive.territory[1] * bee.individual.inactive.territory[2] * bee.individual.inactive.territory[3])) / 2
  513. else
  514. return 0
  515. end
  516. end
  517. }
  518. end
  519.  
  520. function compareBees(scorers, a, b)
  521. for _, trait in ipairs(traitPriority) do
  522. local scorer = scorers[trait]
  523. if scorer ~= nil then
  524. local aScore = scorer(a)
  525. local bScore = scorer(b)
  526. if aScore ~= bScore then
  527. return aScore > bScore
  528. end
  529. end
  530. end
  531. return true
  532. end
  533.  
  534. function compareMates(a, b)
  535. for i, trait in ipairs(traitPriority) do
  536. if a[trait] ~= b[trait] then
  537. return a[trait] > b[trait]
  538. end
  539. end
  540. return true
  541. end
  542.  
  543. function betterTraits(scorers, a, b)
  544. local traits = {}
  545. for _, trait in ipairs(traitPriority) do
  546. local scorer = scorers[trait]
  547. if scorer ~= nil then
  548. local aScore = scorer(a)
  549. local bScore = scorer(b)
  550. if bScore > aScore then
  551. table.insert(traits, trait)
  552. end
  553. end
  554. end
  555. return traits
  556. end
  557.  
  558. -- cataloging functions ---------------
  559.  
  560. function addBySpecies(beesBySpecies, bee)
  561. if bee.individual.isAnalyzed then
  562. if beesBySpecies[bee.individual.active.species] == nil then
  563. beesBySpecies[bee.individual.active.species] = {bee}
  564. else
  565. table.insert(beesBySpecies[bee.individual.active.species], bee)
  566. end
  567. if bee.individual.inactive.species ~= bee.individual.active.species then
  568. if beesBySpecies[bee.individual.inactive.species] == nil then
  569. beesBySpecies[bee.individual.inactive.species] = {bee}
  570. else
  571. table.insert(beesBySpecies[bee.individual.inactive.species], bee)
  572. end
  573. end
  574. else
  575. if beesBySpecies[bee.individual.displayName] == nil then
  576. beesBySpecies[bee.individual.displayName] = {bee}
  577. else
  578. table.insert(beesBySpecies[bee.individual.displayName], bee)
  579. end
  580. end
  581. end
  582.  
  583. function catalogBees(inv, scorers)
  584. catalog = {}
  585. catalog.princesses = {}
  586. catalog.princessesBySpecies = {}
  587. catalog.drones = {}
  588. catalog.dronesBySpecies = {}
  589. catalog.queens = {}
  590. catalog.referenceDronesBySpecies = {}
  591. catalog.referencePrincessesBySpecies = {}
  592. catalog.referencePairBySpecies = {}
  593.  
  594. ---- phase 0 -- analyze bees and ditch product
  595. --inv.condenseItems()
  596. --logLine(string.format("scanning %d slots", inv.size))
  597. --if useAnalyzer == true then
  598. --local analyzeCount = 0
  599. --local bees = getAllBees(inv)
  600. --for slot, bee in pairs(bees) do
  601. --if bee.individual == nil then
  602. --inv.pushItem(config.chestDir, slot)
  603. --elseif not bee.individual.isAnalyzed then
  604. --analyzeBee(inv, slot)
  605. --analyzeCount = analyzeCount + 1
  606. --end
  607. --end
  608. --logLine(string.format("analyzed %d new bees", analyzeCount))
  609. --end
  610. -- phase 1 -- mark reference bees
  611. --inv.condenseItems()
  612. local referenceHashes = {}
  613. local referenceBeeCount = 0
  614. local referenceDroneCount = 0
  615. local referencePrincessCount = 0
  616. local isDrone = nil
  617. local bees = getAllBees(inv)
  618. if useReferenceBees then
  619. for i, bee in pairs(bees) do
  620. if bee.individual ~= nil then
  621. fixBee(bee)
  622. local referenceBySpecies = nil
  623. if bee.name == "forestry:bee_drone_ge" then -- drones
  624. isDrone = true
  625. referenceBySpecies = catalog.referenceDronesBySpecies
  626. elseif bee.name == "forestry:bee_princess_ge" then -- princess
  627. isDrone = false
  628. referenceBySpecies = catalog.referencePrincessesBySpecies
  629. else
  630. isDrone = nil
  631. end
  632. if referenceBySpecies ~= nil and bee.individual.isAnalyzed and bee.individual.active.species == bee.individual.inactive.species then
  633. local species = bee.individual.active.species
  634. if referenceBySpecies[species] == nil or
  635. compareBees(scorers, bee, referenceBySpecies[species]) then
  636. if referenceBySpecies[species] == nil then
  637. referenceBeeCount = referenceBeeCount + 1
  638. if isDrone == true then
  639. referenceDroneCount = referenceDroneCount + 1
  640. elseif isDrone == false then
  641. referencePrincessCount = referencePrincessCount + 1
  642. end
  643. --if slot ~= referenceBeeCount then
  644. --inv.swapStacks(slot, referenceBeeCount)
  645. --end
  646. --bee.slot = referenceBeeCount
  647. referenceHashes[bee.hash] = bee
  648. else
  649. --inv.swapStacks(slot, referenceBySpecies[species].slot)
  650. --bee.slot = referenceBySpecies[species].slot
  651. referenceHashes[bee.hash] = bee
  652. end
  653. referenceBySpecies[species] = bee
  654. if catalog.referencePrincessesBySpecies[species] ~= nil and catalog.referenceDronesBySpecies[species] ~= nil then
  655. catalog.referencePairBySpecies[species] = true
  656. end
  657. end
  658. end
  659. end
  660. end
  661. logLine(string.format("found %d reference bees, %d princesses, %d drones", referenceBeeCount, referencePrincessCount, referenceDroneCount))
  662. log("reference pairs")
  663. for species, _ in pairs(catalog.referencePairBySpecies) do
  664. log(", ")
  665. log(species)
  666. end
  667. logLine()
  668. end
  669. -- phase 2 -- ditch obsolete drones
  670. --bees = getAllBees(inv)
  671. local extraDronesBySpecies = {}
  672. --local ditchSlot = 1
  673. local nonReferenceBees = {}
  674. for i, bee in pairs(bees) do
  675. if not referenceHashes[bee.hash] then
  676. table.insert(nonReferenceBees, bee)
  677. end
  678. end
  679. for i, bee in pairs(nonReferenceBees) do
  680. fixBee(bee)
  681. --bee.slot = slot
  682. -- remove analyzed drones where both the active and inactive species have
  683. -- a both reference princess and drone
  684. if (
  685. bee.name == "forestry:bee_drone_ge" and
  686. bee.individual.isAnalyzed and (
  687. catalog.referencePrincessesBySpecies[bee.individual.active.species] ~= nil and
  688. catalog.referenceDronesBySpecies[bee.individual.active.species] ~= nil and
  689. catalog.referencePrincessesBySpecies[bee.individual.inactive.species] ~= nil and
  690. catalog.referenceDronesBySpecies[bee.individual.inactive.species] ~= nil
  691. )
  692. ) then
  693. local activeDroneTraits = betterTraits(scorers, catalog.referenceDronesBySpecies[bee.individual.active.species], bee)
  694. local inactiveDroneTraits = betterTraits(scorers, catalog.referenceDronesBySpecies[bee.individual.inactive.species], bee)
  695. if #activeDroneTraits > 0 or #inactiveDroneTraits > 0 then
  696. -- keep current bee because it has some trait that is better
  697. -- manipulate reference bee to have better yet less important attribute
  698. -- this ditches more bees while keeping at least one with the attribute
  699. -- the cataloging step will fix the manipulation
  700. for i, trait in ipairs(activeDroneTraits) do
  701. catalog.referenceDronesBySpecies[bee.individual.active.species].individual.active[trait] = bee.individual.active[trait]
  702. catalog.referenceDronesBySpecies[bee.individual.active.species].individual.inactive[trait] = bee.individual.inactive[trait]
  703. end
  704. for i, trait in ipairs(inactiveDroneTraits) do
  705. catalog.referenceDronesBySpecies[bee.individual.inactive.species].individual.active[trait] = bee.individual.active[trait]
  706. catalog.referenceDronesBySpecies[bee.individual.inactive.species].individual.inactive[trait] = bee.individual.inactive[trait]
  707. end
  708. else
  709. -- keep 1 extra drone around if purebreed
  710. -- this speeds up breeding by not ditching drones you just breed from reference bees
  711. -- when the reference bee drone output is still mutating
  712. local ditchDrone = nil
  713. if bee.individual.active.species == bee.individual.inactive.species then
  714. if extraDronesBySpecies[bee.individual.active.species] == nil then
  715. extraDronesBySpecies[bee.individual.active.species] = bee
  716. bee = nil
  717. elseif compareBees(bee, extraDronesBySpecies[bee.individual.active.species]) then
  718. ditchDrone = extraDronesBySpecies[bee.individual.active.species]
  719. extraDronesBySpecies[bee.individual.active.species] = bee
  720. bee = ditchDrone
  721. end
  722. end
  723. -- ditch drone
  724. --if bee ~= nil then
  725. --if inv.pushItem(config.chestDir, bee.slot) == 0 then
  726. --error("ditch chest is full")
  727. --end
  728. --end
  729. if bee ~= nil then
  730. setInterfaceBee(inv, bee, 1)
  731. repeat
  732. os.sleep(1)
  733. inv.transposer.transferItem(sides.west, sides.north, 64)
  734. until inv.transposer.getSlotStackSize(sides.west, 1) == 0
  735. inv.interface.setInterfaceConfiguration()
  736. end
  737. end
  738. end
  739. end
  740. -- phase 3 -- catalog bees
  741. bees = getAllBees(inv)
  742. for i, bee in pairs(bees) do
  743. fixBee(bee)
  744. if not referenceHashes[bee.hash] then
  745. if bee.name == "forestry:bee_drone_ge" then -- drones
  746. table.insert(catalog.drones, bee)
  747. addBySpecies(catalog.dronesBySpecies, bee)
  748. elseif bee.name == "forestry:bee_princess_ge" then -- princess
  749. table.insert(catalog.princesses, bee)
  750. addBySpecies(catalog.princessesBySpecies, bee)
  751. elseif bee.name == "forestry:bee_queen_ge" then -- queens
  752. table.insert(catalog.queens, bee)
  753. end
  754. else
  755. if bee.name == "forestry:bee_drone_ge" and bee.size > 1 then
  756. table.insert(catalog.drones, bee)
  757. addBySpecies(catalog.dronesBySpecies, bee)
  758. end
  759. end
  760. end
  761. logLine(string.format("found %d queens, %d princesses, %d drones",
  762. #catalog.queens, #catalog.princesses, #catalog.drones))
  763. return catalog
  764. end
  765.  
  766. -- interaction functions --------------
  767.  
  768. --function clearApiary(inv, apiary)
  769. --local bees = getAllBees(apiary)
  770. ---- wait for queen to die
  771. --if (bees[1] ~= nil and bees[1].raw_name == "item.for.beequeenge")
  772. --or (bees[1] ~= nil and bees[2] ~= nil) then
  773. --log("waiting for apiary")
  774. --while true do
  775. --sleep(5)
  776. --bees = getAllBees(apiary)
  777. --if bees[1] == nil then
  778. --break
  779. --end
  780. --log(".")
  781. --end
  782. --end
  783. --logLine()
  784. --for slot = 3, 9 do
  785. --local bee = bees[slot]
  786. --if bee ~= nil then
  787. --if bee.raw_name == "item.for.beedronege" or bee.raw_name == "item.for.beeprincessge" then
  788. --apiary.pushItem(config.chestDir, slot, 64)
  789. --else
  790. --apiary.pushItem(config.productDir, slot, 64)
  791. --end
  792. --end
  793. --end
  794. --end
  795.  
  796. --function clearAnalyzer(inv)
  797. --if not useAnalyzer then
  798. --return
  799. --end
  800. --local bees = getAllBees(inv)
  801. --if #bees == inv.size then
  802. --error("chest is full")
  803. --end
  804. --for analyzerSlot = 9, 12 do
  805. --if inv.pullItem(config.analyzerDir, analyzerSlot) == 0 then
  806. --break
  807. --end
  808. --end
  809. --end
  810.  
  811. --function analyzeBee(inv, slot)
  812. --clearAnalyzer(inv)
  813. --log("analyzing bee ")
  814. --log(slot)
  815. --log("...")
  816. --local freeSlot
  817. --if inv.pushItem(config.analyzerDir, slot, 64, 3) > 0 then
  818. --while true do
  819. ---- constantly check in case of inventory manipulation by player
  820. --local bees = getAllBees(inv)
  821. --freeSlot = nil
  822. --for i = 1, inv.size do
  823. --if bees[i] == nil then
  824. --freeSlot = i
  825. --break
  826. --end
  827. --end
  828. --if inv.pullItem(config.analyzerDir, 9) > 0 then
  829. --break
  830. --end
  831. --sleep(1)
  832. --end
  833. --else
  834. --logLine("Missing Analyzer")
  835. --useAnalyzer = false
  836. --return nil
  837. --end
  838. --local bee = getBeeInSlot(inv, freeSlot)
  839. --if bee ~= nil then
  840. --printBee(fixBee(bee))
  841. --end
  842. --return freeSlot
  843. --end
  844.  
  845. function breedBees(inv, apiary, princess, drone)
  846. setInterfaceBee(inv, princess, 1)
  847. setInterfaceBee(inv, drone, 2)
  848. log("Press [Enter] when bees are done breeding")
  849. io.read("*l")
  850. --clearApiary(inv, apiary)
  851. --apiary.pullItem(config.chestDir, princess.slot, 1, 1)
  852. --apiary.pullItem(config.chestDir, drone.slot, 1, 2)
  853. --clearApiary(inv, apiary)
  854. end
  855.  
  856. function breedQueen(inv, apiary, queen)
  857. log("breeding queen")
  858. setInterfaceBee(inv, queen, 1)
  859. log("Press [Enter] when queen is done breeding")
  860. io.read("*l")
  861. --clearApiary(inv, apiary)
  862. --apiary.pullItem(config.chestDir, queen.slot, 1, 1)
  863. --clearApiary(inv, apiary)
  864. end
  865.  
  866. -- selects best pair for target species
  867. -- or initiates breeding of lower species
  868. function selectPair(mutations, scorers, catalog, targetSpecies)
  869. logLine("targetting "..targetSpecies)
  870. local baseChance = 0
  871. if #mutations.getBeeParents(targetSpecies) > 0 then
  872. local parents = mutations.getBeeParents(targetSpecies)[1]
  873. baseChance = parents.chance
  874. for _, s in ipairs(parents.specialConditions) do
  875. logLine(" ", s)
  876. end
  877. end
  878. local mateCombos = choose(catalog.princesses, catalog.drones)
  879. local mates = {}
  880. local haveReference = (catalog.referencePrincessesBySpecies[targetSpecies] ~= nil and
  881. catalog.referenceDronesBySpecies[targetSpecies] ~= nil)
  882. for i, v in ipairs(mateCombos) do
  883. local chance = mutateBeeChance(mutations, v[1], v[2], targetSpecies) or 0
  884. if (not haveReference and chance >= baseChance / 2) or
  885. (haveReference and chance > 25) then
  886. local newMates = {
  887. ["princess"] = v[1],
  888. ["drone"] = v[2],
  889. ["speciesChance"] = chance
  890. }
  891. for trait, scorer in pairs(scorers) do
  892. newMates[trait] = (scorer(v[1]) + scorer(v[2])) / 2
  893. end
  894. table.insert(mates, newMates)
  895. end
  896. end
  897. if #mates > 0 then
  898. table.sort(mates, compareMates)
  899. for i = math.min(#mates, 10), 1, -1 do
  900. local parents = mates[i]
  901. logLine(beeName(parents.princess), " ", beeName(parents.drone), " ", parents.speciesChance, " ", parents.fertility, " ",
  902. parents.flowering, " ", parents.nocturnal, " ", parents.tolerantFlyer, " ", parents.caveDwelling, " ",
  903. parents.lifespan, " ", parents.temperatureTolerance, " ", parents.humidityTolerance)
  904. end
  905. return mates[1]
  906. else
  907. -- check for reference bees and breed if drone count is 1
  908. if catalog.referencePrincessesBySpecies[targetSpecies] ~= nil and
  909. catalog.referenceDronesBySpecies[targetSpecies] ~= nil then
  910. logLine("Breeding extra drone from reference bees")
  911. return {
  912. ["princess"] = catalog.referencePrincessesBySpecies[targetSpecies],
  913. ["drone"] = catalog.referenceDronesBySpecies[targetSpecies]
  914. }
  915. end
  916. -- attempt lower tier bee
  917. local parentss = mutations.getBeeParents(targetSpecies)
  918. if #parentss > 0 then
  919. logLine("lower tier")
  920. --print(textutils.serialize(catalog.referencePrincessesBySpecies))
  921. table.sort(parentss, function(a, b) return a.chance > b.chance end)
  922. local trySpecies = {}
  923. for i, parents in ipairs(parentss) do
  924. fixParents(parents)
  925. if (catalog.referencePairBySpecies[parents.allele2] == nil -- no reference bee pair
  926. or catalog.referenceDronesBySpecies[parents.allele2].size <= 1 -- no extra reference drone
  927. or catalog.princessesBySpecies[parents.allele2] == nil) -- no converted princess
  928. and trySpecies[parents.allele2] == nil then
  929. table.insert(trySpecies, parents.allele2)
  930. trySpecies[parents.allele2] = true
  931. end
  932. if (catalog.referencePairBySpecies[parents.allele1] == nil
  933. or catalog.referenceDronesBySpecies[parents.allele1].size <= 1
  934. or catalog.princessesBySpecies[parents.allele1] == nil)
  935. and trySpecies[parents.allele1] == nil then
  936. table.insert(trySpecies, parents.allele1)
  937. trySpecies[parents.allele1] = true
  938. end
  939. end
  940. for _, species in ipairs(trySpecies) do
  941. local mates = selectPair(mutations, scorers, catalog, species)
  942. if mates ~= nil then
  943. return mates
  944. end
  945. end
  946. end
  947. return nil
  948. end
  949. end
  950.  
  951. function isPureBred(bee1, bee2, targetSpecies)
  952. if bee1.individual.isAnalyzed and bee2.individual.isAnalyzed then
  953. if bee1.individual.active.species == bee1.individual.inactive.species and
  954. bee2.individual.active.species == bee2.individual.inactive.species and
  955. bee1.individual.active.species == bee2.individual.active.species and
  956. (targetSpecies == nil or bee1.individual.active.species == targetSpecies) then
  957. return true
  958. end
  959. elseif bee1.individual.isAnalyzed == false and bee2.individual.isAnalyzed == false then
  960. if bee1.individual.displayName == bee2.individual.displayName then
  961. return true
  962. end
  963. end
  964. return false
  965. end
  966.  
  967. function breedTargetSpecies(mutations, inv, apiary, scorers, targetSpecies)
  968. local catalog = catalogBees(inv, scorers)
  969. while true do
  970. if #catalog.princesses == 0 then
  971. log("Please add more princesses and press [Enter]")
  972. io.read("*l")
  973. catalog = catalogBees(inv, scorers)
  974. elseif #catalog.drones == 0 and next(catalog.referenceDronesBySpecies) == nil then
  975. log("Please add more drones and press [Enter]")
  976. io.read("*l")
  977. catalog = catalogBees(inv, scorers)
  978. else
  979. local mates = selectPair(mutations, scorers, catalog, targetSpecies)
  980. if mates ~= nil then
  981. if isPureBred(mates.princess, mates.drone, targetSpecies) then
  982. break
  983. else
  984. breedBees(inv, apiary, mates.princess, mates.drone)
  985. catalog = catalogBees(inv, scorers)
  986. end
  987. else
  988. log(string.format("Please add more bee species for %s and press [Enter]"), targetSpecies)
  989. io.read("*l")
  990. catalog = catalogBees(inv, scorers)
  991. end
  992. end
  993. end
  994. logLine("Bees are purebred")
  995. end
  996.  
  997. function breedAllSpecies(mutations, inv, apiary, scorers, speciesList)
  998. if #speciesList == 0 then
  999. log("Please add more bee species and press [Enter]")
  1000. io.read("*l")
  1001. else
  1002. for i, targetSpecies in ipairs(speciesList) do
  1003. breedTargetSpecies(mutations, inv, apiary, scorers, targetSpecies)
  1004. end
  1005. end
  1006. end
  1007.  
  1008. function main(tArgs)
  1009. logLine(string.format("openbee version %d.%d.%d", version.major, version.minor, version.patch))
  1010. local targetSpecies = setPriorities(tArgs)
  1011. log("priority:")
  1012. for _, priority in ipairs(traitPriority) do
  1013. log(" "..priority)
  1014. end
  1015. logLine("")
  1016. local inv, apiary = getComponents()
  1017. --inv.size = inv.getInventorySize()
  1018. local mutations, beeNames = buildMutationGraph(apiary)
  1019. local scorers = buildScoring()
  1020. --clearApiary(inv, apiary)
  1021. --clearAnalyzer(inv)
  1022. local catalog = catalogBees(inv, scorers)
  1023. while #catalog.queens > 0 do
  1024. breedQueen(inv, apiary, catalog.queens[1])
  1025. catalog = catalogBees(inv, scorers)
  1026. end
  1027. if targetSpecies ~= nil then
  1028. targetSpecies = tArgs[1]:sub(1,1):upper()..tArgs[1]:sub(2):lower()
  1029. if beeNames[targetSpecies] == true then
  1030. breedTargetSpecies(mutations, inv, apiary, scorers, targetSpecies)
  1031. else
  1032. logLine(string.format("Species '%s' not found.", targetSpecies))
  1033. end
  1034. else
  1035. while true do
  1036. breedAllSpecies(mutations, inv, apiary, scorers, buildTargetSpeciesList(catalog, apiary))
  1037. catalog = catalogBees(inv, scorers)
  1038. end
  1039. end
  1040. end
  1041.  
  1042. local logFileName = setupLog()
  1043. local status, err = pcall(main, {...})
  1044. if not status then
  1045. logLine(err)
  1046. end
  1047. print("Log file is "..logFileName)
Add Comment
Please, Sign In to add comment