mirror of
https://github.com/TwoFX/Morris.git
synced 2026-02-04 04:52:52 +00:00
Game logic bug fixes, finishing up ConsoleInteraction
This commit is contained in:
@@ -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.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Morris
|
namespace Morris
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ermöglicht Eingabe und Ausgabe der Spielsituation auf der Konsole.
|
||||||
|
/// </summary>
|
||||||
class ConsoleInteraction : IGameStateObserver, IMoveProvider
|
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)
|
public void Notify(IReadOnlyGameState state)
|
||||||
{
|
{
|
||||||
// Ein mit Leerzeichen initialisiertes 8*8 Jagged Array
|
// Ein mit Leerzeichen initialisiertes 13*13 Jagged Array
|
||||||
char[][] field = Enumerable.Repeat(0, 8).Select(_ => Enumerable.Repeat(' ', 8).ToArray()).ToArray();
|
char[][] field = Enumerable.Repeat(0, 13).Select(_ => Enumerable.Repeat(' ', 13).ToArray()).ToArray();
|
||||||
|
|
||||||
|
// Spielpositionen mit Belegung
|
||||||
for (int i = 0; i < GameState.FIELD_SIZE; i++)
|
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])
|
switch (state.Board[i])
|
||||||
{
|
{
|
||||||
case Occupation.Free:
|
case Occupation.Free:
|
||||||
field[point.Item1][point.Item2] = 'F';
|
field[point[0]][point[1]] = '▫';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Occupation.Black:
|
case Occupation.Black:
|
||||||
field[point.Item1][point.Item2] = 'B';
|
field[point[0]][point[1]] = '●';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Occupation.White:
|
case Occupation.White:
|
||||||
field[point.Item1][point.Item2] = 'W';
|
field[point[0]][point[1]] = '○';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//for (int i = 0; i < GameState.FIELD_SIZE; 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,
|
||||||
// foreach (int j in GameState.GetConnected(i))
|
// 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)
|
foreach (var row in field)
|
||||||
{
|
{
|
||||||
Console.WriteLine(new string(row));
|
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)
|
public GameMove GetNextMove(IReadOnlyGameState state)
|
||||||
{
|
{
|
||||||
// So lange wieder fragen, bis ein Input eingegeben wird, der geparst werden kann
|
// So lange wieder fragen, bis ein Input eingegeben wird, der geparst werden kann
|
||||||
@@ -57,7 +108,20 @@ namespace Morris
|
|||||||
{
|
{
|
||||||
try
|
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
|
// Eingabe parsen
|
||||||
var input = Console.ReadLine().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
var input = Console.ReadLine().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
@@ -84,7 +148,7 @@ namespace Morris
|
|||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
// Einfach nocheinmal versuchen...
|
// Einfach nocheinmal fragen, wenn der Input nicht geparst werden konnte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ namespace Morris
|
|||||||
|
|
||||||
// Die XML-Kommentare sind hier ausgelassen, weil die Methodennamen ausreichend sprechend sein sollten
|
// Die XML-Kommentare sind hier ausgelassen, weil die Methodennamen ausreichend sprechend sein sollten
|
||||||
|
|
||||||
public static Tuple<int, int> CoordinatesFromHumanReadable(string human)
|
public static int[] CoordinatesFromHumanReadable(string human)
|
||||||
{
|
{
|
||||||
if (!humans.Keys.Contains(human))
|
if (!humans.Keys.Contains(human))
|
||||||
throw new ArgumentException("Dies ist keine gültige Positionsangabe");
|
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<int, int> coord)
|
public static string HumanReadableFromCoordinates(Tuple<int, int> coord)
|
||||||
@@ -65,7 +65,7 @@ namespace Morris
|
|||||||
throw new ArgumentException("Dies sind keine gültigen Koordinaten");
|
throw new ArgumentException("Dies sind keine gültigen Koordinaten");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Tuple<int, int> CoordinatesFromID(int id)
|
public static int[] CoordinatesFromID(int id)
|
||||||
{
|
{
|
||||||
return CoordinatesFromHumanReadable(HumanReadableFromID(id));
|
return CoordinatesFromHumanReadable(HumanReadableFromID(id));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Morris
|
|||||||
new[] { 1, 4, 7 },
|
new[] { 1, 4, 7 },
|
||||||
new[] { 16, 19, 22 },
|
new[] { 16, 19, 22 },
|
||||||
new[] { 8, 12, 17 },
|
new[] { 8, 12, 17 },
|
||||||
new[] { 5, 12, 20 },
|
new[] { 5, 13, 20 },
|
||||||
new[] { 2, 14, 23 }
|
new[] { 2, 14, 23 }
|
||||||
}.Select(mill => Array.AsReadOnly(mill)).ToArray());
|
}.Select(mill => Array.AsReadOnly(mill)).ToArray());
|
||||||
|
|
||||||
@@ -296,8 +296,12 @@ namespace Morris
|
|||||||
// ggf. wegbewegter Stein
|
// ggf. wegbewegter Stein
|
||||||
if (move.From.HasValue)
|
if (move.From.HasValue)
|
||||||
Board[move.From.Value] = Occupation.Free;
|
Board[move.From.Value] = Occupation.Free;
|
||||||
else if (++stonesPlaced[NextToMove] == STONES_MAX)
|
else
|
||||||
playerPhase[NextToMove] = Phase.Moving;
|
{
|
||||||
|
currentStones[NextToMove]++;
|
||||||
|
if (++stonesPlaced[NextToMove] == STONES_MAX)
|
||||||
|
playerPhase[NextToMove] = Phase.Moving;
|
||||||
|
}
|
||||||
|
|
||||||
// Hinbewegter Stein
|
// Hinbewegter Stein
|
||||||
Board[move.To] = (Occupation)NextToMove;
|
Board[move.To] = (Occupation)NextToMove;
|
||||||
@@ -311,7 +315,7 @@ namespace Morris
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gegner hat nur noch zwei Steine
|
// Gegner hat nur noch zwei Steine
|
||||||
if (currentStones[NextToMove.Opponent()] == 2)
|
if (playerPhase[NextToMove.Opponent()] != Phase.Placing && currentStones[NextToMove.Opponent()] == 2)
|
||||||
Result = (GameResult)NextToMove;
|
Result = (GameResult)NextToMove;
|
||||||
|
|
||||||
// Gegner ist jetzt dran
|
// Gegner ist jetzt dran
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Morris
|
|||||||
var g = new Game(a, a);
|
var g = new Game(a, a);
|
||||||
g.AddObserver(a);
|
g.AddObserver(a);
|
||||||
g.Run();
|
g.Run();
|
||||||
|
Console.ReadKey();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user