kriNon

Untitled

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