Last active
March 13, 2022 14:10
-
-
Save TsuMakoto/15ec80fc23adc4d826b5631801b2c8a5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
# Deliver more ore to hq (left side of the map) than your opponent. Use radars to find ore but beware of traps! | |
def std_err(args): | |
print(args, file=sys.stderr, flush=True) | |
def get_line(): | |
i = input() | |
std_err(i) | |
return i | |
class Point(): | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
def eq(self, point): | |
return self.x == point.x and self.y == point.y | |
def is_out_range(self): | |
return self.x < 0 or self.y < 0 | |
def distance(self, point): | |
return abs(self.x - point.x) + abs(self.y - point.y) | |
# TARGET_RADAR_POINTS = [Point(*cord) | |
# for cord in [ | |
# [3, 9], [8, 6], [14, 9], [20, 6], [26, 9], | |
# # [2, 3], [3, 14], | |
# [9, 12], # [8, 0], | |
# [14, 3], #[16, 14], | |
# [20, 12], # [20, 0], | |
# [26, 3], #[27, 14] | |
# ] | |
# ] | |
TARGET_RADAR_POINTS = [Point(*cord) | |
for cord in [ | |
[5,3],[5,11],[10,7],[15,3],[15,11],[20,7],[25,3],[25,11] | |
] | |
] | |
class Entity(): | |
def __init__(self, id, type, x, y, item): | |
self.id = id | |
self.type = type | |
self.point = Point(x, y) | |
self.item = item | |
def item_is_none(self): | |
return self.item == -1 | |
def item_is_radar(self): | |
return self.item == 2 | |
def item_is_trap(self): | |
return self.item == 3 | |
def item_is_crystal(self): | |
return self.item == 4 | |
def eq(self, entity): | |
return self.id == entity.id | |
class Agent(Entity): | |
def __init__(self, id, type, x, y, item): | |
super().__init__(id, type, x, y, item) | |
if self.is_dead(): | |
self.item = 0 | |
self.command = "" | |
def is_dead(self): | |
return self.point.eq(Point(-1, -1)) | |
def next_action(self, command): | |
self.command = command | |
class Radar(Entity): | |
pass | |
class Trap(Entity): | |
pass | |
class AssignEntity(): | |
def __init__(self, agents, opp_agents, radars, traps): | |
self.agents = agents | |
self.opp_agents = opp_agents | |
self.radars = radars | |
self.traps = traps | |
# entity_id: unique id of the entity | |
# entity_type: 0 for your robot, 1 for other robot, 2 for radar, 3 for trap | |
# x, y: position of the entity | |
# item: if this entity is a robot, the item it is carrying (-1 for NONE, 2 for RADAR, 3 for TRAP, 4 for ORE) | |
def assign(self, entity): | |
[lambda: self.agents.append(Agent(*entity)), | |
lambda: self.opp_agents.append(Agent(*entity)), | |
lambda: self.radars.append(Radar(*entity)), | |
lambda: self.traps.append(Trap(*entity))][entity[1]]() | |
class OreMap(): | |
def __init__(self, width, height): | |
self.width = width | |
self.height = height | |
self.map = [("? " * width).split() for _ in range(height)] | |
self.crystal_points = [] | |
# ore: amount of ore or "?" if unknown | |
# hole: 1 if cell has a hole | |
def update(self, point, cell): | |
self.map[point.y][point.x] = cell if cell == "?" else int(cell) | |
if self.is_crystal_pos(point): | |
# クリスタルの場所である場合 | |
if not any([point.eq(p) for p in self.crystal_points]): | |
# クリスタルマップに存在しない場合 | |
self.crystal_points.append(point) | |
else: | |
# クリスタルの場所でない、もしくはなくなった場合 | |
self.crystal_points = [p for p in self.crystal_points if not p.eq(point)] | |
def is_crystal_pos(self, point): | |
cell = self.cell_info(point) | |
return cell != "?" and cell > 0 | |
def cell_info(self, point): | |
return self.map[point.y][point.x] | |
def after_target_cell(self, point): | |
# 掘るのでクリスタルを一つ減らす | |
cell = self.cell_info(point) | |
if cell != "?": | |
self.update(point, cell - 1) | |
class HoleMap(): | |
def __init__(self, width, height): | |
self.width = width | |
self.height = height | |
self.map = [[0 for _ in range(width)] for _ in range(height)] | |
def update(self, point, cell): | |
self.map[point.y][point.x] = cell | |
def cell_info(self, point): | |
return self.map[point.y][point.x] | |
def is_trap(self, point: Point, ore_map: OreMap): | |
return self.cell_info(point) == 1 and not ore_map.is_crystal_pos(point) | |
class Score(): | |
def __init__(self): | |
self.me = 0 | |
self.opponent = 0 | |
def update(self, me, opponent): | |
self.me = me | |
self.opponent = opponent | |
def diff(self): | |
return self.me - self.opponent | |
class Cooldown(): | |
def __init__(self): | |
self.radar = 0 | |
self.trap = 0 | |
def update(self, radar, trap): | |
self.radar = radar | |
self.trap = trap | |
class GlobalInfo(): | |
def __init__(self, width, height): | |
self.width = width | |
self.height = height | |
self.trap_map = [[False for _ in range(width)] for _ in range(height)] | |
self.turn = 1 | |
self.opp_agents = [] | |
def is_trap(self, point: Point): | |
return self.trap_map[point.y][point.x] | |
def turn_up(self): | |
self.turn += 1 | |
def update_trap_map(self, point: Point): | |
if self.trap_map[point.y][point.x]: | |
self.trap_map[point.y][point.x] = False | |
else: | |
self.trap_map[point.y][point.x] = True | |
class GameInput(): | |
def __init__(self, width, height): | |
self.score = Score() | |
self.ore_map = OreMap(width, height) | |
self.hole_map = HoleMap(width, height) | |
self.cooldown = Cooldown() | |
self.agents = [] | |
self.opp_agents = [] | |
self.radars = [] | |
self.traps = [] | |
self.target_radar_point = Point(-1, -1) | |
class AgentCommand(): | |
def __init__(self, agent): | |
self.agent = agent | |
class Wait(AgentCommand): | |
def __call__(self): | |
self.agent.next_action("WAIT") | |
def is_need(self): | |
return self.agent.is_dead() | |
class Move(AgentCommand): | |
def __call__(self, input: GameInput, global_info: GlobalInfo): | |
# レーダーを所持している場合 | |
if self.agent.item_is_radar(): | |
self.agent.next_action( | |
" ".join(["MOVE", str(input.target_radar_point.x), str(input.target_radar_point.y)])) | |
return | |
# クリスタルを所持している場合 | |
if self.agent.item_is_crystal(): | |
self.agent.next_action(" ".join(["MOVE", str(0), str(self.agent.point.y)])) | |
return | |
# 通常ならクリスタルのある場所へ移動する | |
point = Point(-1, -1) | |
for crystal_point in input.ore_map.crystal_points: | |
# トラップのある場所には行かない | |
if global_info.is_trap(crystal_point): | |
continue | |
if point.is_out_range(): | |
point = crystal_point | |
continue | |
if point.distance(self.agent.point) > crystal_point.distance(self.agent.point): | |
# トラップを所持しているエージェントは | |
# クリスタルが2つ以上のところへ向かう | |
# if self.agent.item_is_trap(): | |
# if input.ore_map.cell_info(point) > 1: | |
point = crystal_point | |
if point.is_out_range(): | |
self.agent.next_action("WAIT") | |
return | |
self.agent.next_action(" ".join(["MOVE", str(point.x), str(point.y)])) | |
input.ore_map.after_target_cell(point) | |
def is_need(self, input: GameInput, global_info: GlobalInfo): | |
# 現在、アイテムを所持しているなら移動する | |
if not self.agent.item_is_none(): | |
return True | |
# 現座標にクリスタルが埋まっているなら移動しない | |
# if input.ore_map.is_crystal_pos(self.agent.point): | |
# return False | |
# クリスタルの埋まっている場所が見つからないなら移動しない | |
if input.ore_map.crystal_points == []: | |
return False | |
return True | |
class Dig(AgentCommand): | |
def __call__(self, input: GameInput, global_info: GlobalInfo): | |
# レーダーを所持していて、レーダー設置座標なら掘る | |
if self.agent.item_is_radar(): | |
self.agent.next_action( | |
" ".join(["DIG", str(input.target_radar_point.x), str(input.target_radar_point.y)])) | |
else: | |
# レーダーを所持しておらず、クリスタルがあるなら掘る | |
p = self.agent.point | |
if input.ore_map.is_crystal_pos(p): | |
self.agent.next_action(" ".join(["DIG", str(p.x), str(p.y)])) | |
input.ore_map.after_target_cell(p) | |
if self.agent.item_is_trap(): | |
# トラップを持っている場合 | |
global_info.update_trap_map(p) | |
def is_need(self, input: GameInput, global_info: GlobalInfo): | |
# トラップがあるなら掘らない | |
if global_info.is_trap(self.agent.point): | |
return False | |
if self.agent.item_is_radar(): | |
# レーダーを所持していて、レーダー設置座標 | |
if input.target_radar_point.eq(self.agent.point): | |
return True | |
# elif # トラップについて。。。 | |
elif self.agent.item_is_crystal(): | |
# クリスタルを所持しているなら掘らない | |
return False | |
else: | |
# アイテムを所持しておらず、クリスタルがあるなら掘る | |
if input.ore_map.is_crystal_pos(self.agent.point): | |
return True | |
return False | |
class SelfBomb(AgentCommand): | |
def __call__(self, input: GameInput, global_info: GlobalInfo): | |
point = Point(-1, -1) | |
for x in range(global_info.width): | |
for y in range(global_info.height): | |
trap_point = Point(x, y) | |
if not global_info.is_trap(Point(x, y)): | |
continue | |
if point.is_out_range(): | |
point = trap_point | |
elif point.distance(self.agent.point) > trap_point.distance(self.agent.point): | |
point = trap_point | |
global_info.update_trap_map(point) | |
self.agent.next_action(" ".join(["DIG", str(point.x), str(point.y)])) | |
def is_need(self, input: GameInput, global_info: GlobalInfo): | |
if input.score.diff() >= 10: | |
return True | |
return False | |
class RequestRadar(AgentCommand): | |
def __call__(self): | |
self.agent.next_action("REQUEST RADAR") | |
def is_need(self, input: GameInput, global_info: GlobalInfo): | |
# すでにトラップを持っているならリクエストしない | |
if self.agent.item_is_trap(): | |
return False | |
# 現座標にクリスタルが埋まっているならリクエストしない | |
if input.ore_map.is_crystal_pos(self.agent.point): | |
return False | |
# cooldownが0でない場合はリクエストしない | |
if input.cooldown.radar != 0: | |
return False | |
# target_radar_pointがなければリクエストしない | |
if input.target_radar_point.is_out_range(): | |
return False | |
# レーダー持ちのエージェントがいる場合は、リクエストしない | |
if any([agent.item_is_radar() for agent in input.agents]): | |
return False | |
# すでに他のAgentがレーダー取得に向かっているならリクエストしない | |
if any([agent.command == "REQUEST RADAR" for agent in input.agents]): | |
return False | |
return True | |
class RequestTrap(AgentCommand): | |
def __call__(self): | |
self.agent.next_action("REQUEST TRAP") | |
def is_need(self, input: GameInput, global_info: GlobalInfo): | |
# 50ターン以降はトラップを要求しない | |
if global_info.turn > 150: | |
return False | |
# すでにレーダーを持っているならリクエストしない | |
if self.agent.item_is_radar(): | |
return False | |
# cooldownが0でない場合、リクエストしない | |
if input.cooldown.trap != 0: | |
return False | |
# 本部にいない場合、リクエストしない | |
if self.agent.point.x != 0: | |
return False | |
return True | |
def strategy(turn, input: GameInput, global_info: GlobalInfo): | |
for agent in sorted(input.agents, key=lambda a: a.point.x): | |
# agentが死んでいるならwaitへ | |
wait_command = Wait(agent) | |
if wait_command.is_need(): | |
wait_command() | |
continue | |
# self_bomb = SelfBomb(agent) | |
# if self_bomb.is_need(input, global_info): | |
# self_bomb(input, global_info) | |
# continue | |
# レーダーコマンドに関して | |
radar_command = RequestRadar(agent) | |
# レーダーを要求するか判定 | |
if radar_command.is_need(input, global_info): | |
radar_command() | |
continue | |
# トラップコマンドに関して | |
trap_command = RequestTrap(agent) | |
# トラップを要求するか | |
if trap_command.is_need(input, global_info): | |
trap_command() | |
continue | |
# 穴を掘るかの判定 | |
dig_command = Dig(agent) | |
if dig_command.is_need(input, global_info): | |
dig_command(input, global_info) | |
continue | |
# 移動するかを判定 | |
move_command = Move(agent) | |
if move_command.is_need(input, global_info): | |
move_command(input, global_info) | |
continue | |
# なにもなければ、Wait | |
wait_command() | |
def do_game(input, global_info: GlobalInfo): | |
if global_info.turn == 1: | |
RequestRadar(input.agents[0])() | |
RequestTrap(input.agents[1])() | |
for i in range(3): | |
agent = input.agents[i+2] | |
agent.next_action(" ".join(["MOVE", str(3), str(agent.point.y)])) | |
elif global_info.turn == 2: | |
Move(input.agents[0])(input, global_info) | |
input.agents[1].next_action(" ".join(["MOVE", str(3), str(input.agents[1].point.y)])) | |
for i in range(3): | |
agent = input.agents[i+2] | |
agent.next_action(" ".join(["DIG", str(3), str(agent.point.y)])) | |
elif global_info.turn == 3: | |
Dig(input.agents[0])(input, global_info) | |
for i in range(4): | |
Wait(input.agents[i+1])() | |
else: | |
strategy(global_info.turn, input, global_info) | |
def turn_of_input(game_input: GameInput, global_info: GlobalInfo): | |
game_input.score.update(*[int(i) for i in get_line().split()]) | |
for y in range(height): | |
line = get_line().split() | |
for x in range(width): | |
game_input.ore_map.update(Point(x, y), line[2 * x]) | |
game_input.hole_map.update(Point(x, y), line[2 * x + 1]) | |
entity_count, *cooldown = [int(i) for i in get_line().split()] | |
game_input.cooldown.update(*cooldown) | |
assign_entity = AssignEntity( | |
game_input.agents, | |
game_input.opp_agents, | |
game_input.radars, | |
game_input.traps) | |
for i in range(entity_count): | |
entity = [int(j) for j in get_line().split()] | |
assign_entity.assign(entity) | |
for radar_point in TARGET_RADAR_POINTS: | |
breakpoint = False | |
for radar in game_input.radars: | |
# レーダー設置ポイントで、トラップがなければ、設定 | |
if radar.point.eq(radar_point): | |
breakpoint = True | |
break | |
if global_info.is_trap(radar_point): | |
breakpoint = True | |
break | |
if not breakpoint: | |
game_input.target_radar_point = radar_point | |
break | |
width, height = [int(i) for i in get_line().split()] | |
global_info = GlobalInfo(width, height) | |
# game loop | |
for i in range(200): | |
game_input = GameInput(width, height) | |
turn_of_input(game_input, global_info) | |
turn = i + 1 | |
do_game(game_input, global_info) | |
for agent in game_input.agents: | |
print(agent.command) | |
global_info.turn_up() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment