Guest User

Untitled

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