Connect 4 check diagonally [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Hallo I can see this question been asked before. However I do not understand or can see how I can implment it myself. If its possible could you explain what you are doing and maybe add some kind of pseudo code so I can see the flow of the script.
I've done the vertical and horizontal lines like this and yes PlayingBoard is a 2d Array:
private void HasWon(int xPlaced, int yPlaced) {
//MessageBox.Show(xPlaced.ToString()+","+yPlaced.ToString());
int[] Coords = new int[2];
/// <summary>
/// This part checks if we have a win on East or West
/// </summary>
Coords[0] = xPlaced;
Coords[1] = yPlaced;
while(Coords[0] != 0)
{
Coords[0] -= 1;
if (PlayingBoard[Coords[0], Coords[1]] == playerValue)
{
foundInRow += 1;
}
else { break; }
}
Coords[0] = xPlaced;
Coords[1] = yPlaced;
while (Coords[0] < 6)
{
Coords[0] += 1;
if (PlayingBoard[Coords[0], Coords[1]] == playerValue)
{
foundInRow += 1;
}
else { break; }
}
if (foundInRow > 2) { MessageBox.Show("You won."); Won = true; }
else { foundInRow = 0; Won = false; }
/// <summary>
/// This part checks if we have a win on North or South
/// </summary>
Coords[0] = xPlaced;
Coords[1] = yPlaced;
while(Coords[1] != 0)
{
Coords[1] -= 1;
if (PlayingBoard[Coords[0], Coords[1]] == playerValue)
{
foundInRow += 1;
}
else { break; }
}
Coords[0] = xPlaced;
Coords[1] = yPlaced;
while (Coords[1] < 6)
{
Coords[1] += 1;
if (PlayingBoard[Coords[0], Coords[1]] == playerValue)
{
foundInRow += 1;
}
else { break; }
}
if (foundInRow > 2) { MessageBox.Show("You won."); Won = true; }
else { foundInRow = 0; Won = false; }
}

Here is a connect four solver which works using recursion.
It accepts a 2 dimensional array b which represents the board. each element in the array can be populated with an int where 0 represents an empty space 1 player one, 2 player two etc
the Checkboard method returns an int where -1 represents no winner and positive integers represent the winning players number
the method works by iterating through each square in the array from 0,0 and checking the three possible directions, left, diagonal and down for a further 3 adjacent elements containing the same number. if a row of 4 is found the method returns the number in the square it was checking at the time.
public class Connect4Solver
{
public int Checkboard(int[,] b)
{
for (int x = 0; x < b.GetLength(0); x++)
{
for (int y = 0; y < b.GetLength(1); y++)
{
for (int d = 0; d < 3; d++)
{
int p = b[x, y];
if (p != 0)
{
if (countDir(0, b, x, y, d, p) >= 3)
{
//win for p
return p;
}
}
}
}
}
return -1;
}
protected int countDir(int depth, int[,] b, int x, int y, int dir, int p)
{
int x2;
int y2;
if (getposdir(b, x, y, dir, out x2, out y2) == p)
{
//good
depth++;
return countDir(depth, b, x2, y2, dir, p);
}
else
{
return depth;
}
}
protected int getposdir(int[,] b, int x, int y, int dir, out int x2, out int y2)
{
if (dir == 0)
{
x2 = x + 1;
y2 = y;
}
else if (dir == 1)
{
x2 = x + 1;
y2 = y + 1;
}
else if (dir == 2)
{
x2 = x;
y2 = y + 1;
}
else
{
throw new Exception("unknown");
}
return getpos(b, x2, y2);
}
protected int getpos(int[,] b, int x, int y)
{
if (b.GetLength(0) <= x)
{
return -1;
}
if (b.GetLength(1) <= y)
{
return -1;
}
return b[x, y];
}
}
note: I forgot to check 'down and right' assuming it would like 'down'
and 'left' not be required. I leave this as an exercise for the reader
to add

Related

Knight's destination using recursion

I've been working on a project which needs the knight(we have it's coordinates at the start) travel to destination(also known coordinates).
I tried to write using recursion but my code doesn't seem to be doing anything and I can't find the problem. Here's my code:
static bool Kelias(int dabX, int dabY, string[] Lenta, int dX, int dY, int indeksas)
{
if (dabX == dX && dabY == dY)
return true;
if (!Lenta[dabY][dabX].Equals('0'))
{
return false;
}
if (indeksas > 0)
{
StringBuilder naujas = new StringBuilder(Lenta[dabY]);
naujas[dabX] = (char)indeksas;
Lenta[dabY] = naujas.ToString();
}
// aukstyn desinen
if (GaliJudeti(dabX + 2, dabY + 1)
&& Kelias(dabX + 2, dabY + 1, Lenta, dX, dY, indeksas + 1))
{
return true;
}
// aukstyn desinen
if (GaliJudeti(dabX + 1, dabY + 2)
&& Kelias(dabX + 1, dabY + 2, Lenta, dX, dY, indeksas + 1))
{
return true;
}
// aukstyn kairen
if (GaliJudeti(dabX - 1, dabY + 2)
&& Kelias(dabX - 1, dabY + 2, Lenta, dX, dY, indeksas + 1))
{
return true;
}
// aukstyn kairen
if (GaliJudeti(dabX - 2, dabY + 1)
&& Kelias(dabX - 2, dabY + 1, Lenta, dX, dY, indeksas + 1))
{
return true;
}
// zemyn kairen
if (GaliJudeti(dabX - 2, dabY - 1)
&& Kelias(dabX - 2, dabY - 1, Lenta, dX, dY, indeksas + 1))
{
return true;
}
// zemyn kairen
if (GaliJudeti(dabX - 1, dabY - 2)
&& Kelias(dabX - 1, dabY - 2, Lenta, dX, dY, indeksas + 1))
{
return true;
}
// zemyn desinen
if (GaliJudeti(dabX + 1, dabY - 2)
&& Kelias(dabX + 1, dabY - 2, Lenta, dX, dY, indeksas + 1))
{
return true;
}
// zemyn desinen
if (GaliJudeti(dabX + 2, dabY - 1)
&& Kelias(dabX + 2, dabY - 1, Lenta, dX, dY, indeksas + 1))
{
return true;
}
indeksas--;
return false;
}
static bool GaliJudeti(int x, int y)
{
if (x >= 0 && y >= 0 && x < 8 && y < 8)
{
return true;
}
return false;
}
A little explanation about the variables and what I'm trying to do:
dabX, dabY - are the current coordinates of the Knight
Lenta - is my board(it's a string cause I'm reading the starting data from a txt file).
dX, dY - is the target destination
indeksas - is a tracker of how many moves it takes to reach the destination
Now the first if checks if we reached the destination. Second one checks if the coordinates to which we're traveling too are not obstructed(Since my board is made out of zeroes cause it's in a string we check if the symbol is equal to it cause if it's not means path is obstructed). Then we move onto the knights possible movements which is the main part of the method.
Also there's another function called GaliJudeti which checks if we're in bounds of the board(8x8).
Your code looks like it must work. I just used your algorithm, modified it a bit and everything works fine. I used classes to make it look more generally, but in fact it's all the same.
static bool MoveFirstSolution(Knight knight, Board board, Point destination, int counter, Trace trace)
{
board.Set(knight.X, knight.Y, counter);
if (knight.IsInPoint(destination))
{
//trace is an object to store found path
trace.Counter = counter;
trace.Board = board;
return true;
}
counter++;
Point[] moves = knight.AllPossibleMoves();
foreach (Point point in moves)
{
if (board.Contains(point) && board.IsFree(point))
{
knight.MoveTo(point);
if (MoveFirstSolution(knight, board.GetCopy(), destination, counter, trace))
{
return true;
}
}
}
return false;
}
However, this function will find first solution and stop. If you want best solution, you need to continue the search even when the answer is found. Here is a function to perform it:
static void Move(Knight knight, Board board, Point destination, int counter, Trace trace)
{
board.Set(knight.X, knight.Y, counter);
if (knight.IsInPoint(destination))
{
if (!trace.IsShorterThen(counter))
{
trace.Counter = counter;
trace.Board = board;
Console.WriteLine("Better trace");
Console.WriteLine("Counter: " + trace.Counter);
Console.WriteLine(trace.Board);
}
return;
}
counter++;
Point[] moves = knight.AllPossibleMoves();
foreach(Point point in moves)
{
if (board.Contains(point) && board.IsFree(point))
{
knight.MoveTo(point);
Move(knight, board.GetCopy(), destination, counter, trace);
}
}
}
The trace is overwritten each time when better one is found. But for 8 * 8 board it takes really long time to execute.
For your code I can advise to try Console.WriteLine() to be sure, that everything works. Maybe, you Lenta is not overwritten, as you expected and this causes infinite recursion. Try tracking each action of your function, to find the source of problem.
Here are my main function:
static void Main(string[] args)
{
Knight knight = new Knight(0, 0);
Board board = new Board(8, 8);
Point destination = new Point(0, 4);
Trace bestTrace = new Trace();
MoveFirstSolution(knight, board, destination, 1, bestTrace);
Console.WriteLine("Best trace: " + bestTrace.Counter);
Console.WriteLine(bestTrace.Board);
Console.ReadLine();
}
and the rest of required classes, so you can try it as working example.
class Trace
{
public Trace()
{
this.Board = null;
this.Counter = -1;
}
public Trace(Board board, int counter)
{
this.Board = board;
this.Counter = counter;
}
public bool IsShorterThen(int counter)
{
return this.Counter > 0 && this.Counter <= counter;
}
public Board Board { get; set; }
public int Counter { get; set; }
}
class Board
{
private int[][] _board;
public Board(int N, int M)
{
this._board = new int[N][];
for (int i = 0; i < N; i++)
{
this._board[i] = new int[M];
for (int j = 0; j < M; j++)
{
this._board[i][j] = 0;
}
}
}
public int N
{
get
{
return this._board.Length;
}
}
public int M
{
get
{
return this._board.Length > 0 ? this._board[0].Length : 0;
}
}
public Board GetEmptyCopy()
{
return new Board(this.N, this.M);
}
public Board GetCopy()
{
Board b = new Board(this.N, this.M);
for (int i = 0; i < N; i++)
{
for (int j = 0; j < M; j++)
{
b.Set(i, j, this.Get(i, j));
}
}
return b;
}
public bool Contains(int i, int j)
{
return (i >= 0) && (i < this.N) && (j >= 0) && (j < this.M);
}
public bool Contains(Point point)
{
return this.Contains(point.X, point.Y);
}
public bool IsFree(int i, int j)
{
return this._board[i][j] == 0;
}
public bool IsFree(Point point)
{
return this.IsFree(point.X, point.Y);
}
public void Set(int i, int j, int val)
{
this._board[i][j] = val;
}
public int Get(int i, int j)
{
return this._board[i][j];
}
public override string ToString()
{
string str = "";
for (int i = 0; i < this.N; i++)
{
for (int j = 0; j < this.M; j++)
{
str += String.Format("{0, 3}", this._board[i][j]);
}
str += "\r\n";
}
return str;
}
}
class Knight
{
public Knight(int x, int y)
{
this.X = x;
this.Y = y;
}
public int X { get; private set; }
public int Y { get; private set; }
public Point[] AllPossibleMoves()
{
Point[] moves = new Point[8];
moves[0] = new Point(this.X + 1, this.Y + 2);
moves[1] = new Point(this.X + 1, this.Y - 2);
moves[2] = new Point(this.X + 2, this.Y + 1);
moves[3] = new Point(this.X + 2, this.Y - 1);
moves[4] = new Point(this.X - 1, this.Y + 2);
moves[5] = new Point(this.X - 1, this.Y - 2);
moves[6] = new Point(this.X - 2, this.Y + 1);
moves[7] = new Point(this.X - 2, this.Y - 1);
return moves;
}
public bool IsInPoint(int x, int y)
{
return this.X == x && this.Y == y;
}
public bool IsInPoint(Point point)
{
return this.IsInPoint(point.X, point.Y);
}
public void MoveTo(int x, int y)
{
this.X = x;
this.Y = y;
}
public void MoveTo(Point point)
{
this.MoveTo(point.X, point.Y);
}
}
class Point
{
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public int X { get; private set; }
public int Y { get; private set; }
}

C# Tic-Tac-Toe Minimax

I am currently trying my hand at making a minimax AI for tictactoe. My goal was that it should suffice for larger boards as well. However, I am having quite a hard time wrapping my head around how to implement the algorithm. I have read countless different descriptions of the algorithm but I still don't seem to be able to make it work. My result as of yet is an incredibly stupid AI. Here is my code for it.
Edit: The main point I am wondering about is how I make the AI value me not winning over forwarding itself towards the win. As of now it doesn't really care that I will win the next turn.
namespace TicTacToe_AI
{
public class Move //A class for moves
{
public int x, y, value, MoveNumber;
void SetMove(int a, int b)
{
x = a;
y = b;
}
public Move(int a, int b)
{
SetMove(a, b);
}
public Move()
{ }
}
class AI //AIClass
{
//The minimax algorithm
public Move CalculateMoves(int[,] Board, int BoardSize, int Depth, Move BestMoveAI, Move BestMovePlayer, int OriginalDepth, int CurrentTurn)
{
Depth--; //Decrease the depth for each iteration
bool Alpha = false; //Alpha-beta pruning - needs improvement
bool Beta = false;
bool WinningMove = false;
if (CurrentTurn == 1) CurrentTurn = 2;
if (CurrentTurn == 2) CurrentTurn = 1;
List<Move> DifferentMoves = new List<Move>();
List<Move> PossibleMoves = new List<Move>();
for (int i = 0; i < BoardSize; i++) //Add all possible moves to a list
{
for (int j = 0; j < BoardSize; j++)
{
if (Board[i, j] == 0)
{
Move Possible = new Move(i, j);
PossibleMoves.Add(Possible);
}
}
}
if (CurrentTurn == 2 && Depth >= 0 && Depth < BestMoveAI.MoveNumber) Alpha = true; //Alpha-beta pruning
if (CurrentTurn == 1 && Depth >= 0 && Depth < BestMovePlayer.MoveNumber) Beta = true;
if(Alpha || Beta)
{
foreach (Move TryMove in PossibleMoves) //Try every possible move to see if they are a winning move
{
int[,] Trying = new int[BoardSize, BoardSize];
Trying = (int[,])Board.Clone();
Trying[TryMove.x, TryMove.y] = CurrentTurn;
TryMove.MoveNumber = OriginalDepth - Depth;
if (Form1.Win(Trying) == 2)
{
TryMove.value = -1;
DifferentMoves.Add(TryMove);
if (Depth + 1 == OriginalDepth)
{
if (TryMove.MoveNumber < BestMoveAI.MoveNumber) BestMoveAI = TryMove;
WinningMove = true;
break;
}
else
{
WinningMove = true;
if (TryMove.MoveNumber < BestMoveAI.MoveNumber) BestMoveAI = TryMove;
return TryMove;
}
}
else if (Form1.Win(Trying) == 1)
{
WinningMove = true;
TryMove.value = 1;
BestMovePlayer = TryMove;
DifferentMoves.Add(TryMove);
return TryMove;
}
}
if (!WinningMove) // If no winning move was found, try recursively searching for a winning move
{
if (Alpha || Beta)
{
foreach (Move TryMove2 in PossibleMoves)
{
int[,] TestMove = new int[BoardSize, BoardSize];
TestMove = (int[,])Board.Clone();
TestMove[TryMove2.x, TryMove2.y] = CurrentTurn;
TryMove2.value = CalculateMoves(TestMove, BoardSize, Depth, BestMoveAI, BestMovePlayer, OriginalDepth, CurrentTurn).value;
DifferentMoves.Add(TryMove2);
}
}
}
}
//Find the best possible move and return it
BestMoveAI.value = 0;
BestMoveAI.MoveNumber = OriginalDepth;
BestMovePlayer.value = 0;
BestMovePlayer.MoveNumber = OriginalDepth;
if (CurrentTurn == 2)
{
foreach (Move AllMoves in DifferentMoves)
{
if (AllMoves.value <= BestMoveAI.value && AllMoves.MoveNumber <= BestMoveAI.MoveNumber)
{
BestMoveAI = AllMoves;
}
}
return BestMoveAI;
}
else if(CurrentTurn == 1)
{
foreach (Move AllMoves in DifferentMoves)
{
if (AllMoves.value >= BestMovePlayer.value && AllMoves.MoveNumber <= BestMovePlayer.MoveNumber)
{
BestMovePlayer = AllMoves;
}
}
return BestMovePlayer;
}
Move BadMove = new Move();
BadMove.value = 0;
BadMove.MoveNumber = Depth;
return BadMove;
}
}
}

OutOfMemoryException For Maze Solver of Large Dimensions

The Program Works for arrays upto 20x20 But for anything larger it throws an OutOfMemoryException.
Below is the code:
public static Point GetFinalPath(int x, int y) {
queue.Enqueue(new Point(x,y, null));
while(queue.Count>0) {
Point p = queue.Dequeue();
if (arr[p.x,p.y] == 9) {
Console.WriteLine("Found Destination");
return p;
}
if(IsOpen(p.x+1,p.y)) {
arr[p.x,p.y] = 1;
queue.Enqueue(new Point(p.x+1,p.y, p));
}
//similarly for the other directions
}
return null;
}
public int[,] SolutionMaze()
{
Point p = GetFinalPath(0, 0);
while (p.getParent() != null)
{
solvedarray[p.x, p.y] = 9;
p = p.getParent();
}
return solvedarray;
}
ok people here is the rest of the code
public static Queue<Point> queue=new Queue<Point>();
public static bool IsOpen(int x, int y)
{
//BOUND CHECKING
if ((x >= 0 && x < XMAX) && (y >= 0 && y < YMAX) && (arr[x,y] == 0 || arr[x,y] == 9))
{
return true;
}
return false;
}
public class Point
{
public int x;
public int y;
Point parent;
public Point(int x, int y, Point parent)
{
this.x = x;
this.y = y;
this.parent = parent;
}
public Point getParent()
{
return this.parent;
}
}
Assumes start to be 0,0 and final destination is set as 9 at the constructor.
Help me implement this for an array of size 500x500
Well, looking at your code I found one problem. You perform the wrong check. You should check if your point is already added to a queue. What do you do now? We'll, you are just marking processed cell as not open. It's easy to see that you can add to queue same node twice.
Let's follow my example:
1 | . .
0 | ! .
--+----
yx| 0 1
Queue: point (0,0)
We are starting at point(0,0). At this moment, we are adding points (0, 1) and (1,0) to our queue and mark point(0,0) as processed
1 | . .
0 | X !
--+----
yx| 0 1
Queue: point (0,1), point (1,0)
Now we dequeue point(0,1), marking it processed and adding point(1,1) to queue.
1 | ! .
0 | X X
--+----
yx| 0 1
Queue: point (1,0), point(1,1)
Now we dequeue point(1,0), marking it as processed and adding point(1,1) to queue:
1 | X !
0 | X X
--+----
yx| 0 1
Queue: point (1,1), point (1,1)
And now we have same point twice in a queue. And that is not your last problem. Your points have a reference to all it parents, so your previous points (doubled too) can't be processed with Garbage Collector.
Okay i found an answer to the OutOfMemory. Now the code works even for 500x500 matrix
As it turns out i just implemented a node list which keeps track of added nodes in queue using y*MAX_X_LENGTH + x formula
public static Queue<Point> queue=new Queue<Point>();
public SolveMaze(int[,] array,int staX,int staY,int finX,int finY)
{
//sets Destination as 9
arr = array;
XMAX = array.GetLength(0);
YMAX = array.GetLength(1);
finishY = finX; finishX = finY; startY = staX; startX = staY;
solvedarray = new int[XMAX, YMAX];
}
public static List<int> nodelist=new List<int>();
public void AddPointToQueueIfOpenAndNotAlreadyPresent(Point p,int direction)
{
if (nodelist.Contains(XMAX * p.y + p.x))
return;
else
{
switch(direction){
case 1:
//north
if (IsOpen(p.x, p.y - 1))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x, p.y - 1, p));
nodelist.Add(XMAX * (p.y - 1) + p.x);
}
break;
case 0:
//east
if (IsOpen(p.x + 1, p.y))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x + 1, p.y, p));
nodelist.Add(XMAX * (p.y) + p.x + 1);
}
break;
case 3:
//south
if (IsOpen(p.x, p.y + 1))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x, p.y +1, p));
nodelist.Add(XMAX * (p.y + 1) + p.x);
}
break;
case 2:
//west
if (IsOpen(p.x - 1, p.y))
{
arr[p.x, p.y] = 1;
queue.Enqueue(new Point(p.x - 1, p.y, p));
nodelist.Add(XMAX * (p.y) + p.x-1);
}
break; }
}
}
public Point GetFinalPath(int x, int y) {
if (arr[finishX, finishY] == 0)
arr[finishX, finishY] = 9;
else
return null;
queue.Enqueue(new Point(x, y, null));
nodelist.Add(XMAX * y + x);
while(queue.Count>0) {
Point p = queue.Dequeue();
nodelist.Remove(p.y * XMAX + p.x);
if (arr[p.x,p.y] == 9) {
Console.WriteLine("Exit is reached!");
return p;
}
for (int i = 0; i < 4; i++)
{
AddPointToQueueIfOpenAndNotAlreadyPresent(p, i);
}
}
return null;
}
public static bool IsOpen(int x, int y)
{
//BOUND CHECKING
if ((x >= 0 && x < XMAX) && (y >= 0 && y < YMAX) && (arr[x,y] == 0 || arr[x,y] == 9))
{
return true;
}
return false;
}
public int[,] SolutionMaze()
{
Point p = GetFinalPath(startX, startY);
if(p!=null)
while (p.getParent() != null)
{
solvedarray[p.x, p.y] = 9;
p = p.getParent();
}
return solvedarray;
}
}
public class Point
{
public int x;
public int y;
Point parent;
public Point(int x, int y, Point parent)
{
this.x = x;
this.y = y;
this.parent = parent;
}
public Point getParent()
{
return this.parent;
}
}

