stuck on minimax algorithm - algorithm

I'm attempting my first chess engine. Anyone who is familiar with the subject will know a thing or two about the minimax algorithm. I need to generate a combination of every possible move on the board for every piece. I've looked up a number of examples but can't get mine to work. I don't know what I am doing wrong. I'm just focused on the generation of every possible moves, to a certain depth, to get the leaf nodes.
The problem I'm facing is that my current implementation is making one move for black then making continuous moves for white without letting the black move again when it should be alternating back and fourth. I use Direct recursion to and loop through the available moves. I expect the function to start from the top every time but the direct recursion isn't working the way I thought it would. The loop keeps getting iterated through without starting from the top of the function and I don't know why. This means that the getAvailableMoves(maximizer) isn't being called every time like it should be (I think).
if anyone could point out what I'm doing wrong it would be appreciated.
public int miniMax(int depth, boolean maximizer)
{
if(depth == 0) { return 1234; }
int countMoves = 0;
Map<ChessPiece, Position> availableMoves = getAvailableMoves(maximizer);
int bestMove = 0;
for(Map.Entry<ChessPiece, Position> entry : availableMoves.entrySet())
{
ChessPiece piece = entry.getKey();
Position pos = entry.getValue();
piece.move(board, pos.getX(), pos.getY());
maximizer = !maximizer;
miniMax(depth-1, maximizer);
}
return 1234;
}

Related

How to deal with draws by repetition in a transposition table?

I'm trying to solve Three Men's Morris. The details of the game don't matter, that that it's a game similar to tic tac toe, but players may be able to force a win from some positions, or be able to force the game to repeat forever by playing the same moves over and over in other positions. So I want to make a function to tell whether a player can force a win, or force a draw by repetition.
I've tried using simple negamax, which works fine but is way too slow to traverse the game tree with unlimited depth. I want to use transposition tables since the number of possible positions is very low (<6000) but that's where my problem comes from. As soon as I add in the transposition table (just a list of all fully searched positions and their values, 0, 1, or -1) the AI starts making weird moves, suddenly saying its a draw in positions where I have a forced win.
I think the problem comes from transposition table entries being saved as draws, since it seemed to work when I limited the depth and only saved forced wins, but I'm not sure how to fix the problem and allow for unlimited depth.
Here's the code in case there's an issue with my implementation:
int evaluate(ThreeMensMorris &board){
//game is won or drawn
if(board.isGameWon()) return -1; //current player lost
if(board.isRepetition()) return 0; //draw by repetition
//check if this position is already in the transposition table
//if so, return its value
uint32_t pos = board.getPosInt();
for(int i = 0; i < transIdx; i++)
if(transList[i] == pos)
return valueList[i];
//negamax
//NOTE: moves are formatted as two numbers, "from" and "to",
//where "to" is -1 to place a piece for the first time
//so this nested for loop goes over all possible moves
int bestValue = -100;
for(int i = 0; i < 9; i++){
for(int j = -1; j < 9; j++){
if(!board.makeMove(i, j)) continue; //illegal move
int value = -1 * evaluate(board, depth+1);
board.unmakeMove(i, j);
if(value > bestValue) bestValue = value;
}
}
//we have a new position complete with a value, push it to the end of the list
transList[transIdx] = pos;
valueList[transIdx] = bestValue;
transIdx++;
return bestValue;
}
I suggest you start looking at transposition tables for chess: https://www.chessprogramming.org/Transposition_Table. You need to give each gamestate an (almost) unique number, e.g. through Zobrist hashing, maybe this is what you do in board.getPosInt()?
A possible fault is that you don't consider who's turn it is? Even if a position is the same on the board, it is not the same if in one position it is player A turn and in the other player B. Are there other things to consider in this game? In chess there are things like en passant possibilities that needs to be considered, and other special cases, to know if the position is actually the same, not just the pieces themselves.
Transposition tables are really complex and super hard to debug unfortunately. I hope you get it to work though!

Data structure for fast retrieval of middle element

