View difference between Paste ID: qyJgzgDC and BhC5e8Ys
SHOW: | | - or go back to the newest paste.
1
--Fichier de sauvegarde des variables utiles au reboot :
2
-- turtle.farm.dir: Orientation de la turtle 0,1,2,3 (N-E-S-W)
3
-- turtle.farm.fuel: Niveau de fuel au d?part d'une boucle
4
-- Si le fichier n'existe pas il sera cr??
5
local settingsFile = "/settings/TurtleFarm"
6
7
--Fichier contenant les donn?es ? inscrire sur le moniteur
8
-- N?cessairement sur un disk
9
-- Si le mountPath est modifi?, il sera corrig? au premier disk drive trouv?
10
local dataFile = "/disk/FarmData"
11
12
--Delai appliqu? ? chaque pas
13
local delay=.1
14
15
--Dur?e de la pause entre deux tours
16
local pause=900 --15min
17
18
--Multiplicateur
19
-- pass? ? 10, la turtle pr?voira asez de fuel pour 10 tours
20
local fuelSupply=4 -- minimum 2 par s?curit?
21
22
--Soupape : nombre d'?checs successifs tol?r?s dans une boucle while
23
local limit=32
24
25
-- D?lai d'attente de confirmation pour r?initialiser au reboot
26
-- Hard reboot: Placer un bloc de redstone sur le parcours de la turtle
27
-- La turtle stoppe. Retirer le bloc, la turtle demande confirmation pour r?initialiser
28
-- Sans confirmation pass? le d?lai hardRebootCountDown, elle reprend sa course
29
local hardRebootCountDown = 10
30
31
--Liste des items bons ? jeter (ne seront pas stock?s)
32
unwanted = {
33
["minecraft:poisonous_potato"]=1,
34
--["minecraft:wheat_seeds"]=1, --NB: un stack de graines sera tout de m?me conserv? par la turtle pour replanter
35
}
36
37
--
38
-- API
39
--
40
if not os.loadAPI("/apis/dbFarm") then
41
	error("Missing API: dbFarm\n> pastebin get k0bi24gr /apis/dbFarm")
42
	return
43
end
44
if not os.loadAPI("/apis/dbFuel") then
45
	error("Missing API: dbFuel\n> pastebin get 5g2DntXx /apis/dbFuel")
46
	return
47
end
48
49
-- 
50
-- CONSTANTS
51
-- 
52
NORTH=0
53
EAST=1
54
SOUTH=2
55
WEST=3
56
sDir={"North","East","South","West"}
57
58
plantAges = {
59
["minecraft:wheat"]      =7,     
60
["minecraft:carrots"]    =7,   
61
["minecraft:potatoes"]   =7,  
62
["minecraft:melon_block"]=7,
63
["minecraft:pumpkin"]    =7,   
64
["minecraft:reeds"]      =7,     
65
["minecraft:nether_wart"]=3, --/!\
66
["minecraft:beetroots"]  =3, --/!\
67
}
68
69
--Bloc reference (see function walk() 263)
70
--sBlockType = "minecraft:stained_hardened_clay"
71
sBlockType = "minecraft:stained_glass"
72
local function blockType_north(data)
73
	return data.name==sBlockType
74
	and data.state
75
	and data.state.color=="red"
76
end
77
local function blockType_east(data)
78
	return data.name==sBlockType
79
	and data.state
80
	and data.state.color=="orange"
81
end
82
local function blockType_south(data)
83
	return data.name==sBlockType
84
	and data.state
85
	and data.state.color=="yellow"
86
end
87
local function blockType_west(data)
88
	return data.name==sBlockType
89
	and data.state
90
	and data.state.color=="white"
91
end
92
93
local function isContainer(name)
94
	return name=="minecraft:chest"
95
	or name=="minecraft:trapped_chest"
96
	or name=="minecraft:dispenser"
97
	or name=="minecraft:hopper"
98
	or name=="minecraft:dropper"
99
	or name=="computercraft:CC-Turtle"
100
	or name=="computercraft:CC-TurtleExpanded"
101
	or name=="computercraft:CC-TurtleAdvanced"
102
	or name=="ironchest:BlockIronChest"
