Skip to content

Instantly share code, notes, and snippets.

@tomaszbartoszewski
Last active March 5, 2020 22:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tomaszbartoszewski/09e02706b79d0c66874d23574a3c6c9e to your computer and use it in GitHub Desktop.
Save tomaszbartoszewski/09e02706b79d0c66874d23574a3c6c9e to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
namespace TicTacToe
{
class Program
{
static void Main()
{
while (true)
{
int gameType = 0;
while (!new [] {1,2,3}.Contains(gameType))
{
Console.WriteLine("Select game type: 1 - H vs H, 2 - H vs C, 3 - C vs C");
var typeString = Console.ReadLine();
int.TryParse(typeString, out gameType);
}
IPlayer firstPlayer;
IPlayer secondPlayer;
switch (gameType)
{
case 1:
{
firstPlayer = new HumanConsolePlayer(FieldValue.X);
secondPlayer = new HumanConsolePlayer(FieldValue.O);
break;
}
case 2:
{
string selection = string.Empty;
while (selection != "X" && selection != "O")
{
Console.WriteLine("Select your sign: X, O");
selection = Console.ReadLine();
}
if (selection == "X")
{
firstPlayer = new HumanConsolePlayer(FieldValue.X);
secondPlayer = new PerfectPlayer(FieldValue.O);
}
else
{
secondPlayer = new HumanConsolePlayer(FieldValue.O);
firstPlayer = new PerfectPlayer(FieldValue.X);
}
break;
}
default:
{
firstPlayer = new PerfectPlayer(FieldValue.X);
secondPlayer = new PerfectPlayer(FieldValue.O);
break;
}
}
var game = new Game(firstPlayer, secondPlayer);
game.StartGame();
Console.WriteLine("Press any key for new game");
Console.ReadLine();
}
}
}
public class Game
{
private readonly IPlayer firstPlayer;
private readonly IPlayer secondPlayer;
private readonly FieldValue[,] board;
public Game(IPlayer firstPlayer, IPlayer secondPlayer)
{
this.firstPlayer = firstPlayer;
this.secondPlayer = secondPlayer;
board = new FieldValue[3, 3];
}
public void StartGame()
{
var gameStatus = Result.InProgress;
FieldValue lastPlayer = FieldValue.X;
PrintBoard(GetBoard());
while (gameStatus == Result.InProgress)
{
var firstPlayerMove = firstPlayer.GetNextMove(GetBoard());
board[firstPlayerMove.Column, firstPlayerMove.Row] = firstPlayerMove.FieldValue;
gameStatus = BoardStatusChecker.GetCurrentState(GetBoard(), firstPlayerMove.FieldValue);
lastPlayer = firstPlayerMove.FieldValue;
PrintBoard(GetBoard());
if (gameStatus != Result.InProgress)
break;
var secondPlayerMove = secondPlayer.GetNextMove(GetBoard());
board[secondPlayerMove.Column, secondPlayerMove.Row] = secondPlayerMove.FieldValue;
PrintBoard(GetBoard());
gameStatus = BoardStatusChecker.GetCurrentState(GetBoard(), secondPlayerMove.FieldValue);
lastPlayer = secondPlayerMove.FieldValue;
}
switch (gameStatus)
{
case Result.Win:
Console.WriteLine("{0} won!", lastPlayer);
break;
case Result.Draw:
Console.WriteLine("Draw!");
break;
}
}
private FieldValue[,] GetBoard()
{
return board.CopyTable();
}
private static void PrintBoard(FieldValue[,] board)
{
Console.Write(" ");
for (var column = 1; column <= 3; column++)
Console.Write(column);
Console.WriteLine();
for (var row = 0; row < 3; row++)
{
Console.Write(row + 1);
for (var column = 0; column < 3; column++)
{
Console.Write(board[column, row] == FieldValue.Hidden ? " " : board[column, row].ToString());
}
Console.WriteLine();
}
}
}
public static class ArrayHelper
{
public static T[,] CopyTable<T>(this T[,] tableToCopy)
{
var newTable = new T[tableToCopy.GetLength(0), tableToCopy.GetLength(1)];
for (var i = 0; i < tableToCopy.GetLength(0); i++)
{
for (var j = 0; j < tableToCopy.GetLength(1); j++)
{
newTable[i, j] = tableToCopy[i, j];
}
}
return newTable;
}
}
public interface IPlayer
{
Move GetNextMove(FieldValue[,] board);
}
public class HumanConsolePlayer : IPlayer
{
private readonly FieldValue fieldValue;
public HumanConsolePlayer(FieldValue fieldValue)
{
this.fieldValue = fieldValue;
}
public Move GetNextMove(FieldValue[,] board)
{
while (true)
{
int column = 0;
while (column == 0)
{
Console.WriteLine("Select Column(1-3):");
var columnText = Console.ReadLine();
int.TryParse(columnText, out column);
if (column > 3 || column < 1)
column = 0;
}
int row = 0;
while (row == 0)
{
Console.WriteLine("Select Row(1-3):");
var rowText = Console.ReadLine();
int.TryParse(rowText, out row);
if (row > 3 || row < 1)
row = 0;
}
if (board[column - 1, row - 1] == FieldValue.Hidden)
{
return new Move(column - 1, row - 1, fieldValue);
}
Console.WriteLine("Field is already selected, pick empty field");
}
}
}
public class PerfectPlayer : IPlayer
{
private readonly FieldValue playerFieldValue;
public PerfectPlayer(FieldValue fieldValue)
{
this.playerFieldValue = fieldValue;
}
public Move GetNextMove(FieldValue[,] board)
{
return CheckAllPossibleMoves(board, playerFieldValue).Move;
}
private int CalculatePointsForMove(FieldValue[,] board, Move move)
{
board[move.Column, move.Row] = move.FieldValue;
var status = BoardStatusChecker.GetCurrentState(board, move.FieldValue);
if (status == Result.Win && move.FieldValue == playerFieldValue)
return 10;
if (status == Result.Win && move.FieldValue != playerFieldValue)
return -10;
if (status == Result.Lose && move.FieldValue == playerFieldValue)
return -10;
if (status == Result.Lose && move.FieldValue != playerFieldValue)
return 10;
if (status == Result.Draw)
return 0;
if (status == Result.InProgress)
{
return CheckAllPossibleMoves(board, GetOppositePlayerSign(move.FieldValue)).Points;
}
return 0;
}
private FieldValue GetOppositePlayerSign(FieldValue fieldValue)
{
if (fieldValue == FieldValue.X)
return FieldValue.O;
return FieldValue.X;
}
private PointsForMove CheckAllPossibleMoves(FieldValue[,] board, FieldValue fieldValue)
{
var allResults = new List<PointsForMove>();
for (var column = 0; column < 3; column++)
for (var row = 0; row < 3; row++)
if (board[column, row] == FieldValue.Hidden)
{
var move = new Move(column, row, fieldValue);
var points = CalculatePointsForMove(board.CopyTable(), move);
allResults.Add(new PointsForMove(points, move));
}
if (fieldValue == playerFieldValue)
return allResults.OrderByDescending(r => r.Points).First();
return allResults.OrderBy(r => r.Points).First();
}
private class PointsForMove
{
public PointsForMove(int points, Move move)
{
Points = points;
Move = move;
}
public int Points { get; }
public Move Move { get; }
}
}
public static class BoardStatusChecker
{
public static Result GetCurrentState(FieldValue[,] board, FieldValue playerSign)
{
if (GetAllLineResults(board, playerSign).Any(r => r == Result.Win))
return Result.Win;
if (GetAllLineResults(board, playerSign).Any(r => r == Result.Lose))
return Result.Lose;
var areHiddenFields = false;
for (var column = 0; column < 3; column++)
for (var row = 0; row < 3; row++)
if (board[column, row] == FieldValue.Hidden)
areHiddenFields = true;
if (!areHiddenFields)
return Result.Draw;
return Result.InProgress;
}
private static IEnumerable<Result> GetAllLineResults(FieldValue[,] board, FieldValue fieldValue)
{
foreach (var lineResult in CheckSimpleLines(board, fieldValue, true))
yield return lineResult;
foreach (var lineResult in CheckSimpleLines(board, fieldValue, false))
yield return lineResult;
yield return CheckDiagonal(board, fieldValue, true);
yield return CheckDiagonal(board, fieldValue, false);
}
private static IEnumerable<Result> CheckSimpleLines(FieldValue[,] board, FieldValue fieldValue, bool isVertical)
{
for (var i = 0; i < 3; i++)
{
var checkList = new List<FieldValue>();
for (var j = 0; j < 3; j++)
{
checkList.Add(isVertical ? board[i, j] : board[j, i]);
}
yield return CheckCollection(checkList, fieldValue);
}
}
private static Result CheckDiagonal(FieldValue[,] board, FieldValue fieldValue, bool isReverse)
{
var checkList = new List<FieldValue>();
for (var i = 0; i < 3; i++)
{
checkList.Add(isReverse ? board[2 - i, 2 - i] : board[i, 2 - i]);
}
return CheckCollection(checkList, fieldValue);
}
private static Result CheckCollection(List<FieldValue> checkList, FieldValue fieldValue)
{
var oppositePlayerSign = GetOppositePlayerSign(fieldValue);
if (checkList.All(v => v == fieldValue))
return Result.Win;
if (checkList.All(v => v == oppositePlayerSign))
return Result.Lose;
return Result.InProgress;
}
private static FieldValue GetOppositePlayerSign(FieldValue fieldValue)
{
if (fieldValue == FieldValue.X)
return FieldValue.O;
return FieldValue.X;
}
}
public struct Move
{
public FieldValue FieldValue;
public int Column;
public int Row;
public Move(int column, int row, FieldValue fieldValue)
{
FieldValue = fieldValue;
Column = column;
Row = row;
}
}
public enum FieldValue
{
Hidden,
X,
O
}
public enum Result
{
Win,
Draw,
Lose,
InProgress
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment