Created
July 2, 2016 23:37
-
-
Save afonsomatos/64432b9170b0304fbaf21919007e1c17 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.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace ConnectFour | |
{ | |
enum Tile { Empty, X, O }; | |
class Game | |
{ | |
public Tile[][] Board { get; set; } | |
private int Cols { get; } | |
private int Rows { get; } | |
public Game(int rows, int cols) | |
{ | |
// Create Board | |
this.Board = new Tile[rows] | |
.Select(_ => new Tile[cols]) | |
.ToArray(); | |
this.Cols = cols; | |
this.Rows = rows; | |
} | |
public Tile[][] GetRightDiagonals(Tile[][] board) | |
{ | |
Tile[][] diagonals = new Tile[this.Cols + this.Rows - 1][]; | |
int i = 0; | |
for (int colStart = this.Cols - 1; colStart >= 0; --colStart, ++i) | |
{ | |
Tile[] dia = new Tile[Math.Min(this.Cols - colStart, this.Rows)]; | |
for (int col = colStart, row = 0; col < this.Cols && row < this.Rows; ++col, ++row) | |
{ | |
dia[row] = board[row][col]; | |
} | |
diagonals[i] = dia; | |
} | |
// Reach left edge and start going down | |
for (int rowStart = 1; rowStart < this.Rows; ++rowStart, ++i) | |
{ | |
Tile[] dia = new Tile[this.Rows - rowStart]; | |
for (int row = rowStart, col = 0; row < this.Rows; ++row, ++col) | |
{ | |
dia[col] = board[row][col]; | |
} | |
diagonals[i] = dia; | |
} | |
return diagonals; | |
} | |
// For the sake of consistency | |
private Tile[] GetRow(int rowIndex) => this.Board[rowIndex]; | |
private Tile[] GetCol(int colIndex) | |
{ | |
Tile[] col = new Tile[this.Rows]; | |
for (int i = 0; i < col.Length; ++i) | |
col[i] = GetRow(i)[colIndex]; | |
return col; | |
} | |
public Tile? Winner() | |
{ | |
// Check horizontal repetition | |
for (int i = 0; i < this.Rows; ++i) | |
{ | |
Tile[] row = GetRow(i); | |
if (row.Repeats<Tile>(Tile.X, 4)) return Tile.X; | |
if (row.Repeats<Tile>(Tile.O, 4)) return Tile.O; | |
} | |
// Check vertical repetition | |
for (int i = 0; i < this.Cols; ++i) | |
{ | |
Tile[] col = GetCol(i); | |
if (col.Repeats<Tile>(Tile.X, 4)) return Tile.X; | |
if (col.Repeats<Tile>(Tile.O, 4)) return Tile.O; | |
} | |
// Check diagonal repetition | |
Tile[][] rightDiagonals = GetRightDiagonals(this.Board).Where(a => a.Length >= 4).ToArray(); | |
Tile[][] leftDiagonals = GetRightDiagonals(this.Board.Select(a => a.Reverse().ToArray()).ToArray()); | |
foreach (Tile[] dia in rightDiagonals) | |
{ | |
if (dia.Repeats<Tile>(Tile.X, 4)) return Tile.X; | |
if (dia.Repeats<Tile>(Tile.O, 4)) return Tile.O; | |
} | |
foreach(Tile[] dia in leftDiagonals) | |
{ | |
if (dia.Repeats<Tile>(Tile.X, 4)) return Tile.X; | |
if (dia.Repeats<Tile>(Tile.O, 4)) return Tile.O; | |
} | |
return null; | |
} | |
public void Move(Tile playerTile, int col) | |
{ | |
// Check invalid tile | |
if (playerTile == Tile.Empty) | |
throw new ArgumentException("An empty tile can't make a move."); | |
// Check if out of range | |
if (col < 1 || col > 7) | |
throw new ArgumentOutOfRangeException($"Column must be between 1 and {this.Cols}."); | |
int colIndex = col - 1; | |
// Check if column is full | |
int numberOfTiles = 0; | |
int rowIndex = this.Rows - 1; | |
while (rowIndex >= 0) | |
{ | |
if (this.Board[rowIndex][colIndex] == Tile.Empty) break; | |
rowIndex--; | |
numberOfTiles++; | |
} | |
if (numberOfTiles == this.Rows) | |
throw new ArgumentException($"Column {col} is full."); | |
// Place the tile | |
this.Board[rowIndex][colIndex] = playerTile; | |
} | |
public char ShowTile(Tile t) | |
{ | |
if (t == Tile.O) return 'O'; | |
if (t == Tile.X) return 'X'; | |
return '.'; | |
} | |
public string ShowGameGrid() | |
{ | |
string grid = ""; | |
foreach (Tile[] row in this.Board) | |
grid += string.Join(" ", row.Select(x => ShowTile(x))) + "\n"; | |
return grid; | |
} | |
} | |
} |
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.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace ConnectFour | |
{ | |
static class ExtensionMethods | |
{ | |
public static bool Repeats<T>(this IEnumerable<T> sequence, T item, int times) | |
{ | |
int timesRepeated = 0; | |
foreach (T value in sequence) | |
{ | |
if (value.Equals(item)) | |
{ | |
if (++timesRepeated == times) return true; | |
} | |
else timesRepeated = 0; | |
} | |
return false; | |
} | |
} | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Console.WriteLine("Welcome to ConnectFour!"); | |
Game game = new Game(6, 7); | |
// Choose starting player | |
Random rand = new Random(); | |
Tile player = rand.Next(0, 2) == 1 ? Tile.X : Tile.O; | |
Tile? winner = null; | |
do | |
{ | |
Console.WriteLine(game.ShowGameGrid()); | |
Console.Write($"Player {game.ShowTile(player)}, choose column: "); | |
string input = Console.ReadLine(); | |
int col = Convert.ToInt32(input); | |
try | |
{ | |
game.Move(player, col); | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(e.Message); | |
continue; | |
} | |
winner = game.Winner(); | |
// Change players' turns | |
player = player == Tile.X ? Tile.O : Tile.X; | |
} while (winner == null); | |
Console.WriteLine($"Player {game.ShowTile(winner.Value)} won!"); | |
Console.ReadKey(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment