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();
}
}
}