SHOW:
|
|
- or go back to the newest paste.
1 | --TODO - distribute torches more intelligently? | |
2 | --TODO - more efficient ladder placement? turtle makes many unecessary moves to place them | |
3 | --TODO - better ladder placement? ladders sometimes switch sides of the maze | |
4 | ||
5 | ||
6 | --USAGE | |
7 | --for large mazes, set up 3 chests in a row, each with one block of space between them: | |
8 | --Put blocks in chest 1, to the right in chest 2 put torches, and in chest 3 ladders | |
9 | --put the turtle on top of chest 1 | |
10 | --fuel up your turtle | |
11 | --fill inventory slot 1 with torches, 2 with ladders, the rest with blocks you want the maze to be built of | |
12 | --set the maze dimension by editing w/h/d, values must be odd | |
13 | --run the program - turtle will build forward and to the right, and up! | |
14 | ||
15 | --When the turtle is done you will need to manually poke in any entrance/exit points you want to use. | |
16 | --Any two spots will work, all points of the maze can reach all other points. Some choices will be harder than others. | |
17 | ||
18 | ||
19 | --To pull in API stub for offline development | |
20 | if require then | |
21 | require "minecraft" | |
22 | end | |
23 | ||
24 | --width and height of maze, must be odd | |
25 | w = 15 | |
26 | h = 9 | |
27 | d = 15 | |
28 | ||
29 | ||
30 | --To keep track of current position within the maze | |
31 | --Z is height | |
32 | curX = 0 | |
33 | curY = 0 | |
34 | curZ = 0 -- maze relative z | |
35 | worldZ = 0 --world relative z | |
36 | ||
37 | ||
38 | --These are maze relative N/S/E/W | |
39 | --Wherever you turtle is pointing when you start the maze is "north" | |
40 | --Turtle will start in the 'Southwest' corner of the maze | |
41 | -- 0=N 1=E 2=S 3=W | |
42 | curRotation = 0; | |
43 | ||
44 | ||
45 | ||
46 | math.randomseed(os.time()) | |
47 | --math.randomseed(5) | |
48 | ||
49 | ||
50 | --The light field below is only for debugging purposes | |
51 | --You can remove it for performance reasons | |
52 | cells = {} | |
53 | --initialize cells | |
54 | for i = 0,w-1 do | |
55 | cells[i] = {} | |
56 | for j = 0,d-1 do | |
57 | cells[i][j] = {} | |
58 | for k = 0,h-1 do | |
59 | cell = {visited = false,x=i,y=j,z=k,light=false} | |
60 | cells[i][j][k] = cell | |
61 | end | |
62 | end | |
63 | end | |
64 | ||
65 | --neighbors are two and only two cells away in cardinal directions | |
66 | --this leaves the perimeter maze walls solid | |
67 | --we tag each neighbor as vertical or not so we can go vertical less often | |
68 | --for a more pleasant to navigate maze | |
69 | function getNeighbors(cell) | |
70 | neighbors = {} | |
71 | count = 0 | |
72 | x = cell.x | |
73 | y = cell.y | |
74 | z = cell.z | |
75 | if x+2 < w-1 then | |
76 | neighbor = {cell = cells[x+2][y][z], vertical = false } | |
77 | neighbors[count] = neighbor | |
78 | count = count + 1 | |
79 | end | |
80 | if x-2 >= 1 then | |
81 | neighbor = {cell = cells[x-2][y][z], vertical = false } | |
82 | neighbors[count] = neighbor | |
83 | count = count + 1 | |
84 | end | |
85 | if y+2 < d-1 then | |
86 | neighbor = {cell = cells[x][y+2][z], vertical = false } | |
87 | neighbors[count] = neighbor | |
88 | count = count + 1 | |
89 | end | |
90 | if y-2 >= 1 then | |
91 | neighbor = {cell = cells[x][y-2][z], vertical = false } | |
92 | neighbors[count] = neighbor | |
93 | count = count + 1 | |
94 | end | |
95 | if z+2 < h-1 then | |
96 | neighbor = {cell = cells[x][y][z+2], vertical = true } | |
97 | neighbors[count] = neighbor | |
98 | count = count+1 | |
99 | end | |
100 | if z-2 >= 1 then | |
101 | neighbor = {cell = cells[x][y][z-2], vertical = true} | |
102 | neighbors[count] = neighbor | |
103 | count = count+1 | |
104 | end | |
105 | ||
106 | return neighbors | |
107 | end | |
108 | ||
109 | --get a random neighbor cell that is unvisited | |
110 | function getRandomUnvisitedNeighbor(cell) | |
111 | neighbors = getNeighbors(cell) | |
112 | unvisitedNeighbors = {} | |
113 | count = 0 | |
114 | ||
115 | for k,v in pairs(neighbors) do | |
116 | if v.cell.visited == false then | |
117 | unvisitedNeighbors[count] = v | |
118 | count = count + 1 | |
119 | end | |
120 | end | |
121 | ||
122 | --This holds off on vertical path choices until the last minute | |
123 | --So there are less up and down movements which are tiresome | |
124 | --for the player to navigate | |
125 | --you can just toss this if you prefer not to do this | |
126 | if count > 2 then | |
127 | for i = count-1,0,-1 do | |
128 | neighbor = unvisitedNeighbors[i] | |
129 | if neighbor.vertical == true then | |
130 | table.remove(unvisitedNeighbors,i) | |
131 | count = count - 1 | |
132 | end | |
133 | end | |
134 | end | |
135 | ||
136 | ||
137 | if count == 0 then | |
138 | return nil | |
139 | end | |
140 | ||
141 | --get a random neihgbor | |
142 | r = math.random(count)-1; | |
143 | return unvisitedNeighbors[r].cell | |
144 | ||
145 | end | |
146 | ||
147 | --given two neighboring cells, get the cell between them | |
148 | function cellBetween(cell1,cell2) | |
149 | if cell1.x > cell2.x then | |
150 | return cells[cell1.x-1][cell1.y][cell1.z] | |
151 | end | |
152 | ||
153 | if cell1.x < cell2.x then | |
154 | return cells[cell1.x+1][cell1.y][cell1.z] | |
155 | end | |
156 | ||
157 | if cell1.y > cell2.y then | |
158 | return cells[cell1.x][cell1.y-1][cell1.z] | |
159 | end | |
160 | ||
161 | if cell1.y < cell2.y then | |
162 | return cells[cell1.x][cell1.y+1][cell1.z] | |
163 | end | |
164 | ||
165 | if cell1.z > cell2.z then | |
166 | return cells[cell1.x][cell1.y][cell1.z-1] | |
167 | end | |
168 | ||
169 | if cell1.z < cell2.z then | |
170 | return cells[cell1.x][cell1.y][cell1.z+1] | |
171 | end | |
172 | ||
173 | end | |
174 | ||
175 | --the main recursive function to traverse the maze | |
176 | function recur(curCell) | |
177 | ||
178 | --mark current cell as visited | |
179 | curCell.visited = true | |
180 | ||
181 | --check each neighbor cell | |
182 | for i = 1,6 do | |
183 | nCell = getRandomUnvisitedNeighbor(curCell) | |
184 | if nCell ~= nil then | |
185 | -- clear the cell between the neighbors | |
186 | bCell = cellBetween(curCell,nCell) | |
187 | bCell.visited = true | |
188 | --push current cell onto the stack and move on the neighbor cell | |
189 | recur(nCell) | |
190 | end | |
191 | end | |
192 | ||
193 | end | |
194 | ||
195 | ||
196 | --start point needs to be inside the perimeter | |
197 | --and odd | |
198 | recur(cells[1][1][1]) | |
199 | ||
200 | --Now we are done with the maze algorithm, and have a maze complete | |
201 | --in memory in the cells table | |
202 | --We must being building it with our turtle | |
203 | ||
204 | lightSlot = 1 --inventory slot for lights | |
205 | ladderSlot = 2 --inventory slot for ladders | |
206 | lightCount = 0 --how many blocks since we last placed a light? | |
207 | lightThreshold = 4 --how often to place lights | |
208 | ||
209 | ||
210 | --For debugging | |
211 | function printWorldLocation() | |
212 | print("World: (" .. curX .. "," .. curY .. "," .. worldZ .. ")") | |
213 | end | |
214 | ||
215 | function printMazeLocation() | |
216 | print("Maze: (" .. curX .. "," .. curY .. "," .. curZ ..")") | |
217 | end | |
218 | ||
219 | ||
220 | ||
221 | --find a slot with building material in it | |
222 | function selectBlock() | |
223 | for i = 3, 16 do | |
224 | if turtle.getItemCount(i) > 0 then | |
225 | turtle.select(i) | |
226 | return true | |
227 | end | |
228 | end | |
229 | return false | |
230 | end | |
231 | ||
232 | --Place a block, first checking inventory | |
233 | function blockDown() | |
234 | checkInventory() | |
235 | turtle.select(3) | |
236 | if turtle.getItemCount() == 0 then | |
237 | selectBlock() | |
238 | end | |
239 | turtle.placeDown() | |
240 | end | |
241 | ||
242 | --light can be anything that will place down on the floors | |
243 | function placeLight() | |
244 | if lightCount > lightThreshold then | |
245 | if curZ +1 < h then | |
246 | --we have to much sure this current block has no vertical passage | |
247 | --above or below it as torch will interfere with ladder | |
248 | if cells[curX][curY][curZ+1].visited == false then | |
249 | if cells[curX][curY][curZ-1].visited == false then | |
250 | turtle.select(lightSlot) | |
251 | turtle.placeDown() | |
252 | ||
253 | --below is for debugging only | |
254 | cells[curX][curY][curZ].light = true | |
255 | ||
256 | lightCount = 0 | |
257 | end | |
258 | end | |
259 | end | |
260 | else | |
261 | lightCount = lightCount + 1 | |
262 | end | |
263 | end | |
264 | ||
265 | --We always use these to turn to keep track of rotation | |
266 | function turnRight() | |
267 | turtle.turnRight() | |
268 | curRotation = (curRotation +1) % 4 | |
269 | end | |
270 | ||
271 | function turnLeft() | |
272 | turtle.turnLeft() | |
273 | curRotation = (curRotation-1) % 4 | |
274 | end | |
275 | ||
276 | --Will orient to the direction specified | |
277 | function orient(direction) | |
278 | while curRotation ~= direction do | |
279 | turnRight() | |
280 | end | |
281 | end | |
282 | ||
283 | --We use this to go forward to keep track of our x/y position | |
284 | function forward() | |
285 | turtle.forward() | |
286 | if curRotation == 0 then | |
287 | curY = curY + 1 | |
288 | elseif curRotation == 2 then | |
289 | curY = curY - 1 | |
290 | elseif curRotation == 1 then | |
291 | curX = curX + 1 | |
292 | elseif curRotation == 3 then | |
293 | curX = curX - 1 | |
294 | end | |
295 | end | |
296 | ||
297 | ||
298 | --And we use these to go up and down to keep track of our Z position | |
299 | function up() | |
300 | worldZ = worldZ + 1 | |
301 | turtle.up() | |
302 | end | |
303 | ||
304 | function down() | |
305 | worldZ = worldZ - 1 | |
306 | turtle.down() | |
307 | end | |
308 | ||
309 | ||
310 | function turnAroundOut() | |
311 | --if pointing "north" turn right | |
312 | if curRotation == 0 then | |
313 | turnRight() | |
314 | forward() | |
315 | turnRight() | |
316 | else | |
317 | turnLeft() | |
318 | forward() | |
319 | turnLeft() | |
320 | end | |
321 | end | |
322 | ||
323 | --when going back the other way | |
324 | function turnAroundIn() | |
325 | if curRotation == 2 then | |
326 | turnRight() | |
327 | forward() | |
328 | turnRight() | |
329 | else | |
330 | turnLeft() | |
331 | forward() | |
332 | turnLeft() | |
333 | end | |
334 | end | |
335 | ||
336 | --Descends into vertical passages | |
337 | --places ladders as it comes back up | |
338 | function placeLadder() | |
339 | checkInventory() | |
340 | turtle.select(ladderSlot) | |
341 | down() | |
342 | down() | |
343 | turtle.placeDown() | |
344 | up() | |
345 | turtle.placeDown() | |
346 | up() | |
347 | turtle.placeDown() | |
348 | end | |
349 | ||
350 | ||
351 | function placeFloorLayerOut() | |
352 | print("FloorLayerOut") | |
353 | ||
354 | if curZ ~= 0 then | |
355 | up() | |
356 | turnRight() | |
357 | turnRight() | |
358 | end | |
359 | for x = 0, w-1 do | |
360 | for y = 0, d-1 do | |
361 | if cells[curX][curY][curZ].visited == false then | |
362 | blockDown() | |
363 | end | |
364 | if y < d-1 then | |
365 | forward() | |
366 | end | |
367 | ||
368 | end | |
369 | if x < w-1 then | |
370 | turnAroundOut() | |
371 | end | |
372 | end | |
373 | ||
374 | end | |
375 | ||
376 | function placeFloorLayerIn() | |
377 | print("FloorLayerIn"); | |
378 | up() | |
379 | if curZ ~= 0 then | |
380 | turnRight() | |
381 | turnRight() | |
382 | end | |
383 | for x = w-1, 0,-1 do | |
384 | for y = d-1,0,-1 do | |
385 | if cells[curX][curY][curZ].visited == false then | |
386 | blockDown() | |
387 | end | |
388 | if y > 0 then | |
389 | forward() | |
390 | end | |
391 | ||
392 | end | |
393 | if x > 0 then | |
394 | turnAroundIn() | |
395 | end | |
396 | end | |
397 | end | |
398 | ||
399 | ||
400 | function placeWallLayer1In() | |
401 | print("wallLayer1In") | |
402 | up() | |
403 | turnRight() | |
404 | turnRight() | |
405 | for x = w-1,0,-1 do | |
406 | for y = d-1,0,-1 do | |
407 | if cells[curX][curY][curZ].visited then | |
408 | placeLight() | |
409 | else | |
410 | blockDown() | |
411 | end | |
412 | if y > 0 then | |
413 | forward() | |
414 | end | |
415 | end | |
416 | if x > 0 then | |
417 | turnAroundIn() | |
418 | end | |
419 | end | |
420 | end | |
421 | ||
422 | ||
423 | ||
424 | function placeWallLayer2Out() | |
425 | print("wallLayer2Out") | |
426 | up() | |
427 | turnRight() | |
428 | turnRight() | |
429 | for x = 0,w-1 do | |
430 | for y = 0,d-1 do | |
431 | if cells[curX][curY][curZ].visited == false then | |
432 | blockDown() | |
433 | end | |
434 | if y < d-1 then | |
435 | forward() | |
436 | end | |
437 | end | |
438 | if x < w-1 then | |
439 | turnAroundOut() | |
440 | end | |
441 | end | |
442 | end | |
443 | ||
444 | --This could be made more efficient | |
445 | --Turtle could go directly to each vertical passage | |
446 | function placeLaddersOut() | |
447 | print("placeLaddersOut") | |
448 | if curZ == 0 then | |
449 | return | |
450 | end | |
451 | turnRight() | |
452 | turnRight() | |
453 | for x = 0,w-1 do | |
454 | for y = 0,d-1 do | |
455 | if cells[curX][curY][curZ].visited then | |
456 | placeLadder() | |
457 | end | |
458 | if y < d-1 then | |
459 | forward() | |
460 | end | |
461 | end | |
462 | if x < w-1 then | |
463 | turnAroundOut() | |
464 | end | |
465 | end | |
466 | end | |
467 | ||
468 | ||
469 | --checks if we are running low on blocks, torches, or ladders | |
470 | function checkInventory() | |
471 | ||
472 | --check building blocks | |
473 | totalBlocks = 0 | |
474 | for i=3,16 do | |
475 | totalBlocks = totalBlocks + turtle.getItemCount(i) | |
476 | end | |
477 | ||
478 | --check torches | |
479 | torchCount = turtle.getItemCount(1) | |
480 | ||
481 | --check ladders | |
482 | ladderCount = turtle.getItemCount(2) | |
483 | ||
484 | if totalBlocks < 5 or torchCount < 5 or ladderCount < 5 then | |
485 | returnToOrigin() | |
486 | getMoreBlocks() | |
487 | resumeBuild() | |
488 | end | |
489 | ||
490 | end | |
491 | ||
492 | --resume coordinates | |
493 | resX = 0 | |
494 | resY = 0 | |
495 | resZ = 0 | |
496 | resRotation = 0 | |
497 | ||
498 | --returns to starting chest | |
499 | function returnToOrigin() | |
500 | resX = curX | |
501 | resY = curY | |
502 | resZ = worldZ | |
503 | resRotation = curRotation | |
504 | ||
505 | --point south then go past the edge of the maze | |
506 | orient(2) | |
507 | for i = 0,resY do | |
508 | forward() | |
509 | end | |
510 | ||
511 | --point west then go to point above chest | |
512 | orient(3) | |
513 | for i = 0, resX-1 do | |
514 | forward() | |
515 | end | |
516 | ||
517 | --drop down to above the chest | |
518 | for i = 0, resZ-1 do | |
519 | down() | |
520 | end | |
521 | ||
522 | ||
523 | end | |
524 | ||
525 | --fill up on all 3 resources | |
526 | function getMoreBlocks() | |
527 | ||
528 | --get building blocks from 1st chest | |
529 | while turtle.suckDown(64) do | |
530 | end | |
531 | ||
532 | --get torches from 2nd chest which is assumed to be 2 blocks east | |
533 | --point east | |
534 | orient(1) | |
535 | forward() | |
536 | forward() | |
537 | turtle.select(1) | |
538 | turtle.suckDown(64-turtle.getItemCount()) --just enough to fill upt he stack | |
539 | ||
540 | --get ladders from 3rd chest assumed to be 2 more blocks east | |
541 | forward() | |
542 | forward() | |
543 | turtle.select(2) | |
544 | turtle.suckDown(64-turtle.getItemCount()) --just enough | |
545 | ||
546 | --point west and go back to above the start chest | |
547 | orient(3) | |
548 | forward() | |
549 | forward() | |
550 | forward() | |
551 | forward() | |
552 | ||
553 | end | |
554 | ||
555 | --return to the position we left off | |
556 | function resumeBuild() | |
557 | ||
558 | --back up | |
559 | for i = 0, resZ -1 do | |
560 | up() | |
561 | end | |
562 | ||
563 | --point east | |
564 | orient(1) | |
565 | for i = 0, resX-1 do | |
566 | forward() | |
567 | end | |
568 | ||
569 | orient(0) | |
570 | for i = 0, resY do | |
571 | forward() | |
572 | end | |
573 | --resume original rotation | |
574 | orient(resRotation) | |
575 | end | |
576 | ||
577 | ||
578 | --prints ton of text representing maze state | |
579 | function fullDebug() | |
580 | for z = 0,h-1 do | |
581 | for y = 0,d-1 do | |
582 | for x = 0,w-1 do | |
583 | ||
584 | if cells[x][y][z].light == true and cells[x][y][z].visited == false then | |
585 | io.write("("..x..","..y..","..z.."):FF") --if you see this something went wrong | |
586 | elseif cells[x][y][z].light then | |
587 | io.write("("..x..","..y..","..z.."):**") | |
588 | elseif cells[x][y][z].visited == false then | |
589 | io.write("("..x..","..y..","..z.."):XX") | |
590 | else | |
591 | io.write("("..x..","..y..","..z.."): ") | |
592 | end | |
593 | ||
594 | end | |
595 | io.write("\n") | |
596 | end | |
597 | io.write("\n") | |
598 | end | |
599 | end | |
600 | ||
601 | --quick text representation of each layer of the maze | |
602 | function debug() | |
603 | for z = 0,h-1 do | |
604 | for y = 0,d-1 do | |
605 | for x = 0,w-1 do | |
606 | ||
607 | if (cells[x][y][z].light == true) and (cells[x][y][z].visited == false) then | |
608 | io.write("FF") --if you see this something went wrong | |
609 | elseif cells[x][y][z].light == true then | |
610 | io.write("**") | |
611 | elseif cells[x][y][z].visited == false then | |
612 | io.write("XX") | |
613 | else | |
614 | io.write(" ") | |
615 | end | |
616 | ||
617 | end | |
618 | io.write("\n") | |
619 | end | |
620 | io.write("\n") | |
621 | end | |
622 | end | |
623 | ||
624 | ||
625 | ||
626 | hush = true --silences stub api prints, has no effect in game | |
627 | ||
628 | turtle.forward() --move off the chest to starting position of maze | |
629 | placeFloorLayerOut() | |
630 | curZ = curZ + 1 | |
631 | placeWallLayer1In() | |
632 | placeWallLayer2Out() | |
633 | while true do | |
634 | print("loopstart") | |
635 | ||
636 | curZ = curZ + 1 | |
637 | placeFloorLayerIn() | |
638 | ||
639 | if curZ == h-1 then | |
640 | break | |
641 | end | |
642 | ||
643 | placeLaddersOut() | |
644 | curZ = curZ + 1 | |
645 | ||
646 | placeWallLayer1In() | |
647 | placeWallLayer2Out() | |
648 | end | |
649 | ||
650 | returnToOrigin() |