View difference between Paste ID: eUe6qc3z and S441ZPVS
SHOW: | | - or go back to the newest paste.
1-
-- BeeAlverizer 1 fork of BeeAnalyzer 4
1+
-- *-*  <- Absolutely needed, do not delete.
2
3-
-- Hotfix 1 by Mandydax
3+
-- BeeAlverizer 1.1 fork of BeeAnalyzer 4
4-
-- Hotfix 2 by Mikeyhun/MaaadMike
4+
5
-- Hotfixes by Mandydax, Mikeyhun and MaaadMike
6-
-- Major overhaul to make work without turtle by Damaged
6+
7
-- Major overhaul and rewrite to make work without turtle by Damaged
8
9-
-- JSON lib loaded to debug arrays, you can leave this commented
9+
10-
-- local JSON = (loadfile "JSON.lua")()
10+
-- JSON lib located at http://pastebin.com/jy3QenZn save as JSON.lua
11-
-- JSON.strictTypes = true
11+
-- Be careful of case
12
local JSON = (loadfile "JSON.lua")()
13
JSON.strictTypes = true
14
 
15
-- Build: http://users.on.net/~damageinc/Alveary%20placement.jpg
16
17
-- Make sure you name this program "bee" (careful of case)
18
19
-- Direction definitions.
20
21
-- When looking at computer, what direction is it?
22
-- Or simply put the direction in the label EG: bee1.west
23
24
local alvearyDirection = "west"
25
26
for i,case in pairs({ ".west", ".east", ".north", ".south" }) do
27
   if string.find(os.getComputerLabel(),case) then
28
      alvearyDirection = string.sub(case, 2)
29
      break
30
   end
31
end
32
print("I appear to be facing: "..alvearyDirection)
33
local analyzerDirection
34
local junkDirection
35
36
if alvearyDirection == "west" then
37
   analyzerDirection = "south"
38
   junkDirection = "north"
39
end
40
if alvearyDirection == "north" then
41
   analyzerDirection = "west"
42
   junkDirection = "east"
43
end
44
if alvearyDirection == "east" then
45
   analyzerDirection = "north"
46
   junkDirection = "south"
47
end
48
if alvearyDirection == "south" then
49
   analyzerDirection = "east"
50
   junkDirection = "west"
51
end
52
53
54
55
56
57
58
59
-- attribute scoring for same species tie-breaking -----------------------------
60
local droneTarget
61
local princessSlot = 0
62
alveary = peripheral.wrap("back")
63
memoryChest = peripheral.wrap("top")
64
local maxMem = memoryChest.getInventorySize()
65
-- Fix for some versions returning bees.species.*
66
function fixName(name)
67
   return name:gsub("bees%.species%.",""):gsub("^.", string.upper)
68
end
69
70
function getBeeData(i)
71
   return memoryChest.getStackInSlot(i)["beeInfo"]
72-
   if getBeeData(i)["speciesPrimary"] then
72+
73-
      return fixName(getBeeData(i)["speciesPrimary"])
73+
74
function getPrimarySpecies(i)
75-
      return fixName(getBeeData(i)["active"]["species"])
75+
   local thisBee = getBeeData(i)
76
   if thisBee["speciesPrimary"] ~= nil then
77
      return fixName(thisBee["speciesPrimary"])
78
   else
79
      return fixName(thisBee["active"]["species"])
80-
   if getBeeData(i)["speciesSecondary"] then
80+
81
end
82
83
function getSecondarySpecies(i)
84
   if getBeeData(i)["speciesSecondary"] ~= nil then
85
      return fixName(getBeeData(i)["speciesSecondary"])
86
   else
87
      return fixName(getBeeData(i)["inactive"]["species"])
88
   end