how can i drag a 2d level array from a txt file

i am working on improving this tower defence gtame that i finished from this tutorial http://xnatd.blogspot.com/ i now wish to load the levels from a text file but not quite sure how i would do this using a streamreader, any tips? heres the source code for my level class;
public class Level
{
protected int temperature;
protected int levelNo;
private Queue<Vector2> waypoints = new Queue<Vector2>();
public Queue<Vector2> Waypoints
{
get
{
return waypoints;
}
}
int[,] map = new int[,]
{
{0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,},
{0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,},
{0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,},
{0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,},
{0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,},
{0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,},
{0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,},
{0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,},
{0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,},
{0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,},
{0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,},
{0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,},
{0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,},
{0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,},
{0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,},
{0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,},
};
public int Temperature // get the width of the level (how many numbers are in a row)
{
get
{
return temperature;
}
}
public int LevelNo // get the width of the level (how many numbers are in a row)
{
get
{
return levelNo;
}
set
{
levelNo = value;
}
}
public int Width // get the width of the level (how many numbers are in a row)
{
get
{
return map.GetLength(1);
}
}
public int Height // get the height of our level (how many numbers are there in a column)
{
get
{
return map.GetLength(0);
}
}
public int GetIndex(int cellX, int cellY) // return the index of the requested cell.
{
if (cellX < 0 || cellX > Width - 1 || cellY < 0 || cellY > Height - 1)
{
return 0;
}
else
{
return map[cellY, cellX];
}
}
public List<Texture2D> tileTextures = new List<Texture2D>(); // list to contain all the textures
/// <summary>
/// CONSTRUCTOR NEW LEVEL
/// </summary>
public Level()
{
SetWayPoints(map);
this.temperature = 1000;
this.levelNo = 2;
}
private void SetWayPoints(int[,] map)
{
int currentPosVal = map[0, 0];
int lPos = 0;
int rPos = 0;
int uPos = 0;
int dPos = 0;
int storedXPos = 99;
int storedYPos = 99;
int endstoredXPos = 99;
int endstoredYPos = 99;
int lastY = 0;
int lastX = 0;
//Search top ROW for start
for (int i = 0; i < Width; i++)
{
currentPosVal = map[0, i];
if (currentPosVal == 1)
{
storedXPos = i;
storedYPos = 0;
waypoints.Enqueue(new Vector2(storedXPos, storedYPos) * 32);
lastX = storedXPos;
lastY = storedYPos;
break;
}
}
//if start not set
if (storedXPos == 99 && storedXPos == 99)
{
//look in 1st coloum for start
for (int i = 0; i < Height; i++)
{
currentPosVal = map[i, 0];
if (currentPosVal == 1)
{
storedXPos = 0;
storedYPos = i;
waypoints.Enqueue(new Vector2(storedXPos, storedYPos) * 32);
lastX = storedXPos;
lastY = storedYPos;
break;
}
}
}
//search end COLOUM for end
for (int i = 0; i < Height; i++)
{
currentPosVal = map[i, Width - 1];
if (currentPosVal == 1)
{
endstoredXPos = Width - 1;
endstoredYPos = i;
}
}
//If end not set look in bottom row for end
if (endstoredXPos == 99 && endstoredYPos == 99)
{
for (int i = 0; i < Width; i++)
{
currentPosVal = map[7, i];
if (currentPosVal == 1)
{
endstoredXPos = i;
endstoredYPos = Height - 1;
}
}
if (endstoredXPos == 99 && endstoredYPos == 99)
{
}
}
// start midlle loop
while (true)
{
lPos = 0;
rPos = 0;
uPos = 0;
dPos = 0;
//If current pos is not down the left hand edge
if (storedXPos > 0) { lPos = map[storedYPos, storedXPos - 1]; }
//If current pos square is not down the right hand edge
if (storedXPos < Width - 1) { rPos = map[storedYPos, storedXPos + 1]; }
//If current pos square is not in the top row
if (storedYPos > 0) { uPos = map[storedYPos - 1, storedXPos]; }
//If current pos square is not in the bottom row
if (storedYPos < Height - 1) { dPos = map[storedYPos + 1, storedXPos]; }
if (lPos == 1 && (lastX != storedXPos - 1 || lastY != storedYPos))
{
lastX = storedXPos;
lastY = storedYPos;
storedXPos--;
waypoints.Enqueue(new Vector2(storedXPos, storedYPos) * 32);
}
else if (rPos == 1 && (lastX != storedXPos + 1 || lastY != storedYPos))
{
lastX = storedXPos;
lastY = storedYPos;
storedXPos++;
waypoints.Enqueue(new Vector2(storedXPos, storedYPos) * 32);
}
else if (dPos == 1 && (lastX != storedXPos || lastY != storedYPos + 1))
{
lastX = storedXPos;
lastY = storedYPos;
storedYPos++;
waypoints.Enqueue(new Vector2(storedXPos, storedYPos) * 32);
}
else if (uPos == 1 && (lastX != storedXPos || lastY != storedYPos - 1))
{
lastX = storedXPos;
lastY = storedYPos;
storedYPos--;
waypoints.Enqueue(new Vector2(storedXPos, storedYPos) * 32);
}
if (storedXPos == endstoredXPos && storedYPos == endstoredYPos)
{
break;
}
}
}
public void AddTexture(Texture2D texture) // method adds a texture to our texture list.
{
tileTextures.Add(texture);
}
//Reads number from array, store its value in textureIndex, Use textureIndex to get the texture from tileTextures,
public void Draw(SpriteBatch batch) //Draw appropiate texture, Repeat through all elements of the array
{
int textureIndex;
Texture2D texture;
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
if (levelNo == 0)
{
textureIndex = map[y, x];
if (textureIndex == -1)
continue;
texture = tileTextures[textureIndex];
batch.Draw(texture, new Rectangle(x * 32, y * 32, 32, 32), Color.White);
}
if (levelNo > 0)
{
textureIndex = map[y, x];
textureIndex += (levelNo * 2);
if (textureIndex == -1)
continue;
texture = tileTextures[textureIndex];
batch.Draw(texture, new Rectangle(x * 32, y * 32, 32, 32), Color.White);
}
}
}
}
}
}
Since C# is compiled and can't do evals like scripted languages can (not that you should be doing that anyway) you should probably use streamreader to read the data from file (formatted perhaps as delimited text: csv or tsv)
Assuming you're loading something similar to the map you have up there then a map file could look something like
0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0
0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0
0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0
0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0
0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0
0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0
0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0
0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0
0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0
0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0
0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0
0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0
0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0
0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0
0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0
0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1
where you would then loop through the file and read each line
As each line is read in, you can then split the line by "," to create a 1d array which can then be assigned to an index in the 2d array. Loop for each line to create the entire 2d map array
Take a look at this if you need help splitting strings in C#; note that the sample code splits at spaces " " and that you should use s.Split(','); for csv
With a file that small i would use:
File.ReadAllLines , and after readling it loop over the lines with a foreach loop.