103
end
104
105
--
106
-- GLOBALS
107
--
108
fuelSupply = math.max(2,fuelSupply)
109
fuelUsed=0
110
block={up={},down={}}
111
tick = os.startTimer(.1)
112
tFarmCrops,tFarmSeeds,tFarmBlocks = {},{},{}
113
local _,v
114
local _,_cropsList = dbFarm.query("crop")
115
local _,_seedsList = dbFarm.query("seed")
116
local _,_blocksList = dbFarm.query("block")
117
for _,v in pairs(_cropsList)  do tFarmCrops[v]=1 end
118
for _,v in pairs(_seedsList)  do tFarmSeeds[v]=1 end
119
for _,v in pairs(_blocksList) do tFarmBlocks[v]=1 end
120
121
122
--Demande l'orientation de la turtle (N-E-S-W)
123
--Initialise le fichier de dsauvegarde des settings
124
init=function()
125
	local msg = ""
126
	local confirm=false
127
	while not confirm do
128
		term.setCursorPos(1,1)
129
		term.clear()
130
		print([[TurtleFarming
131
Set Turtle current orientation
132
  0:North
133
  1:East
134
  2:South
135
  3:West]])
136
		print(msg)
137
		local d = read()
138
		if not tonumber(d) or (d~="0" and d~="1" and d~="2" and d~="3") then
139
			msg="Number: 0, 1, 2 ort 3"
140
		else
141
			settings.set("turtle.farm.dir",tonumber(d))
142
			settings.save(settingsFile)
143
			confirm=true
144
			break
145
		end
146
	end
147
end
148
149
--Charge les donn?es stock?es dans le fichier settings
150
if not fs.exists(settingsFile) then init()
151
else settings.load(settingsFile) end
152
local dir = settings.get("turtle.farm.dir",init)
153
--Statistiques par tour
154
lap = {
155
	loop=0,
156
	chrono=0,
157
	crops=settings.get("turtle.farm.crops",0),
158
	fuel=settings.get("turtle.farm.fuel",turtle.getFuelLevel())
159
}
160
161
--Ecrit sur le terminal ? une ligne donn?e
162
local function printLine(str,y)
163
	term.setCursorPos(1,y)
164
	term.clearLine()
165
	print(str)
166
end
167
168
--Hard reboot
169
if rs.getInput("back") or rs.getInput("front")
170
or rs.getInput("top")  or rs.getInput("bottom")
171
or rs.getInput("left") or rs.getInput("right") then
172
	print("Hard Reboot")
173
	os.pullEvent("redstone")
174
	local x,y = term.getCursorPos()
175
	local confirm,countdown,countdownTimer = false,hardRebootCountDown,os.startTimer(1)
176
	while not confirm and countdown>0 do
177
		printLine("Reset Turtle orientation ("..sDir[dir+1]..") ?",y)
178
		printLine("Press Y/N ("..countdown..")", y+1)
179
		local e,k = os.pullEvent()
180
		if e=="key" and k==keys.y then
181
			sleep(.3)
182
			init()
183
			confirm=true
184
			break
185
		elseif e=="key" and k==keys.n then
186
			sleep(.3)
187
			confirm=true
188
			break
189
		elseif e=="timer" then
190
			countdown=countdown-1
191
			countdownTimer = os.startTimer(1)
192
		end
193
	end
194
end
195
196
-- Panne de Fuel
197
-- interrompt la turtle, en l'attente de ravitaillement en fuel
198
local function waitManualyRefuel()
199
	printLine("Give some Fuel then press any key",8)
200
	while turtle.getFuelLevel()==0 do
201
		os.pullEvent("key")
202
		term.setCursorPos(1,9)
203
		term.clearLine()
204
		shell.run("refuel all")
205
	end
206
	printLine("",8)
207
end
208
209
-- Avance la turtle d'une case
210
-- R?colte la canne ? surcre si elle bloque le passage
211
local function forward()
212
	local test = turtle.forward()
213
	if not test and turtle.detect() then
214
		local test,data=turtle.inspect()
215
		if test and data.name=="minecraft:reeds" then
216
			if turtle.dig() and turtle.forward() then 
217
				lap.crops = lap.crops+1
218
				settings.set("turtle.farm.crops",lap.crops)