89
end
90
91
scoresFertility = {
92
   [1] = 0.1,
93
   [2] = 0.2,
94
   [3] = 0.3,
95
   [4] = 0.4
96
}
97
scoresSpeed = {
98
   ["0.3"] = 0.01, -- Slowest
99
   ["0.6"] = 0.02, -- Slower
100
   ["0.8"] = 0.03, -- Slow
101
   ["1"]   = 0.04, -- Normal
102
   ["1.2"] = 0.05, -- Fast
103
   ["1.4"] = 0.06, -- Faster
104
   ["1.7"] = 0.07  -- Fastest
105
}
106
scoresAttrib = {
107
   diurnal       = 0.004,
108
   nocturnal     = 0.003,
109
   tolerantFlyer = 0.002,
110
   caveDwelling  = 0.0001
111
}
112
scoresTolerance = {
113
   ["NONE"]   = 0.00000,
114
   ["UP 1"]   = 0.00001,
115
   ["UP 2"]   = 0.00002,
116
   ["UP 3"]   = 0.00003,
117
   ["UP 4"]   = 0.00004,
118
   ["UP 5"]   = 0.00005,
119
   ["DOWN 1"] = 0.00001,
120
   ["DOWN 2"] = 0.00002,
121
   ["DOWN 3"] = 0.00003,
122
   ["DOWN 4"] = 0.00004,
123
   ["DOWN 5"] = 0.00005,
124
   ["BOTH 1"] = 0.00002,
125
   ["BOTH 2"] = 0.00004,
126
   ["BOTH 3"] = 0.00006,
127
   ["BOTH 4"] = 0.00008,
128
   ["BOTH 5"] = 0.00010
129
}
130
131
-- the bee graph ---------------------------------------------------------------
132
133
bees = {}
134
135
function addParent(parent, offspring)
136
   if bees[parent] then
137
      bees[parent].mutateTo[offspring] = true
138
      else
139
      bees[parent] = {
140
         --name = parent,
141
         score = nil,
142
         mutateTo = {[offspring]=true},
143
         mutateFrom = {}
144
      }
145
   end
146
end
147
148
function addOffspring(offspring, parentss)
149
   if bees[offspring] then
150
      for i, parents in ipairs(parentss) do
151
         table.insert(bees[offspring].mutateFrom, parents)
152
      end
153
      else
154
      bees[offspring] = {
155
         score = nil,
156
         mutateTo = {},
157
         mutateFrom = parentss
158
      }
159
   end
160
   for i, parents in ipairs(parentss) do
161
      for i, parent in ipairs(parents) do
162
         addParent(parent, offspring)
163
      end
164
   end
165
end
166
167
-- score bees that have no parent combinations as 1
168
-- iteratively find the next bee up the line and increase the score
169
function scoreBees()
170
   -- find all bees with no mutateFrom data
171
   local beeCount = 0
172
   local beeScore = 1
173
   for name, beeData in pairs(bees) do
174
      if #beeData.mutateFrom == 0 then
175
         beeData.score = beeScore
176
         else
177
         beeCount = beeCount + 1
178
      end
179
   end
180
   while beeCount > 0 do
181
      beeScore = beeScore * 2
182
      -- find all bees where all parent combos are scored
183
      for name, beeData in pairs(bees) do
184
         if not beeData.score then
185
            local scoreBee = true
186
            for i, beeParents in ipairs(beeData.mutateFrom) do
187
               local parent1 = bees[beeParents[1]]
188
               local parent2 = bees[beeParents[2]]
189
               
190
               if not parent1.score
191
                  or parent1.score == beeScore
192
                  or not parent2.score
193
                  or parent2.score == beeScore then
194
                  scoreBee = false
195
                  break
196
               end
197
            end
198
            if scoreBee then
199
               beeData.score = beeScore
200
               beeCount = beeCount - 1
201
            end
202
         end
203
      end
204
   end
205
end
206
207
-- produce combinations from 1 or 2 lists
208
function choose(list, list2)
209
   local newList = {}
210
   if list2 then
211
      for i = 1, #list2 do
212
         for j = 1, #list do
213
            if list[j] ~= list[i] then
214
               table.insert(newList, {list[j], list2[i]})
215
            end
216
         end
217
      end
218
      else
219
      for i = 1, #list do
220
         for j = i, #list do
221
            if list[i] ~= list[j] then
222
               table.insert(newList, {list[i], list[j]})
223
            end
224
         end
225
      end
226
   end
227
   return newList
228
end
229
230
231
for key, value in pairs (alveary.getBeeBreedingData()) do
232
   addOffspring( fixName(value.result), {{ fixName(value.allele1), fixName(value.allele2) }} )
233
end
234
scoreBees()
235
236
-- logging ---------------------------------------------------------------------
237
238
local logFile = fs.open("bee.log", "w")
239
function log(msg)
240
   msg = msg or ""
241
   logFile.write(tostring(msg))
242
   logFile.flush()
243
   io.write(msg)
244
end
245
function logLine(msg)
246
   msg = msg or ""
247
   logFile.write(msg.."\n")
248
   logFile.flush()
249
   io.write(msg.."\n")
250
end
251
252
function logTable(table)
253
   for key, value in pairs (table) do
254
      logLine(key .. " = " .. tostring(value))
255
   end
256
end
257
258
259
-- analyzing functions ---------------------------------------------------------
260
261
function clearSystem()
262
   -- clear out alveary
263
   for i = 2,9 do
264
      memoryChest.pullItem(alvearyDirection,i)
