-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontroller.py
129 lines (101 loc) · 4.61 KB
/
controller.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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import pygame
from constants import *
class Controller:
""" Class for controlling a player. """
agent = None
def __init__(self):
self.direction = UP
def update(self, events):
pass
def get_move(self):
raise NotImplementedError("Cannot call get_move on superclass")
class KeyboardController(Controller):
""" Class for controlling a player using keyboard inputs. """
def __init__(self):
super().__init__()
self.scheme = {UP: pygame.K_UP,
LEFT: pygame.K_LEFT,
RIGHT: pygame.K_RIGHT,
DOWN: pygame.K_DOWN}
self.last_pressed = None
def update(self, events):
for event in events:
if event.type == pygame.KEYDOWN:
if event.key in self.scheme.values():
self.last_pressed = event.key
def get_move(self):
for direction in self.scheme:
if self.scheme[direction] == self.last_pressed:
# Don't turn in a direction exactly opposite current movement
if sorted((self.direction, direction)) not in (sorted((LEFT, RIGHT)), sorted((UP, DOWN))):
self.direction = direction
self.last_pressed = None
return self.direction
class WASDController(KeyboardController):
def __init__(self):
super().__init__()
self.scheme = {UP: pygame.K_w,
LEFT: pygame.K_a,
RIGHT: pygame.K_d,
DOWN: pygame.K_s}
def node_number_to_board_offset(n):
""" Given a node innovation number, calculates the offset from the player of the corresponding node. """
n = n - NUM_OUTPUT_NODES
x_offset = n % BOARD_WIDTH
y_offset = n//BOARD_WIDTH
return x_offset, y_offset
class AgentController(Controller):
def __init__(self, agent):
super().__init__()
self.agent = agent
self.direction = None
def node_number_to_board_position(self, n):
""" Returns the position on the game board corresponding to the node number, offset by the
position of the player corresponding to the current agent (whew!)
"""
relevant_player = [player for player in self.agent.game.players if player.controller.agent is self.agent][0]
origin = relevant_player.x, relevant_player.y
x_offset, y_offset = node_number_to_board_offset(n)
if y_offset == BOARD_HEIGHT:
return f"{x_offset}"
x = (origin[0] + x_offset) % BOARD_WIDTH
y = (origin[1] + y_offset) % BOARD_HEIGHT
return x, y
def get_move(self):
""" Evaluates the confidence for all output nodes in network, then proceeds in the
direction of highest confidence.
"""
if self.agent.game is None:
raise ValueError("The agent's Game attribute has not been set. Cannot get move.")
# Clear stored values of agent nodes
self.agent.clear_all_nodes()
# Update the set of input nodes to represent the board state
for node in self.agent.input_nodes:
pos = self.node_number_to_board_position(node.number)
if isinstance(pos, str):
node.val = 0
for player in self.agent.game.players:
if not player.controller == self:
for player_self in self.agent.game.players:
if player_self.controller == self:
if pos == "0":
node.val = player.x - player_self.x
else:
node.val = player.y - player_self.y
else:
node.val = TILE_TYPE_TO_WEIGHT[str(self.agent.game.board[pos[0]][pos[1]][0])]
# Check highest output value and move in that direction
max_value = None
max_value_node = None
for node in self.agent.output_nodes:
val = node.value()
# Nodes with higher innovation number are prioritized in case of ties
if max_value is None or val > max_value or (val == max_value and node.number > max_value_node.number):
max_value_node = node
max_value = val
# Return direction corresponding to node number for highest value. Cannot turn 180 degrees.
new_direction = INNOVATION_TO_DIRECTION[max_value_node.number]
if new_direction == OPPOSITE_DIRECTIONS[self.direction]:
new_direction = self.direction
self.direction = new_direction
return self.direction