I need to solve a problem where I can insert left and right. And then extract from the data in the middle of the array.
I already tried to solve this with using linkedlist. However this approach was too slow to be accepted as an solution.
What data structure would you suggest I use if i need fast insertion at the beginning and the end of a list as well as fast retrieval of the middle element?
Here is the code I've already tried with:
private static void middleQueue(int loopLength, String[] commandsArray) {
LinkedList<String> linkedList = new LinkedList<>();
int counterSize = 0;
for (int i = 0; i < commandsArray.length; i++) {
if(commandsArray[i].equals("R")){
linkedList.add(commandsArray[i+1]);
i++;
counterSize++;
}
else if(commandsArray[i].equals("L")){
linkedList.addFirst(commandsArray[i+1]);
i++;
counterSize++;
}
else if(commandsArray[i].equals("E")){
if((linkedList.size() & 1) == 0)
System.out.println(linkedList.remove((counterSize / 2)-1));
else
System.out.println(linkedList.remove((counterSize / 2)));
counterSize--;
}
}
}
Take 3 pointers first, middle, last pointer.
Then there are 2 cases:
if you add from first
ans. You need to shift the middle pointer to left side then its current position.
if you add from last
ans. You just need increase the middle pointer to its next.
As others have pointed out, you need a data structure with 3 pointers.
A doubly linked list will be good. But , if you will use a singly list list (with pointers only in one direction), then you will either be able to only move right or left (not both at the same time). SLL wont serve the purpose.
See this for the implementation of a very similar problem: https://www.geeksforgeeks.org/design-a-stack-with-find-middle-operation/
In Java, you should use two ArrayDeques, one for the first half of the the list and one for the last half. Move elements from one to the other to keep them the same size, so you always have access to the middle.
This will be faster and more memory-efficient than using LinkedList.

Confusion on MiniMax algorithm

So i'm currently working on an assignment that revolves around the MiniMax algorithm on a game that is a combination of Mancala and NIM. The way the program works is to ask user for the current state of board and the program is suppose to spit out what the first move the user should take to win the game. I'm just confused on is am I suppose to generate the entire game tree with all the possible solutions and at the leaf nodes have the utility function first then have the MiniMax algorithm recursively run through it or does the tree get created within the MiniMax algorithm? I'm sorry if this question is very unclear but I'm just kind of stuck on this idea and I can't seem to understand it.
The proper way to write a minimax function is to traverse the search tree by making and unmaking moves. You only ever store one game state at a time, and by making and unmaking moves on that game state you traverse the whole tree. If this is confusing then it will be helpful to look at some minimax psudocode. Note that there are two commonly used variants of minimax, regular minimax and negamax. The psudeocode is minimax because it is more intuituve but in practice I would recommend the negamax variant because it is much simpler:
int max(int depth){
if(this state is terminal){//won, lost, drawn, or desired search depth is reached
return value
}
//if the state is non terminal
//we want to examine all child nodes. We do this by making all possible moves from this state, calling the min function
//(all childs of max nodes are min nodes) and then unmaking the moves.
int bestVal = -infinity;
generate move list;
for(all moves in move list){
makeMove(this move in move list);
int val = min(depth -1);
unMakeMove(this move in move list);
bestVal = max(val,bestVal);
}
return bestVal;
}
int min(int depth){
if(this state is terminal){//won, lost, drawn, or desired search depth is reached
return value
}
//if the state is non terminal
//we want to examine all child nodes. We do this by making all possible moves from this state, calling the max function
//(all childs of min nodes are max nodes) and then unmaking the moves.
int bestVal = +infinity;
generate move list;
for(all moves in move list){
makeMove(this move in move list);
int val = min(depth -1);
unMakeMove(this move in move list);
bestVal = min(val,bestVal);
}
return bestVal;
}
Thus you traverse the entire tree by keeping track of one game state and recursively making and unmaking moves on that game state. Once you understand this look into alpha beta pruning. Also be aware that this function only returns the value of the best move not the move itself. You will want a special function that keeps track of the best move as well to call at the root.

Non recursive DFS algorithm for simple paths between two points

