From 640202dca40234d3a04085974095ec5692b1dbfd Mon Sep 17 00:00:00 2001 From: Markus Himmel Date: Sat, 27 Aug 2016 14:22:15 +0200 Subject: [PATCH] Game logic bug fixes, finishing up ConsoleInteraction --- Morris/ConsoleInteraction.cs | 102 +++++++++++++++++++++++++++------ Morris/CoordinateTranslator.cs | 6 +- Morris/GameState.cs | 12 ++-- Morris/Program.cs | 1 + 4 files changed, 95 insertions(+), 26 deletions(-) diff --git a/Morris/ConsoleInteraction.cs b/Morris/ConsoleInteraction.cs index bc23780..1287429 100644 --- a/Morris/ConsoleInteraction.cs +++ b/Morris/ConsoleInteraction.cs @@ -1,54 +1,105 @@ -using System; -using System.Collections.Generic; +/* + * ConsoleInteraction.cs + * Copyright (c) 2016 Markus Himmel + * This file is distributed under the terms of the MIT license. + */ + +using System; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace Morris { + /// + /// Ermöglicht Eingabe und Ausgabe der Spielsituation auf der Konsole. + /// class ConsoleInteraction : IGameStateObserver, IMoveProvider { + public ConsoleInteraction() + { + // Konsolenparameter setzen + Console.OutputEncoding = Encoding.Unicode; + Console.BackgroundColor = ConsoleColor.White; + Console.ForegroundColor = ConsoleColor.Black; + Console.Clear(); + } + public void Notify(IReadOnlyGameState state) { - // Ein mit Leerzeichen initialisiertes 8*8 Jagged Array - char[][] field = Enumerable.Repeat(0, 8).Select(_ => Enumerable.Repeat(' ', 8).ToArray()).ToArray(); + // Ein mit Leerzeichen initialisiertes 13*13 Jagged Array + char[][] field = Enumerable.Repeat(0, 13).Select(_ => Enumerable.Repeat(' ', 13).ToArray()).ToArray(); + // Spielpositionen mit Belegung for (int i = 0; i < GameState.FIELD_SIZE; i++) { - var point = CoordinateTranslator.CoordinatesFromID(i); + var point = CoordinateTranslator.CoordinatesFromID(i).Select(x => 2 * x).ToArray(); switch (state.Board[i]) { case Occupation.Free: - field[point.Item1][point.Item2] = 'F'; + field[point[0]][point[1]] = '▫'; break; case Occupation.Black: - field[point.Item1][point.Item2] = 'B'; + field[point[0]][point[1]] = '●'; break; case Occupation.White: - field[point.Item1][point.Item2] = 'W'; + field[point[0]][point[1]] = '○'; break; } } - //for (int i = 0; i < GameState.FIELD_SIZE; i++) - //{ - // foreach (int j in GameState.GetConnected(i)) - // { + // Linien auf dem Spielfeld zeichnen. Wo diese hingehören, wird aus den Verbindungsdaten, die GameState + // bereithält, "on the fly" bestimmt. Diesen Schritt des Zeichnens des Spielfelds könnte man cachen, + // das wäre hier aber mit Blick auf die Größe des Spielfelds eine premature optimization. + for (int i = 0; i < GameState.FIELD_SIZE; i++) + { + var pointI = CoordinateTranslator.CoordinatesFromID(i).Select(x => 2 * x).ToArray(); + foreach (int j in GameState.GetConnected(i).Where(j => j < i)) + { + var pointJ = CoordinateTranslator.CoordinatesFromID(j).Select(x => 2 * x).ToArray(); - // } - //} + if (pointI[0] == pointJ[0]) + { + // Horizontale Linien + for (int k = Math.Min(pointI[1], pointJ[1]) + 1; k <= Math.Max(pointI[1], pointJ[1]) - 1; k++) + { + field[pointI[0]][k] = '-'; + } + } + else + { + // Vertikale Linien + for (int k = Math.Min(pointI[0], pointJ[0]) + 1; k <= Math.Max(pointI[0], pointJ[0]) - 1; k++) + { + field[k][pointI[1]] = '|'; + } + } + } + } + // Spielfeld tatsächlich ausgeben foreach (var row in field) { Console.WriteLine(new string(row)); } + + // Spielstatus mitteilen, falls das Spiel nicht mehr läuft. + switch (state.Result) + { + case GameResult.BlackVictory: + Console.WriteLine("Schwarz hat gewonnen."); + break; + case GameResult.WhiteVictory: + Console.WriteLine("Weiß hat gewonnnen."); + break; + case GameResult.Draw: + Console.WriteLine("Unentschieden."); + break; + } } - - public GameMove GetNextMove(IReadOnlyGameState state) { // So lange wieder fragen, bis ein Input eingegeben wird, der geparst werden kann @@ -57,7 +108,20 @@ namespace Morris { try { - Console.Write("Bitte gib einen Zug ein: "); + string phase; + switch (state.GetPhase(state.NextToMove)) + { + case Phase.Placing: + phase = "Platziert"; + break; + case Phase.Moving: + phase = "Bewegt"; + break; + default: + phase = "Fliegt"; + break; + } + Console.Write($"{(state.NextToMove == Player.Black ? "Schwarz" : "Weiß")} am Zug ({phase}): "); // Eingabe parsen var input = Console.ReadLine().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); @@ -84,7 +148,7 @@ namespace Morris } catch { - // Einfach nocheinmal versuchen... + // Einfach nocheinmal fragen, wenn der Input nicht geparst werden konnte } } } diff --git a/Morris/CoordinateTranslator.cs b/Morris/CoordinateTranslator.cs index 5acd7c1..461ef87 100644 --- a/Morris/CoordinateTranslator.cs +++ b/Morris/CoordinateTranslator.cs @@ -47,12 +47,12 @@ namespace Morris // Die XML-Kommentare sind hier ausgelassen, weil die Methodennamen ausreichend sprechend sein sollten - public static Tuple CoordinatesFromHumanReadable(string human) + public static int[] CoordinatesFromHumanReadable(string human) { if (!humans.Keys.Contains(human)) throw new ArgumentException("Dies ist keine gültige Positionsangabe"); - return Tuple.Create(human[0] - 'a', human[1] - '1'); + return new[] { 6 - (human[1] - '1'), human[0] - 'a' }; } public static string HumanReadableFromCoordinates(Tuple coord) @@ -65,7 +65,7 @@ namespace Morris throw new ArgumentException("Dies sind keine gültigen Koordinaten"); } - public static Tuple CoordinatesFromID(int id) + public static int[] CoordinatesFromID(int id) { return CoordinatesFromHumanReadable(HumanReadableFromID(id)); } diff --git a/Morris/GameState.cs b/Morris/GameState.cs index 0be5e8f..7f2c7f5 100644 --- a/Morris/GameState.cs +++ b/Morris/GameState.cs @@ -50,7 +50,7 @@ namespace Morris new[] { 1, 4, 7 }, new[] { 16, 19, 22 }, new[] { 8, 12, 17 }, - new[] { 5, 12, 20 }, + new[] { 5, 13, 20 }, new[] { 2, 14, 23 } }.Select(mill => Array.AsReadOnly(mill)).ToArray()); @@ -296,8 +296,12 @@ namespace Morris // ggf. wegbewegter Stein if (move.From.HasValue) Board[move.From.Value] = Occupation.Free; - else if (++stonesPlaced[NextToMove] == STONES_MAX) - playerPhase[NextToMove] = Phase.Moving; + else + { + currentStones[NextToMove]++; + if (++stonesPlaced[NextToMove] == STONES_MAX) + playerPhase[NextToMove] = Phase.Moving; + } // Hinbewegter Stein Board[move.To] = (Occupation)NextToMove; @@ -311,7 +315,7 @@ namespace Morris } // Gegner hat nur noch zwei Steine - if (currentStones[NextToMove.Opponent()] == 2) + if (playerPhase[NextToMove.Opponent()] != Phase.Placing && currentStones[NextToMove.Opponent()] == 2) Result = (GameResult)NextToMove; // Gegner ist jetzt dran diff --git a/Morris/Program.cs b/Morris/Program.cs index b02db92..ae35359 100644 --- a/Morris/Program.cs +++ b/Morris/Program.cs @@ -14,6 +14,7 @@ namespace Morris var g = new Game(a, a); g.AddObserver(a); g.Run(); + Console.ReadKey(); } } }