Files

330 lines
9.2 KiB
Python

import helpers
# Dinosaurs are ancient, majestic creatures that can be farmed for ancient bones.
# Unfortunately dinosaurs have gone extinct a long time ago, so the best we can do now is dressing up as one.
# For this purpose you have received the new dinosaur hat.
# The hat can be equipped with
# change_hat(Hats.Dinosaur_Hat)
# Unfortunately it doesn't quite look like on the advertisement...
# If you equip the dinosaur hat and have enough cactus, an apple will automatically be purchased and placed
# under the drone.
# When the drone is over an apple and moves again, it will eat the apple and grow its tail by one. If you can
# afford it, a new apple will be purchased and placed in a random location.
# The apple cannot spawn if something else is planted where it wants to be.
# The tail of the dinosaur will be dragged behind the drone filling the previous tiles the drone moved over.
# If a drone tries to move on top of the tail move() will fail and return False.
# The last segment of the tail will move out of the way during the move, so you can move onto it. However,
# if the snake fills out the whole farm, you will not be able to move anymore. So you can check if the snake
# is fully grown by checking if you can't move anymore.
# While wearing the dinosaur hat, the drone can't move over the farm border to get to the other side.
# Using measure() on an apple will return the position of the next apple as a tuple.
# next_x, next_y = measure()
# When the hat is unequipped again by equipping a different hat, the tail will be harvested.
# You will receive bones equal to the tail length squared. So for a tail of length n you will receive n**2 Items.Bone.
# For Example:
# length 1 => 1 bone
# length 2 => 4 bones
# length 3 => 9 bones
# length 4 => 16 bones
# length 16 => 256 bones
# length 100 => 10000 bones
# The Dinosaur Hat is very heavy, so if you equip it, it will make move() take 400 ticks instead of 200. However,
# each time you pick up an apple, the number of ticks used by move() is reduced by 3% (rounded down), because a
# longer tail can help you move.
# The following loop prints the number of ticks used by move() after any number of apples:
# ticks = 400
# for i in range(100):
# print("ticks after ", i, " apples: ", ticks)
# ticks -= ticks * 0.03 // 1
# You only have one dinosaur hat, so only one drone can wear it.
# If you keep moving along the same path that covers the whole field, you can easily get a snake that covers the
# whole field every time. It's not very efficient, but it works.
# Fully traversing a very large farm can take a long time and you might not actually need that many bones. Feel
# free to use set_world_size() to change the size of the farm to something more convenient.
dumb_mode = True
last_move = None
next_x = -1
next_y = -1
OPPOSITE = {
North: South,
South: North,
East: West,
West: East
}
tail = []
tail_length = 0
def any_other_move_available():
for d in [North, East, South, West]:
if last_move != None and d == OPPOSITE[last_move]:
continue
if can_move(d):
return True
return False
def reset():
global last_move
global next_x
global next_y
global tail
global tail_length
last_move = None
next_x = -1
next_y = -1
tail = []
tail_length = 0
def safe_move(direction):
global last_move
global tail
global tail_length
# Prevent immediate reversal only if some other move exists
if last_move != None and direction == OPPOSITE[last_move]:
if any_other_move_available():
return False
if not can_move(direction):
return False
moved = move(direction)
if moved:
last_move = direction
tail.insert(0, (get_pos_x(), get_pos_y()))
if len(tail) > tail_length:
tail.pop()
return True
return False
def flood_can_reach_tail(head, simulated_tail):
# Breadth First Search - can head reach tail end if tail (except last) is blocked?
world = get_world_size()
if len(simulated_tail) == 0:
return True
blocked = set(simulated_tail[:-1])
target = simulated_tail[-1]
queue = [head]
visited = {head}
while queue:
x, y = queue.pop(0)
if (x, y) == target:
return True
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nx = x + dx
ny = y + dy
if 0 <= nx < world and 0 <= ny < world:
pos = (nx, ny)
if (pos not in blocked) and (pos not in visited):
visited.add(pos)
queue.append(pos)
return False
def move_one_step_towards(tx, ty):
# A tiny greedy stepper to head toward a target (helps get unstuck)
cx = get_pos_x()
cy = get_pos_y()
dx = tx - cx
dy = ty - cy
preferred = []
if abs(dx) >= abs(dy):
if dx > 0:
preferred.append(East)
elif dx < 0:
preferred.append(West)
if dy > 0:
preferred.append(North)
elif dy < 0:
preferred.append(South)
else:
if dy > 0:
preferred.append(North)
elif dy < 0:
preferred.append(South)
if dx > 0:
preferred.append(East)
elif dx < 0:
preferred.append(West)
for d in preferred + [North, East, South, West]:
if safe_move(d):
return True
return False
def clear_grid():
columns_per_drone = get_world_size() // max_drones()
for _ in range(columns_per_drone):
for _ in range(get_world_size()):
till()
safe_move(North)
safe_move(East)
def move_to_coords_avoiding_tail(x, y):
if get_pos_x() == x and get_pos_y() == y:
return
while True:
cx = get_pos_x()
cy = get_pos_y()
dx = x - cx
dy = y - cy
moved = False
preferred = []
if abs(dx) >= abs(dy):
if dx > 0:
preferred.append(East)
elif dx < 0:
preferred.append(West)
if dy > 0:
preferred.append(North)
elif dy < 0:
preferred.append(South)
else:
if dy > 0:
preferred.append(North)
elif dy < 0:
preferred.append(South)
if dx > 0:
preferred.append(East)
elif dx < 0:
preferred.append(West)
# Try the preferred direction, then any direction
for d in preferred + [North, East, South, West]:
if safe_move(d):
moved = True
break
if not moved:
# If we still have a tail, see if we can reach the tail end.
if tail_length > 1 and len(tail) > 0:
head = (get_pos_x(), get_pos_y())
can_escape = flood_can_reach_tail(head, tail)
if can_escape:
# We're not truly stuck, our greedy choices boxed us in.
# Unstick by walking toward the tail end a few steps.
tx, ty = tail[-1]
for _ in range(10):
if move_one_step_towards(tx, ty):
break
continue
# Truly stuck sp let's harvest and restart
change_hat(Hats.Straw_Hat)
reset()
change_hat(Hats.Dinosaur_Hat)
return
if get_pos_x() == x and get_pos_y() == y:
return
def do_dumb():
global tail_length
# 32x32 grid
world_size = get_world_size()
moving_up = True
move(North)
for x in range(world_size):
for y in range(world_size - 2):
if get_entity_type() == Entities.Apple:
tail_length = tail_length + 1
if moving_up:
move(North)
if get_entity_type() == Entities.Apple:
tail_length = tail_length + 1
else:
move(South)
if get_entity_type() == Entities.Apple:
tail_length = tail_length + 1
move(East)
if get_entity_type() == Entities.Apple:
tail_length = tail_length + 1
moving_up = not moving_up
# now move back to 0,0
move(South)
if get_entity_type() == Entities.Apple:
tail_length = tail_length + 1
for x in range(world_size):
move(West)
if get_entity_type() == Entities.Apple:
tail_length = tail_length + 1
if tail_length > 900:
harvest()
def process():
global tail_length
global dumb_mode
reset()
world_size = get_world_size()
columns_per_drone = world_size // max_drones()
i = 0
while i < world_size:
helpers.move_to_coords(i, 0)
spawn_drone(clear_grid)
i += columns_per_drone
clear_grid()
change_hat(Hats.Dinosaur_Hat)
tail_length = 1
if dumb_mode:
while True:
do_dumb()
else:
while True:
next_x, next_y = measure()
# Apple is eaten on the next move, tail grows by 1
tail_length = tail_length + 1
safe_move(North)
move_to_coords_avoiding_tail(next_x, next_y)