265
   end
266
   -- clear out analyzer
267
   for i = 1, 12 do
268
      memoryChest.pullItem(analyzerDirection,i)
269
   end
270
end
271
272
function getBees()
273
   -- get bees from apiary
274
   log("waiting for bees.")
275
   while true do
276
      local gotStuff = false
277
      for i = 3,9 do
278
         if memoryChest.pullItem(alvearyDirection,i) ~= 0 then
279
            gotStuff = true
280
         end
281
      end
282
      if gotStuff == true then
283
         break
284
      end
285
      sleep(10)
286
      log(".")
287
   end
288
   log("*")
289
   logLine()
290
end
291
292
local droneData
293
294
function analyzeBees()
295
   princessSlot = 0
296
   logLine("analyzing bees...")
297
   memoryChest.condenseItems()
298
   local highestScore
299
   local highestScoreDrone
300
   droneData = { }
301
   for i,data in pairs(memoryChest.getAllStacks()) do
302
      data = memoryChest.getStackInSlot(i) 
303
      if not ( string.find(data["rawName"], "item.beedrone") or string.find(data["rawName"], "item.beeprincess") ) then
304
         memoryChest.pushItem(junkDirection,i)
305
         print("P")
306
      else
307
         print("B")
308
         --print(JSON:encode(data["beeInfo"]))
309
         --print(data["beeInfo"]["isAnalyzed"]) 
310
         if data["beeInfo"]["isAnalyzed"] == false then
311
            memoryChest.pushItemIntoSlot(analyzerDirection,i,64,3)
312
            while memoryChest.pullItemIntoSlot(analyzerDirection,9,61,i) == 0 do sleep(1) end
313
         end
314
         -- check for untargeted species
315
         if memoryChest.getStackInSlot(i) ~= nil and (not bees[getPrimarySpecies(i)].targeted or not bees[getSecondarySpecies(i)].targeted) then
316
            memoryChest.pushItem(junkDirection,i)
317
         else
318
            if string.find(data["rawName"], "princess") then
319
               princessSlot = i
320
            end
321
         end
322
      end
323
   end
324
   if princessSlot ~= 0 then
325
      for i,data in pairs(memoryChest.getAllStacks()) do
326
         if i ~= princessSlot then
327
            droneData[i] = scoreBee(i)
328
            if droneData[i] == 0 then
329
               memoryChest.pushItem(junkDirection,i)
330
               droneData[i] = nil
331
            end
332
         end
333
      end
334
      -- find the best drone
335
      logLine("sorting drones...")
336
      highestScore = 0
337
      for i,beeScore in pairs(droneData) do
338
         if beeScore > highestScore then
339-
      logLine("cleaning out drones")
339+
340
            highestScoreDrone = i
341-
         if beeScore < ( highestScore / 3 ) then
341+
342
      end
343-
            droneData[i] = nil
343+
344
      for i = 1, maxMem do
345
         if memoryChest.getStackInSlot(i) ~= nil then
346
            printBee(i)
347
         end
348
      end
349
   end
350
   logLine()
351
   return highestScoreDrone
352
end
353
354
function scoreBee(droneID)
355
   local beeDataDrone = getBeeData(droneID)
356
   local beeDataPrincess = getBeeData(princessSlot)
357
   local droneSpecies = {getPrimarySpecies(droneID), getSecondarySpecies(droneID)}
358
   -- check for untargeted species
359
   if not bees[droneSpecies[1]].targeted or not bees[droneSpecies[2]].targeted then
360
      return 0
361
   end
362
   local princessSpecies = {getPrimarySpecies(princessSlot), getSecondarySpecies(princessSlot)}
363
   local score
364
   local maxScore = 0
365
   for _, combo in ipairs({{princessSpecies[1], droneSpecies[1]}
366
      ,{princessSpecies[1], droneSpecies[2]}
367
      ,{princessSpecies[2], droneSpecies[1]}
368
   ,{princessSpecies[2], droneSpecies[2]}}) do
369
      -- find maximum score for each combo
370
      score = (bees[combo[1]].score + bees[combo[2]].score) / 2
371
      for name, beeData in pairs(bees) do
372
         if beeData.targeted then
373
            for i, parents in ipairs(beeData.mutateFrom) do
374
               if combo[1] == parents[1] and combo[2] == parents[2]
375
                  or combo[2] == parents[1] and combo[1] == parents[2] then
376
                  score = (score + beeData.score) / 2
377
               end
378
            end
379
         end
380
      end
381
      maxScore = maxScore + score
382
   end
383
   -- add one for each combination that results in the maximum score
