Okay so just not sure if I'm doing this right then. I'm trying to use negamax with alpha beta pruning to find my move. What I have so far
getbestmove: This is the function I initially call to try and find my best move. I get all the possible moves based off the current players position. I than loop through them all and run them through the negamax function to evaluate how good of a placement they are.
[source lang="csharp"]
public static List<BoardView> GetBestMove(BoardView myview, int depth, int color)
{
var bestMoves = new List<BoardView>();
double highestScore = int.MinValue;
double tmpScore;
Boolean first = true;
List<BoardView> validmoves = new List<BoardView>();
//getting all valid children based off the current players position
validmoves.AddRange(myview.getChildren(color));
for (int x = 0; x < validmoves.Count; x++)
{
BoardView move = validmoves[x];
//doing minimax tree / negamax alpha beta pruning (tree?) for upcoming potential moves to evaluate how well this first move way
if (first)
{
tmpScore = NegaMax(move, int.MinValue, int.MaxValue, depth, -color);
first = false;
}
else
tmpScore = NegaMax(move, highestScore, int.MaxValue, depth, -color);
if (tmpScore > highestScore)
{
bestMoves.Clear();
bestMoves.Add(move);
highestScore = tmpScore;
}
else if (tmpScore == highestScore)
{
move.board.myscore = highestScore;
bestMoves.Add(move);
}
}
return bestMoves;
}
[/source]
[source lang="csharp"]
private static double NegaMax(BoardView myview, double alpha, double beta, int depth, int color)
{
if (depth <= 0 || myview.currentplayer.checkWin())
return myview.getScore();
List<BoardView> validmoves = new List<BoardView>();
validmoves.AddRange(myview.getChildren(color));
foreach (BoardView child in validmoves)
{
double temp = -NegaMax(child, -beta, -alpha, depth - 1, -color);
if (temp > alpha)
alpha = temp;
if (alpha >= beta)
break; // cut-off
}
return alpha;
}
[/source]
and my boardview class. This just pretty much holds the board and 2 players. The constructor takes either an old view or a board and 2 players. It than makes deep copies of the objects.
swap: just swaps the current and opponent references
getChildren: Returns a list of all possible plays based off the current players position on the board. It first checks for movement for the current player than wall positions. For wall positions it first checks if the current player is between the opponent and the goal and which direction the opponent is going. This should create 4 scenarions
i) Opponent is going towards start of the board and I'm between my opponent and the goal spaces. The wall logic will start placing walls behind me going from my position > 0 ; maxsquare > position
ii) Opponent is going towards start of board and I'm not between my opponent and the goal. Same thing as above but I start placing the walls behind the opponent
iii & iv) same thing but replace start of board with end of board and the logic is position > maxsquare; 0 > position
Getscore: I use an A* search to find the shortest path for me to a goal square and my opponent and his goal squares. The main weight is based off the distance to the goal with some weight on bad wall positions. While I don't think this equation is going get great defensive wall positions (see picture posted earlier in this thread). In theory moving away from the goal or bad wall placements should return bad scores resulting in those paths getting cut off.
[source lang="csharp"] public class BoardView
{
public Board board { get; set; }
public Player currentplayer { get; set; }
public Player opponent { get; set; }
//public BoardView parent { get; set; }
//makes a clone of an old view
public BoardView(BoardView oldview)
{
//parent = oldview;
board = new Board(oldview.board);
currentplayer = new Player(oldview.currentplayer);
opponent = new Player(oldview.opponent);
}
//makes a new board view by combining 2 players and a board
public BoardView(Board myboard, Player p1, Player p2)
{
board = new Board(myboard);
currentplayer = new Player(p1);
opponent = new Player(p2);
}
//swap the players
public void swap()
{
Player temp = currentplayer;
currentplayer = opponent;
opponent = temp;
}
/*
* returns a list of all avilable moves from a given view
* color = 1 = current player
* color = -1 = opponent
*/
public List<BoardView> getChildren(int color)
{
List<BoardView> children = new List<BoardView>();
//getting all movement views from the current player
GameSpace myspace = board.grid[currentplayer.position];
List<GameSpace> movement = new List<GameSpace>(AStar.neighborsAndJumps(myspace, board, opponent.symbol));
if (currentplayer.symbol != Global.getType(color))
{
myspace = board.grid[opponent.position];
movement = new List<GameSpace>(AStar.neighborsAndJumps(myspace, board, currentplayer.symbol));
}
foreach (GameSpace mymove in movement)
{
BoardView newReality = new BoardView(this);
if (newReality.currentplayer.symbol != Global.getType(color))
newReality.swap();
newReality.board.MovePawn(newReality.currentplayer.position, mymove.SQNum, newReality.currentplayer.symbol);
newReality.currentplayer.position = mymove.SQNum;
children.Add(newReality);
}
//if the current player has walls left, find all valid moves
if (currentplayer.numberwalls > 0)
{
/*
* 0 = horz walls
* 1 = vertical walls
*/
for (int y = 0; y < 2; y++)
{
//go through and try a wall at every valid position
for (int x = 0; x < board.grid.Count; x++)
{
int pos = 0;
int temp = opponent.position;
//finding where I should start the wall placement. If my goal[0] is > size (length of board) than it means I'm heading towards the back squares
if (currentplayer.goal[0] >= Global.SIZE)
{
/*
* opponent is moving to the front squares. Check if current player or opponent is closer to the goal.
* If I'm closer start placing walls behind me, if opponent is closer place walls behind opponent
*/
if (currentplayer.position < opponent.position)
temp = currentplayer.position;
/*
* starting at the far right of the row the opponent or current player are on (depending who is closer to the goal). Keep moving left checking each square for wall placement.
* Hopefully this will quickly find a good spot to place a wall that slows the opponent and not me
*/
pos = (temp / Global.SIZE) * Global.SIZE + (Global.SIZE - 1) - x;
if (pos < 0)
pos += board.grid.Count;
}
else
{
//opponent is moving to the back squares
if (currentplayer.position > opponent.position)
temp = currentplayer.position;
/*
* starting at the far left of the row the opponent or current player are on (depending who is closer to the goal). Keep moving right checking each square for wall placement.
* Hopefully this will quickly find a good spot to place a wall that slows the opponent and not me
*/
pos = (temp / Global.SIZE) * Global.SIZE + x;
if (pos >= board.grid.Count)
pos -= board.grid.Count;
}
BoardView newReality = new BoardView(this);
if (newReality.currentplayer.symbol != Global.getType(color))
newReality.swap();
if (y == 0)
{
//if true the wall has been placed and add it to the list of vald moves
if (newReality.board.SetHorzWall(pos, newReality.currentplayer, newReality.opponent))
{
children.Add(newReality);
}
}
else
{
//if true the wall has been placed and add it to the list of vald moves
if (newReality.board.SetVertWall(pos, newReality.currentplayer, newReality.opponent))
{
children.Add(newReality);
}
}
}
}
}
return children;
}
public double getScore()
{
double score = 0;
//finding my shortest path to a goal
List<GameSpace> mypath = new List<GameSpace>(board.findPath(true, currentplayer, opponent));
//finding opponents shortest path to a goal
List<GameSpace> opppath = new List<GameSpace>(board.findPath(true, opponent, currentplayer));
score = 100 * (opppath.Count - mypath.Count) + currentplayer.numberwalls - opponent.numberwalls;
return score;
} [/source]
Edit: For some reason my negamax function won't show right with the code tag
private static double NegaMax(BoardView myview, double alpha, double beta, int depth, int color)
{
if (depth <= 0 || myview.currentplayer.checkWin())
return myview.getScore();
List<BoardView> validmoves = new List<BoardView>();
validmoves.AddRange(myview.getChildren(color));
foreach (BoardView child in validmoves)
{
double temp = -NegaMax(child, -beta, -alpha, depth - 1, -color);
if (temp > alpha)
alpha = temp;
if (alpha >= beta)
break; // cut-off
}
return alpha;
}
edit 2: Found a bug when I was calling swap. Did more checking that swapping occurs at the right time. Man that getchild function is atrocious :S