def move_z(delta): if delta == 0: return single() c = 1 - 2 * (delta < 0) delta = abs(delta) full_steps = delta // JUMP_LONG last = delta - JUMP_LONG * full_steps return singles([SMove(Diff(0,0,c * JUMP_LONG))] * full_steps + [SMove(Diff(0,0,c * last))] * (last > 0))
def filling_func(x, y, z, zup, bbox): bbox_min, bbox_max = bbox lookahead = (0 if z == bbox_max.z else int(reassembly))\ if zup else (0 if z == bbox_min.z else -int(reassembly)) lookbehind = (0 if z == bbox_min.z else -int(reassembly))\ if zup else (0 if z == bbox_max.z else int(reassembly)) if reassembly and model_src[Pos(x, y - offset, z + lookahead)]: if lookahead != 0: commands.append(Void(Diff(0, -offset, lookahead))) if x != bbox_min.x: action = choose_action(model_src, model_tgt, Pos(x - 1, y - offset, z)) if action is not None: commands.append(action(Diff(-1, -offset, 0))) if not reassembly or lookbehind != 0: action = choose_action(model_src, model_tgt, Pos(x, y - offset, z + lookbehind), lookbehind != 0) if action is not None: commands.append(action(Diff(0, -offset, lookbehind))) if x != bbox_max.x: action = choose_action(model_src, model_tgt, Pos(x + 1, y - offset, z)) if action is not None: commands.append(action(Diff(1, -offset, 0)))
def distribute_roots(x, y, z, full_w, last_w, seeds): if full_w == 0: return single() if full_w == 1 and last_w == 0: return single() return +single(Fission(Diff(-1,0,0),6)) + \ (+single(Fission(Diff(1,0,0),max(seeds-7-1,0))) // single()) + \ (-(single(FusionP(Diff(-1,0,0))) // single(FusionS(Diff(1,0,0)))) // (move_x(G_DIST) + distribute_roots(x+G_DIST, y, z, full_w - 1, last_w, seeds-7-1)))
def fusion(positions): '''Return a sequence of commands that merges the bot ids given in bids. Assumes bids is in increasing order and their corresponding positions are in an empty xz plane, have identical y and z coordinates and increasing x coordinates. Assumes no other bots exist. Returns commands that end with all bots merged into the first one.''' commands = [] while len(positions) > 1: newpositions = [] i = 0 while i < len(positions): if i + 1 < len(positions): if positions[i].x + 1 == positions[i + 1].x: # FUSE commands.append(FusionP(Diff(1, 0, 0))) commands.append(FusionS(Diff(-1, 0, 0))) newpositions.append(positions[i]) i += 2 else: # MOVE CLOSER dist = Diff(max(-15, positions[i] + 1 - positions[i + 1]), 0, 0) commands.append(Wait()) commands.append(SMove(dist)) newpositions.append(positions[i]) newpositions.append(positions[i + 1] + dist) i += 2 else: dist = Diff(max(-15, positions[i - 1] + 1 - positions[i]), 0, 0) commands.append(SMove(dist)) newpositions.append(positions[i] + dist) i += 1 positions = newpositions return commands
def move_x(dx): if dx == 0: return [] c = 1 - 2 * (dx < 0) dx = abs(dx) full_steps = dx // JUMP_LONG last = dx - JUMP_LONG * full_steps return [SMove(Diff(c * JUMP_LONG, 0, 0)) ] * full_steps + [SMove(Diff(c * last, 0, 0))] * (last > 0)
def return_home_gen(f: 'Pos', t: 'Pos'): # y is last for x in split_linear_move(Diff(-f.x, 0, 0)): yield x for x in split_linear_move(Diff(0, 0, -f.z)): yield x for x in split_linear_move(Diff(0, -f.y, 0)): yield x
def move_y(dy): if dy == 0: return [] c = 1 - 2 * (dy < 0) dy = abs(dy) full_steps = dy // JUMP_LONG last = dy - JUMP_LONG * full_steps return [SMove(Diff(0, c * JUMP_LONG, 0)) ] * full_steps + [SMove(Diff(0, c * last, 0))] * (last > 0)
def move_z(dz): if dz == 0: return [] c = 1 - 2 * (dz < 0) dz = abs(dz) full_steps = dz // JUMP_LONG last = dz - JUMP_LONG * full_steps return [SMove(Diff(0, 0, c * JUMP_LONG)) ] * full_steps + [SMove(Diff(0, 0, c * last))] * (last > 0)
def spawn_down(model, x, y, z): prog = single() if model[Pos(x,y-1,z)]: prog += single(Void(Diff(0,-1,0))) prog += +single(Fission(Diff(0,-1,0), 0)) return prog
def snake_fill_gen(m: 'Model', a: Pos, b: Pos): prevPos = a nextPos = a # The main idea is to skip path points where model has no voxels below it. # And then go to the next "job" using long lenear move. We always above our # buildings so we will never collide with anything. for diff in snake_path_gen(a, b): nextPos = nextPos + diff fillBelow = False for d in [Diff(0, -1, 0), Diff(0, -1, 1), Diff(0, -1, -1)]: if inside_region((nextPos + d), a, b) and m[nextPos + d]: fillBelow = True if fillBelow: dx = nextPos.x - prevPos.x dy = nextPos.y - prevPos.y dz = nextPos.z - prevPos.z # y first for x in split_linear_move(Diff(0, dy, 0)): yield x for x in split_linear_move(Diff(0, 0, dz)): yield x for x in split_linear_move(Diff(dx, 0, 0)): yield x for d in [Diff(0, -1, 0), Diff(0, -1, 1), Diff(0, -1, -1)]: if inside_region((nextPos + d), a, b) and m[nextPos + d]: yield Cmd.Fill(d) m[nextPos + d] = False prevPos = nextPos yield prevPos
def navigate(f: 'Pos', t: 'Pos'): # y is last dx = t.x - f.x dy = t.y - f.y dz = t.z - f.z if dy > 0: for x in split_linear_move(Diff(0, dy, 0)): yield x for x in split_linear_move(Diff(0, 0, dz)): yield x for x in split_linear_move(Diff(dx, 0, 0)): yield x if dy < 0: for x in split_linear_move(Diff(0, dy, 0)): yield x
def print_strip_below(model, i, lbound, z0, rbound, depth, last_layer): prog = single() for z in range(z0, z0 + depth): if model[Pos(lbound,i,z)]: prog += single(Fill(Diff(0,-1,0))) for x in range(lbound + 1, rbound): prog += single(SMove(Diff(1,0,0))) if model[Pos(x,i,z)]: prog += single(Fill(Diff(0,-1,0))) prog += move_x(-1 * (rbound - lbound - 1)) prog += single(SMove(Diff(0,0,1))) # TODO: optimise this part, make an L move prog += move_z(-1 * (depth + last_layer * z0)) return prog
def move_and_unfork(strips): if not strips: return single() # Wait for everyone on the right to finish prog = single() // move_and_unfork(strips[1:]) # Move our child bot to the left prog += single() // move_x(-1 * (strips[0] - 1)) # Unfork the bot immediately to the right prog += -single(FusionP(Diff(1,0,0))) // single(FusionS(Diff(-1,0,0))) return prog
def move_in_empty_space(diff): # Move in XZ plane first, that way we can use this to float above model. commands = [] if diff.dx != 0: for dx in long_distances(diff.dx): commands.append(SMove(Diff(dx, 0, 0))) if diff.dz != 0: for dz in long_distances(diff.dz): commands.append(SMove(Diff(0, 0, dz))) if diff.dy != 0: for dy in long_distances(diff.dy): commands.append(SMove(Diff(0, dy, 0))) return commands
def move_and_unfork(strips): if not strips: return [] ticks = [] # Wait for everyone on the right to finish ticks.extend(wait_for(move_and_unfork(strips[1:]))) # Move our child bot to the left ticks.extend(wait_for(sequential(move_x(-1 * (strips[0] - 1))))) # Unfork the bot immediately to the right ticks.append([FusionP(Diff(1, 0, 0)), FusionS(Diff(-1, 0, 0))]) return ticks
def spawn_bots(self, x, y): row = [1] p = 1 for i in range(x - 1): p = self[p].spawn_keep(Diff(1, 0, 0), y - 1) row.append(p) yield from self.step() all_rows = [row] for j in range(y - 1): row = [self[i].spawn_keep(Diff(0, 1, 0), 0) for i in row] all_rows.append(row) yield from self.step() self.grid = all_rows
def print_strip_below(model, i, lbound, rbound, last_layer): moves = [] for z in range(1, model.R - 1): if model[Pos(lbound, i, z)]: moves.append(Fill(Diff(0, -1, 0))) logging.debug('xyz = %d %d %d', lbound, i, z) for x in range(lbound + 1, rbound): logging.debug('xyz = %d %d %d', x, i, z) moves.append(SMove(Diff(1, 0, 0))) if model[Pos(x, i, z)]: moves.append(Fill(Diff(0, -1, 0))) moves.extend(move_x(-1 * (rbound - lbound - 1))) moves.append(SMove(Diff( 0, 0, 1))) # TODO: optimise this part, make an L move moves.extend(move_z(-1 * (model.R - 2 + last_layer))) return moves
def fork_and_move_new(seeds, space_right): if not seeds: return [] #logger.debug('space_right = %d', space_right) # Each bot gets its own strip bots = 1 + len(seeds) #logger.debug('Bots = %d', bots) strip_width = floor(space_right / bots) #logger.debug('Strip width = %d', strip_width) strips.append(strip_width) nonlocal strips_sum strips_sum += strip_width ticks = [] # fork right giving them all our seeds ticks.append([Fission(Diff(1, 0, 0), len(seeds) - 1)]) # move the new bot to its position ticks.extend(wait_for(sequential(move_x(strip_width - 1)))) # Let the new bot do the same ticks.extend( wait_for(fork_and_move_new(seeds[1:], space_right - strip_width))) return ticks
def deploy_cube(model, x, y, z, width, height, depth): prog7 = move_z(depth) + spawn_down(model, x + (width-1), y, z + (depth+1)) + \ (single() // drill_down(model, x + (width-1), y-1, z + (depth+1), height-1)) prog57 = move_x(width-2) + spawn_down(model, x + (width-1), y, z) + \ (+single(Fission(Diff(0,0,1), 1)) // single()) + \ (single() // move_y(-height+1) // prog7) prog3 = move_z(depth) + spawn_down(model, x, y, z + (depth+1)) + \ (single() // drill_down(model, x, y - 1, z + (depth+1), height-1)) prog1 = +single(Fission(Diff(0,-1,0), 0)) + \ (+single(Fission(Diff(0,0,1), 1)) // single()) + \ (+single(Fission(Diff(1,0,0), 3)) // single() // single()) + \ (single() // move_y(-height+1) // prog3 // prog57) return prog1
def print_hyperrectangle(model, x, z, width, height, depth): prog = single() for i in range(height): last = i == height - 1 prog += print_strip_below(model, i, x, z, x + width, depth, last) if not last: prog += single(SMove(Diff(0,1,0))) return prog
def test_illegal_smoves(): em = Cpp.Emulator(set_cpp_state()) # bot 2 moves out of bounds assert em.check_add_command(ctp(pc.Wait())) == '' msg = em.check_command(ctp(pc.SMove(Diff(0, 0, 3)))) assert msg != '' # bot 2 moves through the filled area msg = em.check_command(ctp(pc.SMove(Diff(3, 0, 0)))) assert msg != '' # ... c = ctp(pc.Wait()) em.add_command(c) em.add_command(c) em.run_step() # bot 1 moves through bot 2 msg = em.check_command(ctp(pc.SMove(Diff(0, 0, 3)))) assert msg != '' # bot 2 moves through volatile area assert em.check_add_command(ctp(pc.Fill(Diff(0, 1, 1)))) == '' msg = em.check_command(ctp(pc.SMove(Diff(0, 1, 0)))) assert msg != '' # bots 2 & 3 move freely assert em.check_add_command(ctp(pc.SMove(Diff(1, 0, 0)))) == '' assert em.check_add_command(ctp(pc.SMove(Diff(0, 4, 0)))) == '' assert em.steptrace_is_complete() em.run_step()
def encode_sld(v : Diff): assert v.is_short_linear() if v.dx: return 0b01_0000 | (v.dx + 5) if v.dy: return 0b10_0000 | (v.dy + 5) if v.dz: return 0b11_0000 | (v.dz + 5) assert False, f'Invalid sld: {v}'
def encode_lld(v : Diff): assert v.is_long_linear() if v.dx: return 0b010_0000 | (v.dx + 15) if v.dy: return 0b100_0000 | (v.dy + 15) if v.dz: return 0b110_0000 | (v.dz + 15) assert False, f'Invalid lld: {v}'
def fill_plot(model, model_height, plot): pos, plot_height = plot trace = [] while True: for diff in eight_around_one_below(pos, model.R): if model[pos + diff]: trace.append(Fill(diff)) if pos.y == plot_height: break else: shift = Diff(0, 1, 0) trace.append(SMove(shift)) pos += shift if pos.y != model_height: shift = Diff(0, model_height - pos.y, 0) trace.extend(move_in_empty_space(shift)) pos += shift return trace, pos
def bfs(m, src: 'Point', dst: 'Point'): weights = Matrix(m.R) sources = Matrix(m.R) queue = [src] weights[src] = 1 while len(queue) and weights[dst] == 0: p = queue.pop(0) ds = [ Diff(0, 1, 0), Diff(0, -1, 0), Diff(1, 0, 0), Diff(-1, 0, 0), Diff(0, 0, 1), Diff(0, 0, -1) ] for d in ds: nxt = p + d if nxt.is_inside_matrix(m.R) and not m[nxt] and not weights[nxt]: queue.append(nxt) weights[nxt] = weights[p] + 1 sources[nxt] = p if weights[dst] == 0: return None else: res = [] p = dst while p != src: res.insert(0, p) p = sources[p] return res
def lld_table(): for i in range(1, 16): yield Diff( i, 0, 0) yield Diff(-i, 0, 0) yield Diff( 0, i, 0) yield Diff( 0, -i, 0) yield Diff( 0, 0, i) yield Diff( 0, 0, -i)
def nearby_voxel(voxel: 'Pos'): ''' Compute a voxel near enough to the requested voxel so that `Fill`ing it is possible but not *on* the requested voxel (as a bot cannot `Fill` the voxel it is occupying). Right now, just the voxel's top neighbor. This will most likely need to change, as that voxel may already be full or may have another bot there. Can actually be any voxel within a near coordinate difference (nd) of the requested one. ''' #TODO: detect a voxel near the requested one that is not occupied by an already-filled # voxel or another bot return voxel + Diff(0, 1, 0)
def solve(source, target): state = State(source, target) state.bots[0].command = Fission(Diff(1, 0, 0), 20) state.tick() state.bots[0].command = Fill(Diff(0, 1, 0)) state.bots[1].command = SMove(Diff(0, 1, 0)) state.tick() state.bots[1].command = Void(Diff(-1, 0, 0)) state.tick() state.bots[0].fuse(state.bots[1]) state.tick() pprint(state.bots) state.bots[0].command = Halt() state.tick() print(state.correct()) pprint(state.trace) return state.dump_trace()
def drill_down(model, x, y, z, depth) -> GroupProgram: prog = single() while depth > 0: if model[Pos(x, y - 1, z)]: prog += single(Void(Diff(0, -1, 0))) step = 1 for i in range(2, depth + 1): if model[Pos(x, y - i, z)]: break else: step += 1 prog += move_y(-step) y -= step depth -= step return prog
def collapse_cube(width, height, depth): # Move 2 up to 1 (4 to 3, 6 to 5, 8 to 7) prog_contract_up = (single() // move_y(height - 1)) + -( single(FusionP(Diff(0, -1, 0))) // single(FusionS(Diff(0, 1, 0)))) # Move 3 back to 1 (7 to 5) prog_contract_back = (single() // move_z(-depth)) + -( single(FusionP(Diff(0, 0, 1))) // single(FusionS(Diff(0, 0, -1)))) # Move 5 left to 1 prog_contract_left = (single() // move_x(-width + 2)) + -( single(FusionP(Diff(1, 0, 0))) // single(FusionS(Diff(-1, 0, 0)))) return (prog_contract_up**4) + (prog_contract_back**2) + prog_contract_left