384
   score = maxScore
385
   -- score attributes
386
   score = score + math.max(scoresFertility[beeDataDrone["active"]["fertility"]], scoresFertility[beeDataPrincess["active"]["fertility"]])
387
   score = score + math.min(scoresSpeed[tostring(beeDataDrone["active"]["speed"])], scoresSpeed[tostring(beeDataPrincess["active"]["speed"])])
388
   if beeDataDrone["active"]["nocturnal"] or beeDataPrincess["active"]["nocturnal"] then score = score + scoresAttrib["nocturnal"] end
389
   if beeDataDrone["active"]["tolerantFlyer"] or beeDataPrincess["active"]["tolerantFlyer"] then score = score + scoresAttrib["tolerantFlyer"] end
390
   if beeDataDrone["active"]["caveDwelling"] or beeDataPrincess["active"]["caveDwelling"] then score = score + scoresAttrib["caveDwelling"] end
391
   score = score + math.max(scoresTolerance[string.upper(beeDataDrone["active"]["temperatureTolerance"])], scoresTolerance[string.upper(beeDataPrincess["active"]["temperatureTolerance"])])
392
   score = score + math.max(scoresTolerance[string.upper(beeDataDrone["active"]["humidityTolerance"])], scoresTolerance[string.upper(beeDataPrincess["active"]["humidityTolerance"])])
393
   return score
394
end
395
396
function printHeader()
397
   logLine()
398
   logLine("sl t species f spd n f c tmp hmd score ")
399
   logLine("--|-|-------|-|---|-|-|-|---|---|------")
400
end
401
402
-- string constants for console output
403
toleranceString = {
404
   ["NONE"] = "  - ",
405
   ["UP 1"] = " +1 ",
406
   ["UP 2"] = " +2 ",
407
   ["UP 3"] = " +3 ",
408
   ["UP 4"] = " +4 ",
409
   ["UP 5"] = " +5 ",
410
   ["DOWN 1"] = " -1 ",
411
   ["DOWN 2"] = " -2 ",
412
   ["DOWN 3"] = " -3 ",
413
   ["DOWN 4"] = " -4 ",
414
   ["DOWN 5"] = " -5 ",
415
   ["BOTH 1"] = "+-1 ",
416
   ["BOTH 2"] = "+-2 ",
417
   ["BOTH 3"] = "+-3 ",
418
   ["BOTH 4"] = "+-4 ",
419
   ["BOTH 5"] = "+-5 "
420
}
421
422
speedString = {
423
   ["0.3"] = "0.3", -- Slowest
424
   ["0.6"] = "0.6", -- Slower
425
   ["0.8"] = "0.8", -- Slow
426
   ["1"]   = "1.0", -- Normal
427
   ["1.2"] = "1.2", -- Fast
428
   ["1.4"] = "1.4", -- Faster
429
   ["1.7"] = "1.7"  -- Fastest
430
}
431
432
function printBee(i)
433
   local beeData = getBeeData(i)
434
   log(i < 10 and " "..i.." " or i.." ")
435
   -- type
436
   log(string.find(memoryChest.getStackInSlot(i)["rawName"], "princess") and "P " or "D ")
437
   -- species
438
   log(getPrimarySpecies(i):gsub("bees%.species%.",""):sub(1,3)..":"..getSecondarySpecies(i):gsub("bees%.species%.",""):sub(1,3).." ")
439
   -- fertility
440
   log(tostring(beeData["active"]["fertility"]).." ")
441
   -- speed
442
   log(speedString[tostring(beeData["active"]["speed"])].." ")
443
   -- nocturnal
444
   log(beeData["active"]["nocturnal"] and "X " or "- ")
445
   -- flyer
446
   log(beeData["active"]["tolerantFlyer"] and "X " or "- ")
447
   -- cave dwelling
448
   log(beeData["active"]["caveDwelling"] and "X " or "- ")
449
   -- temperature tolerance
450
   log(toleranceString[string.upper(beeData["active"]["temperatureTolerance"])])
451
   -- humidity tolerance
452
   log(toleranceString[string.upper(beeData["active"]["humidityTolerance"])])
453
   -- score
454
   log(droneData[i] and string.format("%5.1d", droneData[i]).." " or "      ")
455
   logLine()
456
end
457
458
function dropExcess()
459
   logLine("dropping excess...")
460
   for i = ( maxMem - 9 ), maxMem do
461
      -- drop drones over ( max inv - 9 ) to clear space for newly bred bees and product
462
      memoryChest.pushItem(junkDirection,i)
463
   end 
