Tursom Log
恐龙与骨头 · 实验版

恐龙完整回路

沿预先生成的完整回路前进,用较多移动换取更低的逐步计算开销。

使用说明

  1. 可作为独立游戏脚本运行。成本模型中的 new 指这个版本,原生 A/B 测试也用它与旧基线比较。

运行前提与限制

独立脚本需要 32×32 农场、恐龙科技和足够支付一局苹果的材料;每局开始与结束都会清场,运行前停止其他无人机。

当前轻量捷径保留了原策略的增长死路,不保证每局填满。起点 (0,0)、苹果依次为 (0,1) 和 (1,0) 就是已知反例;遇到死路后收尾并停止。

20 个随机种子的本地模型中,轻量版与旧版移动步数一致,算法主体预计 tick 减少约 92.3%。这是成本模型估算,尚未由游戏引擎实测确认;综合脚本中的恐龙实现仍是旧策略。

运行环境:游戏内代码窗口。说明由 AI 辅助整理;更多对照版本、测试和记录见恐龙与骨头目录。

完整源码

dinosaur_32_full_cycle_experiment.py · 107 行。代码块右上角可复制完整代码。

# The Farmer Was Replaced - standalone 32x32 dinosaur snake farm.
# Run in the game, with no other drones working on the farm.
# Each round clears the entire farm before and after playing.
# Requires Dinosaurs and enough apple materials for a full round.
# A complete Hamilton cycle guarantees coverage without shortcut dead ends.
# Directions are built once; each movement needs no body tracking or search.
WORLD_SIZE = 32


def dino_cycle_index(x, y):
	if y == 0:
		return x
	if x == 0:
		return WORLD_SIZE * WORLD_SIZE - y
	if y % 2 == 1:
		return WORLD_SIZE + (y - 1) * (WORLD_SIZE - 1) + WORLD_SIZE - 1 - x
	return WORLD_SIZE + (y - 1) * (WORLD_SIZE - 1) + x - 1


def build_dino_route():
	# Every entry is the direction leaving that Hamilton-cycle index.
	# Reserve column 0 for the return path from the top to the origin.
	route = []
	for x in range(WORLD_SIZE - 1):
		route.append(East)
	for y in range(1, WORLD_SIZE):
		route.append(North)
		direction = East
		if y % 2 == 1:
			direction = West
		for x in range(WORLD_SIZE - 2):
			route.append(direction)
	route.append(West)
	for y in range(WORLD_SIZE - 1):
		route.append(South)
	return route


def follow_dino_route(route, head_index, target_index):
	# Split at the wrap point: no modulo, position queries, scoring or queue
	# operations in the movement loop. The game tracks growth and the tail.
	if target_index < head_index:
		for index in range(head_index, WORLD_SIZE * WORLD_SIZE):
			if not move(route[index]):
				return False
		head_index = 0
	for index in range(head_index, target_index):
		if not move(route[index]):
			return False
	return True


def dinosaur_once(route):
	if num_unlocked(Unlocks.Dinosaurs) == 0:
		quick_print("Dinosaur farming stopped: Dinosaurs is not unlocked.")
		return False
	cost = get_cost(Entities.Apple)
	if cost == None:
		quick_print("Dinosaur farming stopped: apple cost is unavailable.")
		return False
	for item in cost:
		if num_items(item) < cost[item] * WORLD_SIZE * WORLD_SIZE:
			quick_print("Dinosaur farming stopped: not enough apple materials.")
			return False

	start_ticks = get_tick_count()
	start_bones = num_items(Items.Bone)
	clear()
	change_hat(Hats.Dinosaur_Hat)
	head_index = dino_cycle_index(get_pos_x(), get_pos_y())
	apples = 0
	steps = 0
	next_apple = measure()

	while next_apple != None:
		target_index = dino_cycle_index(next_apple[0], next_apple[1])
		if target_index == head_index:
			break
		if not follow_dino_route(route, head_index, target_index):
			break
		steps += (target_index - head_index) % (WORLD_SIZE * WORLD_SIZE)
		head_index = target_index
		apples += 1
		# Reaching the current apple reveals the following apple.
		next_apple = measure()

	change_hat(Hats.Straw_Hat)
	clear()
	elapsed = get_tick_count() - start_ticks
	bones = num_items(Items.Bone) - start_bones
	quick_print("Dinosaur round: apples", apples, "moves", steps, "ticks", elapsed, "bones", bones)
	if next_apple != None or apples != WORLD_SIZE * WORLD_SIZE - 1:
		quick_print("Dinosaur farming stopped: round ended before full coverage.")
		return False
	return True


def main():
	if get_world_size() != WORLD_SIZE:
		quick_print("Dinosaur script requires a 32x32 farm.")
		return
	route = build_dino_route()
	while dinosaur_once(route):
		pass


main()