219
				settings.save(settingsFile)
220
				turtle.suckUp()
221
				turtle.suck()
222
				turtle.suckDown()
223
				return true
224
			end
225
		end
226
		error("Program stopped by a block forward\n"..textutils.serialize(data),-1) exit()
227
	end
228
	local count=1
229
	while not test and not turtle.detect() and turtle.getFuelLevel()>0 and count<limit do
230
		sleep(delay)
231
		count = count+1
232
		test = turtle.forward()
233
	end
234
	if not test then
235
		if turtle.getFuelLevel()==0 then
236
			waitManualyRefuel()
237
		else
238
			error("Program stopped. Can't go forward (tries:"..count..")",-1)
239
			exit()
240
		end
241
	end
242
end
243
244
-- Rel?ve les donn?es des blocs dessus/dessous
245
local function inspect()
246
	local test,data
247
	test,data = turtle.inspectUp()
248
	block.up = test and data or false
249
	test,data = turtle.inspectDown()
250
	block.down = test and data or false
251
end
252
253
-- Aligne la turtle dans la direction indiqu?e
254
-- Sauvegarde de la nouvelle direction dans le fichier settings
255
local function turn(d)
256
	if dir ~= d then
257
		if d == NORTH then
258
			    if dir==EAST  then turtle.turnLeft()
259
			elseif dir==SOUTH then turtle.turnLeft() turtle.turnLeft()
260
			elseif dir==WEST  then turtle.turnRight()
261
			end                    
262
		elseif d == EAST then      
263
			    if dir==NORTH then turtle.turnRight()
264
			elseif dir==SOUTH then turtle.turnLeft()
265
			elseif dir==WEST  then turtle.turnLeft() turtle.turnLeft()
266
			end                    
267
		elseif d == SOUTH then     
268
			    if dir==NORTH then turtle.turnLeft() turtle.turnLeft()
269
			elseif dir==EAST  then turtle.turnRight()
270
			elseif dir==WEST  then turtle.turnLeft()
271
			end                    
272
		elseif d == WEST then      
273
			    if dir==NORTH then turtle.turnLeft()
274
			elseif dir==EAST  then turtle.turnLeft() turtle.turnLeft()
275
			elseif dir==SOUTH then turtle.turnRight()
276
			end
277
		end
278
		dir = d
279
		settings.set("turtle.farm.dir",d)
280
		settings.save(settingsFile)
281
	end
282
end
283
284
-- G?re le mouvement de la turtle
285
local function walk()
286
	printLine("",7)
287
	if block.up then
288
		if blockType_north(block.up) then
289
			printLine("North",7) turn(NORTH) forward()
290
		elseif blockType_east(block.up) then
291
			printLine("East",7)  turn(EAST)  forward()
292
		elseif blockType_south(block.up) then
293
			printLine("South",7) turn(SOUTH) forward()
294
		elseif blockType_west(block.up) then
295
			printLine("West",7)  turn(WEST)  forward()
296
		elseif isContainer(block.up.name) then
297
			printLine("Container",7) forward()
298
		else
299
			printLine(block.up.name,7)
300
		end
301
	end
302
end
303
304
-- Formate un nombre de secondes en dur?e lisible
305
local function timeStr(sec)
306
	local d = math.floor(sec/86400) --24*60*60 sec
307
	local h = math.floor(sec/3600)%24 -- 60x60 sec
308
	local m = math.floor(sec/60)%60 -- 60sec
309
	local s = math.floor(sec%60) --Reste
310
	local str = ""
311
	if d>0 then str = d>1 and d.. "days " or "1 day " end
312
	if h>0 then str = str..h.."h" end
313
	if m>0 then str = str..(m>9 and m or "0"..m) end
314
	if h==0 then str = m>0 and str..":"..(s>9 and s or "0"..s) or s.."s" end
315
	return str
316
end
317
318
-- V?rifie que le r?pertoire racine de la disquette est bien pr?sent
319
-- Sinon la variable est corrig?e (au premier disk drive trouv?)
320
local function getMountedDisk()
321
	if fs.exists(dataFile) then return true end
322
	local _,name
323
	for _,name in pairs(peripheral.getNames()) do
