Skip to content

Commit 182164c

Browse files
committed
Add recorder
1 parent fefc859 commit 182164c

1 file changed

Lines changed: 31 additions & 9 deletions

File tree

agents/attackers/sarsa/sarsa_agent.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
# Authors: Ondrej Lukas - ondrej.lukas@aic.fel.cvut.cz
22
from os import path, makedirs
3+
from typing import Optional, Tuple
34
import numpy as np
45
import random
56
import pickle
67
import argparse
78
import logging
9+
from datetime import datetime
810

911
from netsecgame import Action, GameState, BaseAgent, generate_valid_actions, state_as_ordered_string
1012
from netsecgame.game_components import AgentRole
13+
from netsecgame.utils.trajectory_recorder import TrajectoryRecorder
1114

1215
class SARSAAgent(BaseAgent):
1316

@@ -36,7 +39,7 @@ def get_state_id(self, state:GameState) -> int:
3639
self._str_to_id[state_str] = len(self._str_to_id)
3740
return self._str_to_id[state_str]
3841

39-
def select_action(self, state:GameState, testing=False) -> Action:
42+
def select_action(self, state:GameState, testing=False) -> Tuple[Action,int]:
4043
actions = generate_valid_actions(state)
4144
state_id = self.get_state_id(state)
4245

@@ -55,9 +58,11 @@ def select_action(self, state:GameState, testing=False) -> Action:
5558
self.q_values[state_id, action] = 0
5659
return action, state_id
5760

58-
def play_episode(self, testing=False)->list:
61+
def play_episode(self, testing=False, recorder:Optional[TrajectoryRecorder]=None, filename=None)->list:
5962
observation = self.request_game_reset()
6063
episodic_returns = []
64+
if recorder is not None:
65+
recorder.add_initial_state(observation.state)
6166
action1 ,state_id1 = self.select_action(observation.state, testing)
6267
done = observation.end
6368
while not done:
@@ -71,13 +76,18 @@ def play_episode(self, testing=False)->list:
7176
if not testing:
7277
self.q_values[state_id1, action1] += self.alpha*(observation2.reward+ self.gamma*self.q_values[state_id2, action2]-self.q_values[state_id1, action1])
7378

79+
if recorder is not None:
80+
recorder.add_step(action1, observation2.reward, observation2.state)
7481
# move1 step
7582
action1 = action2
7683
state_id1= state_id2
7784
done = observation2.end
85+
if recorder is not None:
86+
recorder.save_to_file(filename=filename)
87+
recorder.reset()
7888
return episodic_returns
7989

80-
def play_game(self, num_episodes=1, testing=False):
90+
def play_game(self, num_episodes=1, testing=False, recorder:TrajectoryRecorder=None):
8191
"""
8292
The main function for the gameplay. Handles agent registration and the main interaction loop.
8393
"""
@@ -90,13 +100,21 @@ def play_game(self, num_episodes=1, testing=False):
90100
if episode and episode % args.eval_each == 0:
91101
testing_returns = []
92102
for _ in range(args.eval_for):
93-
testing_returns.append(np.sum(self.play_episode(testing=True)))
103+
if recorder:
104+
filename = filename = f"{datetime.now():%Y-%m-%d}_SARSA_Attacker_{episode:06d}"
105+
testing_returns.append(np.sum(self.play_episode(testing=True, recorder=recorder, filename=filename)))
106+
else:
107+
testing_returns.append(np.sum(self.play_episode(testing=True)))
94108
self._logger.info(f"Eval after {episode} episodes: ={np.mean(testing_returns)}±{np.std(testing_returns)}")
95109
if episode % args.store_models_every == 0 and episode != 0:
96-
self.store_q_table(f'sarsa_agent_marl.experiment{args.experiment_id}-episodes-{episode}.pickle')
110+
self.store_q_table(f'sarsa_agent_marl.experiment{args.experiment_id}-episodes-{episode:06d}.pickle')
97111
returns = []
98112
for _ in range(args.eval_for):
99-
returns.append(np.sum(self.play_episode(testing=True)))
113+
if recorder:
114+
filename = filename = f"{datetime.now():%Y-%m-%d}_SARSA_Attacker_{num_episodes:06d}"
115+
returns.append(np.sum(self.play_episode(testing=True, recorder=recorder, filename=filename)))
116+
else:
117+
returns.append(np.sum(self.play_episode(testing=True)))
100118
self._logger.info(f"Final results for {self.__class__.__name__} after {num_episodes} episodes: {np.mean(returns)}±{np.std(returns)}")
101119
self._logger.info("Terminating interaction")
102120
self.terminate_connection()
@@ -116,6 +134,7 @@ def play_game(self, num_episodes=1, testing=False):
116134
parser.add_argument("--experiment_id", help="Id of the experiment to record into Mlflow.", default='sarsa_006_coordinatorV3', type=str)
117135
parser.add_argument("--store_models_every", help="Store a model to disk every these number of episodes.", default=2000, type=int)
118136
parser.add_argument("--previous_model", help="Store a model to disk every these number of episodes.", type=str)
137+
parser.add_argument("--record_trajectories", type=bool, default=False)
119138
args = parser.parse_args()
120139

121140
if not path.exists(args.logdir):
@@ -124,11 +143,14 @@ def play_game(self, num_episodes=1, testing=False):
124143

125144
# Create agent
126145
agent = SARSAAgent(args.host, args.port, alpha=args.alpha, gamma=args.gamma, epsilon=args.epsilon)
127-
146+
if args.record_trajectories:
147+
recorder= TrajectoryRecorder("SARSA", agent_role="Attacker")
148+
else:
149+
recorder = None
128150
if args.test_only:
129151
agent.load_q_table(args.previous_model)
130-
agent.play_game(args.episodes, testing=True)
152+
agent.play_game(args.episodes, testing=True, recorder=recorder)
131153
else:
132-
agent.play_game(args.episodes, testing=False)
154+
agent.play_game(args.episodes, testing=False, recorder=recorder)
133155
agent.store_q_table("./sarsa_agent_marl.pickle")
134156

0 commit comments

Comments
 (0)