464
end
465
466
function isPurebred(droneID)
467
   -- check if princess and drone are exactly the same and no chance for mutation
468
   local princessPrimary = getPrimarySpecies(princessSlot)
469
   if princessPrimary ~= getSecondarySpecies(princessSlot) then
470
      return false
471
   end
472
   if getPrimarySpecies(droneID) ~= princessPrimary then
473
      return false
474
   end
475
   if getSecondarySpecies(droneID) ~= princessPrimary then
476
      return false
477
   end
478
   local droneBeeData = getBeeData(droneID)
479
   local princessBeeData = getBeeData(princessSlot)
480
   for i,key in pairs({ "fertility", "speed", "nocturnal", "tolerantFlyer", "caveDwelling", "temperatureTolerance", "humidityTolerance" }) do
481
      if
482
         droneBeeData["active"][key] ~= princessBeeData["active"][key] or
483
         droneBeeData["inactive"][key] ~= princessBeeData["inactive"][key]
484
      then
485
         return false
486
      end
487
   end
488
   return true
489
end
490
491
-- targeting -------------------------------------------------------------------
492
493
-- set species and all parents to targeted
494
function targetBee(name)
495
   local bee = bees[name]
496
   if bee and not bee.targeted then
497
      bee.targeted = true
498
      for i, parents in ipairs(bee.mutateFrom) do
499
         for j, parent in ipairs(parents) do
500
            targetBee(parent)
501
         end
502
      end
503
   end
504
end
505
506
tArgs = { ... }
507
508
if fs.exists("bee.data") and #tArgs == 0 then
509
   local recoveredData = fs.open("bee.data", "r")
510
   local historyData = recoveredData.readLine()
511
   if historyData then
512
      historyData = JSON:decode(historyData)
513
      tArgs[1] = historyData["target"]
514
      logLine("Recovered old data.")
515
   end
516
   recoveredData.close()
517
end 
518
519
520
function findMe()
521
   local myProgramName
522
   for i,fileName in pairs( fs.list("") ) do
523
      if type(fileName) == "string" then
524
         if fileName ~= "" then
525
            local tempFile = fs.open(fileName,"r")
526
            if string.sub(tempFile.readLine(),4,6) == "*-*" then
527
               myProgramName = fileName
528
               break
529
            end
530
         end
531
      end
532
   end
533
   return myProgramName
534
end
535
      
536
537
-- set bee graph entry to targeted if species was specified on the command line
538
-- otherwise set all entries to targeted
539
540
if #tArgs > 0 then
541
   logLine("targeting bee species:")
542
   fs.delete("bee.data")
543
   recoveredData = fs.open("bee.data", "w")
544
   local tempArray = {}
545
   tempArray["target"] = tArgs[1]
546
   recoveredData.write( JSON:encode( tempArray ) )
547
   recoveredData.close()
548
   for i, target in ipairs(tArgs) do
549
      targetBee(target)
550
      for name, data in pairs(bees) do
551
         if data.targeted and data.score > 1 then
552
            logLine(name .. string.rep(" ", 20-#name), data.score)
553
         end
554
      end
555
   end
556
   local myProgramName = findMe()
557
   logLine("Found program:"..myProgramName)
558
   if myProgramName ~= nil then
559
      fs.delete("startup")
560
      local startupFile = fs.open("startup","w")
561
      startupFile.write("shell.run(\""..myProgramName.."\")")
562
      startupFile.close()
563
   else
564
      logLine("Cannot find program name, you edited the first line of the program didn't you!")
565
   end
566
else
567
   for _, beeData in pairs(bees) do
568
      beeData.targeted = true
569
   end
570
end
571
572
-- breeding loop ---------------------------------------------------------------
573
574
  
575
576
logLine("Clearing system...")
577
clearSystem()
578
if not memoryChest.getAllStacks() then
579
   print("No bees to work on, add bees and restart")
580
else
581
   redstone.setOutput("bottom", true)
582
   while true do
583
      droneTarget = analyzeBees()
584
      if princessSlot ~= 0 then
585
         if isPurebred(droneTarget) then
586
            logLine("Bees are purebred.")
587
            logLine("Princess: "..princessSlot)
588
            logLine("Drone: "..droneTarget)
589
            redstone.setOutput("bottom", false)
590
            fs.delete("bee.data")
591
            fs.delete("startup")
592
            break
593
         end
594
         memoryChest.pushItemIntoSlot(alvearyDirection,princessSlot,1,1)
595
         memoryChest.pushItemIntoSlot(alvearyDirection,droneTarget,1,2)
596
         dropExcess()
597
      end
598
      getBees()
599
   end
600
end
601
logFile.close()