mirror of
https://github.com/TwoFX/Morris.git
synced 2025-12-13 08:22:51 +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.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Morris
|
||||
{
|
||||
/// <summary>
|
||||
/// Ermöglicht Eingabe und Ausgabe der Spielsituation auf der Konsole.
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,12 +47,12 @@ namespace Morris
|
||||
|
||||
// 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))
|
||||
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)
|
||||
@@ -65,7 +65,7 @@ namespace Morris
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Morris
|
||||
var g = new Game(a, a);
|
||||
g.AddObserver(a);
|
||||
g.Run();
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user