mrkarp

Untitled

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