54 lines
1.6 KiB
C#
54 lines
1.6 KiB
C#
|
|
using OECS;
|
||
|
|
|
||
|
|
namespace OECS.PlayTest;
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Abstract agent that picks the action with the highest score.
|
||
|
|
/// When <see cref="ScoreAction"/> returns 0 for all actions (the default),
|
||
|
|
/// the agent behaves as a uniform random agent over legal actions.
|
||
|
|
/// Ties are broken randomly.
|
||
|
|
/// </summary>
|
||
|
|
public abstract class GreedyAgent<TDecision> : IAgent<TDecision>
|
||
|
|
{
|
||
|
|
private static readonly Random _rng = new();
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Returns the list of currently legal actions.
|
||
|
|
/// </summary>
|
||
|
|
protected abstract List<TDecision> GetLegalActions(World world);
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// Scores an action. Defaults to 0 (uniform random).
|
||
|
|
/// </summary>
|
||
|
|
protected virtual float ScoreAction(World world, TDecision action) => 0f;
|
||
|
|
|
||
|
|
public TDecision Decide(World world)
|
||
|
|
{
|
||
|
|
var actions = GetLegalActions(world);
|
||
|
|
if (actions.Count == 0)
|
||
|
|
throw new InvalidOperationException(
|
||
|
|
$"{GetType().Name}: no legal actions available");
|
||
|
|
|
||
|
|
if (actions.Count == 1)
|
||
|
|
return actions[0];
|
||
|
|
|
||
|
|
float bestScore = float.MinValue;
|
||
|
|
var bestActions = new List<TDecision>();
|
||
|
|
foreach (var action in actions)
|
||
|
|
{
|
||
|
|
float score = ScoreAction(world, action);
|
||
|
|
if (score > bestScore)
|
||
|
|
{
|
||
|
|
bestScore = score;
|
||
|
|
bestActions.Clear();
|
||
|
|
bestActions.Add(action);
|
||
|
|
}
|
||
|
|
else if (score == bestScore)
|
||
|
|
{
|
||
|
|
bestActions.Add(action);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return bestActions[_rng.Next(bestActions.Count)];
|
||
|
|
}
|
||
|
|
}
|