I'm trying to figure out how to write a loop to check the position of a circle against a variable number of rectangles so that the apple is not placed on top of the snake, but I'm having a bit of trouble thinking it through. I tried:
do
apple.setPosition(randX()*20+10, randY()*20+10); // apple is a CircleShape
while (apple.getPosition() == snakeBody[i].getPosition());
Although, in this case, if it detects a collision with one rectangle of the snake's body, it could end up just placing the apple at a previous position of the body. How do I make it check all positions at the same time, so it can't correct itself only to have a chance of repeating the same problem again?
There are three ways (I could think of) of generating a random number meeting a requirement:
The first way, and the simpler, is what you're trying to do: retry if it doesn't.
However, you should change the condition so that it checks all the forbidden cells at once:
bool collides_with_snake(const sf::Vector2f& pos, //not sure if it's 2i or 2f
const /*type of snakeBody*/& snakeBody,
std::size_t partsNumber) {
bool noCollision = true;
for( std::size_t i = 0 ; i < partsNumber && noCollision ; ++i )
noCollision = pos != snakeBody[i].getPosition()
return !noCollision;
}
//...
do
apple.setPosition(randX()*20+10, randY()*20+10);
while (collides_with_snake(apple.getCollision(), snakeBody,
/* snakeBody.size() ? */));
The second way is to try to generate less numbers and find a function which will map these numbers to the set you want. For instance, if your grid has N cells, you could generate a number between 0 and N - [number of parts of your Snake] then map this number X to the smallest number Y such that this integer doesn't refer to a cell occupied by a snake part and X = Y + S where S is the number of cells occupied by a snake part referred by a number smaller than Y.
It's more complicated though.
The third way is to "cheat" and choose a stronger requirement which is easier to enforce. For instance, if you know that the cell body is N cells long, then only spawn the apple on a cell which is N + 1 cells away of the snakes head (you can do that by generating the angle).
The question is very broad, but assuming that snakeBody is a vector of Rectangles (or derived from Rectanges), and that you have a checkoverlap() function:
do {
// assuming that randX() and randY() allways return different random variables
apple.setPosition(randX()*20+10, randY()*20+10); // set the apple
} while (any_of(snakeBody.begin(), snakeBody.end(), [&](Rectangle &r)->bool { return checkoverlap(r,apple); } );
This relies on standard algorithm any_of() to check in one simple expression if any of the snake body elements overlaps the apple. If there's an overlap, we just iterate once more and get a new random position until it's fine.
If snakebody is an array and not a standard container, just use snakeBody, snakeBody+snakesize instead of snakeBody.begin(), snakeBody.end() in the code above.
If the overlap check is as simple as to compare the postition you can replace return checkoverlap(r,apple); in the code above with return r.getPosition()==apple.getPosition();
The "naive" approach would be generating apples and testing their positions against the whole snake until we find a free spot:
bool applePlaced = false;
while(!applePlaced) { //As long as we haven't found a valid place for the apple
apple.setPosition(randX()*20+10, randY()*20+10);
applePlaced = true; //We assume, that we can place the apple
for(int i=0; i<snakeBody.length; i++) { //Check the apple position with all snake body parts
if(apple.getPosition() == snakeBody[i].getPosition()) {
applePlaced=false; //Our prediction was wrong, we could not place the apple
break; //No further testing necessary
}
}
}
The better way would be storing all free positions in an array and then pick a Position out of this array(and delete it from the array), so that no random testing is necessary. It requires also updating the array if the snakes moves.
Related
Im very new to c++ and only know the very basics. array, if while for dynamic and pointer..
I am working on a code and here is what i want to do.
For example on a 2D square plane(10x10) I have randomly scattered 1000 points(an array of size 100).
The 2D square plane is divided into 10 smaller rectangles. Now I want to sort the 1000 points into these smaller rectangles. Basically, I want to make 10 dynamic arrays(one for each "small rectangle") and each of these array will contain the scattered points that are inside the corresponding region.
The most basic iteration i thought of was just use if, if, if...
But with this, I have to repeat the iteration 1000times for each region. And I think it is very inefficient.
Write a function to classify a single point, i.e. determine into which region it belongs. As a simple example that you can expand:
std::size_t classify(double px, double py, double split) {
if (px < split) {
return 0; // left plane
} else {
return 1; // right plane
}
}
Then, iterate over the points and put them into respective containers:
std::vector<std::vector<point_t>> region{2};
for (auto const & point : points) {
region[classify(point.x, point.y, split)].push_back(point);
}
This way you iterate once over all points, doing a classification (that should be possible to do in constant time in your case) for every point, which is the minimum work required.
I need a graph-search algorithm that is enough in our application of robot navigation and I chose Dijkstra's algorithm.
We are given the gridmap which contains free, occupied and unknown cells where the robot is only permitted to pass through the free cells. The user will input the starting position and the goal position. In return, I will retrieve the sequence of free cells leading the robot from starting position to the goal position which corresponds to the path.
Since executing the dijkstra's algorithm from start to goal would give us a reverse path coming from goal to start, I decided to execute the dijkstra's algorithm backwards such that I would retrieve the path from start to goal.
Starting from the goal cell, I would have 8 neighbors whose cost horizontally and vertically is 1 while diagonally would be sqrt(2) only if the cells are reachable (i.e. not out-of-bounds and free cell).
Here are the rules that should be observe in updating the neighboring cells, the current cell can only assume 8 neighboring cells to be reachable (e.g. distance of 1 or sqrt(2)) with the following conditions:
The neighboring cell is not out of bounds
The neighboring cell is unvisited.
The neighboring cell is a free cell which can be checked via the 2-D grid map.
Here is my implementation:
#include <opencv2/opencv.hpp>
#include <algorithm>
#include "Timer.h"
/// CONSTANTS
static const int UNKNOWN_CELL = 197;
static const int FREE_CELL = 255;
static const int OCCUPIED_CELL = 0;
/// STRUCTURES for easier management.
struct vertex {
cv::Point2i id_;
cv::Point2i from_;
vertex(cv::Point2i id, cv::Point2i from)
{
id_ = id;
from_ = from;
}
};
/// To be used for finding an element in std::multimap STL.
struct CompareID
{
CompareID(cv::Point2i val) : val_(val) {}
bool operator()(const std::pair<double, vertex> & elem) const {
return val_ == elem.second.id_;
}
private:
cv::Point2i val_;
};
/// Some helper functions for dijkstra's algorithm.
uint8_t get_cell_at(const cv::Mat & image, int x, int y)
{
assert(x < image.rows);
assert(y < image.cols);
return image.data[x * image.cols + y];
}
/// Some helper functions for dijkstra's algorithm.
bool checkIfNotOutOfBounds(cv::Point2i current, int rows, int cols)
{
return (current.x >= 0 && current.y >= 0 &&
current.x < cols && current.y < rows);
}
/// Brief: Finds the shortest possible path from starting position to the goal position
/// Param gridMap: The stage where the tracing of the shortest possible path will be performed.
/// Param start: The starting position in the gridMap. It is assumed that start cell is a free cell.
/// Param goal: The goal position in the gridMap. It is assumed that the goal cell is a free cell.
/// Param path: Returns the sequence of free cells leading to the goal starting from the starting cell.
bool findPathViaDijkstra(const cv::Mat& gridMap, cv::Point2i start, cv::Point2i goal, std::vector<cv::Point2i>& path)
{
// Clear the path just in case
path.clear();
// Create working and visited set.
std::multimap<double,vertex> working, visited;
// Initialize working set. We are going to perform the djikstra's
// backwards in order to get the actual path without reversing the path.
working.insert(std::make_pair(0, vertex(goal, goal)));
// Conditions in continuing
// 1.) Working is empty implies all nodes are visited.
// 2.) If the start is still not found in the working visited set.
// The Dijkstra's algorithm
while(!working.empty() && std::find_if(visited.begin(), visited.end(), CompareID(start)) == visited.end())
{
// Get the top of the STL.
// It is already given that the top of the multimap has the lowest cost.
std::pair<double, vertex> currentPair = *working.begin();
cv::Point2i current = currentPair.second.id_;
visited.insert(currentPair);
working.erase(working.begin());
// Check all arcs
// Only insert the cells into working under these 3 conditions:
// 1. The cell is not in visited cell
// 2. The cell is not out of bounds
// 3. The cell is free
for (int x = current.x-1; x <= current.x+1; x++)
for (int y = current.y-1; y <= current.y+1; y++)
{
if (checkIfNotOutOfBounds(cv::Point2i(x, y), gridMap.rows, gridMap.cols) &&
get_cell_at(gridMap, x, y) == FREE_CELL &&
std::find_if(visited.begin(), visited.end(), CompareID(cv::Point2i(x, y))) == visited.end())
{
vertex newVertex = vertex(cv::Point2i(x,y), current);
double cost = currentPair.first + sqrt(2);
// Cost is 1
if (x == current.x || y == current.y)
cost = currentPair.first + 1;
std::multimap<double, vertex>::iterator it =
std::find_if(working.begin(), working.end(), CompareID(cv::Point2i(x, y)));
if (it == working.end())
working.insert(std::make_pair(cost, newVertex));
else if(cost < (*it).first)
{
working.erase(it);
working.insert(std::make_pair(cost, newVertex));
}
}
}
}
// Now, recover the path.
// Path is valid!
if (std::find_if(visited.begin(), visited.end(), CompareID(start)) != visited.end())
{
std::pair <double, vertex> currentPair = *std::find_if(visited.begin(), visited.end(), CompareID(start));
path.push_back(currentPair.second.id_);
do
{
currentPair = *std::find_if(visited.begin(), visited.end(), CompareID(currentPair.second.from_));
path.push_back(currentPair.second.id_);
} while(currentPair.second.id_.x != goal.x || currentPair.second.id_.y != goal.y);
return true;
}
// Path is invalid!
else
return false;
}
int main()
{
// cv::Mat image = cv::imread("filteredmap1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
cv::Mat image = cv::Mat(100,100,CV_8UC1);
std::vector<cv::Point2i> path;
for (int i = 0; i < image.rows; i++)
for(int j = 0; j < image.cols; j++)
{
image.data[i*image.cols+j] = FREE_CELL;
if (j == image.cols/2 && (i > 3 && i < image.rows - 3))
image.data[i*image.cols+j] = OCCUPIED_CELL;
// if (image.data[i*image.cols+j] > 215)
// image.data[i*image.cols+j] = FREE_CELL;
// else if(image.data[i*image.cols+j] < 100)
// image.data[i*image.cols+j] = OCCUPIED_CELL;
// else
// image.data[i*image.cols+j] = UNKNOWN_CELL;
}
// Start top right
cv::Point2i goal(image.cols-1, 0);
// Goal bottom left
cv::Point2i start(0, image.rows-1);
// Time the algorithm.
Timer timer;
timer.start();
findPathViaDijkstra(image, start, goal, path);
std::cerr << "Time elapsed: " << timer.getElapsedTimeInMilliSec() << " ms";
// Add the path in the image for visualization purpose.
cv::cvtColor(image, image, CV_GRAY2BGRA);
int cn = image.channels();
for (int i = 0; i < path.size(); i++)
{
image.data[path[i].x*cn*image.cols+path[i].y*cn+0] = 0;
image.data[path[i].x*cn*image.cols+path[i].y*cn+1] = 255;
image.data[path[i].x*cn*image.cols+path[i].y*cn+2] = 0;
}
cv::imshow("Map with path", image);
cv::waitKey();
return 0;
}
For the algorithm implementation, I decided to have two sets namely the visited and working set whose each elements contain:
The location of itself in the 2D grid map.
The accumulated cost
Through what cell did it get its accumulated cost (for path recovery)
And here is the result:
The black pixels represent obstacles, the white pixels represent free space and the green line represents the path computed.
On this implementation, I would only search within the current working set for the minimum value and DO NOT need to scan throughout the cost matrix (where initially, the initially cost of all cells are set to infinity and the starting point 0). Maintaining a separate vector of the working set I think promises a better code performance because all the cells that have cost of infinity is surely to be not included in the working set but only those cells that have been touched.
I also took advantage of the STL which C++ provides. I decided to use the std::multimap since it can store duplicating keys (which is the cost) and it sorts the lists automatically. However, I was forced to use std::find_if() to find the id (which is the row,col of the current cell in the set) in the visited set to check if the current cell is on it which promises linear complexity. I really think this is the bottleneck of the Dijkstra's algorithm.
I am well aware that A* algorithm is much faster than Dijkstra's algorithm but what I wanted to ask is my implementation of Dijkstra's algorithm optimal? Even if I implemented A* algorithm using my current implementation in Dijkstra's which is I believe suboptimal, then consequently A* algorithm will also be suboptimal.
What improvement can I perform? What STL is the most appropriate for this algorithm? Particularly, how do I improve the bottleneck?
You're using a std::multimap for 'working' and 'visited'. That's not great.
The first thing you should do is change visited into a per-vertex flag so you can do your find_if in constant time instead of linear times and also so that operations on the list of visited vertices take constant instead of logarithmic time. You know what all the vertices are and you can map them to small integers trivially, so you can use either a std::vector or a std::bitset.
The second thing you should do is turn working into a priority queue, rather than a balanced binary tree structure, so that operations are a (largish) constant factor faster. std::priority_queue is a barebones binary heap. A higher-radix heap---say quaternary for concreteness---will probably be faster on modern computers due to its reduced depth. Andrew Goldberg suggests some bucket-based data structures; I can dig up references for you if you get to that stage. (They're not too complicated.)
Once you've taken care of these two things, you might look at A* or meet-in-the-middle tricks to speed things up even more.
Your performance is several orders of magnitude worse than it could be because you're using graph search algorithms for what looks like geometry. This geometry is much simpler and less general than the problems that graph search algorithms can solve. Also, with a vertex for every pixel your graph is huge even though it contains basically no information.
I heard you asking "how can I make this better without changing what I'm thinking" but nevertheless I'll tell you a completely different and better approach.
It looks like your robot can only go horizontally, vertically or diagonally. Is that for real or just a side effect of you choosing graph search algorithms? I'll assume the latter and let it go in any direction.
The algorithm goes like this:
(0) Represent your obstacles as polygons by listing the corners. Work in real numbers so you can make them as thin as you like.
(1) Try for a straight line between the end points.
(2) Check if that line goes through an obstacle or not. To do that for any line, show that all corners of any particular obstacle lie on the same side of the line. To do that, translate all points by (-X,-Y) of one end of the line so that that point is at the origin, then rotate until the other point is on the X axis. Now all corners should have the same sign of Y if there's no obstruction. There might be a quicker way just using gradients.
(3) If there's an obstruction, propose N two-segment paths going via the N corners of the obstacle.
(4) Recurse for all segments, culling any paths with segments that go out of bounds. That won't be a problem unless you have obstacles that go out of bounds.
(5) When it stops recursing, you should have a list of locally optimised paths from which you can choose the shortest.
(6) If you really want to restrict bearings to multiples of 45 degrees, then you can do this algorithm first and then replace each segment by any 45-only wiggly version that avoids obstacles. We know that such a version exists because you can stay extremely close to the original line by wiggling very often. We also know that all such wiggly paths have the same length.
A have a set of lights -> l1, l2, l3, l4,.....lx etc.
At any given point in time, only a contiguous sequence of lights is on. For example:
l2,l3,l4 can be on. Or l9,l10,l11 can be on.
But it is not possible to have a gap in between. For example, you can't have l3,l7,l8.
At every tick of the clock, I'm told which are the starting and finishing lights in the sequence, and I need to make sure only the lights within the range are on. (This means I may have to switch on or off lights, depending on which lights were on and off in the previous tick. If lights are already on or off, then I don't need to update those lights.).
What is the quickest way to compute which lights to turn on and off ?
I've come up with a number of solutions, but cannot decide which is best:
Linear approach:
In this solution, I iterate through all the lights from l0...lx. I check if each light is in the range. If it is, I make sure its on, if not, I make sure its off. This is simple to implement, but is wasteful if there are no changes, and worse, if the range is towards the end (e.g.: l9,l10,l11 etc)
Series of if's:
In this approach, I keep track of the start / end of the range in the previous tick of the clock, and compare it with the range from the current tick.
If the ranges are same, do nothing. (ls,us)
If lower limit increases, turn of lights till new lower limit. (li)
if upper limit increases, turn on lights till new upper limit. (ui)
If lower limit decreases, turn on lights from new lower limit to old (ld)
if upper limit decreases, turn off lights from old upper limit to new upper limit. (ud). As you can see, in approach (2), there are nine possible combinations of changes. So the second approach has a lot of if's, and so it is unclear to me what the best way to code this (have 9 if else blocks ?- seems messy):
li,ui li,us li,ud
ls,ui ls,us ls,ud
ld,ui ld,us ld,ud
Pointer approach - In this approach, I create arrays of both the ranges..
Eg: array 1: {3,4,5}
array 2: {4,5,6,7}
I use p,q to point to the current cell in each array that I am observing.
I start reading from the lowest index of both arrays
If array1[p] < array2[q] , then I turn of the light at index array1[p], and increment p
If array1[p] == array2[q] , then I increment p and q, since the light is already on.
if array1[p] > array2[q] , then I turn on the light at array2[q], and increment q.
I can optimise this approach, by checking if the start / finish index is the same.
I think approach 3 is the best - since the code is easiest to read and seems quick.
I can implement all 3, but can't decide which approach is best. Would appreciate suggestions on better methods, or how to choose from these three and optimise them. Either C or C++ can be used in my app.
EDIT: The ranges in two consecutive ticks could be far apart and differ in size.. Eg: Tick 1 could be {3,4,5} and tick 2 could be {7,8,9,10}. So in this example, I would have to switch off lights at 3,4,5 and then turn on at 7,8,9,10.
EDIT: "Lights" are an analogy I'm using to explain the problem. Lights just refer to an object in software.
EDIT: I have access to only one thread, so cannot parallelise any part of this.
Is this a contrived problem, or are you physically controlling lights via a port?
The fact that there's only ever one contiguous sequence to keep track of makes this an easy problem. The best, I would say, is option 2. Something like this should handle all cases of overlapping and non-overlapping ranges:
if( old_right < new_left || old_left > new_right ) {
// Non-overlapping
for( int i = old_left; i <= old_right; i++ ) set_light_state( i, OFF );
for( int i = new_left; i <= new_right; i++ ) set_light_state( i, ON );
} else {
// Overlapping
for( int i = old_left; i < new_left; i++ ) set_light_state( i, OFF );
for( int i = new_left; i < old_left; i++ ) set_light_state( i, ON );
for( int i = old_right+1; i <= new_right; i++ ) set_light_state( i, ON );
for( int i = new_right+1; i <= old_right; i++ ) set_light_state( i, OFF );
}
This solution should only modify those lights that are necessary, with minimal loss to branch-prediction inefficiency (if that matters). I am assuming that the problem to solve is minimizing the number of state changes.
I would start with the approach that is
easiest to read (because readability is important) and
easiest to implement (because you want a quick result / feedback)
Then do profiling. If this algorithm is sufficient then you are done. If not, move on to the next approach.
Well, because this question piqued my interest the most today, and assuming this is simply a thought exercise, I've come up with some code...
The criteria I applied (not 100% sure if this exactly matches the original poster's requirements):
only iterate through lights that require a state change
minimize data needing to be stored
never turn any light ON that is already On.
never turn any light OFF that is already Off.
assume the turnOn() and turnOff() methods are potentially expensive
First of all, observe that between any two light updates, one of 7 possible state changes takes place as shown by this rudimentary picture:
the lights turning on are entirely inside the current range of lights that are on.
the lights turning on completely overlap the whole sequence that is currently on.
the lights turning on overlap the start of the sequence that is currently on.
the lights turning on overlap the end of the sequence that is currrently on.
the lights turning on are all entirely before the current range of lights that are on.
the lights turning on are all entirely after the current range of lights that are on.
all the lights are being turned off
And here is my implementation...
We keep track only of the start and end range of lights that are currently on (in the member variables start and end).
Note that all ranges includes start but exclude end, so if start=2 and end=5 that means lights 2,3, and 4 are on.
int start;
int end;
void Switcher::toggleLights(int new_start, int new_end) {
if( new_start > start && new_end < end ) {
//case 1: new lights are within range of lights that are already on
turnOffRange(start, new_start);
turnOffRange(new_end, end);
}
else if( new_start < start && new_end > end) {
//case 2: lights already on are entirely within new range
turnOnRange(new_start, start);
turnOnRange(end, new_end);
}
else if( new_start < start && new_end > start ) {
//case 3: new light sequence overlaps start of current range
turnOffRange(new_end, end);
turnOnRange(new_start, start);
}
else if( new_start < end && new_end > end ){
//case 4: new light sequence overlaps end of current range
turnOffRange(start, new_start);
turnOnRange(end, new_end);
}
else {
//case 5,6 or 7 (no overlap at all)
turnOffRange(start,end);
turnOnRange(new_start,new_end);
}
// keep track of the lights that are now on
start = new_start;
end = new_end;
}
void Switcher::turnOffRange(int from, int to) {
for(int i=from; i < to; ++i) {
turnOff(i); // (potentially expensive) method to turn light off
}
}
void Switcher::turnOnRange(int from, int to) {
for(int i=from; i < to; ++i) {
turnOn(i); // (potentially expensive) method to turn lamp on
}
}
as the question is formulated now, my first thought would be to just use the starting and finishing lights directly in a loop.
for (i = startinglight ; i <= finishinglight ; i++){
lightarray[i-1] * turn on lights * ;
}
...but since this is very easy, maybe I am missing something in the question
(it also only works of course if the numbers of the lights in the array starts at 1 and is continuous)
A simple solution would be a combination of a for loop and an if statement:
where X is total number of lights
int lightArray[X] = {0};
int startLight;
int endLight;
for (int i = 0, i < X, i++){
if (i >= startLight && i <= endLight){
if (lightArray[i] == 0){
lightArray[i] = 1; // or you can change the lights in this loop
}
}
else{
lightArray[i] = 0; // or you can change the lights in this loop
}
}
for (int i = 0, i < x, i++){
//some function that sets lights on and off based on lightArray[i]
}
You could have your lights in an array of binary values {0,1}, say ar
Then if [first, last) is the range where the the lights are on, you split the array in 3 ranges [begin, first), [first, last), [last, end), (where each of each range can be empty).
begin = &ar[0];
end = &ar[N];
Finally you run an operation for each elememt on each range
for_each(begin, first, [](int i) { i &= 0x0; });
for_each(first, last , [](int i) { i |= 0x1; });
for_each(last, end , [](int i) { i &= 0x0; });
the first and last loop will set the state to 0 and the second loop will set the state to 1
A similar solution would have all values kept in a bitset instead of an array, where you could use the member functions set and flip, but you'd again have to loop over the values of each bit.
I would make sure to bit-pack the values, i.e. instead of doing i.e.
bool lightStatus[NUMBER_OF_LIGHTS];
do
uint64_t lightStatus[(NUMBER_OF_LIGHTS + 63) / 64];
assuming you're on a 64-bit processor. Else, use uint32_t. This will make sure you waste no bits when reading/writing, since all bits of lightStatus are really used to encode the status of a light.
It will also make it very efficient to clear all the lights, by just doing a memset() of the entire array. That will be probably be fast enough to work even though you risk doing needless clears.
Regarding the update, I'd do the obvious, since it's basically the core information anyway: store the current range, so that whenever you get a new one you can clear precisely the old one. If it turns out to be a bottleneck you can get fancy and eliminate the overlap of course but I wouldn't worry unless this is running on a seriously slow CPU or there are (many?) thousands of lights.
Let T(x,y) be the number of tours over a X × Y grid such that:
the tour starts in the top left square
the tour consists of moves that are up, down, left, or right one
square
the tour visits each square exactly once, and
the tour ends in the bottom left square.
It’s easy to see, for example, that T(2,2) = 1, T(3,3) = 2, T(4,3) = 0, and T(3,4) = 4. Write a program to calculate T(10,4).
I have been working on this for hours ... I need a program that takes the dimensions of the grid as input and returns the number of possible tours?
I have been working on this for hours ... I need a program that takes the dimensions of the grid as input and returns the number of possible tours?
I wrote this code to solve the problem ... I cant seem to figure out how to check all directions.
#include <iostream>
int grid[3][3];
int c = 0;
int main(){
solve (0, 0, 9);
}
int solve (int posx, int posy, steps_left){
if (grid[posx][posy] = 1){
return 0;
}
if (steps_left = 1 && posx = 0 && posy = 2){
c = c+1;
return 0;
}
grid[posx][posy] = 1;
// for all possible directions
{
solve (posx_next, posy_next, steps_left-1)
}
grid[posx][posy] = 0;
}
Algorithm by #KarolyHorvath
You need some data structure to represent the state of the cells on the grid (visited/not visited).
Your algorithm:
step(posx, posy, steps_left)
if it is not a valid position, or already visited
return
if it's the last step and you are at the target cell
you've found a solution, increment counter
return
mark cell as visited
for each possible direction:
step(posx_next, posy_next, steps_left-1)
mark cell as not visited
and run with
step(0, 0, sizex*sizey)
It's not difficult, since you've been given the algorithm. In order to
solve the problem, you'll probably want some sort of dynamic data
structure (unless you're only interested in the exact case of T(10,4)).
For the rest, left is -1 on the x index, right +1, and down is -1 on the
y dimension, up +1. Add bounds checking and verification that you've
not visited, and the job is done.
But I wonder how much time such an obvious algorithm will take. There's
a four way decision on each cell; for the fourty cells of T(10,4),
that's 4^40 decisions. Which is not feasable. Things like eliminating
already visited cells and bounds checking eliminate a lot of branches,
but still... The goal of the competition might be to make you find a
better algorithm.
You really should pick a debugger and see what's going on on a small board (2x2, 3x3).
One obvious problem is that = is assignment, not comparison. Compare with ==.
There are more problems. Find them.
Currently I'm working on an Othello/Reversi game in c++. I have it "finished" except that the Minimax algorithm I'm using for the Computer player is painfully slow when I set it at a depth that produces a semi-challenging AI.
The basic setup of my game is that the board is represented by a 2-dimensional array, with each cell on the board assigned a value in the array (xMarker, oMarker, or underscore).
Here's the minimax algorithm so far:
signed int Computer::simulate(Board b, int depth, int tempMarker) {
if (depth > MAX_DEPTH || b.gameOver()) {
int oppMarker = (marker == xMarker) ? oMarker : xMarker;
return b.countForMarker(marker) - b.countForMarker(oppMarker);
}
//if we're simulating our turn, we want to find the highest value (so we set our start at -64)
//if we're simulating the opponent's turn, we want to find the lowest value (so we set our start at 64)
signed int start = (tempMarker == marker) ? -64 : 64;
for (int x = 0; x < b.size; x++) {
for (int y = 0; y < b.size; y++) {
if (b.markerArray[x][y] == underscore) {
Board *c = b.duplicate();
if(c->checkForFlips(Point(x,y), tempMarker, true) > 0) {
int newMarker = (tempMarker == xMarker) ? oMarker : xMarker;
int r = simulate(*c, depth+1, newMarker);
//'marker' is the marker assigned to our player (the computer), if it's our turn, we want the highest value
if (tempMarker == marker) {
if(r > start) start = r;
} else {
//if it's the opponent's turn, we want the lowest value
if(r < start) start = r;
}
}
delete c;
}
}
}
return start;
}
The function checkForFlips() returns the number of flips that would result from playing at the given cell. MAX_DEPTH is set to 6 at the moment, and it's quite slow (maybe about 10-15 seconds per play)
The only idea I've come up with so far would be to store the tree each time, and then pick up from where I left off, but I'm not sure how to go about implementing that or if it would be too effective. Any ideas or suggestions would be appreciated!
Calculating minimax is slow.
The first possible optimization is alpha-beta pruning:
http://en.wikipedia.org/wiki/Alpha-beta_pruning
You shouldn't duplicate board, that's very inefficient. Make the move before you call yourself recursively, but save enough information to undo the same move after you return from the recursive call. That way you only need one board.
But Shiroko is right, alpha-beta pruning is the first step.
#Shiroko's suggestion is great, but there are more optimization opportunities.
You pass the state of the Board by value, and then copy it inside the loop. I'd pass the Board as a pointer or as const Board& b. If this is still expensive, you could use a poinger to a single board, and reverse every move after you evaluate it. In any case don't allocate it on the heap.
You can also run this algorithm on multiple cores. You will need to write a variation of the for loop at the first level using openmp (or equivalent).
The most obvious way to improve it would be through alpha-beta pruning or negascout.
However, if you want to stick with minimax, you can't make it go too fast, as it is a brute force algorithm. One way to improve it would be to change it to Negamax, which would get rid of some of the logic required in this code. Another way would be to use a one dimensional array for the board instead of Board. To make calculations easier, use a length of 100, so the positions are in row-column form(e.g. index 27 is row 2, column 7).
But if you want it to go faster, try pruning.