-
Notifications
You must be signed in to change notification settings - Fork 0
/
transformers.py
103 lines (79 loc) · 3.32 KB
/
transformers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
State transformers are wrappers around Game model states.
"""
import operator
from dataclasses import dataclass
from typing import Iterable
from game import Game
from snake import Snake
from utils import Coordinate, Direction
@dataclass(frozen=True, eq=True, order=True, kw_only=True, slots=True)
class ObstacleFoodDirectionState:
game: Game
def check_dir(self, to: Iterable[Coordinate] | Coordinate, dir: Direction) -> bool:
from_ = self.game.snake.head
def _check(to, dir, from_):
op = operator.lt if dir in (Direction.LEFT, Direction.UP) else operator.gt
attr = "X" if Direction in (Direction.LEFT, Direction.RIGHT) else "Y"
return op(getattr(to, attr), getattr(from_, attr))
if isinstance(to, Coordinate):
return _check(to, dir, from_)
else:
return any(_check(t, dir, from_) for t in to)
@property
def food(self) -> frozenset[Coordinate]:
return self.game.food
@property
def snake(self) -> Snake:
return self.game.snake
@property
def food_left(self) -> bool:
return self.check_dir(to=self.game.food, dir=Direction.LEFT)
@property
def food_right(self) -> bool:
return self.check_dir(to=self.game.food, dir=Direction.RIGHT)
@property
def food_up(self) -> bool:
return self.check_dir(to=self.game.food, dir=Direction.UP)
@property
def food_down(self) -> bool:
return self.check_dir(to=self.game.food, dir=Direction.DOWN)
@property
def obstacle_left(self) -> bool:
return self.check_dir(to=self.game.snake.segments, dir=Direction.LEFT)
@property
def obstacle_right(self) -> bool:
return self.check_dir(to=self.game.snake.segments, dir=Direction.RIGHT)
@property
def obstacle_up(self) -> bool:
return self.check_dir(to=self.game.snake.segments, dir=Direction.UP)
@property
def obstacle_down(self) -> bool:
return self.check_dir(to=self.game.snake.segments, dir=Direction.DOWN)
def obstacle_food_direction_state(game: Game):
"""Returns a tuple (FL, FR, FU, FD, OL, OR, OU, OD) constructed from game state.
Notation:
F = Food, O = Obstacle
L = Left, R = Right, U = Up, D = Down
e.g., FL = "Food Left"
"""
def check_dir(to: Iterable[Coordinate] | Coordinate, dir: Direction) -> bool:
# outter func generalizes to multiple "to" coordinates
def _check(to, dir): # inner func checks single coordinate
op = operator.lt if dir in (Direction.LEFT, Direction.UP) else operator.gt
attr = "X" if Direction in (Direction.LEFT, Direction.RIGHT) else "Y"
return op(getattr(to, attr), getattr(game.snake.head, attr))
if isinstance(to, Coordinate):
return _check(to, dir)
else:
return any(_check(t, dir) for t in to)
return (
check_dir(to=game.food, dir=Direction.LEFT),
check_dir(to=game.food, dir=Direction.RIGHT),
check_dir(to=game.food, dir=Direction.UP),
check_dir(to=game.food, dir=Direction.DOWN),
check_dir(to=game.snake.segments, dir=Direction.LEFT),
check_dir(to=game.snake.segments, dir=Direction.RIGHT),
check_dir(to=game.snake.segments, dir=Direction.UP),
check_dir(to=game.snake.segments, dir=Direction.DOWN),
)