324
		if peripheral.getType(name) == "drive" then
325
			if disk.hasData(name) then
326
				local mount = disk.getMountPath(name)
327
				local pattern = "^/?disk%d*/(.+)$"
328
				dataFile = string.gsub(dataFile, pattern, "/"..mount.."/%1")
329
				return true
330
			end
331
		end
332
	end
333
	return false
334
end
335
336
-- Sauvegarde les donn?es statistiques dans une disquette
337
-- ? fin d'affichage sur un moniteur
338
local function storeData()
339
	lap.loop=lap.loop+1
340
	if getMountedDisk() then --S'il y a bien une disquette disponible
341
		local sChrono,sFuel="",""
342
		if lap.chrono>0 then
343
			sChrono = " / "..math.floor(os.clock()-lap.chrono)
344
		end
345
		if lap.fuel>0 then
346
			sFuel = " / used: "..(lap.fuel - turtle.getFuelLevel())
347
		end
348
		local report = string.format([[Loops: %d
349
Running until: %s%s
350
Fuel level: %d%s
351
Crops: %d]],
352
			lap.loop,
353
			timeStr(os.clock()), sChrono,
354
			turtle.getFuelLevel(),sFuel,
355
			lap.crops)
356
		local fh = fs.open(dataFile,"w")
357
		fh.write(report)
358
		fh.close()
359
	end
360
	lap.chrono = os.clock()
361
	lap.fuel = turtle.getFuelLevel()
362
	settings.set("turtle.farm.fuel",lap.fuel)
363
	settings.save(settingsFile)
364
end
365
366
-- Regroupe les items identiques dans l'inventaire
367
local function compactInventory()
368
	printLine("Compact inventory",8)
369
	local i,j,d1,d2
370
	for i=1,16 do
371
		d1 = turtle.getItemDetail(i)
372
		if d1~=nil and turtle.getItemSpace(i)>0 then
373
			for j=i+1,16 do
374
				d2 = turtle.getItemDetail(j)
375
				if d2~=nil and d1.name==d2.name then
376
					turtle.select(j)
377
					turtle.transferTo(i)
378
				end
379
			end
380
		end
381
	end
382
end
383
384
--Renvoi le premier slot vide trouv?
385
--Renvoi false si linventaire est plein
386
local function findFreeSlot()
387
	local i
388
	for i=1,16 do
389
		if turtle.getItemCount(i)==0 then
390
			return i
391
		end
392
	end
393
	return false
394
end
395
396
--Renvoi le premier slot contenant un combustible compatible
397
--Renvoi false si aucun combustible n'est trouv?
398
local function findFuelSlot()
399
	local i
400
	for i=1,16 do
401
		local data = turtle.getItemDetail(i)
402
		if data and dbFuel.isFuel(data.name) then
403
			return i
404
		end
405
	end
406
	return false
407
end
408
409
-- https://github.com/garrynewman/garrysmod/blob/master/garrysmod/lua/includes/extensions/table.lua#L93
410
local function tableHasValue( t, val )
411
	for k, v in pairs( t ) do
412
		if ( v == val ) then return true end
413
	end
414
	return false
415
end
416
417
--Compare les quantit?s de chaque slot indiqu?s par slotList
418
--_v: Retourne le num?ro du slot le moins fourni
419
--_k: Retourne la cl? correspondante de la table slotList
420
local function getMinQtySlot(slotList)
421
	slotList = slotList or {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}
422
	local _min,k,v,_k,_v=64
423
	for k,v in pairs(fuelSlots) do
424
		if _min >= turtle.getItemCount(v) then
425
			_min = turtle.getItemCount(v)
426
			_k = k
427
			_v = v
428
		end
429
	end
430
	return _v,_k
431
end
432
433
--Vide les slots contenant des items inutiles
434
-- drop : function (turtle.drop, turtle.dropUp, turtle.dropDown)
435
-- n : nombre de slot ? lib?rer
436
local function dumpUnwanted(drop,n)
437
	n = n or 16
438
	compactInventory()
439
	printLine("Dump unwanted",8)
440
	local i,farmSlots,fuelSlots=1,{},{},{}
441
-- >global	local tFarmCrops = dbFarm.query("crop")
442
-- >global	local tFarmSeeds = dbFarm.query("seed")
443
	for i=1,16 do
