Last active
February 3, 2024 17:10
-
-
Save td2sk/8baf287bd090eb9f02bd2d299515e206 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
■■□□□□□□□□■■ | |
■■□ □■■ | |
□□□ □□□ | |
□ □ | |
□ □ | |
□ □ | |
□□□ □□ | |
■■□□□□□□□ □■ |
This file contains hidden or 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
from __future__ import annotations | |
from enum import Enum, IntEnum | |
from logging import INFO, basicConfig, getLogger | |
basicConfig(level=INFO, filename="log.txt") | |
logger = getLogger(__name__) | |
class Direction(IntEnum): | |
"""アイテムの飛んでいく方向""" | |
UpperLeft = 0 | |
LowerLeft = 1 | |
LowerRight = 2 | |
UpperRight = 3 | |
@staticmethod | |
def tostring(direction: Direction): | |
return "⬉⬋⬊⬈"[direction] | |
def op(d: Direction): | |
"""方向を反転""" | |
return Direction((d + 2) & 3) | |
def cw(d: Direction): | |
"""方向を時計回りに回転 | |
アイテムが壁に反射して時計回りに進行方向を変えたときの向きを返す | |
Examples: | |
>>> cw(Direction.UpperLeft) | |
Direction.UpperRight | |
""" | |
return Direction((d + 3) & 3) | |
def ccw(d: Direction): | |
"""方向を反時計回りに回転 | |
アイテムが壁に反射して反時計回りに進行方向を変えたときの向きを返す | |
Examples: | |
>>> cw(Direction.UpperLeft) | |
Direction.LowerLeft | |
""" | |
return Direction((d + 1) & 3) | |
class ReflectedWallOrientation(IntEnum): | |
"""反射した壁の向き""" | |
LEFT = 0 | |
DOWN = 1 | |
RIGHT = 2 | |
UP = 3 | |
class Point: | |
def __init__(self, x: int, y: int): | |
self.x = x | |
self.y = y | |
def __eq__(self, rhs): | |
return self.x == rhs.x and self.y == rhs.y | |
def __add__(self, rhs): | |
return Point(self.x + rhs.x, self.y + rhs.y) | |
def __repr__(self): | |
return f"P({self.x}, {self.y})" | |
# アイテムが飛んでいく方向の座標変化分 | |
Forward = [Point(-1, -1), Point(-1, 1), Point(1, 1), Point(1, -1)] | |
# アイテムの進行方向に対して左手側のマス目の座標変化分 | |
# 例: アイテムが右上に飛んでいるとき | |
# | |
# @: ここの座標 | |
# ➚ | |
LeftHandSide = [Point(-1, 0), Point(0, 1), Point(1, 0), Point(0, -1)] | |
# アイテムの進行方向に対して右手側のマス目の座標変化分 | |
# 例: アイテムが右上に飛んでいるとき | |
# | |
# ➚@: ここの座標 | |
RightHandSide = [Point(0, -1), Point(-1, 0), Point(0, 1), Point(1, 0)] | |
class Wall(Enum): | |
"""部屋の表現""" | |
Block = "■" | |
Reflect = "□" | |
Space = " " | |
class Room: | |
def __init__(self, data): | |
self.data = data | |
self.width = max(len(line) for line in data) | |
self.height = len(data) | |
def __getitem__(self, p: Point): | |
if p.y < 0 or self.height <= p.y or p.x < 0 or self.width <= p.x: | |
return Wall.Block | |
return Wall(self.data[p.y][p.x]) | |
@staticmethod | |
def load(path: str): | |
with open(path, encoding=("utf-8")) as f: | |
data = [list(line) for line in f.read().splitlines()] | |
return Room(data) | |
class Path: | |
"""アイテムの飛んだルートと反射した壁の方向を記録""" | |
path: list[tuple[Point, Direction]] | |
reflected_wall_orientations: set[ReflectedWallOrientation] | |
catched: bool | |
crashed: bool | |
def __init__(self): | |
self.path = [] | |
self.reflected_wall_orientations = set() | |
self.catched = False | |
self.crashed = False | |
def add(self, position: Point, direction: Direction): | |
self.path.append((position, direction)) | |
def reflect(self, orig_direction: Direction, clockwise: bool): | |
# note: Direction, Orientation の具体的な Enum 数値に依存 | |
if clockwise: | |
orientation = ReflectedWallOrientation(orig_direction) | |
else: | |
orientation = ReflectedWallOrientation(cw(orig_direction)) | |
self.reflected_wall_orientations.add(orientation) | |
def catch(self): | |
self.catched = True | |
def crash(self): | |
self.crashed = True | |
def __len__(self): | |
return len(self.path) | |
def satisfy(self): | |
"""アイテムの飛行ルートが分裂条件を満たしているか""" | |
return len(self.reflected_wall_orientations) == 4 | |
def dump(self, room: Room, player: Point, f): | |
for item, direction in self.path: | |
r = ["".join(l) for l in room.data] | |
r[item.y] = ( | |
r[item.y][: item.x] | |
+ Direction.tostring(direction) | |
+ r[item.y][item.x + 1 :] | |
) | |
r[player.y] = r[player.y][: player.x] + "@" + r[player.y][player.x + 1 :] | |
print( | |
f"player: {player}, item: {item}", | |
file=f, | |
) | |
print("\n".join(r), file=f) | |
print("", file=f) | |
def _search(room: Room, player: Point, item: Point, direction: Direction, path: Path): | |
"""アイテムの飛行ルートを追跡 | |
Args: | |
room (Room): room data | |
player (Point): シレンの座標 | |
item (Point): 現在追跡中のアイテム位置 | |
direction (Direction): アイテムの飛行方向 | |
path (Path): アイテムの軌跡 | |
Returns: | |
Path: アイテムの軌跡 | |
""" | |
# アイテムがプレイヤー位置に来たら終了 | |
# 投げた直後(len(path) == 0) のときは終了しない | |
if player == item and len(path) != 0: | |
path.catch() | |
return path | |
# 飛び先が普通の壁なら終了 | |
if room[item] == Wall.Block: | |
# 壺が割れる | |
path.crash() | |
return path | |
path.add(item, direction) | |
f = item + Forward[direction] | |
l = item + LeftHandSide[direction] | |
r = item + RightHandSide[direction] | |
if room[l] == room[r] == Wall.Reflect: | |
# 角にあたって停止 | |
# ⬊□ | |
# □ | |
return path | |
if room[f] == Wall.Reflect: | |
if room[l] == Wall.Reflect: | |
# 時計回りに反射 | |
# ⬊□ | |
# ⬋□ | |
path.reflect(direction, clockwise=True) | |
return _search(room, player, r, cw(direction), path) | |
if room[r] == Wall.Reflect: | |
# 反時計回りに反射 | |
# ⬉□ | |
# ⬈□ | |
path.reflect(direction, clockwise=False) | |
return _search(room, player, l, ccw(direction), path) | |
# 角で反射 | |
return _search(room, player, item, op(direction), path) | |
return _search(room, player, f, direction, path) | |
def search(room: Room): | |
f = open("out.txt", "w", encoding="utf-8") | |
for y in range(room.height): | |
for x in range(room.width): | |
player = Point(x, y) | |
if room[player] != Wall.Space: | |
continue | |
for d in range(4): | |
d = Direction(d) | |
path = _search(room, player, player, d, Path()) | |
if path.satisfy(): | |
crashed = " , 壺割れる" if path.crashed else "" | |
catched = " , キャッチ" if path.catched else "" | |
print( | |
f"分裂成功: 初期位置: {player}, 投げる方向: {Direction.tostring(d)}{crashed}{catched}" | |
) | |
path.dump(room, player, f) | |
def main(): | |
room = Room.load("input.txt") | |
search(room) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
課題