Check if won in Tic Tac Toe 2D array c# - c#

Console App C#
I made a 2D array to represent a grid for the game. I cant find a way to check if the player has won.
I tried many things and searched online and I cant find it
I tried to loop thru the grid and check each one of the elements inside that row/column
I would love to get the explanation of why its not working and how to do it instead of the code answer.
public static void Win_check()
{
for (int i = 0; i < 3; i++)
{
if (grid[1, i] == 'x' && grid[1, i] == 'x' && grid[1, i] == 'x')
{
gameStatus = 'x';
}
if (grid[2, i] == 'o' && grid[2, i] == 'o' && grid[2, i] == 'o')
{
gameStatus = 'o';
}
if (grid[3, i] == 'o' && grid[3, i] == 'o' && grid[3, i] == 'o')
{
gameStatus = 'o';
}
if (grid[i, 1] == 'x' && grid[i, 1] == 'x' && grid[i, 1] == 'x')
{
gameStatus = 'x';
}
if (grid[i, 2] == 'o' && grid[i, 2] == 'o' && grid[i, 2] == 'o')
{
gameStatus = 'o';
}
if (grid[i, 3] == 'o' && grid[i, 3] == 'o' && grid[i, 3] == 'o')
{
gameStatus = 'o';
}
}
}

I suggest enumerating all the lines which bring win: 3 horizontal, 3 vertical and finally 2 diagonals.
private static IEnumerable<char[]> Lines(char[,] field) {
char[] result;
int size = Math.Min(field.GetLength(0), field.GetLength(1));
for (int r = 0; r < size; ++r) {
result = new char[size];
for (int c = 0; c < size; ++c)
result[c] = field[r, c];
yield return result;
}
for (int c = 0; c < size; ++c) {
result = new char[size];
for (int r = 0; r < size; ++r)
result[r] = field[r, c];
yield return result;
}
result = new char[size];
for (int d = 0; d < size; ++d)
result[d] = field[d, d];
yield return result;
result = new char[size];
for (int d = 0; d < size; ++d)
result[d] = field[d, size - d - 1];
yield return result;
}
Then you can easily query these lines with a help of Linq:
using System.Linq;
...
private static bool IsWon(char[,] field, char who) {
return Lines(field).Any(line => line.All(item => item == who));
}
For instance:
char[,] field = new char[,] {
{ 'x', 'o', 'o'},
{ 'x', 'o', ' '},
{ 'o', 'x', 'x'},
};
// Is field won by x?
Console.WriteLine(IsWon(field, 'x') ? "Y" : "N");
// Is field won by o?
Console.WriteLine(IsWon(field, 'o') ? "Y" : "N");

Related

Minimax Algorithm for tic tac toe in C#

I am trying to make the tic tac toe game in C# Windows Forms using the minimax algorithm as the opponent. So instead of making the best move, like preventing the player from winning or playing a move that allows it to get closer to winning, it moves to the following empty index on the board. So how could I fix the algorithm?
int evaluate(int[] grid)
{
for(int i = 0; i < 9; i += 3)
{
if (grid[i] == 0) continue;
if (grid[i] == grid[i + 1] && grid [i] == grid[i + 2])
return grid[i]==1 ? 10 :-10;
}
for (int i = 0; i < 3; i++)
{
if (grid[i] == 0) continue;
if (grid[i] == grid[i + 3] && grid[i] == grid[i + 6])
return grid[i] == 1 ? 10 : -10;
}
if ((grid[0] == grid[4] && grid[4] == grid[8]) || (grid[2] == grid[4] && grid[4] == grid[6]))
return grid[4] == 1 ? 10 : -10;
return 0;
}
bool isMovesLeft(int[] grid)
{
for(int i = 0; i< grid.Length; i++)
{
if (grid[i] == 0) return true;
}
return false;
}
int miniMax(int[] grid,int depth, bool isMax)
{
int isWon = evaluate(grid);
if (isWon == 10 || isWon == -10)
return isWon;
if(!isMovesLeft(grid))
return 0;
if (isMax)
{
int best = int.MinValue;
for(int i = 0;i< grid.Length;i++)
{
if (grid[i] == 0)
{
grid[i] = 1;
best = Math.Max(best,miniMax(grid, depth+1, !isMax));
grid[i] = 0;
}
}
return best;
}
else
{
int best = int.MaxValue;
for (int i = 0; i < grid.Length; i++)
{
if (grid[i] == 0)
{
grid[i] = 2;
best = Math.Min(best, miniMax(grid, depth+1, !isMax));
grid[i] = 0;
}
}
return best;
}
}
void moveByAI()
{
int best = int.MinValue;
int move = -1;
for(int i =0; i<9;i++)
{
if (grids[i]==0)
{
grids[i] = 2;
int locValue = miniMax(grids, 0, true);
grids[i] = 0;
if(locValue > best)
{
move = i;
best = locValue;
MessageBox.Show(""+i);
}
}
}
buttons[move].PerformClick();
}