444
		local data = turtle.getItemDetail(i)
445
		if not data then
446
			n=n-1
447
		elseif dbFuel.isFuel(data.name) then
448
			table.insert(fuelSlots,i)
449
		elseif tFarmSeeds[data.name]~=nil then
450
			table.insert(farmSlots,i)
451
		elseif tFarmCrops[data.name]~=nil then
452
			table.insert(farmSlots,i)
453
		else
454
			turtle.select(i)
455
			drop(64)
456
			n=n-1
457
		end
458
		if n<=0 then return true end
459
	end
460
	while n>1 do
461
		if #fuelSlots > 0 then
462
			local slot,key = getMinQtySlot(fuelSlots)
463
			table.remove (fuelSlots, key)
464
		else
465
			local slot,key = getMinQtySlot(farmSlots)
466
			table.remove (farmSlots, key)
467
		end
468
		turtle.select(s)
469
		drop(64)
470
		n=n-1
471
	end
472
	return not n>1
473
end
474
475
-- Gestion automatique du fuel ? la station
476
local function autoRefuel()
477
	printLine("Auto refuel",8)
478
	if turtle.getFuelLevel()<fuelUsed*fuelSupply then
479
		compactInventory()
480
		slot = findFreeSlot()
481
		if not slot then slot=findFuelSlot() end
482
		if slot then
483
			turtle.select(slot)
484
			while turtle.getFuelLevel()<fuelUsed*fuelSupply do
485
				if not turtle.suckUp(1) then
486
					error("Fuel tank is empty...",-1)
487
					exit()
488
				end
489
				turtle.refuel()
490
				lap.fuel = turtle.getFuelLevel()+1 -- diff de la distance entre les stations storeData et autorefuel
491
				settings.set("turtle.farm.fuel",lap.fuel)
492
				settings.save(settingsFile)
493
			end
494
		end
495
	end
496
end
497
498
-- Trouve un slot contenant l'item indiqu?
499
-- Renvoi false s'il ne trouve rien
500
local function findSlotItem(refName)
501
	local slot=1
502
	for slot=1,16 do
503
		local data = turtle.getItemDetail(slot)
504
		if data and data.name==refName then return slot end
505
	end
506
	return false
507
end
508
509
-- Ramasse et replante, dans la mesure du possible
510
-- Renvoi false si la manoeuvre n'est pas parfaitement execut?e.
511
local function crop()
512
	printLine("Crop manager",8)
513
	if block.down and block.down.state and block.down.state.age then
514
		local requiredAge = plantAges[block.down.name]
515
		printLine(block.down.name.." "..block.down.state.age.."/"..requiredAge,5)
516
		if block.down.state.age>=requiredAge then
517
			local test,result = dbFarm.query("block",block.down.name)
518
			if test and result["seed"] then
519
				local slot = findSlotItem(result["seed"])
520
				if not slot then slot = findFreeSlot() end
521
				if not slot and dumpUnwanted(turtle.dropDown,1) then slot = findFreeSlot() end
522
				if not slot  then error("Inventory saturation..",-1) exit() end
523
				turtle.select(slot)
524
				if turtle.digDown() then
525
					lap.crops=lap.crops+1
526
					settings.set("turtle.farm.crops",lap.crops)
527
					settings.save(settingsFile)
528
					turtle.select(findSlotItem(result["seed"]))
529
					return turtle.placeDown()
530
				end
531
			end	
532
		end
533-
 or name=="computercraft:CC-TurtleExpanded"
533+
534
	return false
535
end
536
537
local function storeCrops()
538
	if lap.crops>0 and isContainer(block.up.name) then
539
		local skip = {
540
			wheat_seeds =    findSlotItem("minecraft:wheat_seeds"),
541
			potato =         findSlotItem("minecraft:potato"),
542
			carrot =         findSlotItem("minecraft:carrot"),
543
			nether_wart =    findSlotItem("minecraft:nether_wart"),
544
			beetroot_seeds = findSlotItem("minecraft:beetroot_seeds"),
545
		}
546
		local slot
547
		for slot=1,16 do
548
			if  slot ~= skip.wheat_seeds
