Skip to content

Commit 787fdf7

Browse files
authored
Enable patron configuration (#91)
1 parent f633306 commit 787fdf7

2 files changed

Lines changed: 104 additions & 18 deletions

File tree

Engine/src/AI/ScriptsOfTribute.cs

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
using ScriptsOfTribute.Board;
2-
using ScriptsOfTribute.utils;
3-
using ScriptsOfTribute.Serializers;
42

53
namespace ScriptsOfTribute.AI;
64

75
public class ScriptsOfTribute
86
{
9-
private AI[] _players = new AI[2];
7+
private static readonly PatronId[] DefaultPatrons =
8+
Enum.GetValues(typeof(PatronId))
9+
.Cast<PatronId>()
10+
.Where(id => id != PatronId.TREASURY)
11+
.ToArray();
12+
13+
public static PatronId[] GetDefaultPatrons() => (PatronId[])DefaultPatrons.Clone();
1014

15+
private AI[] _players = new AI[2];
16+
private PatronId[] _patrons;
1117
private ScriptsOfTributeGame? _game;
1218
private ulong _seed;
1319

@@ -25,17 +31,22 @@ public ulong Seed
2531
public bool P2LoggerEnabled { get; set; } = false;
2632
public TimeSpan Timeout { get; set; }
2733

28-
public ScriptsOfTribute(AI player1, AI player2, TimeSpan timeout)
34+
public ScriptsOfTribute(AI player1, AI player2, TimeSpan timeout, PatronId[] patrons)
2935
{
3036
_players[0] = player1;
3137
_players[1] = player2;
3238
player1.Id = PlayerEnum.PLAYER1;
3339
player2.Id = PlayerEnum.PLAYER2;
3440
Seed = (ulong)Environment.TickCount;
3541
Timeout = timeout;
42+
_patrons = patrons;
3643
}
3744

38-
public ScriptsOfTribute(AI player1, AI player2) : this(player1, player2, TimeSpan.FromSeconds(30))
45+
public ScriptsOfTribute(AI player1, AI player2) : this(player1, player2, TimeSpan.FromSeconds(30), DefaultPatrons)
46+
{ }
47+
48+
public ScriptsOfTribute(AI player1, AI player2, TimeSpan timeout, IEnumerable<string>? patronTokens)
49+
: this(player1, player2, timeout, ResolvePatrons(patronTokens))
3950
{ }
4051

4152
private Task<PatronId> SelectPatronTask(AI currentPlayer, List<PatronId> availablePatrons, int round)
@@ -61,11 +72,9 @@ private Task<PatronId> SelectPatronTask(AI currentPlayer, List<PatronId> availab
6172
return (new EndGameState(playerToWin, GameEndReason.PATRON_SELECTION_TIMEOUT), null);
6273
}
6374

64-
private (EndGameState? ,PatronId[]?) PatronSelection()
75+
private (EndGameState?, PatronId[]?) PatronSelection()
6576
{
66-
List<PatronId> patrons = Enum.GetValues(typeof(PatronId)).Cast<PatronId>()
67-
// TODO: Hardcoded banned patrons, improve this.
68-
.Where(patronId => patronId != PatronId.TREASURY && patronId != PatronId.PSIJIC).ToList();
77+
List<PatronId> patrons = _patrons.ToList();
6978

7079
List<PatronId> patronsSelected = new List<PatronId>();
7180
var (endGameState, patron) = SelectPatronWithTimeout(PlayerEnum.PLAYER2, _players[0], patrons, 1);
@@ -121,7 +130,7 @@ private Task<PatronId> SelectPatronTask(AI currentPlayer, List<PatronId> availab
121130
PatronId[]? patrons;
122131

123132
endGameState = PrepareBots(_players[0], _players[1], 5);
124-
133+
125134
if (endGameState is not null)
126135
{
127136
_players[0].GameEnd(endGameState, null);
@@ -180,4 +189,66 @@ private Task<PatronId> SelectPatronTask(AI currentPlayer, List<PatronId> availab
180189

181190
return null;
182191
}
192+
193+
public static PatronId[] ResolvePatrons(IEnumerable<string>? rawTokens)
194+
{
195+
bool userSpecified = false;
196+
197+
if (rawTokens == null)
198+
return GetDefaultPatrons();
199+
200+
var tokens = rawTokens
201+
.SelectMany(s => s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
202+
.Select(t => t.Trim())
203+
.Where(t => t.Length > 0)
204+
.ToArray();
205+
206+
userSpecified = tokens.Length > 0;
207+
208+
if (!userSpecified)
209+
return GetDefaultPatrons();
210+
211+
if (tokens.Length == 1 && (tokens[0].Equals("default", StringComparison.OrdinalIgnoreCase)
212+
|| tokens[0].Equals("all", StringComparison.OrdinalIgnoreCase)))
213+
return GetDefaultPatrons();
214+
215+
var parsed = new List<PatronId>(tokens.Length);
216+
var invalid = new List<string>();
217+
218+
foreach (var t in tokens)
219+
{
220+
if (TryParsePatronToken(t, out var id))
221+
parsed.Add(id);
222+
else
223+
invalid.Add(t);
224+
}
225+
226+
if (invalid.Count > 0)
227+
{
228+
var valid = string.Join(", ", Enum.GetNames(typeof(PatronId)));
229+
throw new ArgumentException($"Unknown patron(s): {string.Join(", ", invalid)}\nValid: {valid}");
230+
}
231+
232+
var result = parsed.Where(p => p != PatronId.TREASURY).Distinct().ToArray();
233+
234+
if (userSpecified && result.Length < 4)
235+
throw new ArgumentException("Provide at least 4 selectable patrons.");
236+
237+
return result.Length > 0 ? result : GetDefaultPatrons();
238+
}
239+
240+
private static bool TryParsePatronToken(string token, out PatronId id)
241+
{
242+
if (Enum.TryParse(token, ignoreCase: true, out id))
243+
return true;
244+
245+
if (int.TryParse(token, out var numeric) && Enum.IsDefined(typeof(PatronId), numeric))
246+
{
247+
id = (PatronId)numeric;
248+
return true;
249+
}
250+
251+
id = default;
252+
return false;
253+
}
183254
}

GameRunner/Program.cs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
var timeoutOption = CreateOption<int>("--timeout", "Game timeout in seconds.", 30, "-to");
3636
var clientPortOption = CreateOption<int>("--client-port", "Base client port for gRPC bots.", 50000, "-cp");
3737
var serverPortOption = CreateOption<int>("--server-port", "Base server port for gRPC bots.", 49000, "-sp");
38+
var patronsOption = CreateOption<string[]>("--patrons", "Allowed patrons for this run. Use 'default' (alias: 'all') for standard set (all selectable, no TREASURY).", Array.Empty<string>(), "-p");
3839

3940
var bot1NameArgument = CreateBotArgument("bot1", "Name of the first bot or command.");
4041
var bot2NameArgument = CreateBotArgument("bot2", "Name of the second bot or command.");
@@ -49,6 +50,7 @@
4950
timeoutOption,
5051
clientPortOption,
5152
serverPortOption,
53+
patronsOption,
5254
bot1NameArgument,
5355
bot2NameArgument,
5456
};
@@ -154,9 +156,13 @@
154156

155157
#region Prepare game
156158

157-
ScriptsOfTribute.AI.ScriptsOfTribute PrepareGame(AI bot1, AI bot2, LogsEnabled enableLogs, ulong seed, LogFileNameProvider? logProvider, int timeout)
159+
ScriptsOfTribute.AI.ScriptsOfTribute PrepareGame(
160+
AI bot1,
161+
AI bot2,
162+
LogsEnabled enableLogs, ulong seed, LogFileNameProvider? logProvider, int timeout, IEnumerable<string>? patronTokens
163+
)
158164
{
159-
var game = new ScriptsOfTribute.AI.ScriptsOfTribute(bot1, bot2, TimeSpan.FromSeconds(timeout))
165+
var game = new ScriptsOfTribute.AI.ScriptsOfTribute(bot1, bot2, TimeSpan.FromSeconds(timeout), patronTokens)
160166
{
161167
Seed = seed,
162168
};
@@ -203,6 +209,7 @@ ScriptsOfTribute.AI.ScriptsOfTribute PrepareGame(AI bot1, AI bot2, LogsEnabled e
203209
int timeout = context.ParseResult.GetValueForOption(timeoutOption);
204210
int baseClientPort = context.ParseResult.GetValueForOption(clientPortOption);
205211
int baseServerPort = context.ParseResult.GetValueForOption(serverPortOption);
212+
IEnumerable<string>? patrons = context.ParseResult.GetValueForOption(patronsOption);
206213
BotInfo? bot1Info = context.ParseResult.GetValueForArgument(bot1NameArgument);
207214
BotInfo? bot2Info = context.ParseResult.GetValueForArgument(bot2NameArgument);
208215

@@ -219,9 +226,9 @@ ScriptsOfTribute.AI.ScriptsOfTribute PrepareGame(AI bot1, AI bot2, LogsEnabled e
219226
ulong actualSeed = seed ?? (ulong)new Random().NextInt64();
220227

221228
if (threads == 1)
222-
RunSingleThreaded(runs, bot1Info, bot2Info, logs, logProvider, actualSeed, timeout, baseClientPort, baseServerPort, baseHost);
229+
RunSingleThreaded(runs, bot1Info, bot2Info, logs, logProvider, actualSeed, timeout, baseClientPort, baseServerPort, patrons, baseHost);
223230
else
224-
RunMultiThreaded(runs, threads, bot1Info, bot2Info, logs, logProvider, actualSeed, timeout, baseClientPort, baseServerPort, baseHost);
231+
RunMultiThreaded(runs, threads, bot1Info, bot2Info, logs, logProvider, actualSeed, timeout, baseClientPort, baseServerPort, patrons, baseHost);
225232
});
226233

227234
void RunSingleThreaded(
@@ -234,6 +241,7 @@ void RunSingleThreaded(
234241
int timeout,
235242
int baseClientPort,
236243
int baseServerPort,
244+
IEnumerable<string>? patrons,
237245
string baseHost = "localhost"
238246
)
239247
{
@@ -262,7 +270,7 @@ void RunSingleThreaded(
262270

263271
for (var i = 0; i < runs; i++)
264272
{
265-
var game = PrepareGame(bot1, bot2, enableLogs, currentSeed, logFileNameProvider, timeout);
273+
var game = PrepareGame(bot1, bot2, enableLogs, currentSeed, logFileNameProvider, timeout, patrons);
266274
currentSeed += 1;
267275

268276
granularWatch.Reset();
@@ -301,6 +309,7 @@ void RunMultiThreaded(
301309
int timeout,
302310
int baseClientPort,
303311
int baseServerPort,
312+
IEnumerable<string>? patrons,
304313
string baseHost = "localhost"
305314
)
306315
{
@@ -310,7 +319,13 @@ void RunMultiThreaded(
310319
var gamesPerThreadRemainder = runs % noOfThreads;
311320
var threads = new Task<List<ScriptsOfTribute.Board.EndGameState>>[noOfThreads];
312321

313-
List<ScriptsOfTribute.Board.EndGameState> PlayGames(int amount, BotInfo bot1Info, BotInfo bot2Info, int threadNo, ulong seed)
322+
List<ScriptsOfTribute.Board.EndGameState> PlayGames(
323+
int amount,
324+
BotInfo bot1Info,
325+
BotInfo bot2Info,
326+
int threadNo,
327+
ulong seed,
328+
IEnumerable<string>? patronsLocal)
314329
{
315330
var results = new ScriptsOfTribute.Board.EndGameState[amount];
316331
var timeMeasurements = new long[amount];
@@ -321,7 +336,7 @@ void RunMultiThreaded(
321336

322337
for (var i = 0; i < amount; i++)
323338
{
324-
var game = PrepareGame(bot1, bot2, enableLogs, seed, logFileNameProvider, timeout);
339+
var game = PrepareGame(bot1, bot2, enableLogs, seed, logFileNameProvider, timeout, patronsLocal);
325340
seed += 1;
326341

327342
watch.Reset();
@@ -374,7 +389,7 @@ void RunMultiThreaded(
374389
ServerPort = baseServerPort + noOfThreads + i,
375390
}
376391
: bot2Info;
377-
threads[i] = Task.Factory.StartNew(() => PlayGames(gamesToPlay, bot1ThreadInfo, bot2ThreadInfo, threadNo, currentSeedCopy));
392+
threads[i] = Task.Factory.StartNew(() => PlayGames(gamesToPlay, bot1ThreadInfo, bot2ThreadInfo, threadNo, currentSeedCopy, patrons));
378393
currentSeed += (ulong)gamesToPlay;
379394
}
380395

0 commit comments

Comments
 (0)