How to make sure that players of tic tac toe game don't fill an already filled board in C#

I am trying to make a Tic Tac Toe game all on my own, I made everything including
Who won and if there is a draw or not, The problem is I failed to find a way to not make
Any players overwrite a specific position in a 2D array
In the main program I used a loop to fill the board from each player and as I said
I tried hard to find a way to not make a player accidentally fill an already filled board
please help and thank you
Here is the code in OOP in a console application
class Player
{
public int player1 { get; set; }
public int player2 { get; set; }
public int GetPlayer1(Board board)
{
while (true)
{
Console.Write("Player 1,chose from the above numbers to Print X on the specific position: ");
player1 = int.Parse(Console.ReadLine());
if (player1 < 0 || player1 > 9)
{
Console.WriteLine("Try again and enter a valid number");
}
else
{
break;
}
}
return player1;
}
public int GetPlayer2(Board board)
{
while (true)
{
Console.Write("Player 2,chose from the above numbers to Print O on the specific position: ");
player2 = int.Parse(Console.ReadLine());
if (player2 < 0 || player2 > 9)
{
Console.WriteLine("Try again and enter a valid number");
}
else
{
break;
}
}
return player2;
}
class Board
{
public Player players = new Player();
public readonly char[,] gamee = new char[3, 3];
public char GetStateOfFirstPlayer(Board board)
{
players.GetPlayer1(board);
return players.player1 switch
{
1 => gamee[0, 0] = 'X',
2 => gamee[0, 1] = 'X',
3 => gamee[0, 2] = 'X',
4 => gamee[1, 0] = 'X',
5 => gamee[1, 1] = 'X',
6 => gamee[1, 2] = 'X',
7 => gamee[2, 0] = 'X',
8 => gamee[2, 1] = 'X',
9 => gamee[2, 2] = 'X',
_ => '0',
};
}
public char GetStateOfSecondPlayer(Board board)
{
players.GetPlayer2(board);
return players.player2 switch
{
1 => gamee[0, 0] = 'O',
2 => gamee[0, 1] = 'O',
3 => gamee[0, 2] = 'O',
4 => gamee[1, 0] = 'O',
5 => gamee[1, 1] = 'O',
6 => gamee[1, 2] = 'O',
7 => gamee[2, 0] = 'O',
8 => gamee[2, 1] = 'O',
9 => gamee[2, 2] = 'O',
_ => '0',
};
}
public void PrintBoard()
{
Console.WriteLine($" {gamee[0, 0]} |{gamee[0, 1]} |{gamee[0, 2]}");
Console.WriteLine($" {gamee[1, 0]} |{gamee[1, 1]} |{gamee[1, 2]}");
Console.WriteLine($" {gamee[2, 0]} |{gamee[2, 1]} |{gamee[2, 2]}");
}
static public void BoardNumbers()
{
Console.WriteLine($" (1) |(2) |(3)");
Console.WriteLine($" (4) |(5) |(6)");
Console.WriteLine($" (7) |(8) |(9)");
}
}
class WinCondition
{
static public bool IsFirstPlayerWon(Board board)
{
for (var x = 0; x < board.gamee.GetLength(0); x++)
{
for (var y = 0; y < board.gamee.GetLength(1); y++)
{
if (board.gamee[x, 0] == 'X' && board.gamee[x, 1] == 'X' && board.gamee[x, 2] == 'X') return true;
if (board.gamee[0, y] == 'X' && board.gamee[1, y] == 'X' && board.gamee[2, y] == 'X') return true;
if (board.gamee[0, 0] == 'X' && board.gamee[1, 1] == 'X' && board.gamee[2, 2] == 'X') return true;
if (board.gamee[0, 2] == 'X' && board.gamee[1, 1] == 'X' && board.gamee[2, 0] == 'X') return true;
}
}
return false;
}
static public bool IsSecondPlayerWon(Board board)
{
for (var x = 0; x < board.gamee.GetLength(0); x++)
{
for (var y = 0; y < board.gamee.GetLength(1); y++)
{
if (board.gamee[x, 0] == 'O' && board.gamee[x, 1] == 'O' && board.gamee[x, 2] == 'O') return true;
if (board.gamee[0, y] == 'O' && board.gamee[1, y] == 'O' && board.gamee[2, y] == 'O') return true;
if (board.gamee[0, 0] == 'O' && board.gamee[1, 1] == 'O' && board.gamee[2, 2] == 'O') return true;
if (board.gamee[0, 2] == 'O' && board.gamee[1, 1] == 'O' && board.gamee[2, 0] == 'O') return true;
}
}
return false;
}
static public bool IsDraw(Board board)
{
for (var x = 0; x < 3; x++)
{
for (var y = 0; y < 3; y++)
{
if (board.gamee[x, y] == '\0') return false;// default value of char
}
}
return true;
}
static void Main(string[] args)
{
Board board = new Board();
Board.BoardNumbers();
Player player = new Player();
for(var x=1;x<=6;x++)
{
if(WinCondition.IsDraw(board)==true)
{
Console.WriteLine("Draw");
}
board.GetStateOfFirstPlayer(board);
board.PrintBoard();
if(WinCondition.IsFirstPlayerWon(board)==true)
{
Console.WriteLine("First Player Won");
}
board.GetStateOfSecondPlayer(board);
board.PrintBoard();
if (WinCondition.IsSecondPlayerWon(board) == true)
{
Console.WriteLine("Second Player Won");
}
}
Nice start, programming is difficult, but you came very far.
I created an Index class for you, it basically allows to calculate the array indices for your gamee array based on the number the player enters. If the player enters 3, you get x = 2, y=0. Therefore the switch satement is no longer required, and it's easier to check if the board is free at the position.
The new workflow now is like this:
Get a number from the player
Get the array indices for the number the player entered
Check that the board is empty at that point. If not, go to step 1
If it is empty, place the player there
Here's a new Index class which helps with the calculation
class Index
{
public int x;
public int y;
public Index(int x, int y)
{
this.x = x;
this.y = y;
}
public static Index GetIndexFromNumber(int number)
{
// remove one from the number, required for the following calculation
int zeroBasedNumber = number - 1;
// Use modulo board width to get the remainder, this is the x value
int x = zeroBasedNumber % 3;
// Use division board height to get the row, this is the y value
int y = zeroBasedNumber / 3;
return new Index(x, y);
}
}
Here's the new GetStateOfPlayer method, it can be used for both players
public void GetStateOfPlayer(Board board, char playerChar)
{
Index index;
do
{
players.GetPlayer1(board);
index = Index.GetIndexFromNumber(players.player1);
Console.WriteLine();
if (gamee[index.y, index.x] != '\0')
{
Console.WriteLine("You can't place your mark there");
}
// Repeat until the field is actually empty
} while (gamee[index.y, index.x] != '\0');
// Place it when it's clear
gamee[index.y, index.x] = playerChar;
}
The main method now looks like this:
static void Main(string[] args)
{
Board board = new Board();
Board.BoardNumbers();
Player player = new Player();
for (var x = 1; x <= 6; x++)
{
if (IsDraw(board) == true)
{
Console.WriteLine("Draw");
}
board.GetStateOfPlayer(board, 'X');
board.PrintBoard();
if (IsFirstPlayerWon(board) == true)
{
Console.WriteLine("First Player Won");
}
board.GetStateOfPlayer(board, 'O');
board.PrintBoard();
if (IsSecondPlayerWon(board) == true)
{
Console.WriteLine("Second Player Won");
}
}
}

C# Tic Tac Toe/ Font and Array

In Tic Tac Toe, Program works for horizontal, and vertical, but it does not work for diagonal.
I think that private bool checkWin part is wrong.
private bool checkWin()
{
for (int row=0; row<3; row++)
{
if (values[row,0] != ' ' && values[row,0]==values[row,1]&&values[row,0]==values[row, 2])
{
lockButton(false);
return true;
}
}
for (int col = 0; col < 3; col++)
{
if (values[0, col] != ' ' && values[0, col] == values[1, col] && values[0, col] == values[2, col])
{
lockButton(false);//asdfasdfsdafadsfasdfasdfasdf
return true;
}
}
return false;
}
Maybe you can try to add something like this betwen the second for and the return false:
if (values[0, 0] != ' ' && values[0, 0] == values[1, 1] && values[0, 0] == values[2, 2])
{
lockButton(false);
return true;
}else if(values[0, 2] != ' ' && values[0, 2] == values[1, 1] && values[0, 2] == values[2, 0])
{
lockButton(false);
return true;
}
The fist one is for diagonal for 0,0 to 2,2 and the second one for 0,2 to 2,0

Implementing minimax algorithm into tic-tac-toe using c#

I am trying to implement the minimax algorithm into a tic-tac-toe game, where the computer will play the optimal move based on the minimax algorithm. I have written the whole algorithm but get weird and seemingly random moves by the AI. For example, when the tic-tac-toe board
is laid out like shown:
' ' 'o' 'x'
' ' 'o' ' '
'x' ' ' ' '
The Minimax algorithm then chooses the first spot, which is obviously not the optimal choice:
'o' 'o' 'x'
' ' 'o' ' '
'x' ' ' ' '
Here is my code:
namespace MinimaxAlg
{
class Program
{
static void Main(string[] args)
{
char[,] board = { { '-', 'o', 'x'},
{ '-', 'o', '-'},
{ 'x', '-', '-'} };
int bestScore = int.MinValue;
int moveI = -1;
int moveJ = -1;
for (var i = 0; i < 3; i++)
{
for (var j = 0; j < 3; j++)
{
if (board[i, j] == '-')
{
board[i, j] = 'o';
var score = Minimax(board, 'o');
board[i, j] = '-';
Console.WriteLine("Score: " + score);
Console.WriteLine("BestScore: " + bestScore);
if (score > bestScore)
{
bestScore = score;
moveI = i;
moveJ = j;
}
}
}
}
board[moveI, moveJ] = 'p';
foreach (var i in board)
{
Console.WriteLine(i);
}
}
static int Minimax(char[,] board, char forWho)
{
if (CheckWhoWins(board, forWho))
{
return 1;
}
if (forWho == 'o')
{
var bestScore = int.MinValue;
for (var i = 0; i < 3; i++)
{
for (var j = 0; j < 3; j++)
{
if (board[i, j] == '-')
{
board[i, j] = forWho;
var score = Minimax(board, 'x');
board[i, j] = '-';
bestScore = Math.Max(bestScore, score);
}
}
}
return bestScore;
}
else
{
var bestScore = int.MaxValue;
for (var i = 0; i < 3; i++)
{
for (var j = 0; j < 3; j++)
{
if (board[i, j] == '-')
{
board[i, j] = forWho;
var score = Minimax(board, 'o');
board[i, j] = '-';
bestScore = Math.Min(bestScore, score);
}
}
}
return bestScore;
}
}
static bool CheckWhoWins(char[,] board, char forWho)
{
if ((board[0, 0] == forWho && board[0, 1] == forWho && board[0, 2] == forWho) || (board[1, 0] == forWho && board[1, 1] == forWho && board[1, 2] == forWho) ||
(board[2, 0] == forWho && board[2, 1] == forWho && board[2, 2] == forWho) || (board[0, 0] == forWho && board[1, 0] == forWho && board[2, 0] == forWho) ||
(board[0, 1] == forWho && board[1, 1] == forWho && board[2, 1] == forWho) || (board[0, 2] == forWho && board[1, 2] == forWho && board[2, 2] == forWho) ||
(board[0, 0] == forWho && board[1, 1] == forWho && board[0, 0] == forWho) || (board[0, 2] == forWho && board[1, 1] == forWho && board[2, 0] == forWho))
return true;
else
return false;
}
}
}
If anyone has any idea of what might be wrong, the help would be greatly appreciated.
You have three issues in your code.
First up, a simple typo. One of your conditions for the win is board[0, 0] == forWho && board[1, 1] == forWho && board[0, 0] == forWho. Clearly the last one should be board[2, 2].
Secondly, you're giving o two goes straight away. These lines show it:
board[i, j] = 'o';
var score = Minimax(board, 'o');
The second line should be var score = Minimax(board, 'x');.
Finally, user3386109 nailed it. You're giving a long game the same score as a short game.
If we change the scoring system to give bigger scores for the least moves then it works just fine.
static int CheckWhoWins(char[,] board, char forWho)
{
if ((board[0, 0] == forWho && board[0, 1] == forWho && board[0, 2] == forWho)
|| (board[1, 0] == forWho && board[1, 1] == forWho && board[1, 2] == forWho)
|| (board[2, 0] == forWho && board[2, 1] == forWho && board[2, 2] == forWho)
|| (board[0, 0] == forWho && board[1, 0] == forWho && board[2, 0] == forWho)
|| (board[0, 1] == forWho && board[1, 1] == forWho && board[2, 1] == forWho)
|| (board[0, 2] == forWho && board[1, 2] == forWho && board[2, 2] == forWho)
|| (board[0, 0] == forWho && board[1, 1] == forWho && board[2, 2] == forWho)
|| (board[0, 2] == forWho && board[1, 1] == forWho && board[2, 0] == forWho))
{
var score = 1;
for (var i = 0; i < 3; i++)
{
for (var j = 0; j < 3; j++)
{
if (board[i, j] == '-')
{
score++;
}
}
}
return score;
}
else
return 0;
}
Minimax (which should be just MinMax) is now:
static int Minimax(char[,] board, char forWho)
{
var score = CheckWhoWins(board, forWho);
if (score != 0)
{
return score;
}
if (forWho == 'o')
{
var bestScore = int.MinValue;
for (var i = 0; i < 3; i++)
{
for (var j = 0; j < 3; j++)
{
if (board[i, j] == '-')
{
board[i, j] = forWho;
var currentScore = Minimax(board, 'x');
board[i, j] = '-';
bestScore = Math.Max(bestScore, currentScore);
}
}
}
return bestScore;
}
else
{
var bestScore = int.MaxValue;
for (var i = 0; i < 3; i++)
{
for (var j = 0; j < 3; j++)
{
if (board[i, j] == '-')
{
board[i, j] = forWho;
var currentScore = Minimax(board, 'o');
board[i, j] = '-';
bestScore = Math.Min(bestScore, currentScore);
}
}
}
return bestScore;
}
}
When you run this you get the following result:
And for bonus points, in my opinion, here's a slightly better version of Minimax:
static int Minimax(char[,] board, char forWho)
{
var score = CheckWhoWins(board, forWho);
if (score != 0)
{
return score;
}
var bestScore = forWho == 'o' ? int.MinValue : int.MaxValue;
int CalcBest(int x, int y) => (forWho == 'o' ? x > y : y > x) ? x : y;
for (var i = 0; i < 3; i++)
{
for (var j = 0; j < 3; j++)
{
if (board[i, j] == '-')
{
board[i, j] = forWho;
var currentScore = Minimax(board, forWho == 'o' ? 'x' : 'o');
board[i, j] = '-';
bestScore = CalcBest(bestScore, currentScore);
}
}
}
return bestScore;
}

How to send copy of array in c# [duplicate]

This question already has answers here:
C# Copy Array by Value
(8 answers)
Closed 4 years ago.
So, I'm trying to make simple Console Tic Tac Toe Game, with AI.
Current board state is stored inside multi-dimensional array.
To check what the best move for AI is, im using recursion.
The function "CheckMove" changes board position, and then calls itself to see where it goes.
But the problem is that if i change board state inside called function, it will also change inside a caller.
How to avoid that?
Simplified code:
static void Main()
{
int[] board = { 1 };
CheckMove(board);
//board = 2
}
static void CheckMove(int[] board)
{
board[0] = 2;
}
full code (WIP):
enum Sym
{
E, X, O
}
class Program
{
static void Main(string[] args)
{
Sym[,] board = new Sym[3,3];
int x, y;
while (End(board) == 2)
{
Display(board);
Console.WriteLine("Make your move - column: ");
x = Convert.ToInt32(Console.ReadLine())-1;
Console.WriteLine("Make your move - row: ");
y = Convert.ToInt32(Console.ReadLine())-1;
board[y, x] = Sym.X;
int[,] chances = new int[3, 3];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
chances[i, j] = CheckMove(board, i, j, true);
}
}
Display(board);
Console.WriteLine("GG");
Console.WriteLine("____________________________________________________________");
Console.ReadLine();
}
static void Display(Sym[,] board) //Displays whole board
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine($"{board[i,0]} {board[i,1]} {board[i,2]}");
}
}
static int End(Sym[,] board) //Chcecks if the game shall end (-1 player won) (0 tie) (1 cpu won) (2 game in progress)
{
bool Full = true;
for (int i = 0; i < 3; i++)
{ //This part is currently broken
if (board[i, 0] == Sym.E || board[i, 1] == Sym.E || board[i, 2] == Sym.E) Full = false;
if (board[i, 0] == board[i, 1] && board[i, 1] == board[i, 2] && board[i, 0] == Sym.X) return -1;
if (board[0, i] == board[1, i] && board[1, i] == board[2, i] && board[0, i] == Sym.X) return -1;
if (board[i, 0] == board[i, 1] && board[i, 1] == board[i, 2] && board[i, 0] == Sym.O) return 1;
if (board[0, i] == board[1, i] && board[1, i] == board[2, i] && board[0, i] == Sym.O) return 1;
}
if (board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2] && board[0, 0] == Sym.X) return -1;
if (board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2] && board[0, 0] == Sym.O) return 1;
if (board[2, 0] == board[1, 1] && board[1, 1] == board[0, 2] && board[2, 0] == Sym.X) return -1;
if (board[2, 0] == board[1, 1] && board[1, 1] == board[0, 2] && board[2, 0] == Sym.O) return 1;
if (Full == true) return 0;
return 2;
}
static int CheckMove(Sym[,] board, int a, int b, bool cpuTurn) //Check how good subjected move is
{
if (board[a, b] == Sym.E)
if (cpuTurn == true) board[a, b] = Sym.O;
else board[a, b] = Sym.X;
else return 0;
if (End(board) != 2) return End(board);
int Value = 0;
for (int m = 0; m < 3; m++)
for(int n = 0; n < 3; n++)
{
Value += CheckMove(board, m, n, !cpuTurn);
}
return Value;
}
}
As far as I can see, you have problems with 2D array Sym[,] in the
static int CheckMove(Sym[,] board, int a, int b, bool cpuTurn)
method; since board is 2D array, typical solutions like board.ToArray() don't work (they don't even compile). Try Clone() the board instance:
// Let's rename board into value...
static int CheckMove(Sym[,] value, int a, int b, bool cpuTurn) {
// ... in order to preserve all the other code:
// we are now working with the copy of the passed board
Sym[,] board = value.Clone() as Sym[,];
// Your code from CheckMove here
...
}
Since Sym is a enum (i.e. a value type), shallow copy is enough

Categories

Resources