I'm trying to implement the MinMax algorithm for four in a row (or connect4 or connect four) game.
I think I got the idea of it, it should build a tree of possible boards up to a certain depth, evaluate them and return their score, then we just take the max of those scores.
So, aiChooseCol() checks the score of every possible column by calling MinMax() and returns the column with the max score.
Now I wasn't sure, is this the right way to call MinMax()?
Is it right to check temp = Math.Max(temp, 1000);?
I still haven't made the heuristic function but this should at least recognize a winning column and choose it, but currently it just choose the first free column from the left... I can't figure out what am I doing wrong.
private int AiChooseCol()
{
int best = -1000;
int col=0;
for (int i = 0; i < m_Board.Cols; i++)
{
if (m_Board.CheckIfColHasRoom(i))
{
m_Board.FillSignInBoardAccordingToCol(i, m_Sign);
int t = MinMax(5, m_Board, board.GetOtherPlayerSign(m_Sign));
if (t > best)
{
best = t;
col = i;
}
m_Board.RemoveTopCoinFromCol(i);
}
}
return col;
}
private int MinMax(int Depth, board Board, char PlayerSign)
{
int temp=0;
if (Depth <= 0)
{
// return from heurisitic function
return temp;
}
char otherPlayerSign = board.GetOtherPlayerSign(PlayerSign);
char checkBoard = Board.CheckBoardForWin();
if (checkBoard == PlayerSign)
{
return 1000;
}
else if (checkBoard == otherPlayerSign)
{
return -1000;
}
else if (!Board.CheckIfBoardIsNotFull())
{
return 0; // tie
}
if (PlayerSign == m_Sign) // maximizing Player is myself
{
temp = -1000;
for (int i = 0; i < Board.Cols; i++)
{
if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // so we don't open another branch in a full column
{
var v = MinMax(Depth - 1, Board, otherPlayerSign);
temp = Math.Max(temp, v);
Board.RemoveTopCoinFromCol(i);
}
}
}
else
{
temp = 1000;
for (int i = 0; i < Board.Cols; i++)
{
if (Board.FillSignInBoardAccordingToCol(i, PlayerSign)) // so we don't open another branch in a full column
{
var v = MinMax(Depth - 1, Board, otherPlayerSign);
temp = Math.Min(temp, v);
Board.RemoveTopCoinFromCol(i);
}
}
}
return temp;
}
Some notes:
FillSignInBoardAccordingToCol() returns a boolean if it was successful.
The board type has a char[,] array with the actual board and signs of the players.
This code is in the AI Player class.
So I decided to write my own MinMax Connect 4. I used the depth to determine the value of a win or loss so that a move that gets you closer to winning or blocking a loss will take precedence. I also decide that I will randomly pick the move if more than one has the same heuristic. Finally I stretched out the depth to 6 as that's how many moves are required to find possible win paths from the start.
private static void Main(string[] args)
{
var board = new Board(8,7);
var random = new Random();
while (true)
{
Console.WriteLine("Pick a column 1 -8");
int move;
if (!int.TryParse(Console.ReadLine(), out move) || move < 1 || move > 8)
{
Console.WriteLine("Must enter a number 1-8.");
continue;
}
if (!board.DropCoin(1, move-1))
{
Console.WriteLine("That column is full, pick another one");
continue;
}
if (board.Winner == 1)
{
Console.WriteLine(board);
Console.WriteLine("You win!");
break;
}
if (board.IsFull)
{
Console.WriteLine(board);
Console.WriteLine("Tie!");
break;
}
var moves = new List<Tuple<int, int>>();
for (int i = 0; i < board.Columns; i++)
{
if (!board.DropCoin(2, i))
continue;
moves.Add(Tuple.Create(i, MinMax(6, board, false)));
board.RemoveTopCoin(i);
}
int maxMoveScore = moves.Max(t => t.Item2);
var bestMoves = moves.Where(t => t.Item2 == maxMoveScore).ToList();
board.DropCoin(2, bestMoves[random.Next(0,bestMoves.Count)].Item1);
Console.WriteLine(board);
if (board.Winner == 2)
{
Console.WriteLine("You lost!");
break;
}
if (board.IsFull)
{
Console.WriteLine("Tie!");
break;
}
}
Console.WriteLine("DONE");
Console.ReadKey();
}
private static int MinMax(int depth, Board board, bool maximizingPlayer)
{
if (depth <= 0)
return 0;
var winner = board.Winner;
if (winner == 2)
return depth;
if (winner == 1)
return -depth;
if (board.IsFull)
return 0;
int bestValue = maximizingPlayer ? -1 : 1;
for (int i = 0; i < board.Columns; i++)
{
if (!board.DropCoin(maximizingPlayer ? 2 : 1, i))
continue;
int v = MinMax(depth - 1, board, !maximizingPlayer);
bestValue = maximizingPlayer ? Math.Max(bestValue, v) : Math.Min(bestValue, v);
board.RemoveTopCoin(i);
}
return bestValue;
}
public class Board
{
private readonly int?[,] _board;
private int? _winner;
private bool _changed;
public Board(int cols, int rows)
{
Columns = cols;
Rows = rows;
_board = new int?[cols, rows];
}
public int Columns { get; }
public int Rows { get; }
public bool ColumnFree(int column)
{
return !_board[column, 0].HasValue;
}
public bool DropCoin(int playerId, int column)
{
int row = 0;
while (row < Rows && !_board[column,row].HasValue)
{
row++;
}
if (row == 0)
return false;
_board[column, row - 1] = playerId;
_changed = true;
return true;
}
public bool RemoveTopCoin(int column)
{
int row = 0;
while (row < Rows && !_board[column, row].HasValue)
{
row++;
}
if (row == Rows)
return false;
_board[column, row] = null;
_changed = true;
return true;
}
public int? Winner
{
get
{
if (!_changed)
return _winner;
_changed = false;
for (int i = 0; i < Columns; i++)
{
for (int j = 0; j < Rows; j++)
{
if (!_board[i, j].HasValue)
continue;
bool horizontal = i + 3 < Columns;
bool vertical = j + 3 < Rows;
if (!horizontal && !vertical)
continue;
bool forwardDiagonal = horizontal && vertical;
bool backwardDiagonal = vertical && i - 3 >= 0;
for (int k = 1; k < 4; k++)
{
horizontal = horizontal && _board[i, j] == _board[i + k, j];
vertical = vertical && _board[i, j] == _board[i, j + k];
forwardDiagonal = forwardDiagonal && _board[i, j] == _board[i + k, j + k];
backwardDiagonal = backwardDiagonal && _board[i, j] == _board[i - k, j + k];
if (!horizontal && !vertical && !forwardDiagonal && !backwardDiagonal)
break;
}
if (horizontal || vertical || forwardDiagonal || backwardDiagonal)
{
_winner = _board[i, j];
return _winner;
}
}
}
_winner = null;
return _winner;
}
}
public bool IsFull
{
get
{
for (int i = 0; i < Columns; i++)
{
if (!_board[i, 0].HasValue)
return false;
}
return true;
}
}
public override string ToString()
{
var builder = new StringBuilder();
for (int j = 0; j < Rows; j++)
{
builder.Append('|');
for (int i = 0; i < Columns; i++)
{
builder.Append(_board[i, j].HasValue ? _board[i,j].Value.ToString() : " ").Append('|');
}
builder.AppendLine();
}
return builder.ToString();
}
}
Related
I am trying to create a word search puzzle matrix, this is the code I have,
static void PlaceWords(List<string> words)
{
Random rn = new Random();
foreach (string p in words)
{
String s = p.Trim();
bool placed = false;
while (placed == false)
{
int nRow = rn.Next(0,10);
int nCol = rn.Next(0,10);
int nDirX = 0;
int nDirY = 0;
while (nDirX == 0 && nDirY == 0)
{
nDirX = rn.Next(3) - 1;
nDirY = rn.Next(3) - 1;
}
placed = PlaceWord(s.ToUpper(), nRow, nCol, nDirX, nDirY);
}
}
}
static bool PlaceWord(string s, int nRow, int nCol, int nDirX, int nDirY)
{
bool placed = false;
int LetterNb = s.Length;
int I = nRow;
int J = nCol;
if (MatriceIndice[nRow, nCol] == 0)
{
placed = true;
for (int i = 0; i < s.Length-1; i++)
{
I += nDirX;
J += nDirY;
if (I < 10 && I>0 && J < 10 && J>0)
{
if (MatriceIndice[I, J] == 0)
placed = placed && true;
else
placed = placed && false;
}
else
{
return false;
}
}
}
else
{
return false;
}
if(placed==true)
{
int placeI = nRow;
int placeJ = nCol;
for (int i = 0; i < s.Length - 1; i++)
{
placeI += nDirX;
placeJ += nDirY;
MatriceIndice[placeI,placeJ] = 1;
MatriceChars[placeJ, placeJ] = s[i];
}
}
return placed;
}
However it seems like it is an infinite loop. I am trying to add the code in a 1010 char matrix linked to a 1010 int matrix initially filled with 0 where I change the cases to 1 if the word is added to the matrix. How should I fix the code?
There are several errors. First,
MatriceChars[placeJ, placeJ] = s[i];
should be
MatriceChars[placeI, placeJ] = s[i];
Second,
for (int i = 0; i < s.Length - 1; i++)
(two occurrences) should be
for (int i = 0; i < s.Length; i++)
(You do want all the letters in the words, right?)
Third, when testing indices, you should use I >= 0, not I > 0, as the matrix indices start at 0.
However, the main logic of the code seems to work, but if you try to place too many words, you will indeed enter an infinite loop, since it just keeps trying and failing to place words that can never fit.
I am trying to make a Sudoku solver. Whenever I try to run it it gives me a Stack Overflow Error:
System.StackOverflowException: 'Exception of type 'System.StackOverflowException' was thrown.'
I believe that it probably has to do with the solve function calling on the FindEmpy function too much?
Any help is greatly appreciated!
Thanks so much!
using System;
namespace sudokusolver
{
class Program
{
static public void PrintBoard(int[][] bo)
{
for (int i = 0; i < bo.Length; i++)
{
if (i % 3 == 0 && i != 0)
{
Console.WriteLine("- - - - - - - - - - - - -");
}
for (int j = 0; j < bo[0].Length; j++)
{
if (j % 3 == 0 && j != 0)
{
Console.Write(" | ");
}
if (j == 8)
{
Console.WriteLine(bo[i][j]);
}
else
{
Console.Write(bo[i][j] + " ");
}
}
}
}
static public (int,int) FindEmpty(int[][] bo)
{
for (int i = 0; i < bo.Length; i++)
{
for (int j = 0; j < bo[0].Length; j++)
{
if (bo[i][j] == 0)
{
return (i, j);
}
}
}
return (100, 100);
}
static public bool Solve(int[][] bo)
{
int x;
int y;
if (FindEmpty(bo) == (100, 100))
{
return true;
}
else
{
y = FindEmpty(bo).Item1;
x = FindEmpty(bo).Item2;
}
for (int i = 0; i < 10; i++)
{
if (IsValid(bo, i, x, y) == true)
{
bo[y][x] = i;
if (Solve(bo) == true)
{
return true;
}
else
{
bo[y][x] = 0;
}
}
}
return false;
}
static public bool IsValid(int[][] bo, int num, int x, int y)
{
for (int i = 0; i < bo.Length; i++)
{
if (bo[y][i] == num && x != i)
{
return false;
}
}
for (int i = 0; i < bo[0].Length; i++)
{
if (bo[i][x] == num && y != i)
{
return false;
}
}
int boxx = x / 3;
int boxy = y / 3;
for (int i = boxy * 3; i < boxy * 3 + 3; i++)
{
for (int j = boxx * 3; j < boxx * 3 + 3; j++)
{
if (bo[i][j] == num && i != y && j != x)
{
return false;
}
}
}
return true;
}
static void Main(string[] args)
{
int[][] board = {
new int[] {7,0,0,0,0,0,2,0,0},
new int[] {4,0,2,0,0,0,0,0,3},
new int[] {0,0,0,2,0,1,0,0,0},
new int[] {3,0,0,1,8,0,0,9,7},
new int[] {0,0,9,0,7,0,6,0,0},
new int[] {6,5,0,0,3,2,0,0,1},
new int[] {0,0,0,4,0,9,0,0,0},
new int[] {5,0,0,0,0,0,1,0,6},
new int[] {0,0,6,0,0,0,0,0,8}
};
PrintBoard(board);
Solve(board);
PrintBoard(board);
}
}
}
static public bool Solve(int[][] bo)
{
int x;
int y;
if (FindEmpty(bo) == (100, 100))
{
return true;
}
else
{
y = FindEmpty(bo).Item1;
x = FindEmpty(bo).Item2;
}
- for (int i = 0; i < 10; i++)
+ for (int i = 1; i < 10; i++)
{
if (IsValid(bo, i, x, y) == true)
{
bo[y][x] = i;
if (Solve(bo) == true)
{
return true;
}
else
{
bo[y][x] = 0;
}
}
}
return false;
}
At some point IsValid(bo, 0, x, y) returns true, so you replace the zero with another zero forever.
here will be my code for famous Knights Tour for 8x8 deck. So, the main idea of my code is: we will choose from Turns our destination, check it with isPossible and then go recursevly to it, marked this cell to '1'. So, check every cell, and if we will be in 64's cell - return true.
But my code goes to infinite recurssion, and I can't debug it, any recommendation will be greatly appreciated.
class Class1
{
static void Main(string[] args)
{
int x = 0;
int y = 0;
Console.WriteLine("Enter X and press enter");
x = Int32.Parse(Console.ReadLine());
Console.WriteLine("Enter Y and press enter");
y = Int32.Parse(Console.ReadLine());
TurnVariation Turns = new TurnVariation();
EmptyBoard Board = new EmptyBoard();
if (TryPut.Put(Board, x, y, Turns, 1, false))
{
Console.WriteLine("МОЖНА!!!!");
}
else
{
Console.WriteLine("NET!!");
}
}
}
public class TryPut : EmptyBoard
{
public static bool Put(EmptyBoard Board, int x, int y, TurnVariation Turns, int count, bool flag)
{
int tempX = 0;
int tempY = 0;
if (count >= 64)
{
Console.WriteLine("yeab");
return true;
}
for (int i = 0; i <= 7; i++)
{
tempX = x + Turns.Turns[i,0];
tempY = y + Turns.Turns[i,1];
//Console.WriteLine(count);
if (IsPossible(Board, tempX, tempY))
{
Board.Array[tempX, tempY] = 1;
flag = Put(Board, tempX, tempY, Turns, count+1, flag);
if (flag)
{
break;
}
Board.Array[tempX, tempY] = 0;
}
}
if (flag)
return true;
else
return false;
}
public static bool IsPossible(EmptyBoard Board, int x, int y)
{
if ((x < 0) || (x > 7) || (y < 0) || (y > 7))
return false;
if (Board.Array[x, y] == 1)
return false;
return true;
}
}
public class TurnVariation
{
public int[,] Turns = new int[8, 2];
public TurnVariation()
{
Turns[0, 0] = -2; Turns[0, 1] = 1;
Turns[1,0] = -2; Turns[1,1] = -1;
Turns[2,0] = -1; Turns[2,1] = 2;
Turns[3,0] = 1; Turns[3,1] = 2;
Turns[4,0] = 2; Turns[4,1] = 1;
Turns[5,0] = 2; Turns[5,1] = -1;
Turns[6,0] = 1; Turns[6,1] = -2;
Turns[7,0] = -1; Turns[7,1] = -2;
}
}
public class EmptyBoard
{
public const int N = 8;
public int[,] Array = new int[N, N];
public EmptyBoard()
{
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
Array[i, j] = 0;
}
}
I think your problem is that your testing for count<64, but you never assign to count. You just pass (by value!) 'Count + 1' to the put method. You are probably thinking that this will bewritten back to the count variable. But that is not the case...
Do note that debugging is the first skill you need to learn!
Good day to all,
I've been working on a C# Sudoku Solver application but I sincerely underestimated the difficulty of the algorithms to solve a sudoku. I've been searching the web for possible algorithms that I could implement, but I've had no luck finding an easy algorithm that I can get my head around.
I found an algorithm that may work in my application, however this person is using a single-dimensional array to work it out.
I've tried to change it to make it work with a multidimensional array, but I can't get it to work properly.
Could anybody give me advice or show me how I could change the code so that it works with a multidimensional array(int[,])? I just can't seem to find it on my own. The code is found here: http://blah.winsmarts.com/2007-1-sudoku_solver_in_c-.aspx
If you have another algorithm that would work with an int[,], that's also wonderful of course.
Help would be greatly appreciated since I've been searching for a long time.
Thanks in advance!
The code you linked already logically uses a 2D array, it just uses a 1D array as a backing. Change this:
private int[] vals = new int[81];
public int this[int row, int column]
{
get { return vals[FindIndex(row, column)]; }
set
{
vals[FindIndex(row, column)] = value;
}
}
private int FindIndex(int row, int column)
{
return (((column - 1) * 9) + row - 1);
}
To:
private int[,] vals = new int[9,9];
public int this[int row, int column]
{
get { return vals[row - 1, column - 1]; }
set
{
vals[row - 1, column - 1] = value;
}
}
(The - 1's are necessary with the rest of the current code because row and column start at 1 instead of 0.)
Recursive backtracking , brute force, algorithm.
public class SudokoSolver
{
private readonly Grid _grid;
public SudokoSolver(Grid grid)
{
_grid = grid;
_grid.Validate();
}
public int?[,] SolvePuzzle()
{
Solve();
Console.WriteLine(_grid.Assigns + " tries total.");
return _grid.Data;
}
private bool Solve()
{
int row, col;
if (!_grid.FindUnassignedLoc(out row, out col))
{
return true;
}
for (int num = 1; num <= 9; num++)
{
if (_grid.NoConflicts(row, col, num))
{
_grid.Assign(row, col, num);
if (Solve())
{
return true;
}
_grid.Unassign(row, col);
}
}
return false;
}
public int?[,] Data
{
get { return _grid.Data; }
}
}
public class Grid
{
public int?[,] Data { get; private set; }
private int _curC = 0;
private int _curR = 0;
private int _assigns = 0;
public Grid(int?[,] data)
{
Data = data ?? new int?[9,9];
}
public bool FindUnassignedLoc(out int row, out int col)
{
while (Data[_curR, _curC].HasValue)
{
_curC++;
if (_curC == 9)
{
_curR++;
_curC = 0;
}
if (_curR == 9)
{
row = -1;
col = -1;
return false;
}
}
row = _curR;
col = _curC;
return true;
}
public bool NoConflicts(int row, int col, int num)
{
for (int r = 0; r < 9; ++r)
{
if (Data[r, col] == num)
{
return false;
}
}
for (int c = 0; c < 9; c++)
{
if (Data[row, c] == num)
{
return false;
}
}
int fromC = 3 * (col/3);
int fromR = 3 * (row / 3);
for (int c = fromC; c < fromC + 3; c++)
{
for (int r = fromR; r < fromR + 3; r++)
{
if (Data[r, c] == num)
{
return false;
}
}
}
return true;
}
public void Assign(int row, int col, int num)
{
_assigns++;
Data[row, col] = num;
}
public void Unassign(int row, int col)
{
Data[row, col] = null;
_curC = col;
_curR = row;
}
public int Assigns
{
get { return _assigns; }
}
public void Validate()
{
if (Data.Length != 81)
{
throw new Exception("Invalid dimentions!");
}
if (!IsLegal())
{
throw new Exception("Illigal numbers populated!");
}
}
public bool IsLegal()
{
var container = new HashSet<int>();
//vertical check
for (int c = 0; c < 9; ++c)
{
container.Clear();
for (int r = 0; r < 9; ++r)
{
if (Data[r, c].HasValue)
{
if (container.Contains(Data[r, c].Value))
{
return false;
}
container.Add(Data[r, c].Value);
}
}
}
// horizontal check
for (int r = 0; r < 9; ++r)
{
container.Clear();
for (int c = 0; c < 9; ++c)
{
if (Data[r, c].HasValue)
{
if (container.Contains(Data[r, c].Value))
{
return false;
}
container.Add(Data[r, c].Value);
}
}
}
// square check
var topLeftCorners = new List<Tuple<int, int>>
{
new Tuple<int, int>(0,0),
new Tuple<int, int>(0,3),
new Tuple<int, int>(0,6),
new Tuple<int, int>(3,0),
new Tuple<int, int>(3,3),
new Tuple<int, int>(3,6),
new Tuple<int, int>(6,0),
new Tuple<int, int>(6,3),
new Tuple<int, int>(6,6)
};
foreach (var topLeftCorner in topLeftCorners)
{
int fromC = topLeftCorner.Item2;
int fromR = topLeftCorner.Item1;
container.Clear();
for (int c = fromC; c < fromC + 3; c++)
{
for (int r = fromR; r < fromR + 3; r++)
{
if (Data[r, c].HasValue)
{
if (container.Contains(Data[r, c].Value))
{
return false;
}
container.Add(Data[r, c].Value);
}
}
}
}
return true;
}
}
I want to create an insertionsort but I can't get any further.
I solved it on my way until I get out of bounds, in the second "for-command" which I can't solve. I'm not sure if I am on the right way, but I want to keep the solution simple.
int arrayzähler = 0;
int[] Speicherarray = new int[randomarray.Length];
//ausgabearray[1] = randomarray[1]; //für vergleich
foreach (int wert in randomarray)
{
if (wert == randomarray[0])
{
Speicherarray[0] = wert;
ausgabearray[0] = wert;
arrayzähler++;
continue; // erster wert = ausgabearray[0]
}
arrayzähler++;
for (int i = arrayzähler - 1; i >= arrayzähler - 1; i--)
{
for (int a = arrayzähler - 2; a >= arrayzähler - 2; a--)
{
if (Speicherarray[i] < Speicherarray[a])
{
Speicherarray[a] = Speicherarray[a + 1];
}
else if (Speicherarray[i] >= Speicherarray[a])
{
Speicherarray[a] = wert;
ausgabearray[i] = Speicherarray[i];
}
}
}
}
This is exact coding for insertion sort,
public void Sort(int[] collection)
{
int inner, temp;
for (int i = 1; i < collection.Length; i++)
{
temp = collection[i];
inner = i;
while (inner > 0 && collection[inner - 1] >= temp)
{
collection[i] = collection[inner - 1];
--inner;
}
collection[inner] = temp;
}
Console.WriteLine("Printing Insertion Sorted Items");
Print();
}