I am looking for a non-recursive Depth first search algorithm to find all simple paths between two points in undirected graphs (cycles are possible).
I checked many posts, all showed recursive algorithm.
seems no one interested in non-recursive version.
a recursive version is like this;
void dfs(Graph G, int v, int t)
{
path.push(v);
onPath[v] = true;
if (v == t)
{
print(path);
}
else
{
for (int w : G.adj(v))
{
if (!onPath[w])
dfs(G, w, t);
}
}
path.pop();
onPath[v] = false;
}
so, I tried it as (non-recursive), but when i check it, it computed wrong
void dfs(node start,node end)
{
stack m_stack=new stack();
m_stack.push(start);
while(!m_stack.empty)
{
var current= m_stack.pop();
path.push(current);
if (current == end)
{
print(path);
}
else
{
for ( node in adj(current))
{
if (!path.contain(node))
m_stack.push(node);
}
}
path.pop();
}
the test graph is:
(a,b),(b,a),
(b,c),(c,b),
(b,d),(d,b),
(c,f),(f,c),
(d,f),(f,d),
(f,h),(h,f).
it is undirected, that is why there are (a,b) and (b,a).
If the start and end nodes are 'a' and 'h', then there should be two simple paths:
a,b,c,f,h
a,b,d,f,h.
but that algorithm could not find both.
it displayed output as:
a,b,d,f,h,
a,b,d.
stack become at the start of second path, that is the problem.
please point out my mistake when changing it to non-recursive version.
your help will be appreciated!
I think dfs is a pretty complicated algorithm especially in its iterative form. The most important part of the iterative version is the insight, that in the recursive version not only the current node, but also the current neighbour, both are stored on the stack. With this in mind, in C++ the iterative version could look like:
//graph[i][j] stores the j-th neighbour of the node i
void dfs(size_t start, size_t end, const vector<vector<size_t> > &graph)
{
//initialize:
//remember the node (first) and the index of the next neighbour (second)
typedef pair<size_t, size_t> State;
stack<State> to_do_stack;
vector<size_t> path; //remembering the way
vector<bool> visited(graph.size(), false); //caching visited - no need for searching in the path-vector
//start in start!
to_do_stack.push(make_pair(start, 0));
visited[start]=true;
path.push_back(start);
while(!to_do_stack.empty())
{
State &current = to_do_stack.top();//current stays on the stack for the time being...
if (current.first == end || current.second == graph[current.first].size())//goal reached or done with neighbours?
{
if (current.first == end)
print(path);//found a way!
//backtrack:
visited[current.first]=false;//no longer considered visited
path.pop_back();//go a step back
to_do_stack.pop();//no need to explore further neighbours
}
else{//normal case: explore neighbours
size_t next=graph[current.first][current.second];
current.second++;//update the next neighbour in the stack!
if(!visited[next]){
//putting the neighbour on the todo-list
to_do_stack.push(make_pair(next, 0));
visited[next]=true;
path.push_back(next);
}
}
}
}
No warranty it is bug-free, but I hope you get the gist and at least it finds the both paths in your example.
The path computation is all wrong. You pop the last node before you process it's neighbors. Your code should output just the last node.
The simplest fix is to trust the compiler to optimize the recursive solution sufficiently that it won't matter. You can help by not passing large objects between calls and by avoiding allocating/deallocating many objects per call.
The easy fix is to store the entire path in the stack (instead of just the last node).
A harder fix is that you have 2 types of nodes on the stack. Insert and remove. When you reach a insert node x value you add first remove node x then push to the stack insert node y for all neighbours y. When you hit a remove node x you need to pop the last value (x) from the path. This better simulates the dynamics of the recursive solution.
A better fix is to just do breadth-first-search since that's easier to implement in an iterative fashion.

Move along a path of points with even speed

I have a sequence of points and i have an object that should move along the path described by these points.
I know that i need to find the corresponding segment depending on the time, but i dont know how to do this precisely.
Note that the speed should be constant along the path, so you have to take the distance of a segment into account.
Something like this? To find the segment at a particular time, loop through all the segments, add the length of the segment until you reach the target time.
public int findSegment(int [] segments, int time) {
time_so_far = 0;
int i = 0;
while (time_so_far < time) {
time_so_far += segments[i];
i++;
}
return i-1;
}
If the segment lengths are distances, and you are considering a speed that isn't 1, then you'd need to do time_so_far += segments[i] / speed
Beofre start assign time moments to each point: the first point gets time=0, every next point gets time[i+1]=time[i]+distance(i,i+1)/velocity.
Then for each time moment you can calculate position in the following way:
Find first point, for which time[i]<=current_time
Find exact position: point[i]+(current_time-time[i])*(point[i+1]-point[i])

Resources