From aa517fbd43099eb0fcab117353005745d7b24130 Mon Sep 17 00:00:00 2001 From: Markus Himmel Date: Sat, 27 Aug 2016 16:27:04 +0200 Subject: [PATCH] Add RandomBot, fix a bug in the game logic --- Morris/ExtensionMethods.cs | 10 ++++++++++ Morris/GameState.cs | 6 +++++- Morris/Morris.csproj | 1 + Morris/Program.cs | 3 ++- Morris/RandomBot.cs | 39 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 Morris/RandomBot.cs diff --git a/Morris/ExtensionMethods.cs b/Morris/ExtensionMethods.cs index 1d32fe4..961def8 100644 --- a/Morris/ExtensionMethods.cs +++ b/Morris/ExtensionMethods.cs @@ -19,5 +19,15 @@ namespace Morris // der .Opponent verwendet, ist einfacher zu erkennen (Kapselung). return ~p; } + + private static Random rng = new Random(); + + /// + /// Gibt ein zufälliges Element der IList zurück + /// + public static T ChooseRandom(this IList it) + { + return it[rng.Next(it.Count)]; + } } } diff --git a/Morris/GameState.cs b/Morris/GameState.cs index 497052b..cf2ca97 100644 --- a/Morris/GameState.cs +++ b/Morris/GameState.cs @@ -244,7 +244,11 @@ namespace Morris return MoveValidity.Invalid; // Darf keinen Stein mehr platzieren // 3.: Wurde eine Mühle geschlossen? - bool millClosed = Mills.Any(mill => mill.Contains(move.To) && mill.All(point => (int)Board[point] == (int)NextToMove || point == move.To)); + bool millClosed = Mills.Any(mill => // Es muss eine potentielle Mühle geben, die + mill.Contains(move.To) && // den neu gesetzten Stein enthält und + mill.All(point => // bei der alle Punkte + (!move.From.HasValue || point != move.From) && // nicht der Ursprungspunkt der aktuellen Steinbewegung sind und + (int)Board[point] == (int)NextToMove || point == move.To)); // entweder schon vom Spieler bestzt sind oder Ziel der aktuellen Steinbewegung sind. // 4.: Verifikation des Mühlenparameters if (millClosed) diff --git a/Morris/Morris.csproj b/Morris/Morris.csproj index b865345..a5a8f20 100644 --- a/Morris/Morris.csproj +++ b/Morris/Morris.csproj @@ -49,6 +49,7 @@ + diff --git a/Morris/Program.cs b/Morris/Program.cs index ae35359..d2243c3 100644 --- a/Morris/Program.cs +++ b/Morris/Program.cs @@ -11,7 +11,8 @@ namespace Morris static void Main(string[] args) { var a = new ConsoleInteraction(); - var g = new Game(a, a); + var b = new RandomBot(); + var g = new Game(b, b); g.AddObserver(a); g.Run(); Console.ReadKey(); diff --git a/Morris/RandomBot.cs b/Morris/RandomBot.cs new file mode 100644 index 0000000..6f195a7 --- /dev/null +++ b/Morris/RandomBot.cs @@ -0,0 +1,39 @@ +/* + * RandomBot.cs + * Copyright (c) 2016 Markus Himmel + * This fileis distributed under the terms of the MIT license + */ + +using System; +using System.Linq; + +namespace Morris +{ + /// + /// Ein extrem einfacher KI-Spieler, der einen zufälligen gültigen Spielzug auswählt + /// + class RandomBot : IMoveProvider + { + // Anhand dieser Klasse können wir sehen, wie einfach es ist, einen Computerspieler zu implementieren. + // Es muss lediglich eine einzige, einfache Methode implementiert werden. Der Spielzustandparameter stellt + // Methoden wie BasicMoves und IsValidMove bereit, mit denen viele verschiedene Strategien sehr einfach + // implementiert werden können. + + private Random rng = new Random(); + + public GameMove GetNextMove(IReadOnlyGameState state) + { + // Ein zufälliger Spielzug + GameMove chosen = state.BasicMoves().ToList().ChooseRandom(); + + // Wenn wir einen Stein entfernen dürfen, wählen wir einen zufälligen Punkt aus, auf dem sich ein gegnerischer Stein befindet + if (state.IsValidMove(chosen) == MoveValidity.ClosesMill) + return chosen.WithRemove(Enumerable + .Range(0, GameState.FIELD_SIZE) + .Where(d => (int)state.Board[d] == (int)state.NextToMove.Opponent()) + .ToList().ChooseRandom()); + + return chosen; + } + } +}