I've been trying to implement minimax algorithm in my C# chess engine for a week now and it makes legal moves but not meaningful moves. I cannot find the error, hopefully someone can spot it. I have tried to isolate the problem by testing each method and they all seem to work correctly except minimax.
public enum Piece
{
Empty, Pawn_W, Pawn_B, Knight_W, Knight_B,
Bishop_W, Bishop_B, Rook_W, Rook_B,
Queen_W, Queen_B, King_W, King_B
}
public bool IsPieceWhite(Piece piece)
{
if (piece == Piece.Pawn_W || piece == Piece.Knight_W ||
piece == Piece.Bishop_W || piece == Piece.Rook_W ||
piece == Piece.Queen_W || piece == Piece.King_W)
return true;
else return false;
}
public bool IsPieceBlack(Piece piece)
{
if (piece == Piece.Pawn_B || piece == Piece.Knight_B ||
piece == Piece.Bishop_B || piece == Piece.Rook_B ||
piece == Piece.Queen_B || piece == Piece.King_B)
return true;
else return false;
}
public int GetPieceWorth(Piece piece)
{
if (piece == Piece.Pawn_W || piece == Piece.Pawn_B)
return 1;
if (piece == Piece.Knight_W || piece == Piece.Knight_B)
return 3;
if (piece == Piece.Bishop_W || piece == Piece.Bishop_B)
return 3;
if (piece == Piece.Rook_W || piece == Piece.Rook_B)
return 5;
if (piece == Piece.Queen_W || piece == Piece.Queen_B)
return 9;
if (piece == Piece.King_W || piece == Piece.King_B)
return 9999999;
return 0;
}
Piece[,] CurrentBoard = GetStartingBoard();
Piece[,] bestMove;
public int depthB = 3;
public double minimax(Piece[,] board, int depth, bool maximizingPlayer)
{
if (depth == 0)
{
double result = EvaluatePosition(board, maximizingPlayer);
return result;
}
if (maximizingPlayer)
{
double best = Double.MinValue;
double value = Double.MinValue;
foreach (var move in GenerateMoves(board, maximizingPlayer))
{
Piece[,] clonedMove = CloneBoard(move);
value = Math.Max(value, minimax(clonedMove, depth - 1, false));
if (depth == depthB && value >= best)
{
best = value;
bestMove = clonedMove;
}
}
return value;
}
else
{
double best = Double.MaxValue;
double value = Double.MaxValue;
foreach (var move in GenerateMoves(board, maximizingPlayer))
{
Piece[,] clonedMove = CloneBoard(move);
value = Math.Min(value, minimax(clonedMove, depth - 1, true));
if (depth == depthB && value <= best)
{
best = value;
bestMove = clonedMove;
}
}
return value;
}
}
public Piece[,] CloneBoard(Piece[,] boardPos)
{
Piece[,] copy = boardPos.Clone() as Piece[,];
return copy;
}
public double EvaluatePosition(Piece[,] boardPos, bool ForWhite)
{
double eval = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (boardPos[i, j] != Piece.Empty)
{
if (IsPieceWhite(boardPos[i, j]))
{
eval += GetPieceWorth(boardPos[i, j]);
}
else if (IsPieceBlack(boardPos[i, j]))
{
eval -= GetPieceWorth(boardPos[i, j]);
}
}
}
}
if (ForWhite)
return eval;
else
return eval * -1;
}
//a-h,0-7
//Piece[,] board = new Piece[8, 8];
public static Piece[,] GetStartingBoard()
{
Piece[,] board = new Piece[8, 8];
for (int i = 0; i < 8; i++)
{
//initiate pawns
board[1, i] = Piece.Pawn_W;
board[6, i] = Piece.Pawn_B;
}
//white pieces
board[0, 0] = Piece.Rook_W;
board[0, 1] = Piece.Knight_W;
board[0, 2] = Piece.Bishop_W;
board[0, 3] = Piece.Queen_W;
board[0, 4] = Piece.King_W;
board[0, 5] = Piece.Bishop_W;
board[0, 6] = Piece.Knight_W;
board[0, 7] = Piece.Rook_W;
//black pieces
board[7, 0] = Piece.Rook_B;
board[7, 1] = Piece.Knight_B;
board[7, 2] = Piece.Bishop_B;
board[7, 3] = Piece.Queen_B;
board[7, 4] = Piece.King_B;
board[7, 5] = Piece.Bishop_B;
board[7, 6] = Piece.Knight_B;
board[7, 7] = Piece.Rook_B;
//test
//board[1, 4] = Piece.Pawn_B;
//board[6, 2] = Piece.Pawn_W;
return board;
}
I have uploaded a short clip of the engine playing against itself, to show the wierd moves: https://www.youtube.com/watch?v=A0HVgXYSciY
Finally was able to make the minimax function work. Thanks for all the help!
Working method:
public int minimax(Piece[,] board, int depth, bool maximizingPlayer, bool WhiteToPlay)
{
if (depth == 0)
{
int result = EvaluatePosition(board, WhiteToPlay);
return result;
}
var moves = GenerateMoves(board, WhiteToPlay);
if (maximizingPlayer)
{
int value = int.MinValue;
foreach (var move in moves)
{
int minmaxResult = minimax(move, depth - 1, false, !WhiteToPlay);
value = Math.Max(value, minmaxResult);
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
else
{
int value = int.MaxValue;
foreach (var move in moves)
{
int minmaxResult = minimax(move, depth - 1, true, !WhiteToPlay);
value = Math.Min(value, minmaxResult);
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
}
Related
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();
}
My problem is when applying Alpha/Beta Pruning to Minimax. It does very wierd and bad moves. When I use Minimax without Alpha/Beta, it works fine. The two functions look like this:
Minimax With Alpha/Beta Pruning:
public int minimaxAB(Piece[,] board, int depth, int a, int b, bool maximizingPlayer, bool WhiteToPlay)
{
if (depth == 0)
{
return EvaluatePosition(board, WhiteToPlay);
}
var moves = GenerateMoves(board, WhiteToPlay);
if (maximizingPlayer)
{
int value = int.MinValue;
foreach (var move in moves)
{
int minmaxResult = minimaxAB(move, depth - 1, a, b, false, !WhiteToPlay);
value = Math.Max(value, minmaxResult);
a = Math.Max(a, value);
if (a >= b)
return a;
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
else
{
int value = int.MaxValue;
foreach (var move in moves)
{
int minmaxResult = minimaxAB(move, depth - 1, a, b, true, !WhiteToPlay);
value = Math.Min(value, minmaxResult);
b = Math.Min(b, value);
if (b <= a)
return b;
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
}
Minimax without A/B:
public int minimax(Piece[,] board, int depth, bool maximizingPlayer, bool WhiteToPlay)
{
if (depth == 0)
{
int result = EvaluatePosition(board, WhiteToPlay);
return result;
}
var moves = GenerateMoves(board, WhiteToPlay);
if (maximizingPlayer)
{
int value = int.MinValue;
foreach (var move in moves)
{
int minmaxResult = minimax(move, depth - 1, false, !WhiteToPlay);
value = Math.Max(value, minmaxResult);
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
else
{
int value = int.MaxValue;
foreach (var move in moves)
{
int minmaxResult = minimax(move, depth - 1, true, !WhiteToPlay);
value = Math.Min(value, minmaxResult);
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
}
My evaluation function:
public int EvaluatePosition(Piece[,] boardPos, bool ForWhite)
{
int eval_W = 0;
int eval_B = 0;
int eval = 0;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
if (boardPos[i, j] != Piece.Empty)
{
if (IsPieceWhite(boardPos[i, j]))
{
eval_W += GetPieceWorth(boardPos[i, j]) + DistanceToCenter(i, j);
eval += GetPieceWorth(boardPos[i, j]);
}
else if (IsPieceBlack(boardPos[i, j]))
{
eval_B += GetPieceWorth(boardPos[i, j]) + DistanceToCenter(i, j);
eval -= GetPieceWorth(boardPos[i, j]);
}
}
}
}
if (ForWhite)
return eval_W - eval_B;
else
return eval_B - eval_W;
}
I call with: minimaxAB(CurrentBoard, depthB, int.MinValue, int.MaxValue, true, whiteToMove);
I am aware that Minimax with AB is suppose to produce exactly the same result, but in my case it does not. I hope someone is able to spot what I did wrong.
I figured it out, I needed an alpha and beta for both white and black. The reason for this is that I call the minimaxAB function for both white and black moves.
Working method:
public int minimaxAB(Piece[,] board, int depth, int alpha_White, int beta_White, int alpha_Black, int beta_Black, bool maximizingPlayer, bool WhiteToPlay)
{
if (depth == 0 || !HasKings(board))
{
return EvaluatePosition(board, WhiteToPlay);
}
var moves = GenerateMoves(board, WhiteToPlay);
if (maximizingPlayer)
{
int value = int.MinValue;
foreach (var move in moves)
{
int minmaxResult = minimaxAB(move, depth - 1, alpha_White, beta_White, alpha_Black, beta_Black, false, !WhiteToPlay);
value = Math.Max(value, minmaxResult);
if (WhiteToPlay)
{
alpha_White = Math.Max(alpha_White, value);
if (alpha_White >= beta_White)
return alpha_White;
}
else
{
alpha_Black = Math.Max(alpha_Black, value);
if (alpha_Black >= beta_Black)
return alpha_Black;
}
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
else
{
int value = int.MaxValue;
foreach (var move in moves)
{
int minmaxResult = minimaxAB(move, depth - 1, alpha_White, beta_White, alpha_Black, beta_Black, true, !WhiteToPlay);
value = Math.Min(value, minmaxResult);
if (WhiteToPlay)
{
beta_White = Math.Min(beta_White, value);
if (beta_White <= alpha_White)
return beta_White;
}
else
{
beta_Black = Math.Min(beta_Black, value);
if (beta_Black <= alpha_Black)
return beta_Black;
}
if (depth == depthB)
{
moveScores.Add(move, minmaxResult);
}
}
return value;
}
}
Called with:
minimaxAB(CurrentBoard, depthB, int.MinValue, int.MaxValue, int.MinValue, int.MaxValue, true, whiteToMove);
I'm trying to come up with my "own" a* pathfinding algorithm following the explanation of this article : https://www.redblobgames.com/pathfinding/a-star/introduction.html
But it seems that my pathfinding code runs into some sort of infinte-while-loop which inmediatly crashes Unity. But I honestly do not know what am I doing wrong
Here's my code
EDIT : it seems that unity editor crashes due to an OutOfMemory exception. I do not know what is going on
public class PathFinding {
List<PathNode> openList;
List<PathNode> closedList;
Grid_Ian<PathNode> grid;
public PathFinding(Grid_Ian<PathNode> grid)
{
openList = new List<PathNode>();
closedList = new List<PathNode>();
this.grid = grid;
}
public List<PathNode> makePath(PathNode startNode, PathNode endNode)
{
if (startNode == null || endNode == null)
{
return new List<PathNode>();
}
if(grid.getGridObject(startNode.x,startNode.y) == null
|| grid.getGridObject(endNode.x, endNode.y) == null)
{
return new List<PathNode>();
}
startNode.hCost = calculateDistanceCost(startNode, endNode);
startNode.gCost = 0;
startNode.calculateFCost();
startNode.cameFrom = null;
openList.Add(startNode);
PathNode currentNode = startNode;
while (openList.Count > 0)
{
Debug.Log("LOOPING");
currentNode = getLowestFcost(openList);
openList.Remove(currentNode);
closedList.Add(currentNode);
if (currentNode.x == endNode.x &&
currentNode.y == endNode.y)
{
return getPath(currentNode);
}
foreach (PathNode next in getNeighbors(currentNode))
{
int newCost = currentNode.fCost + calculateDistanceCost(currentNode, next);
if (closedList.Contains(next)) continue;
if (next.cameFrom == null || newCost < next.fCost)
{
Debug.Log("NUEVO VECINO");
int nextCost = calculateDistanceCost(currentNode, next);
next.gCost = currentNode.gCost + nextCost;
next.hCost = currentNode.hCost + nextCost;
next.calculateFCost();
next.cameFrom = currentNode;
openList.Add(next);
}
}
}
return new List<PathNode>();
}
public List<PathNode> getNeighbors(PathNode currentNode)
{
List<PathNode> neighborNodes = new List<PathNode>();
if (currentNode.x - 1 >= 0)
{
//left
neighborNodes.Add(getNode(currentNode.x - 1, currentNode.y));
}
if (currentNode.x + 1 >= 0)
{
//right
neighborNodes.Add(getNode(currentNode.x + 1, currentNode.y));
}
//up
if (currentNode.y + 1 >= 0)
{
neighborNodes.Add(getNode(currentNode.x, currentNode.y + 1));
}
//down
if (currentNode.y - 1 >= 0)
{
neighborNodes.Add(getNode(currentNode.x, currentNode.y - 1));
}
return neighborNodes;
}
public PathNode getNode(int x, int y)
{
if(grid.getGridObject(x,y) == null)
{
}
return grid.getGridObject(x, y);
}
private PathNode getLowestFcost(List<PathNode> nodeList)
{
PathNode lowestNode = getNode(0,0); // TODO : ARREGLAR
int fCost = 0;
foreach (PathNode node in nodeList)
{
if (fCost > node.fCost)
{
fCost = node.fCost;
lowestNode = node;
}
}
return lowestNode;
}
private int calculateDistanceCost(PathNode a, PathNode b)
{
return Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y);
}
private List<PathNode> getPath(PathNode currentNode ){
List<PathNode> path = new List<PathNode>();
path.Add(currentNode);
while (currentNode.cameFrom != null)
{
currentNode = currentNode.cameFrom;
path.Add(currentNode);
}
path.Reverse();
return path;
}
public void getXY(Vector3 worldPosition, out int x, out int y)
{
grid.GetXY(worldPosition, out x, out y);
}
}
public class PathNode {
public int x, y;
public int hCost, gCost,fCost;
public PathNode cameFrom;
// G = start
// H = end
// F = G + H
public PathNode(int x,int y)
{
this.x = x;
this.y = y;
}
public void calculateFCost()
{
fCost = hCost + gCost;
}
}
I tried to check your Code with my and 2 Ideas you could try.
First is I think you shouldn't set the in the foreach the newCost before the if. So you could try:
foreach (PathNode next in getNeighbors(currentNode))
{
if (closedList.Contains(next)) continue;
int newCost = currentNode.fCost + calculateDistanceCost(currentNode, next);
if (next.cameFrom == null || newCost < next.fCost)
{
Debug.Log("NUEVO VECINO");
int nextCost = calculateDistanceCost(currentNode, next);
next.gCost = currentNode.gCost + nextCost;
next.hCost = currentNode.hCost + nextCost;
next.calculateFCost();
next.cameFrom = currentNode;
openList.Add(next);
}
}
Or one more thing. I have for some Reasons again a openList.Contains check before I add it to the openList. I do not know why again I did this but I think maybe you can try it, don't know if this helps. I made it quite a long time ago:
foreach (PathNode next in getNeighbors(currentNode))
{
int newCost = currentNode.fCost + calculateDistanceCost(currentNode, next);
if (closedList.Contains(next)) continue;
if (next.cameFrom == null || newCost < next.fCost)
{
Debug.Log("NUEVO VECINO");
int nextCost = calculateDistanceCost(currentNode, next);
next.gCost = currentNode.gCost + nextCost;
next.hCost = currentNode.hCost + nextCost;
next.calculateFCost();
next.cameFrom = currentNode;
if(!openList.Contains(next))
{
openList.Add(next);
}
}
}
I found two more things.
First your getNode function shouldn't look like this:
public PathNode getNode(int x, int y)
{
if(grid.getGridObject(x,y) == null)
{
}
return grid.getGridObject(x, y);
}
It should be:
public PathNode getNode(int x, int y)
{
if(grid.getGridObject(x,y) != null)
{
return grid.getGridObject(x, y);
}
return null; //need error handling
}
And the Second thing is for your getNeighbors. I do not know exactly how you create the Nodes, but for example they are in a Grid you should check if there is something for X and Y and not OutOfIndex.
Here my check out of my Code for one NeighborNode:
if (checkX >= 0 && checkX < gridSizeX)
{
if (checkY >= 0 && checkY < gridSizeY)
{
neighborList.Add(nodeArray[checkX, checkY]);
}
}
Because of getNode function you add to the openList NULL
I have a list of objects with lists of child objects which also have lists of child objects, etc. Here is a simplification with objects A and H:
I would like to print them out in a 2D array, like so:
But I keep getting this:
I'm using recursion for the first time, so tracking which row I'm on for which nested level is difficult. The code I'm using is cluttered with a bunch of carve-outs, so I'm hesitant to post it.
Does anyone have some pseudo-code that could help me fix my alignment issues?
List<int> levelStart = new List<int>();
List<int> levelEnd = new List<int>();
int tempEnding = 0;
public void propertyValues2(Object inv, object[,] dataTest)
{
Type objType = inv.GetType();
PropertyInfo[] properties = inv.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
DisplayNameAttribute DNA = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault();
object propValue = property.GetValue(inv, null);
var elems = propValue as System.Collections.IList;
if (exportAll == true || exportColumns.Contains(DNA.DisplayName))
{
if (elems != null && elems.Count != 0)
{
foreach (var item in elems)
{
if (!item.GetType().FullName.StartsWith("System.String"))
{
levelStart.Add(levelStart[levelStart.Count-1]);
propertyValues2(item, dataTest);
levelStart.RemoveAt(levelStart.Count - 1);
}
else if (exportColumns.Contains(DNA.DisplayName) || exportAll == true)
{
int counter = tempEnding;
if (dataTest[tempEnding, columnIDs[DNA.DisplayName]] != null)
{
//dataRow = level[0];
for (int di = tempEnding; dataTest[di, columnIDs[DNA.DisplayName]] != null; di++)
{
tempEnding = di+1;
}
}
dataTest[tempEnding, columnIDs[DNA.DisplayName]] = item;
if (tempEnding > endingRow)
{
endingRow = tempEnding;
}
}
}
tempEnding = levelStart[levelStart.Count-1];
//dataRow = level[0];//level[0]
}
else if (elems != null && elems.Count == 0)
{
int i = 0;
}
else if (propValue != null && propValue.ToString() != "")
{
//dataRow = level[0];//level[0]
//int counter = level[level.Count-1];
if(DNA.DisplayName == "Procedure" || DNA.DisplayName == "Revenue Code")
{
if (columnIDs.Keys.Contains("Procedure") && columnIDs.Keys.Contains("Revenue Code"))
{
for (int di = tempEnding; dataTest[di, columnIDs["Procedure"]] != null || dataTest[di, columnIDs["Revenue Code"]] != null; di++)
{
tempEnding++;
}
}
}
if (dataTest[tempEnding, columnIDs[DNA.DisplayName]] != null)
{
for (int di = tempEnding; dataTest[di, columnIDs[DNA.DisplayName]] != null; di++)
{
tempEnding++;
}
}
dataTest[tempEnding, columnIDs[DNA.DisplayName]] = propValue.ToString();
if (tempEnding > endingRow)
{
endingRow = tempEnding;
}
}
}
else if (elems != null && elems.Count != 0)
{
levelStart.Add(levelStart[levelStart.Count - 1]);
foreach (var item in elems)
{
if (!item.GetType().FullName.StartsWith("System.String"))
{
propertyValues2(item, dataTest);
}
else if (exportColumns.Contains(DNA.DisplayName) || exportAll == true)
{
int counter = levelStart[levelStart.Count - 1];
if (dataTest[counter, columnIDs[DNA.DisplayName]] != null)//level[level.Count - 1]
{
for (int di = levelStart[0]; dataTest[di, columnIDs[DNA.DisplayName]] != null; di++)
{
counter++;//level[level.Count - 1]
}
}
dataTest[counter, columnIDs[DNA.DisplayName]] = item;//level[level.Count - 1]
if (endingRow < counter)
{
endingRow = counter;
}
}
levelStart[levelStart.Count - 1] = endingRow + 1;
}
levelStart.RemoveAt(levelStart.Count - 1);
}
}
foreach (string columnToCopy in columnsToCopy)
{
if (exportColumns.Contains(columnToCopy) || exportAll == true)
{
for (int i = levelStart[0] + 1; i <= endingRow; i++)//level[0]
{
dataTest[i, columnIDs[columnToCopy]] = dataTest[i - 1, columnIDs[columnToCopy]];
}
}
}
}
Array Structure:
Here is the code I worked on yesterday. It seems to work in my test runs, but I will continue testing today. It has too many row changes while printing for me to keep track of, so I'm not completely sure it always prints correctly:
int startInvoice = 1;
List<int> levelStart = new List<int> { 1 };
List<int> levelEnd = new List<int> { 1 };
List<int> maxWithinLevel = new List<int> { 0 };
int depth = 0;
public int propertyValues3(Object inv, object[,] dataTest)
{
//int maxWithinLevel = 0;
PropertyInfo[] properties = inv.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
DisplayNameAttribute DNA = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), false).FirstOrDefault();
object propValue = property.GetValue(inv, null);
var elems = propValue as System.Collections.IList;
//if its a list
if (elems != null && elems.Count != 0)
{
levelStart.Add(levelEnd[levelEnd.Count - 1]);
levelEnd.Add(levelStart[levelStart.Count - 1]);
foreach (var item in elems)
{
//if its a class
if (!item.GetType().FullName.StartsWith("System.String"))
{
depth++;
levelStart[levelStart.Count-1] = propertyValues3(item, dataTest);
depth--;
if (levelEnd[levelEnd.Count - 1] < levelStart[levelStart.Count - 1])
{
levelEnd[levelEnd.Count - 1] = levelStart[levelStart.Count - 1];
}
}
//if its a string
else
{
if (dataTest[levelEnd[levelEnd.Count - 1], columnIDs[DNA.DisplayName]] != null)
{
for (int i = levelEnd[levelEnd.Count - 1]; dataTest[i, columnIDs[DNA.DisplayName]] != null; i++)
{
levelEnd[levelEnd.Count - 1] = i + 1;
}
}
dataTest[levelEnd[levelEnd.Count - 1], columnIDs[DNA.DisplayName]] = item; //legit
if (levelEnd[levelEnd.Count - 1] > endingRow)//legit
{
endingRow = levelEnd[levelEnd.Count - 1];
}
}
}
levelStart.RemoveAt(levelStart.Count - 1);
//if (!elems[0].GetType().FullName.StartsWith("System.String"))
if(depth>0)
{
maxWithinLevel.Add(levelEnd[levelEnd.Count - 1]);
}
levelEnd.RemoveAt(levelEnd.Count - 1);
}
//if elems is empty
else if (elems != null && elems.Count == 0)
{
int i = 0;
}
//if it's not a list
else if (propValue != null && propValue.ToString() != "")
{
//handle procedure/revcode
if (DNA.DisplayName == "Procedure" || DNA.DisplayName == "Revenue Code")
{
if (columnIDs.Keys.Contains("Procedure") && columnIDs.Keys.Contains("Revenue Code"))
{
for (int i = levelEnd[levelEnd.Count - 1]; dataTest[i, columnIDs["Procedure"]] != null || dataTest[i, columnIDs["Revenue Code"]] != null; i++)
{
levelEnd[levelEnd.Count - 1]++;
}
}
}
if (dataTest[levelEnd[levelEnd.Count - 1], columnIDs[DNA.DisplayName]] != null)
{
for (int i = levelEnd[levelEnd.Count - 1]; dataTest[i, columnIDs[DNA.DisplayName]] != null; i++)//legit
{
levelEnd[levelEnd.Count - 1]++;
}
}
dataTest[levelEnd[levelEnd.Count - 1], columnIDs[DNA.DisplayName]] = propValue.ToString();//level[level.Count - 1]
if (levelEnd[levelEnd.Count - 1] > endingRow)
{
endingRow = levelEnd[levelEnd.Count - 1];
}
}
}
if (levelStart.Count==0 || levelStart.Max() < maxWithinLevel.Max())
{
return maxWithinLevel.Max() + 1;
}
else return levelStart.Max();
}
This question already has answers here:
Iterating through the Alphabet - C# a-caz
(10 answers)
Closed 8 years ago.
I am trying to increment an alphanumeric string using a given charset
say the input is 0000a
then the output should be 0000b
However when the input reaches the defined limit, say zzzzz
it should reset to 00001
I have read the following Increment of Alphabet in c#
using
char c
c++
seem to be the best way to do this.
I have the following class
Namespace BatchNo_Generator
{
class RandomStringGenerator
{
public enum Mode
{
ALPHANUMERIC = 1,
ALPHA = 2,
NUMERIC = 3,
}
public string genbase(int length, string mode)
{
int j = 0;
if (mode == "ALPHANUMERIC")
{
StringBuilder s = new StringBuilder();
while (j < length - 1)
{
s.Insert(0, "0");
j++;
}
s.Insert(s.Length, "1");
return s.ToString();
}
if (mode == "ALPHA")
{
StringBuilder s = new StringBuilder();
while (j < length - 1)
{
s.Insert(0, "a");
j++;
}
s.Insert(s.Length, "a");
return s.ToString();
}
if (mode == "NUMERIC")
{
StringBuilder s = new StringBuilder();
while (j < length - 1)
{
s.Insert(0, "0");
j++;
}
s.Insert(s.Length, "1");
return s.ToString();
}
return "";
}
public string gennext(string current, string mode, char endchar)
{
StringBuilder s = new StringBuilder(current);
int i = current.Length;
if (mode == "ALPHA")
{
for (int j = i; j > 0; j--)
{
if (checkend(s[j - 1], endchar, 'a', mode) == true)
{
s[j] = resetchar(s[j], 'a');
incrementchar(s[j + 1], mode);
}
else
{
char c = incrementchar(s[j - 1], mode);
s.Remove(j - 1, 1);
s.Insert(j - 1, c);
break;
}
}
return s.ToString();
}
if (mode == "NUMERIC")
{
for (int j = i; j > 0; j--)
{
if (checkend(s[j - 1], endchar, '0', mode) == true)
{
s[j-1] = resetchar(s[j-1], '0');
incrementchar(s[j - 1], mode);
}
else
{
char c = incrementchar(s[j - 1], mode);
s.Remove(j - 1, 1);
s.Insert(j - 1, c);
}
}
return s.ToString();
}
if (mode == "ALPHANUMERIC")
{
for (int j = i; j > 0; j--)
{
if (checkend(s[j - 1], endchar, '0', mode) == true)
{
s[j-1] = resetchar(s[j-1], '0');
char c = incrementchar(s[j - 1], mode);
}
else
{
{
char c = incrementchar(s[j - 1], mode);
s.Remove(j - 1, 1);
s.Insert(j - 1, c);
break;
}
}
}
return s.ToString();
}
return "";
}
public char incrementchar(char c, string mode)
{
char cnew = c++;
switch (mode)
{
case "ALPHA":
{
if (char.IsLetter(c) == false) { cnew++; }
else { return char.ToLower(c); }
break;
}
case "NUMERIC":
{
if (char.IsDigit(c) == false) { cnew++; }
else { return c; }
break;
}
case "ALPHANUMERIC":
{
while (char.IsLetterOrDigit(c) == false) { c++; }
// if (char.IsLetterOrDigit(c) == false) { cnew++; }
// else { return char.ToLower(c); }
return char.ToLower(c);
break;
}
}
return '?';
}
public bool checkend(char current, char end, char start, string mode)
{
if (current == end) { return true; }
else { return false; }
}
public char resetchar(char inputchar, char defaultchar)
{
return defaultchar;
}
public static IEnumerable<string> GetColumns(char startchar, char endchar)
{
string s = null;
for (char c2 = startchar; c2 <= endchar + 1; c2++)
{
for (char c = 'A'; c <= 'Z'; c++)
{
if (char.IsLetter(c) == true)
{
yield return s + char.ToLower(c);
}
}
if (char.IsLetterOrDigit(c2) == true)
{
s = c2.ToString();
}
}
}
}
}
Which I tried with
RandomStringGenerator test = new RandomStringGenerator();
MessageBox.Show(test.gennext("0zzzz","ALPHANUMERIC",'0'));
The issue I have is that this input 0zzzz returns 0zzzÂȘ
Any help would be appreciated
The code is sloppy I know, i will be cleaning it up when I get a working set
public enum Mode
{
AlphaNumeric = 1,
Alpha = 2,
Numeric = 3
}
public static string Increment(string text, Mode mode)
{
var textArr = text.ToCharArray();
// Add legal characters
var characters = new List<char>();
if (mode == Mode.AlphaNumeric || mode == Mode.Numeric)
for (char c = '0'; c <= '9'; c++)
characters.Add(c);
if (mode == Mode.AlphaNumeric || mode == Mode.Alpha)
for (char c = 'a'; c <= 'z'; c++)
characters.Add(c);
// Loop from end to beginning
for (int i = textArr.Length - 1; i >= 0; i--)
{
if (textArr[i] == characters.Last())
{
textArr[i] = characters.First();
}
else
{
textArr[i] = characters[characters.IndexOf(textArr[i]) + 1];
break;
}
}
return new string(textArr);
}
// Testing
var test1 = Increment("0001", Mode.AlphaNumeric);
var test2 = Increment("aab2z", Mode.AlphaNumeric);
var test3 = Increment("0009", Mode.Numeric);
var test4 = Increment("zz", Mode.Alpha);
var test5 = Increment("999", Mode.Numeric);