Brute force Algorithm for creation of Sudoku Board

What I am developing is that initially the entire sudoku board is empty.
One of the random cells(out of 81) is filled with a random value(1-9).
Now I want to fill all the remaining cells using brute force approach.
From what I came to know after googling is that we should start with the first cell and fill it with 1(if it's valid), then fill the second cell with 2(if it's valid, we will begin checking with a number greater than the last filled cell, which in this case is 1, once we reach 9, we reset it with 1).
The thing is that it's not working properly!
Can anyone link me to the exact algorithm.
Here's an implementation of the backtracking approach:
import java.util.Random;
public class Sudoku {
public static void main(String[] args) {
Random rand = new Random();
int r = rand.nextInt(9);
int c = rand.nextInt(9);
int value = rand.nextInt(9) + 1;
Board board = new Board();
board.set(r, c, value);
System.out.println(board);
solve(board, 0);
System.out.println(board);
}
private static boolean solve(Board board, int at) {
if (at == 9*9)
return true;
int r = at / 9;
int c = at % 9;
if (board.isSet(r, c))
return solve(board, at + 1);
for (int value = 1; value <= 9; value++) {
if (board.canSet(r, c, value)) {
board.set(r, c, value);
if (solve(board, at + 1))
return true;
board.unSet(r, c);
}
}
return false;
}
static class Board {
private int[][] board = new int[9][9];
private boolean[][] rs = new boolean[9][10];
private boolean[][] cs = new boolean[9][10];
private boolean[][][] bs = new boolean[3][3][10];
public Board() {}
public boolean canSet(int r, int c, int value) {
return !isSet(r, c) && !rs[r][value] && !cs[c][value] && !bs[r/3][c/3][value];
}
public boolean isSet(int r, int c) {
return board[r][c] != 0;
}
public void set(int r, int c, int value) {
if (!canSet(r, c, value))
throw new IllegalArgumentException();
board[r][c] = value;
rs[r][value] = cs[c][value] = bs[r/3][c/3][value] = true;
}
public void unSet(int r, int c) {
if (isSet(r, c)) {
int value = board[r][c];
board[r][c] = 0;
rs[r][value] = cs[c][value] = bs[r/3][c/3][value] = false;
}
}
public String toString() {
StringBuilder ret = new StringBuilder();
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++)
ret.append(board[r][c]);
ret.append("\n");
}
return ret.toString();
}
}
}
I used a method without backtracing, although the while loop might be it. To quote an algorithm book I've read "Nothing in recursion can't be duplicated using iteration".
I've been using my eyes to inspect this, and since I can't wrap my head around the recursive method, even though recursion is relatively understood:
This method, I kinda wrote with some guidance, had a bug in the grid checker, when I found it, it seems to be working now. I'm positing it 'cause it's hard to find complete-and-working code. IOS SDK.
#define WIDTH 9
#define HEIGHT 9
#interface ViewController ()
//- (BOOL) numberConflicts:(int)testNum;
- (BOOL) number:(int)n conflictsWithRow:(int)r;
- (BOOL) number:(int)n conflictsWithColumn:(int)c;
- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
- (int) incrementSudokuValue:(int)v;
#end
static int sudoku[WIDTH][HEIGHT];
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
/// Initialize it
for (int x = 0; x < WIDTH; x++)
{
for (int y = 0; y < HEIGHT; y++)
{
sudoku[x][y] = 0;
}
}
///
int tries = 0;
for (int j = 0; j < HEIGHT; j++)
{
for (int i = 0; i < WIDTH; i++)
{
int num = arc4random()%9 + 1;
while ([self number:num conflictsAtGridPointX:i andPointY:j])
{
num = [self incrementSudokuValue:num];
tries++;
if (tries > 10) { //restart the column
tries = 0;
for(int count = 0; count < WIDTH; count++)
{
sudoku[count][j] = 0;
}
i = 0;
}
}
if(sudoku[i][j] == 0)
sudoku[i][j] = num;
tries = 0;
for (int y = 0; y < HEIGHT; y++)
{
for (int x = 0; x < WIDTH; x++)
{
printf("%i ", sudoku[x][y]);
}
printf("\n");
}
printf("\n");
}
}
for (int x = 0; x < WIDTH; x++)
{
for (int y = 0; y < HEIGHT; y++)
{
printf("%i ", sudoku[y][x]);
}
printf("\n"); //newline
}
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL) number:(int)n conflictsWithRow:(int)r;
{
for (int y = 0; y < HEIGHT; y++) {
if (sudoku[y][r] == n) {
return YES;
}
}
return NO;
}
- (BOOL) number:(int)n conflictsWithColumn:(int)c;
{
for (int x = 0; x < WIDTH; x++) {
if (sudoku[c][x] == n) {
return YES;
}
}
return NO;
}
- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
{
if ([self number:n conflictsWithRow:yPoint])
{
return YES;
}
if ([self number:n conflictsWithColumn:xPoint])
{
return YES;
}
if ([self number:n conflictsWithSquareInPointX:xPoint andPointY:yPoint]) {
return YES;
}
return NO;
}
- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
{
int leftX = x - (x % 3); //used to use int division
// leftX *= 3;
int topY = y - (y % 3);
// topY *= 3;
int rightX = leftX + 2;
int bottomY = topY + 2;
for(int subY = topY; subY <= bottomY; subY++) //bug was here, used < instead of less N equal to...
{
for ( int subX = leftX; subX <= rightX; subX++)
{
if (sudoku[subX][subY] == n) {
return YES;
}
}
}
NSLog(#"Testing grid at %i, %i", x/3, y/3);
NSLog(#"LeftX: %i TopY: %i", leftX, topY);
return NO;
}
- (int) incrementSudokuValue:(int)v;
{
if (v < 9) {
v++;
return v;
}
return 1;
}
Note: The header file is empty, paste this into iOS single View application if you desire.
Caution: might loop infinitely( and above does sometimes, but is very fast), may want another more global "tries" variable, and restart the algorithm as a safety, or give it a seed/do both
edit: the below should be safe from infinite loops, if the source grid is solvable (or nonexistant)
#define WIDTH 9
#define HEIGHT 9
#interface ViewController ()
//- (BOOL) numberConflicts:(int)testNum;
- (BOOL) number:(int)n conflictsWithRow:(int)r;
- (BOOL) number:(int)n conflictsWithColumn:(int)c;
- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
- (int) incrementSudokuValue:(int)v;
#end
static int sudoku[WIDTH][HEIGHT];
#implementation ViewController
- (BOOL) fillGridWithNext:(int)next;
{
for (int y = 0; y < HEIGHT; y++)
{
for (int x = 0; x < WIDTH; x++)
{
if (sudoku[x][y] != 0)
{
if (x == 8 && y == 8) {
return YES;
}
continue;
}
for (int count = 0; count < (HEIGHT-1); count++)
{
if ([self number:next conflictsAtGridPointX:x andPointY:y])
{
next = [self incrementSudokuValue:next];
}
else
{
sudoku[x][y] = next;
if( [self fillGridWithNext:arc4random()%9+1])
{
return YES;
}
}
}
sudoku[x][y] = 0;
return NO;
}
}
return NO;
}
- (void)viewDidLoad
{
[super viewDidLoad];
/// Initialize it
for (int x = 0; x < WIDTH; x++)
{
for (int y = 0; y < HEIGHT; y++)
{
sudoku[x][y] = 0;
}
}
sudoku[0][0]=9;
int next;
next = (arc4random()%9)+1;
if( [self fillGridWithNext:next]) //seeded
{
NSLog(#"Solved");
}
else
{
NSLog(#"No solution");
}
for (int x = 0; x < WIDTH; x++)
{
for (int y = 0; y < HEIGHT; y++)
{
printf("%i ", sudoku[y][x]);
}
printf("\n"); //newline
}
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL) number:(int)n conflictsWithRow:(int)r;
{
for (int y = 0; y < HEIGHT; y++) {
if (sudoku[y][r] == n) {
return YES;
}
}
return NO;
}
- (BOOL) number:(int)n conflictsWithColumn:(int)c;
{
for (int x = 0; x < WIDTH; x++) {
if (sudoku[c][x] == n) {
return YES;
}
}
return NO;
}
- (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
{
if ([self number:n conflictsWithRow:yPoint])
{
return YES;
}
if ([self number:n conflictsWithColumn:xPoint])
{
return YES;
}
if ([self number:n conflictsWithSquareInPointX:xPoint andPointY:yPoint]) {
return YES;
}
return NO;
}
- (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
{
int leftX = x - (x % 3); //used to use int division
// leftX *= 3;
int topY = y - (y % 3);
// topY *= 3;
int rightX = leftX + 2;
int bottomY = topY + 2;
for(int subY = topY; subY <= bottomY; subY++) //bug was here, used < instead of less N equal to...
{
for ( int subX = leftX; subX <= rightX; subX++)
{
if (sudoku[subX][subY] == n) {
return YES;
}
}
}
NSLog(#"Testing grid at %i, %i", x/3, y/3);
NSLog(#"LeftX: %i TopY: %i", leftX, topY);
return NO;
}
- (int) incrementSudokuValue:(int)v;
{
if (v < 9) {
v++;
return v;
}
return 1;
}
#end
Summary: The first version is flawed but (mostly) gets the job done. It generates every row at random, if the row is invalid, it wipes and starts over. This will wipe out source grids, and can go forever, but works most of the time.
The lower code uses recursion. I don't think it backtracks properly, but it has solved empty and semi-seeded grids on my tests. I think I need to save a "state" grid to backtrack with, but I'm not doing this. I'm posting both since they both answer "Brute force"... on my own, I should study recursion, I can't explain why the lower one works, I personally could use help with doing it.
Note: The first one finishes in a blink or so when it does finish... if speed means more than reliability to your application (somewhat counter-intuitive in this case, with the infinite looping, heh).
This simple random walk algorithm should work too (but is inefficient- use at your own risk!!!):
EDIT: - added fix for unresolvable solutions.
For each empty cell in grid
array = Get_Legal_Numbers_for_cell(row,col);
If (array is empty) {
Clear_All_cells()
} else {
number = Random_number_from(array);
Put_Number_in_Cell(number);
}
EDIT 2
If someone are interested here are described methods for solving sudoku with random-based search.

Categories