549
			and slot ~= skip.potato
550
			and slot ~= skip.carrot
551
			and slot ~= skip.nether_wart
552
			and slot ~= skip.beetroot_seeds
553
			and turtle.getItemCount(slot)>0
554
			then
555
				turtle.select(slot)
556
				local data = turtle.getItemDetail(slot)
557
				if unwanted[data.name]~=nil then turtle.drop(64)
558
				else turtle.dropUp(64) end
559
			end
560
		end
561
		lap.crops = 0
562
		settings.set("turtle.farm.crops",lap.crops)
563
		settings.save(settingsFile)
564
	end
565
end
566
567
-- G?re les op?rations de la turtle
568
local function step()
569
	printLine("",5)
570
	if block.down then
571
		--End loop
572
		if block.down.name=="computercraft:CC-Peripheral" then
573
			compactInventory()
574
			fuelUsed = lap.fuel - turtle.getFuelLevel()
575
			if block.down.state.variant=="disk_drive_full" then
576
				printLine("Disk-Drive",5)
577
				storeData()
578
				storeCrops()
579
			else
580
				printLine(block.down.name.." / "..block.down.state.variant,5)
581
			end
582
			--init pause delay
583
			settings.set("turtle.farm.pause",pause)
584
			settings.save(settingsFile)
585
		--Start Loop
586
		elseif block.down.name=="computercraft:CC-Computer" then
587
			autoRefuel()
588
			local computer = peripheral.wrap("bottom")
589
			if computer.isOn() then computer.reboot()
590
			else computer.turnOn() end
591
			--Pause
592
			settings.load(settingsFile)
593
			local pauseDelay = settings.get("turtle.farm.pause",pause)
594
			while pauseDelay>0 do
595
				printLine("Pause : "..timeStr(pauseDelay),5)
596
				--Sauvegarde le temps ?coul? toutes les secondes
597
				local pauseTimer = os.startTimer(1)
598
				local e = os.pullEvent("timer")
599
				pauseDelay=pauseDelay-1
600
				settings.set("turtle.farm.pause",pauseDelay)
601
				settings.save(settingsFile)
602
			end
603
		--Crops
604
		elseif block.down.name=="minecraft:pumpkin"
605
		or block.down.name=="minecraft:melon_block"
606
  or block.down.name=="minecraft:reeds" then
607
			local cropName = (block.down.name == "minecraft:pumpkin") and "minecraft:pumpkin" or "minecraft:melon"
608
   if block.down.name=="minecraft:reeds" then cropName="minecraft:reeds" end
609
			local slot = findSlotItem(cropName)
610
			if slot then turtle.select(slot) end
611
			if turtle.digDown() then
612
				lap.crops = lap.crops+1
613
				settings.set("turtle.farm.crops",lap.crops)
614
				settings.save(settingsFile)
615
			end
616
		elseif tFarmBlocks[block.down.name]==1 and block.down.name~="minecraft:reeds" then
617
			printLine(block.down.name,5)
618
			crop()
619
		else
620
			printLine(block.down.name.."*",5)
621
		end
622
	end
623
	printLine("Crops: "..lap.crops,3)
624
	printLine("",8)
625
end
626
627
-- S?quence des manoeuvres
628
local function process()
629
	printLine("Reading blocks",2)
630
	inspect()
631
	printLine("Process step",4)
632
	step()
633
	printLine("Process walk",6)
634
	walk()
635
end
636
637
--Gestionaire principal
638
function main()
639
	term.clear()
640
	term.setCursorPos(1,1)
641
	local continue=true
642
	tick=os.startTimer(delay)
643
	while continue do
644
		local e,k=os.pullEvent()
645
		if e=="redstone" then
646
			os.reboot()
647
			continue=false
648
		elseif e=="key" and k==keys.t then
649
			continue=false
650
			os.cancelTimer(tick)
651
		elseif e=="timer" then
652
			printLine("Process new step "..os.clock(),1)
653
			printLine("Fuel level is  "..turtle.getFuelLevel(),9)
654
			process()
655
			tick=os.startTimer(delay)
656
		end
657
	end
658
	print("Terminate")
659
	ignoreReboot = false
660
	sleep(delay)
661
end
662
663
main()