I'm using LigGdx to make a game, it looks like a RPG game. When enemy is in alert state, it have to follow the player, but it can move only forward, backward, left and right, and also have to divert objects when it collides, searching for the best way to reach the player, i'm newbie on game development, and my algorithm may be completely wrong, so, I really need help...
private void enemyInAlert(Enemy enemy, float delta) {
detectDirection(enemy);
enemyWalking(enemy, delta);
if (Math.round(getDistanceXofLastPosition(enemy)) == 0 && Math.round(getDistanceYofLastPosition(enemy)) == 0) {
enemy.setState(States.IDLE);
lastPosition = null;
}
}
private void detectDirection(Enemy enemy) {
float diff = getDistanceXofLastPosition(enemy) - getDistanceYofLastPosition(enemy);
if (diff < 0) {
getDirectionX(enemy);
} else if (diff > 0) {
getDirectionY(enemy);
}
}
private void getDirectionY(Enemy enemy) {
int enemyY = Math.round(enemy.getY());
int lastPositionY = Math.round(lastPosition.getY());
if (enemyY < lastPositionY && enemy.isDirectionBlocked(Direction.FORWARD) == false) { //Enemy needs to go forward
enemy.setDirection(Direction.FORWARD);
enemy.blockDirection(Direction.BACKWARD);
} else if (enemyY > lastPositionY && enemy.isDirectionBlocked(Direction.FORWARD) == false) { //Enemy needs to go backward
enemy.setDirection(Direction.BACKWARD);
enemy.blockDirection(Direction.FORWARD);
} else { //Enemy needs to change direction
if (enemy.isDirectionBlocked(Direction.LEFT) == false || enemy.isDirectionBlocked(Direction.LEFT) == false) {
enemy.blockDirection(Direction.BACKWARD);
enemy.blockDirection(Direction.FORWARD);
getDirectionX(enemy);
} else {
sortRandomDirection(enemy);
}
}
}
private void getDirectionX(Enemy enemy) {
int enemyX = Math.round(enemy.getX());
int lastPositionX = Math.round(lastPosition.getX());
if (enemyX < lastPositionX && enemy.isDirectionBlocked(Direction.RIGHT) == false) { //Enemy needs to go right
enemy.setDirection(Direction.RIGHT);
enemy.blockDirection(Direction.LEFT);
} else if (enemyX > lastPositionX && enemy.isDirectionBlocked(Direction.LEFT) == false) {
enemy.setDirection(Direction.LEFT);
enemy.blockDirection(Direction.RIGHT);
} else { //Enemy needs to change direction
if (enemy.isDirectionBlocked(Direction.FORWARD) == false && enemy.isDirectionBlocked(Direction.BACKWARD) == false) {
enemy.blockDirection(Direction.LEFT);
enemy.blockDirection(Direction.RIGHT);
getDirectionY(enemy);
} else {
sortRandomDirection(enemy);
}
}
}
I'm accepting suggestions, I can change all the code, no mercy... Sorry for the bad English :D
Thanks!!
Edit: now, I'm trying to use A*, or something like it. :D ... my code:
private void calculateRoute(Enemy enemy) {
int lowerPath = getDistanceXofLastPosition(enemy.getBounds()) + getDistanceYofLastPosition(enemy.getBounds());
path = new ArrayList<Rectangle>();
Rectangle finalRect = new Rectangle(enemy.getBounds());
List<Rectangle> openList = new ArrayList<Rectangle>();
while (getDistanceXofLastPosition(finalRect) > 0 || getDistanceYofLastPosition(finalRect) > 0) {
for (int i = -1; i < 2; i+= 1) {
outerloop:
for (int j = -1; j < 2; j+= 1) {
Rectangle temp = new Rectangle(finalRect);
temp.offSet(i, j);
if (openList.contains(temp)) {
continue;
}
if ((i == -1 && j == -1) || (i == 1 && j == -1) || (i == 0 && j == 0) || (i == 1 && j == -1) || (i == 1 && j == 1)) {
continue;
}
for (Collider collider : colliders) {
if (collider.isSolid() && Utils.detectCollision(temp, collider.getBounds())) {
continue outerloop;
}
}
openList.add(temp);
}
}
int lowerDistance = Integer.MAX_VALUE;
for (Rectangle rect : openList) {
int distance = getDistanceXofLastPosition(rect) + getDistanceYofLastPosition(rect);
distance = distance + lowerPath;
if (distance < lowerDistance) {
lowerDistance = distance;
finalRect = rect;
}
}
path.add(new Rectangle(finalRect));
}
}
but is very slow, what I can do to increase performance?
You may want to look into A* .
You can easily convert your map into a graph, using each tile as a vertex, and having said vertex connected with an edge to all the other vertices close to it. The cost associated with the edge may be variable, for instance, moving i tile on a river could cost more than moving one tile in a plane.
Then you can use a path search algorithm to find the best path from one point to another. Using this algorithm will have 2 downsides :
It has an high computational cost
It always finds the optimal solution, making your bot smarter than the average player :)
If computational and storage cost are indeed a problem, you can resort to one of A*'s cousins, such as
IDA* for cheaper memory requirements, iterate over the depth of the solutions
SMA* bounds the amount of memory the algorithm can use
Related
I've coded an algorithm that can enter multiple trades based on several signals from different indicators. I am now trying to figure out how to close half of my open positions (partial close) when they've reached a target profit range and then add a trailing stop on the other half of those particular trades that have been closed.
Any ideas on how this can be done? The main issue I'm having is when it comes to writing a code that allows the EA to detect when an open order is the other half of a previously closed order.
Any advice will be much appreciated.
//CLOSING ORDER LOOP
int ticket = OrderTicket();
double orderlots = OrderLots();
for (int b = OrdersTotal() -1 ; b >=0 ;b--)
{
if (!OrderSelect (b, SELECT_BY_POS, MODE_TRADES))continue;
if (OrderSymbol() == Symbol() && OrderType() <= OP_SELL && NormalizeDouble(orderlots,1) == NormalizeDouble(lots, 1))
//CHECK PARTIAL CLOSE
{
if(OrderType() == OP_BUY && (Bid - OrderOpenPrice()) >= TakeProfit)
{
if(!OrderClose(ticket, orderlots/2, Bid, 3, Blue))
return;
}
else
{
if(OrderType() == OP_SELL && (OrderOpenPrice() - Ask) >= (TakeProfit))
{
if(!OrderClose(ticket, orderlots/2, Ask, 3, Blue))
{
bool answer = OrderSelect (b, SELECT_BY_POS, MODE_TRADES);
ticket = OrderTicket();
orderlots = OrderLots();
}
return;
//REDECLARE TICKETS
}
}
if (OrderSymbol() == Symbol() && OrderType() <= OP_SELL && NormalizeDouble(orderlots,1) != NormalizeDouble(lots, 1))continue;
//END
{
if(OrderType() == OP_BUY)
{
if(Bid - OrderOpenPrice() > stopLossATR)
{
if(!OrderModify(ticket, OrderOpenPrice(),Bid + (stopLossATR), OrderTakeProfit(), 0 , Green))
return;
}
}
else
if(OrderType() == OP_SELL)
{
if(OrderOpenPrice() - Ask > (stopLossATR))
{
if(!OrderModify(ticket, OrderOpenPrice(), Ask + (stopLossATR), OrderTakeProfit(), 0 , Green))
return;
if (!OrderSelect (b, SELECT_BY_POS, MODE_TRADES))continue;
}
}
}
}
}
I've been trying for a few days to think of a simple case when my solution to the word ladders problem breaks down. I tried to implement a DP solution with memorization. I would greatly appreciate an explanation why DP doesnt work here. Here is how I implemented my (incorrect) DP solution.
public class Solution {
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
int[] visited = new int[wordList.size()];
HashMap<String, Integer> map = new HashMap<>();
int res = ladderHelper( beginWord,endWord, wordList,visited,map);
return res;
}
private int ladderHelper(String beginWord, String endWord, List<String> wordList, int[] visited, HashMap<String, Integer> map) {
if (beginWord.equals(endWord)) return 1;
int bestSeen = 0;
for (int i = 0; i < wordList.size(); i++) {
if (visited[i] == 1) continue;
if (!validJump(beginWord, wordList.get(i))) continue;
if (map.containsKey(wordList.get(i))) {
int val = map.get(wordList.get(i));
if (val != 0 && val+ 1 < bestSeen) bestSeen = map.get(wordList.get(i))+1;
}else {
visited[i] = 1;
int distance = ladderHelper(wordList.get(i), endWord, wordList, visited, map);
visited[i] = 0;
if (distance != 0 && (bestSeen == 0 || distance + 1 < bestSeen)) bestSeen = distance+1;
}
}
map.put(beginWord, bestSeen);
return bestSeen;
}
private boolean validJump(String a, String b) {
int mistakes = 0;
for (int i = 0; i < a.length(); i++) {
if (a.charAt(i) != b.charAt(i) && ++mistakes > 1) return false;
}
return true;
}
}
The question is given more in detail here.
I think this code has one trivial and one interesting problem.
Trivial bug
In the line:
if (val != 0 && val+ 1 < bestSeen) bestSeen = map.get(wordList.get(i))+1;
if bestSeen is equal to 0 (this is the case if all values so far have been in the cache), then this condition will never activate. You need something more like:
if (val != 0 && (bestSeen==0 || val+ 1 < bestSeen)) bestSeen = map.get(wordList.get(i))+1;
The effect of this is that sometimes a shorter route will be ignored.
Interesting bug
You are using DFS to try and find the shortest path. If you switch to using BFS I would expect your solution to pass.
The reason DFS fails is due to the visited array. The visited array is used to keep track of the words on the current path to prevent infinite recursion. The problem is that we ignore all paths that go through these visited nodes.
At first sight this seems fine, after all our shortest path will never need to loop round on itself!
However, consider a pattern of words represented by the graph below:
Imagine that your DFS code has visited A,B,C,D.
When it visits D it has a look at its neighbours, sees that they are all visited, and concludes that it is impossible for there to be a route from D to the end!
When the algorithm backtracks, it will eventually try the route start->D, but the cache will report that this route is impossible so it will not find the shortest path.
Im trying to add settings to a snake game made in processing. I want to have something like easy, normal and hard or something along the lines of that and change the speed and maybe size of the grid. If anyone coudl explain how to id greatly appreciate it!
ArrayList<Integer> x = new ArrayList<Integer>(), y = new ArrayList<Integer>();
int w = 30, h = 30, bs = 20, dir = 2, applex = 12, appley = 10;
int[] dx = {0,0,1,-1}, dy = {1,-1,0,0};
boolean gameover = false;
void setup() {
size(600,600);
x.add(5);
y.add(5);
}
void draw() {
background(255);
for(int i = 0 ; i < w; i++) line(i*bs, 0, i*bs, height); //Vertical line for grid
for(int i = 0 ; i < h; i++) line(0, i*bs, width, i*bs); //Horizontal line for grid
for(int i = 0 ; i < x.size(); i++) {
fill (0,255,0);
rect(x.get(i)*bs, y.get(i)*bs, bs, bs);
}
if(!gameover) {
fill(255,0,0);
rect(applex*bs, appley*bs, bs, bs);
if(frameCount%5==0) {
x.add(0,x.get(0) + dx[dir]);
y.add(0,y.get(0) + dy[dir]);
if(x.get(0) < 0 || y.get(0) < 0 || x.get(0) >= w || y.get(0) >= h) gameover = true;
for(int i = 1; i < x.size(); i++) if(x.get(0) == x.get(i) && y.get(0) == y.get(i)) gameover = true;
if(x.get(0)==applex && y.get(0)==appley) {
applex = (int)random(0,w);
appley = (int)random(0,h);
}else {
x.remove(x.size()-1);
y.remove(y.size()-1);
}
}
} else {
fill(0);
textSize(30);
text("GAME OVER. Press Space to Play Again", 20, height/2);
if(keyPressed && key == ' ') {
x.clear(); //Clear array list
y.clear(); //Clear array list
x.add(5);
y.add(5);
gameover = false;
}
}
if (keyPressed == true) {
int newdir = key=='s' ? 0 : (key=='w' ? 1 : (key=='d' ? 2 : (key=='a' ? 3 : -1)));
if(newdir != -1 && (x.size() <= 1 || !(x.get(1) ==x.get(0) + dx[newdir] && y.get (1) == y.get(0) + dy[newdir]))) dir = newdir;
}
}
You need to break your problem down into smaller steps:
Step one: Can you store the difficulty in a variable? This might be an int that keeps track of a level, or a boolean that switches between easy and hard. Just hardcode the value of that variable for now.
Step two: Can you write your code so it changes behavior based on the difficulty level? Use the variable you created in step one. You might use an if statement to check the difficulty level, or maybe the speed increases over time. It's completely up to you. Start out with a hard-coded value. Change the value to see different behaviors.
Step three: Can you programatically change that value? Maybe this requires a settings screen where the user chooses the difficulty, or maybe it gets more difficult over time. But you have to do the first two steps before you can start this step.
If you get stuck on a specific step, then post an MCVE and we'll go from there.
I am trying to implement an artificial intelligence player for Othello using the Minimax algorithm. The computer plays decently, but its not great. Did I implement it correctly in my following code?
Coordinate bestCoordinate = null;
public int minimax(MyButton[][] gameBoard, int depth, boolean maximizingPlayer) {
if (depth == 0) {
return evaluateBoard(gameBoard);
}
if (maximizingPlayer) {
int bestValue = Integer.MIN_VALUE;
LinkedList<Coordinate> moves = generateMoves(gameBoard);
for (Coordinate move : moves) {
MyButton[][] newBoard = cloneBoard(gameBoard);
processMove(newBoard, newBoard[move.getxCoordinate()][move.getyCoordinate()]);
int v = minimax(newBoard, depth - 1, !maximizingPlayer);
if (v > bestValue) {
bestValue = v;
bestCoordinate = move;
}
}
return bestValue;
}
else {
int bestValue = Integer.MAX_VALUE;
LinkedList<Coordinate> moves = generateMoves(gameBoard);
for (Coordinate move : moves) {
MyButton[][] newBoard = cloneBoard(gameBoard);
processMove(newBoard, newBoard[move.getxCoordinate()][move.getyCoordinate()]);
int v = minimax(newBoard, depth - 1, !maximizingPlayer);
if (v < bestValue) {
bestValue = v;
bestCoordinate = move;
}
}
return bestValue;
}
}
Also, here is my evaluation function:
public int evaluateBoard(MyButton[][] gameBoard) {
int blackPieces = 0;
int whitePiecess = 0;
for (MyButton[] array : gameBoard) {
for (MyButton button : array) {
if (button.getBackground().equals(Color.black)) {
blackPieces++;
} else if (button.getBackground().equals(Color.WHITE)) {
whitePiecess++;
}
}
}
int cornerBonus = 10;
if (gameBoard[0][0].getBackground().equals(Color.BLACK)) {
blackPieces += cornerBonus;
}
if (gameBoard[0][getBoardWidth() - 1].getBackground().equals(Color.BLACK)) {
blackPieces += cornerBonus;
}
if (gameBoard[getBoardHeight() - 1][0].getBackground().equals(Color.BLACK)) {
blackPieces += cornerBonus;
}
if (gameBoard[getBoardHeight() - 1][getBoardWidth() - 1].getBackground().equals(Color.BLACK)) {
blackPieces += cornerBonus;
}
if (gameBoard[0][0].getBackground().equals(Color.WHITE)) {
whitePiecess += cornerBonus;
}
if (gameBoard[0][getBoardWidth() - 1].getBackground().equals(Color.WHITE)) {
whitePiecess += cornerBonus;
}
if (gameBoard[getBoardHeight() - 1][0].getBackground().equals(Color.WHITE)) {
whitePiecess += cornerBonus;
}
if (gameBoard[getBoardHeight() - 1][getBoardWidth() - 1].getBackground().equals(Color.WHITE)) {
whitePiecess += cornerBonus;
}
return whitePiecess - blackPieces;
}
(The computer always plays white, and the human is black).
I'm mainly unsure because the computer doesn't seem to protect corners, despite the bonus points that they give. Is there anything wrong with my code/logic?
You are updating your best move at each depth. Make a constant called SEARCH_DEPTH outside of your function that you use every time you call the function and do an if check:
if(depth == SEARCH_DEPTH) {
bestCoordinate = move;
}
Also, assuming you are the maximizing player, you only want to set the move in the if(maximizingPlayer) block.
I did not test your code out myself, but that is the minimax algorithm, and it appears to be written correctly (assuming your helper functions are implemented correctly). I have some points that might give you insight as to why your agent is not acting optimally:
I see your objective function is the number of pieces your agent has minus the number the opponent has, plus a bonus for corner pieces. This might seem like the best strategy, but I would read up on how good Othello players make their moves. Often, they try to flip only one piece if they can until late game, as they have more opportunities that way.
Minimax won't necessarily return the moves that will lead to capturing corners, even if you weigh them highly, because it might be undermined by the opponent's choice of moves. For example, lets say your algorithm looks three turns ahead on the computer's turn, so it first looks at a state where it capture a corner with a high objective function. However, your opponent will be choosing the route that will minimize your objective function, and as such the computer will not view moves moving towards capturing a corner piece as optimal because of the risk. I don't know how easy this will be, but if you can somehow visualize the tree, you might be able to figure out if this is the case.
I implemented backtracking based solution for my problem which I specified in my previous post: Packing items into fixed number of bins
(Bin is a simple wrapper for vector<int> datatype with additional methods such as sum() )
bool backtrack(vector<int>& items, vector<Bin>& bins, unsigned index, unsigned bin_capacity)
{
if (bin_capacity - items.front() < 0) return false;
if (index < items.size())
{
//try to put an item into all opened bins
for(unsigned i = 0; i < bins.size(); ++i)
{
if (bins[i].sum() + items[index] + items.back() <= bin_capacity || bin_capacity - bins[i].sum() == items[index])
{
bins[i].add(items[index]);
return backtrack(items, bins, index + 1, bin_capacity);
}
}
//put an item without exceeding maximum number of bins
if (bins.size() < BINS)
{
Bin new_bin = Bin();
bins.push_back(new_bin);
bins.back().add(items[index]);
return backtrack(items, bins, index + 1, bin_capacity);
}
}
else
{
//check if solution has been found
if (bins.size() == BINS )
{
for (unsigned i = 0; i <bins.size(); ++i)
{
packed_items.push_back(bins[i]);
}
return true;
}
}
return false;
}
Although this algorithm works quite fast, it's prone to stack overflow for large data sets.
I'm looking for any ideas and suggestions how to improve it.
Edit:
I decided to try an iterative approach with explicit stack, but my solution doesn't work as expeced - sometimes it gives incorrect results.
bool backtrack(vector<int>& items, vector<Bin>& bins, unsigned index, unsigned bin_capacity)
{
stack<Node> stack;
Node node, child_node;
Bin new_bin;
//init the stack
node.bins.add(new_bin);
node.bins.back().add(items[item_index]);
stack.push(node);
item_index++;
while(!stack.empty())
{
node = stack.top();
stack.pop();
if (item_index < items.size())
{
if (node.bins.size() < BINS)
{
child_node = node;
Bin empty;
child_node.bins.add(empty);
child_node.bins.back().add(items[item_index]);
stack.push(child_node);
}
int last_index = node.bins.size() - 1;
for (unsigned i = 0; i < node.bins.size(); i++)
{
if (node.bins[last_index - i]->get_sum() + items[item_index]+ items.back() <= bin_capacity ||
bin_capacity - node.bins[last_index - i]->get_sum() == items[item_index])
{
child_node = node;
child_node.bins[last_index - i]->push_back(items[item_index]);
stack.push(child_node);
}
}
item_index++;
}
else
{
if (node.bins() == BINS)
{
//copy solution
bins = node.bins;
return true;
}
}
}
return false;
}
Any suggestions are highly appreciated.
I think there's a dynamic programming algorithm for solving the multiple-bin packing problem, or at least, a polynomial approximation algorithm. Take a look here and here.