Skip to content

Commit

Permalink
add call heuristic: only call winnable games
Browse files Browse the repository at this point in the history
  • Loading branch information
Bonifatius94 committed Nov 20, 2023
1 parent 90f4a45 commit 93cf4f9
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Schafkopf.Lib/Hand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ public int FarbeCount(CardColor farbe)
return count;
}

public int TrumpfCount()
=> BitOperations.PopCount(((cards & EXISTING_BITMASK) << 1) & TRUMPF_BITMASK);

#region SimpleImplForBenchmarks

[Obsolete("Optimized version is faster!")]
Expand Down
4 changes: 3 additions & 1 deletion Schafkopf.Training/Dataset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ private static (Matrix2D, Matrix2D) generateDataset(int size)
private static IEnumerable<SarsExp> generateExperiences(
int? numExamples = null)
{
var agent = new RandomAgent();
var gameCaller = new HeuristicGameCaller(
new GameMode[] { GameMode.Sauspiel });
var agent = new RandomAgent(gameCaller);
var table = new Table(
new Player(0, agent), new Player(1, agent),
new Player(2, agent), new Player(3, agent));
Expand Down
1 change: 0 additions & 1 deletion Schafkopf.Training/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public static void Main(string[] args)
trainSize: 1_000_000, testSize: 10_000);
var optimizer = new AdamOpt(learnRate: 0.002);
var lossFunc = new MeanSquaredError();
var accMetric = new MeanSquaredError();

var session = new SupervisedTrainingSession(
model, optimizer, lossFunc, dataset);
Expand Down
65 changes: 64 additions & 1 deletion Schafkopf.Training/RandomAgent.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,78 @@
namespace Schafkopf.Training;

public class HeuristicGameCaller
{
public HeuristicGameCaller(IEnumerable<GameMode> modes)
=> allowedModes = modes;

private IEnumerable<GameMode> allowedModes;

public GameCall MakeCall(
ReadOnlySpan<GameCall> possibleCalls,
int position, Hand hand, int klopfer)
{
if (allowedModes.Contains(GameMode.Sauspiel))
{
var call = canCallSauspiel(possibleCalls, hand);
if (call.Mode == GameMode.Sauspiel)
return call;
}

return GameCall.Weiter();
}

private static readonly CardColor[] rufbareFarben = new CardColor[] {
CardColor.Schell, CardColor.Gras, CardColor.Eichel };

private GameCall canCallSauspiel(
ReadOnlySpan<GameCall> possibleCalls, Hand hand)
{
var sauspielCalls = possibleCalls.ToArray()
.Where(x => x.Mode == GameMode.Sauspiel).ToArray();
if (!sauspielCalls.Any())
return GameCall.Weiter();

hand = hand.CacheTrumpf(sauspielCalls[0].IsTrumpf);

if (hand.TrumpfCount() < 4)
return GameCall.Weiter();

var trumpfOrdered = hand.OrderByDescending(
x => x, new CardComparer(GameMode.Sauspiel));
var bestTrumpf = trumpfOrdered.ElementAt(0);
var secondBestTrumpf = trumpfOrdered.ElementAt(1);

bool noRennerForOpponents = bestTrumpf.Type == CardType.Ober
&& bestTrumpf.Color >= CardColor.Herz;
bool twoStammtrumpf = secondBestTrumpf.Type == CardType.Unter
|| secondBestTrumpf.Type == CardType.Ober && noRennerForOpponents;
bool isFrei = rufbareFarben.Any(x => hand.FarbeCount(x) == 0);
bool hasFiveOrMoreTrumpf = hand.TrumpfCount() >= 5;

bool canPlay = noRennerForOpponents && twoStammtrumpf
&& (hasFiveOrMoreTrumpf || isFrei);

if (!canPlay)
return GameCall.Weiter();

return sauspielCalls.OrderBy(x => hand.FarbeCount(x.GsuchteFarbe)).First();
}
}

public class RandomAgent : ISchafkopfAIAgent
{
public RandomAgent(HeuristicGameCaller caller)
=> this.caller = caller;

private HeuristicGameCaller caller;
private static readonly Random rng = new Random();

public void OnGameFinished(GameLog final) { }

public GameCall MakeCall(
ReadOnlySpan<GameCall> possibleCalls,
int position, Hand hand, int klopfer)
=> possibleCalls[rng.Next(possibleCalls.Length)];
=> caller.MakeCall(possibleCalls, position, hand, klopfer);

public Card ChooseCard(GameLog history, ReadOnlySpan<Card> possibleCards)
=> possibleCards[rng.Next(possibleCards.Length)];
Expand Down
8 changes: 5 additions & 3 deletions Schafkopf.Training/RandomPlayBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ public class RandomPlayBenchmark
{
public void Benchmark(ISchafkopfAIAgent agentToEval, int epochs = 10_000)
{
var gameCaller = new HeuristicGameCaller(
new GameMode[] { GameMode.Sauspiel, GameMode.Wenz, GameMode.Solo });
var players = new Player[] {
new Player(0, agentToEval),
new Player(1, new RandomAgent()),
new Player(2, new RandomAgent()),
new Player(3, new RandomAgent())
new Player(1, new RandomAgent(gameCaller)),
new Player(2, new RandomAgent(gameCaller)),
new Player(3, new RandomAgent(gameCaller))
};
var table = new Table(
players[0], players[1],
Expand Down

0 comments on commit 93cf4f9

Please sign in to comment.