-
-
Save mwolicki/b7d16369090be8c5c18a10bad1a6b642 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Collections.Generic; | |
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 sw = System.Diagnostics.Stopwatch.StartNew(); | |
var game = new Game(firstPlayer, secondPlayer); | |
game.StartGame(); | |
System.Console.WriteLine(sw.Elapsed); | |
Console.WriteLine("Press any key for new game"); | |
Console.ReadLine(); | |
} | |
} | |
} | |
public class Board { | |
public static readonly Board Empty = new Board(new FieldValue[9]); | |
private Board (FieldValue[] arr) => _arr = arr; | |
private readonly FieldValue[] _arr; | |
public FieldValue this [int c, int r] => _arr[ c + r *3]; | |
public Board With(int c, int r, FieldValue fv) { | |
var arr = new FieldValue[_arr.Length]; | |
_arr.CopyTo(arr, 0); | |
arr[c + r * 3] = fv; | |
return new Board(arr); | |
} | |
} | |
public class Game | |
{ | |
private readonly IPlayer firstPlayer; | |
private readonly IPlayer secondPlayer; | |
public Game(IPlayer firstPlayer, IPlayer secondPlayer) | |
{ | |
this.firstPlayer = firstPlayer; | |
this.secondPlayer = secondPlayer; | |
} | |
public void StartGame() | |
{ | |
var board = Board.Empty; | |
var gameStatus = Result.InProgress; | |
FieldValue lastPlayer = FieldValue.X; | |
PrintBoard(board); | |
while (gameStatus == Result.InProgress) | |
{ | |
var firstPlayerMove = firstPlayer.GetNextMove(board); | |
board = board.With(firstPlayerMove.Column, firstPlayerMove.Row, firstPlayerMove.FieldValue); | |
gameStatus = BoardStatusChecker.GetCurrentState(board, firstPlayerMove.FieldValue); | |
lastPlayer = firstPlayerMove.FieldValue; | |
PrintBoard(board); | |
if (gameStatus != Result.InProgress) | |
break; | |
var secondPlayerMove = secondPlayer.GetNextMove(board); | |
board = board.With(secondPlayerMove.Column, secondPlayerMove.Row, secondPlayerMove.FieldValue); | |
PrintBoard(board); | |
gameStatus = BoardStatusChecker.GetCurrentState(board, secondPlayerMove.FieldValue); | |
lastPlayer = secondPlayerMove.FieldValue; | |
} | |
switch (gameStatus) | |
{ | |
case Result.Win: | |
Console.WriteLine("{0} won!", lastPlayer); | |
break; | |
case Result.Draw: | |
Console.WriteLine("Draw!"); | |
break; | |
} | |
} | |
private static void PrintBoard(Board 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 interface IPlayer | |
{ | |
Move GetNextMove(Board board); | |
} | |
public class HumanConsolePlayer : IPlayer | |
{ | |
private readonly FieldValue fieldValue; | |
public HumanConsolePlayer(FieldValue fieldValue) | |
{ | |
this.fieldValue = fieldValue; | |
} | |
public Move GetNextMove(Board 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(Board board) | |
{ | |
return CheckAllPossibleMoves(board, playerFieldValue).Move; | |
} | |
private int CalculatePointsForMove(Board board, Move move) | |
{ | |
board = board.With(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(Board 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, 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 struct 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(Board board, FieldValue playerSign) | |
{ | |
var x = GetAllLineResults(board, playerSign).ToArray(); | |
if (x.Any(r => r == Result.Win)) | |
return Result.Win; | |
if (x.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; | |
break; | |
} | |
if (!areHiddenFields) | |
return Result.Draw; | |
return Result.InProgress; | |
} | |
private static IEnumerable<Result> GetAllLineResults(Board 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(Board 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(Board 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.TrueForAll(v => v == fieldValue)) | |
return Result.Win; | |
if (checkList.TrueForAll(v => v == oppositePlayerSign)) | |
return Result.Lose; | |
return Result.InProgress; | |
} | |
private static FieldValue GetOppositePlayerSign(FieldValue fieldValue) => | |
fieldValue == FieldValue.X ? FieldValue.O : 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 : byte | |
{ | |
Hidden, | |
X, | |
O | |
} | |
public enum Result : byte | |
{ | |
Win, | |
Draw, | |
Lose, | |
InProgress | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment