-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsimulate.py
executable file
·156 lines (119 loc) · 5.51 KB
/
simulate.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python3
import argparse
import sys
import logging
import matplotlib
import numpy as np
from random import shuffle
from matplotlib import pyplot as plt
from scipy.interpolate import make_interp_spline # For interpolation
from engine.players import Player
from engine.game import Game
from utils.stats import StatsRecorder
# Loading the available player types (excluding Human)
PLAYER_TYPES = {clazz.__name__: clazz for clazz in Player.__subclasses__() if clazz.__name__ != 'Human'}
def run_simulations(iterations, delay, player_types, graph, interpolate, output, display=True):
player_instances = []
# Create player instances based on the specified player types
for i in range(len(player_types)):
player_class = PLAYER_TYPES[player_types[i]]
if player_class:
player_instance = player_class(f"Player {i + 1}")
player_instances.append(player_instance)
# Validate the number of players
if len(player_instances) < 2 or len(player_instances) > 6:
print('Invalid number of players. \nWhen using --player, specify between 2 and 6 players.')
sys.exit(1)
stats = StatsRecorder()
for i in range(iterations):
game = Game(stats_recorder=stats, delay=delay, log_level=logging.ERROR)
# Shuffle the player starting positions
shuffle(player_instances)
# Add player instances to the game
for player in player_instances:
game.add_player(player)
game.start_match()
while not game.is_over():
game.next_round()
if display:
print(f"Simulated game {i + 1}/{iterations}", end="\r")
if display:
# Display stats table
print()
stats.print_table()
if graph:
# Display the graph
display_graph(stats, interpolate=interpolate)
if (output):
# Save the data to file
stats.save(output)
return [stats.get_player_stats(player) for player in stats.rank_players("wins")]
def display_graph(stats, interpolate=False):
bar_criteria = ['wins', 'draws', 'average_points_per_game', 'highest_game_turnover', 'average_points_per_trick', 'highest_trick_turnover']
line_criteria = ['game_turnovers']
generations = range(1, len(list(stats.player_stats.values())[0]['game_turnovers']) + 1)
num_players = len(stats.player_stats)
ranked_players = stats.rank_players()
# Bar chart for bar_criteria
plt.figure("Overall Player Statistics")
width = 0.2
x = np.arange(num_players)
for i, criterion in enumerate(bar_criteria):
y = [stats.get_player_stats(player)[criterion] for player in ranked_players]
plt.bar(x + (i * width), y, width=width, label=criterion)
plt.xlabel('Players')
plt.ylabel('Value')
plt.title('Overall Player Statistics')
plt.xticks(x + width * (len(bar_criteria) - 1) / 2, [f"{player.name} ({player.type})" for player in ranked_players])
plt.grid(True)
plt.legend()
# Line graph with smoothing for line_criteria
plt.figure("Per-iteration Player Statistics")
for player in ranked_players:
for criterion in line_criteria:
y = stats.player_stats[player][criterion]
if interpolate:
# Perform spline interpolation for smoothing
# Choose number of points to interpolate based on the number of iterations
points = 100 if len(generations) < 100 else 1000 if len(generations) < 1000 else 10000
x = np.linspace(min(generations), max(generations), points) # Increase the number of points for smoother curve
spl = make_interp_spline(generations, y, k=3) # Cubic spline interpolation
y = spl(x)
else:
x = generations
plt.plot(x, y, label=f"{player.name} ({criterion})")
plt.xlabel('Iterations')
plt.ylabel('Values per player')
plt.title('Per-iteration Player Statistics')
plt.grid(True)
plt.legend()
# Display the plots on separate windows
plt.show(block=False)
plt.figure(1)
plt.show()
def main():
# Create the parser
parser = argparse.ArgumentParser(description='BASIS Multi-Agent Platform')
# Add arguments
parser.add_argument('--iterations', type=int, default=1000, help='Number of simulations to run')
parser.add_argument('--delay', type=int, default=0, help='Delay in seconds between each round')
parser.add_argument('--player', action="append", nargs='+', choices=list(PLAYER_TYPES.keys()), help='Player types')
parser.add_argument('--graph', action="store_true", help='Display graph')
parser.add_argument('--interpolate', action="store_true", help='Interpolate graph, useful for large number of iterations')
parser.add_argument('--output', default=None, action="store", help='Save data to file')
# Parse the arguments
args = parser.parse_args()
# --interpolate is only valid if --graph is specified
if args.interpolate and not args.graph:
print('Invalid argument. --interpolate is only valid if --graph is specified.')
sys.exit(1)
# Flatting the player-argument list
player_types = [item for sublist in args.player for item in sublist] if args.player else [
"SimpleGreedyAgent",
"MinimizePointLossGreedyAgent",
"MPLGreedyTrumpSaveAgent",
"GreedyCountingAgent"]
# Run the simulations
run_simulations(args.iterations, args.delay, player_types, graph=args.graph, interpolate=args.interpolate, output=args.output)
if __name__ == '__main__':
main()