· 7 years ago · Jan 01, 2019, 03:48 PM
1---*- mode: lua;-*-
2
3local r = {
4 VERSION = '0.1',
5 _constants = {},
6 _pri = {
7 -- Workaround to Lua's nature that it's not possible to create
8 -- constants
9 protect = function ( tbl )
10 return setmetatable( {}, {
11 __index = tbl,
12 __newindex = function( t, k, v )
13 error( "attempting to change constant "
14 .. tostring( k ) .. " to " .. tostring( v ), 2 )
15 end
16 } )
17 end,
18 }
19}
20
21r._constants = { slotsNum = 16, }
22r._constants = r._pri.protect( r._constants )
23
24function table.shallow_find ( arr, pred )
25 for _, v in ipairs( arr ) do
26 if pred( v ) then return v end
27 end
28 return nil
29end
30
31function table.shallow_exists( arr, elm )
32 return table.shallow_find( arr, function ( v ) return v == elm end )
33end
34
35function table.shallow_len ( tbl )
36 local count = 0
37 for _ in pairs( tbl ) do count = count + 1 end
38 return count
39end
40
41function table.dump ( tbl, indent )
42 local buffer = ''
43 if not indent then indent = 0 end
44 for k, v in pairs( tbl ) do
45 formatting = string.rep( " ", indent ) .. k .. ": "
46 if type( v ) == "table" then
47 buffer = buffer
48 .. formatting .. "\n"
49 .. table.dump( v, indent + 1 )
50 else
51 buffer = buffer .. formatting .. tostring( v ) .. "\n"
52 end
53 end
54 return buffer
55end
56
57--[[
58 ON FAILURE: Returns an error message.
59]]
60function bpsave ( tbl, filename )
61 local _exportstring = function ( s )
62 return string.format( "%q", s )
63 end
64
65 local cs, ce = ' ', "\n"
66 local fh = io.open( filename, 'w' )
67
68 if not fh then
69 return "Failed to open " .. filename .. " to write"
70 end
71
72 -- initiate variables for save procedure
73 local tables, lookup = { tbl }, { [tbl] = 1 }
74 fh:write( "return {" .. ce )
75
76 for idx, t in ipairs( tables ) do
77 fh:write( "-- Table: {" .. idx .. '}' .. ce )
78 fh:write( '{' .. ce )
79
80 local thandled = {}
81
82 for i, v in ipairs( t ) do
83 thandled[i] = true
84
85 local stype = type( v )
86
87 -- only handling value
88 if stype == "table" then
89 if not lookup[v] then
90 table.insert( tables, v )
91 lookup[v] = #tables
92 end
93 fh:write( cs .. '{' .. lookup[v] .. '},' .. ce )
94
95 elseif stype == "string" then
96 fh:write( cs .. _exportstring( v ) .. ',' .. ce )
97
98 elseif stype == "number" or stype == "boolean" then
99 fh:write( cs .. tostring( v ) .. ',' .. ce )
100 end
101 end
102
103 for i, v in pairs( t ) do
104
105 -- escape handled values
106 if not thandled[i] then
107 local str = ''
108 local stype = type( i )
109
110 -- handling index
111 if stype == "table" then
112 if not lookup[i] then
113 table.insert( tables, i )
114 lookup[i] = #tables
115 end
116 str = cs .. "[{" .. lookup[i] .. "}]="
117
118 elseif stype == "string" then
119 str = cs .. '[' .. _exportstring( i ) .. "]="
120
121 elseif stype == "number" or stype == "boolean" then
122 str = cs .. '[' .. tostring( i ) .. "]="
123 end
124
125 if str ~= '' then
126 stype = type( v )
127 -- handling value
128 if stype == "table" then
129 if not lookup[v] then
130 table.insert( tables, v )
131 lookup[v] = #tables
132 end
133 fh:write( str .. '{' .. lookup[v] .. '},' .. ce )
134
135 elseif stype == "string" then
136 fh:write( str .. _exportstring( v ) .. ',' .. ce )
137
138 elseif stype == "number" or stype == "boolean" then
139 fh:write( str .. tostring( v ) .. ',' .. ce )
140 end
141 end
142 end
143 end
144 fh:write( '},' .. ce )
145 end
146
147 fh:write( '}' )
148 fh:close()
149end
150
151--[[
152 ON SUCCESS: Returns a previously saved blueprint.
153 ON FAILURE: Returns as second argument an error message.
154]]
155function bpload ( sfile )
156 local ftables, err = loadfile( sfile )
157 if err then return _, err end
158
159 local tables = ftables()
160 for idx = 1, #tables do
161 local tolinki = {}
162 for i, v in pairs( tables[idx] ) do
163 if type( v ) == "table" then
164 tables[idx][i] = tables[ v[1] ]
165 end
166 if type( i ) == "table" and tables[ i[1] ] then
167 table.insert( tolinki, { i, tables[ i[1] ] } )
168 end
169 end
170 -- link indices
171 for _, v in ipairs( tolinki ) do
172 tables[idx][ v[2] ], tables[idx][ v[1] ] = tables[idx][ v[1] ], nil
173 end
174 end
175 return tables[1]
176end
177
178--------------------------------------------------------------------------------
179--------------------------------------------------------------------------------
180-- Replaces the movement related functions in the turtle API.
181--------------------------------------------------------------------------------
182--------------------------------------------------------------------------------
183
184function turtle._repeat ( action, times )
185 if not ( action and type( action ) == 'function' ) then
186 error( 'Action function must be specified' )
187 end
188 local n
189 if times then n = times else n = 1 end -- einmal ist keinmal
190 for _ = 1, n do action() end
191 return turtle
192end
193
194function turtle.lt ( times ) return turtle._repeat( turtle.turnLeft, times ) end
195function turtle.rt ( times ) return turtle._repeat( turtle.turnRight, times ) end
196
197function turtle.lf ( times, destroy )
198 function __up ( destroy )
199 local destroy = destroy == nil and true or destroy
200 if destroy and turtle.detectUp() then turtle.digUp() end turtle.up()
201 end
202 return turtle._repeat( function () __up( destroy ) end, times )
203end
204
205function turtle.dn ( times, destroy )
206 function __dn ( destroy )
207 local destroy = destroy == nil and true or destroy
208 if destroy and turtle.detectDown() then turtle.digDown() end turtle.down()
209 end
210 return turtle._repeat( function () __dn( destroy ) end, times )
211end
212
213function turtle.fd ( times, destroy )
214 function __fd ( destroy )
215 local destroy = destroy == nil and true or destroy
216 if destroy and turtle.detect() then turtle.dig() end turtle.forward()
217 end
218 return turtle._repeat( function () __fd( destroy ) end, times )
219end
220
221function turtle.gbo ( x, y, di )
222 if di == 1 then turtle.rt(2) end
223 if di == 2 then turtle.rt() end
224 if di == 4 then turtle.lt() end
225
226 return turtle.fd( y + 1 ).rt().fd(x).rt()
227end
228
229--------------------------------------------------------------------------------
230--------------------------------------------------------------------------------
231
232local G_ATTACHABLE_BLOCKS = {
233 'minecraft:redstone_torch',
234 'minecraft:torch',
235 'minecraft:lever'
236}
237
238local NAMEID_VARIANTS = {
239 ['minecraft:redstone'] = { 'minecraft:redstone_wire' },
240 ['minecraft:repeater'] = { 'minecraft:powered_repeater', 'minecraft:unpowered_repeater' },
241 ['minecraft:comparator'] = { 'minecraft:powered_comparator', 'minecraft:unpowered_comparator' },
242 ['minecraft:redstone_torch'] = { 'minecraft:unlit_redstone_torch' },
243 ['minecraft:redstone_lamp'] = { 'minecraft:lit_redstone_lamp' },
244}
245
246local G_DIRECTIONS = { 'fd', 'rt', 'bk', 'lt' }
247local G_FACINGS = { 'north', 'east', 'south', 'west' }
248
249local function dir2idx ( d )
250 if table.shallow_exists( G_DIRECTIONS, d ) then
251 for i, v in ipairs( G_DIRECTIONS ) do
252 if v == d then return i end
253 end
254 end
255 return nil
256end
257
258local function idx2dir ( di )
259 if di >= 1 and di <= 4 then return G_DIRECTIONS[di] end
260 return nil
261end
262
263local function fac2idx ( f )
264 if table.shallow_exists( G_FACINGS, f ) then
265 for i, v in ipairs( G_FACINGS ) do
266 if v == f then return i end
267 end
268 end
269 return nil
270end
271
272local function idx2fac ( fi )
273 if fi >= 1 and fi <= 4 then return G_FACINGS[fi] end
274 return nil
275end
276
277function turtle.rotate_facing ( fac, times )
278 local i = fac2idx( fac )
279 for _ = 1, times or 1 do i = i % 4 + 1 end
280 return idx2fac( i )
281end
282
283function turtle.rotate_direction ( dir, times )
284 local i = dir2idx( dir )
285 for _ = 1, times or 1 do i = i % 4 + 1 end
286 return idx2dir( i )
287end
288
289local function nameid_lookup ( id )
290 for std, variants in pairs( NAMEID_VARIANTS ) do
291 if table.shallow_exists( variants, id ) then
292 return std
293 end
294 end
295 return id
296end
297
298-- turtle.select_empty_slot selects inventory slot that is empty,
299-- returns `true` if found, `false` if no empty spaces
300
301function turtle.select_empty_slot()
302 if turtle.getItemCount( turtle.getSelectedSlot() ) == 0 then
303 return true
304 end
305
306 -- loop through all slots
307 for slot = 1, r._constants.slotsNum do
308 if turtle.getItemSpace( slot ) == 64 then
309 turtle.select( slot )
310 return true
311 end
312 end
313
314 return false -- couldn't find empty space
315end
316
317-- turtle.count_inventory() returns the total number of items in
318-- the inventory
319
320function turtle.count_inventory ()
321 local total = 0
322
323 for slot = 1, r._constants.slotsNum do
324 total = total + turtle.getItemCount( slot )
325 end
326
327 return total
328end
329
330function num_of_rounds ( length, width )
331 return math.ceil( math.min( length, width ) / 2 )
332end
333
334function coordinate_calculus ( flat_len, flat_wid, x, y )
335 local h = math.ceil( flat_wid / 2 )
336 local v = math.ceil( flat_len / 2 )
337
338 local maxx, maxy = flat_len - 1, flat_wid - 1
339
340 local r = math.min( x >= v and v - ( x - ( maxx - v ) ) or x,
341 y >= h and h - ( y - ( maxy - h ) ) or y )
342 local d, n
343 local delta = r
344 local tp = 1 -- number of block occupied by a turning point
345
346 if x == delta and y <= maxy - delta then
347 d = 'fd'
348 n = ( y + 1 ) - delta
349 elseif x == maxx - delta and y <= maxy - ( delta + tp ) then
350 d = 'bk'
351 n = ( maxy - y + 1 ) - ( delta + tp )
352 elseif x >= delta + tp then
353 if x <= maxx - delta and y == maxy - delta then
354 d = 'rt'
355 n = x + 1 - ( delta + tp )
356 elseif x <= maxx - delta - 1 and y == delta then
357 d = 'lt'
358 n = ( maxx - x + 1 ) - ( delta + tp )
359 end
360 end
361
362 return { round = r, direction = d, nth_step = n }
363end
364
365function turtle.sweep_flat ( length, width, sweepCallback )
366
367 local minimum = length * width
368 if turtle.getFuelLevel() < minimum then
369 return nil, 'HAVE NO ENOUGH FUEL'
370 end
371
372 -- Yes, we count the number of rounds from zero
373 for ri = 0, num_of_rounds( length, width ) - 1 do
374
375 local paths = nil
376
377 local evenDelta = ri * 2
378 local oddDelta = evenDelta + 1
379
380 --[[
381 right >
382 +-----------------+
383 | ^ ------------> |
384 | | ^ --------> | |
385 | | | + + + + | | |
386 ^ | | | + + + + | | | back
387 forward | | | ^ + + + | | | v
388 | | | | + + + | | |
389 | | | | <-----v | |
390 | O <-----------v |
391 +-----------------+
392 < left
393 ]]
394
395 paths = {
396 [1] = width - evenDelta, -- forward
397 [2] = length - oddDelta, -- right
398 [3] = width - oddDelta, -- back
399 [4] = length - ( evenDelta + 2 ), -- left
400 }
401
402 io.write( string.format( "Fd: %2d, Rt: %2d, Bk: %2d, Lt: %2d\n",
403 paths[1], paths[2], paths[3], paths[4] ) )
404
405 -- Mark `done` as true in order to get rid of the nested
406 -- loop. Lua does not support goto-style statement until
407 -- 5.2.0-beta-rc1
408
409 local done = false
410
411 if sweepCallback then
412 assert( type( sweepCallback ) == 'function', 'Callback is required to be a function' )
413 end
414
415 local x = ri
416 local y = ri - 1
417
418 for direction, nsteps in pairs( paths ) do
419 local ctx_tbl = {
420 round = ri, direction = direction,
421 nthStep = nil, x = nil, y = nil,
422 }
423 if nsteps == 0 then
424 if sweepCallback then
425 ctx_tbl.x = x; ctx_tbl.y = y; ctx_tbl.nthStep = 0; ctx_tbl.done = true
426
427 local success, err = sweepCallback( ctx_tbl )
428 if not success then
429 return nil, err
430 end
431 end
432 done = true
433 break
434 else
435 for n = 1, nsteps do turtle.fd()
436 if sweepCallback then
437 if direction == 1 then
438 y = y + 1
439 elseif direction == 2 then
440 x = x + 1
441 elseif direction == 3 then
442 y = y - 1
443 elseif direction == 4 then
444 x = x - 1
445 end
446 ctx_tbl.x = x; ctx_tbl.y = y; ctx_tbl.nthStep = n
447
448 local success, err = sweepCallback( ctx_tbl )
449 if not success then
450 return nil, err
451 end
452 end
453 end
454 turtle.turnRight()
455 end
456 end
457 if done then
458 break
459 end
460 end
461 return true
462end
463
464function turtle.sweep_solid ( args )
465
466 local length, width = args.length, args.width
467 assert( length and width, 'Length and width must be specified' )
468
469 local height = args.height or 1
470 local reversed = args.reversed or false
471 local f = args.sweepCallback
472
473 local from, to, step
474 if reversed then
475 from = height - 1; to = 0; step = -1
476 else
477 from = 0; to = height - 1; step = 1
478 end
479
480 -- lift turtle one more block so as to let turtle apply
481 -- callback to the block underneath it:
482
483 turtle.lf( from + 1 )
484
485 for z = from, to, step do
486 local success, err = turtle.sweep_flat(
487 length, width,
488 function ( ctx )
489 ctx.z = z;
490 if f then
491 assert( type( f ) == 'function', 'Callback is required to be a function' )
492 local success, err = f( ctx )
493 if not success then
494 return nil, err
495 end
496 end
497 if ctx.done then
498 turtle.gbo( ctx.x, ctx.y, ctx.direction )
499 end
500 return true
501 end
502 )
503 if success then
504 if reversed then
505 turtle.dn()
506 else
507 turtle.lf()
508 end
509 else
510 return nil, err
511 end
512
513 end
514 return true
515end
516
517--[[ POSIX style command line argument parser.
518
519 PARAM `args` contains the command line arguments in a standard table.
520 PARAM `options` is a string with the letters that expect string values.
521
522 Returns a table where associated keys are true, nil, or a string value.
523
524 The following example styles are supported:
525
526 -a one ==> opts['a'] == 'one'
527 -bone ==> opts['b'] == 'one'
528 -c ==> opts['c'] == true
529 --c=one ==> opts['c'] == 'one'
530 -cdaone ==> opts['c'] == true; opts['d'] == true; opts['a'] == 'one'
531
532 NOTE: POSIX demands the parser ends at the first non option
533 this behavior isn't implemented.
534]]
535function posix_getopt ( args, options )
536 local opts = {}
537
538 for k, v in ipairs( args ) do
539 if string.sub( v, 1, 2 ) == "--" then
540 local x = string.find( v, '=', 1, true )
541 if x then
542 opts[ string.sub( v, 3, x - 1 ) ] = string.sub( v, x + 1 )
543 else
544 opts[ string.sub( v, 3 ) ] = true
545 end
546
547 elseif string.sub( v, 1, 1 ) == '-' then
548 local y = 2
549 local l = string.len( v )
550 local jopt
551
552 while ( y <= l ) do
553 jopt = string.sub( v, y, y )
554 if string.find( options, jopt, 1, true ) then
555 if y < l then
556 opts[ jopt ] = string.sub( v, y + 1 )
557 y = l
558 else
559 opts[ jopt ] = args[ k + 1 ]
560 end
561 else
562 opts[ jopt ] = true
563 end
564 y = y + 1
565 end
566 end
567 end
568 return opts
569end
570
571---8<---
572
573-- draft
574
575function draft ( args )
576
577 local turtle_facing, err = turtle.figure_facing()
578 if not turtle_facing then return nil, err end
579
580 local tree = {}
581 local success, err = turtle.sweep_solid {
582 length = args.l, width = args.w, height = args.h,
583 reversed = true,
584 sweepCallback = function ( ctx )
585 local exists, details = turtle.inspectDown()
586 if exists then
587 details.name = nameid_lookup( details.name )
588 else
589 return true
590 end
591
592 local direction = idx2dir( ctx.direction )
593
594 local zTbl
595 if not tree[ ctx.z ] then zTbl = {}; tree[ ctx.z ] = zTbl else zTbl = tree[ ctx.z ] end
596
597 local roundTbl
598 if not zTbl[ ctx.round ] then roundTbl = {}; zTbl[ ctx.round ] = roundTbl else roundTbl = zTbl[ ctx.round ] end
599
600 local dirTbl
601 if not roundTbl[ direction ] then dirTbl = {}; roundTbl[ direction ] = dirTbl else dirTbl = roundTbl[ direction ] end
602
603 table.insert( dirTbl,
604 ctx.nthStep,
605 { x = ctx.x, y = ctx.y, block = details, } )
606 return true
607 end
608 }
609
610 if not success then return nil, err end
611
612 -- destroy the lowest layer
613 turtle.sweep_flat(
614 args.l,
615 args.w,
616 function ( ctx )
617 if ctx.done then
618 turtle.gbo( ctx.x, ctx.y, ctx.direction )
619 end
620 return true
621 end
622 )
623
624 tree[ 'turtle_facing' ] = turtle_facing
625 tree[ 'l' ] = args.l
626 tree[ 'w' ] = args.w
627
628 local err_msg = bpsave( tree, args.o )
629 if err_msg ~= nil then
630 return nil, err_msg
631 end
632
633 return true
634end
635
636-- craft
637
638function turtle.temporarily_godn ( action_f )
639 assert( action_f and type( action_f ) == 'function',
640 'Bad argument: action_f must be a valid function' )
641
642 local destroyed, destroyed_block = turtle.inspectDown()
643 turtle.dn()
644
645 local success, err_msg = action_f()
646 turtle.lf()
647
648 if destroyed then
649 return turtle.select_and_place {
650 name = nameid_lookup( destroyed_block.name ),
651 down = true,
652 destroy = true,
653 }
654 end
655
656 return success, err_msg
657end
658
659function type_of_intersection ( dparams )
660 local td, bd = dparams.td, dparams.bd -- turtle direction vs. block direction
661
662 assert( td and bd
663 and table.shallow_exists( G_DIRECTIONS, td )
664 and table.shallow_exists( G_DIRECTIONS, bd )
665 , "Bad arguments" )
666
667 if td == bd then
668 return 1
669
670 elseif ( td == 'fd' and bd == 'bk' ) or ( td == 'bk' and bd == 'fd' )
671 or ( td == 'lt' and bd == 'rt' ) or ( td == 'rt' and bd == 'lt' )
672 then
673 return 2
674
675 elseif ( td == 'fd' and bd == 'lt' ) or ( td == 'bk' and bd == 'rt' )
676 or ( td == 'rt' and bd == 'fd' ) or ( td == 'lt' and bd == 'bk' )
677 then
678 return 3
679
680 elseif ( td == 'fd' and bd == 'rt' ) or ( td == 'bk' and bd == 'lt' )
681 or ( td == 'rt' and bd == 'bk' ) or ( td == 'lt' and bd == 'fd' )
682 then
683 return 4
684 end
685end
686
687function enumerate_blocks ( bptree, f )
688 for zi = 0, #tree do
689 for ri = 0, #tree[zi] do
690 for _, steps in pairs( tree[zi][ri] ) do
691 for _, s in pairs( steps ) do
692 f( s )
693 end
694 end
695 end
696 end
697end
698
699function craft ( args )
700 local tree, err_msg = bpload( args.i )
701 if not tree then return nil, err_msg end
702
703 local h = #tree + 1
704 local l = tree.l
705 local w = tree.w
706
707 local tdraft_facing = tree.turtle_facing
708 if not tdraft_facing then
709 return nil, "Failed to pick up the clue about where turtle faced towards while it was drafting. "
710 .. "Considering upgrading rdaneel and redrafting your structures."
711 end
712
713 local turtle_facing, err = turtle.figure_facing();
714 if not turtle_facing then return nil, err end
715
716 local roll = 0
717 if tdraft_facing ~= turtle_facing then
718 for t = 1, 3 do
719 if turtle.rotate_facing( tdraft_facing, t ) == turtle_facing then
720 roll = t
721 break
722 end
723 end
724 end
725
726 local dir2fac_lookup = {}
727 for i = 1, 4 do dir2fac_lookup[ idx2dir( i ) ] = turtle.rotate_facing( turtle_facing, i - 1 ) end
728
729 local fac2dir_lookup = {}
730 for k, v in pairs( dir2fac_lookup ) do fac2dir_lookup[v] = k end
731
732 local preinstalled_blocks = {}
733
734 return turtle.sweep_solid {
735 length = l, width = w, height = h,
736 reversed = false,
737 sweepCallback = function ( ctx )
738 if ctx.nthStep == 0 then return true end
739 local x, y, z = ctx.x, ctx.y, ctx.z
740
741 if table.shallow_find(
742 preinstalled_blocks,
743 function ( v ) return v[1] == x and v[2] == y and v[3] == z end )
744 then
745 return true
746 end
747
748 local r = ctx.round
749 local di = ctx.direction
750 local d = idx2dir( di )
751 local n = ctx.nthStep
752
753 local success, b = pcall( function() return tree[z][r][d][n].block end )
754 if not success or table.shallow_len( b ) == 0 then
755 -- second condition for older version of .rdbp file
756 return true
757 end
758
759 local place_f = function () -- vanilla placing function
760 return turtle.select_and_place { name = b.name, down = true, destroy = true, }
761 end
762
763 local bfac = b.state.facing
764
765 if bfac and not ( bfac == 'up' or bfac == 'down' ) then
766 local bdir = turtle.rotate_direction( fac2dir_lookup[ bfac ], roll )
767
768 local is_attachable = table.shallow_exists( G_ATTACHABLE_BLOCKS, b.name )
769 local t = type_of_intersection { td = d, bd = bdir }
770
771 if t == 1 then
772 place_f = function ()
773 turtle.fd().rt(2).dn()
774 local success, err = turtle.select_and_place { name = b.name, destroy = true }
775 if not success then
776 return nil, err
777 end
778 turtle.lf().fd().rt(2)
779 return true
780 end
781
782 elseif is_attachable and ( t == 2 or t == 3 ) then
783 place_f = function ()
784 local base_block_x, base_block_y = x, y
785
786 if bdir == 'fd' then
787 base_block_y = y - 1
788 elseif bdir == 'bk' then
789 base_block_y = y + 1
790 elseif bdir == 'lt' then
791 base_block_x = x + 1
792 elseif bdir == 'rt' then
793 base_block_x = x - 1
794 end
795
796 local res = coordinate_calculus( l, w, base_block_x, base_block_y )
797 local base_block = tree[z][res.round][res.direction][res.nth_step].block
798
799 if t == 3 then turtle.rt() end
800 turtle.fd()
801
802 local success, err = turtle.select_and_place {
803 name = base_block.name, down = true, destroy = true }
804 if not success then
805 return nil, err
806 end
807
808 -- bookkeeping base block
809 table.insert( preinstalled_blocks, { base_block_x, base_block_y, z } )
810 turtle.rt(2).fd(2).rt(2)
811 turtle.temporarily_godn( function () return turtle.select_and_place { name = b.name, destroy = true } end )
812
813 turtle.fd()
814 if t == 3 then turtle.lt() end
815
816 return true
817 end
818
819 elseif is_attachable and t == 4 then
820 place_f = function ()
821 turtle.rt().fd().rt(2)
822 turtle.temporarily_godn( function () return turtle.select_and_place { name = b.name, destroy = true } end )
823 turtle.fd().rt()
824 return true
825 end
826 end
827 end
828 return place_f()
829 end
830 }
831end
832
833local G_COMPASSLIKE_BLOCKS = {
834 'minecraft:torch',
835 'minecraft:redstone_torch',
836 'minecraft:ladder',
837 'minecraft:lever',
838}
839
840local G_COMPASS_BASE_BLOCKS = {
841 'minecraft:log',
842 'minecraft:log2',
843 'minecraft:log3',
844 'minecraft:log4',
845}
846
847local function is_table_of_type ( tbl, t )
848 if not ( tbl and t ) then
849 return false
850 end
851
852 -- If tbl is empty,
853 -- it is considered as a table of any type
854
855 for _, v in ipairs( tbl ) do
856 if type( v ) ~= t then
857 return false
858 end
859 end
860
861 return true
862end
863
864function turtle.seek_item ( arg )
865 local names =
866 type( arg ) == 'string' and { arg } or ( type( arg ) == 'table' and arg or nil )
867
868 assert( is_table_of_type( names, 'string' ),
869 'Bad argument: arg must be either a string or a table of string' )
870
871 local curslot = turtle.getSelectedSlot()
872 local curslot_detail = turtle.getItemDetail( curslot )
873
874 for _, name in ipairs( names ) do
875 if curslot_detail and curslot_detail.name == name then
876 return curslot
877 end
878
879 -- check all inventory slots except for current slot
880
881 for slot = 1, r._constants.slotsNum do
882 if slot ~= curslot then
883 local item = turtle.getItemDetail( slot )
884 if item and item.name == name then
885 return slot
886 end
887 end
888 end
889 end
890
891 return nil -- couldn't find item
892end
893
894-- turtle.select_item() selects the inventory slot with the names
895-- item, returns the slot index if found and nil if not
896
897function turtle.select_item ( arg )
898 local slot_idx = turtle.seek_item( arg )
899 if slot_idx then
900 return turtle.select( slot_idx ) and slot_idx or nil
901 end
902 return nil
903end
904
905function turtle.select_and_place ( args )
906 assert( not ( args.slot and args.name ),
907 "Index of slot and ID name of block must not be specified simultaneously" )
908
909 local slot
910
911 if args.name then
912 slot = turtle.seek_item( args.name )
913 if not slot then
914 return nil, "Not found " .. args.name
915 end
916 else
917 slot = args.slot or turtle.getSelectedSlot()
918 if turtle.getItemCount( slot ) == 0 then
919 return nil, 'Slot ' .. tostring( slot ) .. ' is empty or non-exist'
920 end
921 end
922
923 if not turtle.select( slot ) then
924 return nil, 'Failed selecting specified slot ' .. tostring( slot )
925 end
926
927 function __fvars ( up, down )
928 -- UP and DOWN must not be specified simultaneously
929 assert( not ( up and down ), 'Conflict placing direction' )
930
931 if up then
932 return turtle.detectUp, turtle.digUp, turtle.placeUp, turtle.inspectUp
933 elseif down then
934 return turtle.detectDown, turtle.digDown, turtle.placeDown, turtle.inspectDown
935 else
936 return turtle.detect, turtle.dig, turtle.place, turtle.inspect
937 end
938 end
939
940 local detect_f, dig_f, place_f, inspect_f = __fvars( args.up, args.down )
941 local destroy = args.destroy
942
943 if detect_f() then
944 if destroy then
945 dig_f()
946 else
947 local exists, details = inspect_f()
948 return nil, 'Irrelevant block ' .. ( exists and details.name .. ' ' or '' ) .. 'stands in the way'
949 end
950 end
951
952 -- If the invoker didn't pass a slot, place_f() will pick
953 -- block from current selected slot
954
955 return place_f()
956end
957
958function turtle.figure_facing ( keeping )
959
960 local compass_slot = turtle.seek_item( G_COMPASSLIKE_BLOCKS )
961 if not compass_slot then
962 return nil, "Failed obtaining a compass-like block used for figuring the facing out"
963 end
964
965 local base_slot = turtle.seek_item( G_COMPASS_BASE_BLOCKS )
966 if not base_slot then
967 return nil, "Failed obtaining a base block for the compass block"
968 end
969
970 turtle.rt(2).fd()
971
972 local success, err = turtle.select_and_place { slot = base_slot, destroy = true }
973 if not success then
974 return nil, err
975 end
976
977 turtle.back()
978 turtle.select_and_place { slot = compass_slot, destroy = true }
979
980 local exists, compass_details = turtle.inspect()
981 if not exists then return nil, 'Compass damaged' end
982
983 if not keeping then
984 turtle.fd().dig()
985 turtle.back()
986 end
987 turtle.lt(2)
988
989 return compass_details.state.facing
990end
991
992do
993 local cli_args = {...}
994 local verb = table.remove( cli_args, 1 )
995
996 if verb == 'draft' then
997 local opts = posix_getopt( cli_args, 'lwhog' ) -- TODO: To process -g flag
998
999 assert( opts.h and opts.w and opts.h, "Length [-l], width [-w] and height [-h] must all be specified correctly" )
1000 assert( opts.o and #opts.o > 0, "Output file [-o] must be specified correctly" )
1001
1002 local l, w, h = tonumber( opts.l ), tonumber( opts.w ), tonumber( opts.h )
1003 local o = opts.o
1004
1005 assert( type( l ) == 'number' and type( w ) == 'number' and type( h ) == 'number',
1006 "Length, width, and height must all be numbers" )
1007
1008 assert( draft { l = l,
1009 w = w,
1010 h = h,
1011 o = o,
1012 g = true } )
1013
1014 elseif verb == 'craft' then
1015 local opts = posix_getopt( cli_args, 'ig' ) -- TODO: To process -g flag
1016 assert( opts.i and #opts.i > 0, "Input file [-i] must be specified correctly" )
1017
1018 local i = opts.i
1019 assert( craft { i = i, g = true } )
1020 else
1021 io.write( "Usages:\n"
1022 .. "\trdaneel draft -l4 -w3 -l3 -o output\n"
1023 .. "\trdaneel craft --i=input -g" )
1024 return false
1025 end
1026 return true
1027end