I'm looking for possibly efficient algorithm to detect "win" situation in a gomoku (five-in-a-row) game, played on a 19x19 board. Win situation happens when one of the players manages to get five and NO MORE than five "stones" in a row (horizontal, diagonal or vertical).
I have the following data easily accessible:
previous moves ("stones") of both players stored in a 2d array (can be also json notation object), with variables "B" and "W" to difference players from each other,
"coordinates" of the incoming move (move.x, move.y),
number of moves each player did
I'm doing it in javascript, but any solution that doesn't use low-level stuff like memory allocation nor higher-level (python) array operations would be good.
I've found similiar question ( Detect winning game in nought and crosses ), but solutions given there only refer to small boards (5x5 etc).
A simple to understand solution without excessive loops (only pseudocode provided, let me know if you need more explanation):
I assume your 2-d array runs like this:
board = [
[...],
[...],
[...],
...
];
I.e. the inner arrays represent the horizontal rows of the board.
I also assume that the array is populated by "b", "w", and "x", representing black pieces, white pieces, and empty squares, respectively.
My solution is somewhat divide-and-conquer, so I've divided it into the 3 cases below. Bear with me, it may seem more complex than simply running multiple nested loops at first, but the concept is easy to understand, read, and with the right approach, quite simple to code.
Horizontal lines
Let's first consider the case of detecting a win situation ONLY if the line is horizontal - this is the easiest. First, join a row into a single string, using something like board[0].join(""). Do this for each row. You end up with an array like this:
rows = [
"bxwwwbx...",
"xxxwbxx...",
"wwbbbbx...",
...
]
Now join THIS array, but inserting an "x" between elements to separate each row: rows.join("x").
Now you have one long string representing your board, and it's simply a matter of applying a regexp to find consecutive "w" or "b" of exactly 5 length: superString.test(/(b{5,5})|(w{5,5})/). If the test returns true you have a win situation. If not, let's move on to vertical lines.
Vertical lines
You want to reuse the above code, so create a function testRows for it. Testing for vertical lines is exactly the same process, but you want to transpose the board, so that rows become columns and columns become rows. Then you apply the same testRows function. Transposing can be done by copying values into a new 2-d array, or by writing a simple getCol function and using that within testRows.
Diagonal lines
Again, we want to reuse the `testRows' function. A diagonal such as this:
b x x x x
x b x x x
x x b x x
x x x b x
x x x x b
Can be converted to a vertical such as this:
b x x x x
b x x x
b x x
b x
b
By shifting row i by i positions. Now it's a matter of transposing and we are back at testing for horizontals. You'll need to do the same for diagonals that go the other way, but this time shift row i by length - 1 - i positions, or in your case, 18 - i positions.
Functional javascript
As a side note, my solution fits nicely with functional programming, which means that it can be quite easily coded if you have functional programming tools with you, though it's not necessary. I recommend using underscore.js as it's quite likely you'll need basic tools like map, reduce and filter in many different game algorithms. For example, my section on testing horizontal lines can be written in one line of javascript with the use of map:
_(board).map(function (row) {return row.join("")}).join("x").test(/(b{5,5})|(w{5,5})/);
Even though this is a really old question I want to provide my answer because I took a deeper look into this problem today and solved it in a much (much) more efficient way.
I'm using a bit board, which is used in most of the board games and engines (chess engines) due to the efficiency, to represent my field.
You can do everything you need in this game with bitwise operations.
A bit can just have 2 states (0 and 1) however what we need are 3 states e.g. p1, p2 or empty.
To solve this problem we're going to have 2 boards instead, one for each player.
Another problem is that Gomoku has a lot of fields (19x19) and there is no number type that has that many bits to represent the field.
We will use an array of numbers to represent each line and just use the first lsb 15bits of it.
Vertical rows
A simplified board of player 1 could look like this
000000
101100
001000
011000
000000
Lets say we want to detect 3 in a row. We take the first 3 rows(0-2) and took at them.
000000
001100
101000
With the & (AND) operator you can check if there is a 1 in every row.
var result = field[player][0] & field[player][1] & field[player][2];
In this case the result will be 0 which means no winner. Lets continue... The next step is to take rows 1-3
101100
001000
011000
Apply the AND again and that we will get is 001000. We don't have to care what number this is, just if it's 0 or not. (result != 0)
Horizontal rows
Ok now we can detect vertical rows. To detect the horizontal rows we need to save another 2 boards, again one for each player. But we need to invert x and y axis. Then we can do the same check again to detect horizontal lines. Your array would then be:
//[player][hORv][rows]
var field[2][2][19];
Diagonals :)
The trickiest part are the diagonals of course but with a simple trick you can do the same check as above. A simple board:
000000
010000
001000
000100
000000
Basically we do the same as above but before we do that we need to shift the rows. Lets say we're at row 1-3.
010000
001000
000100
The first row stays as it is. Then you shift the second row one to the left and the third 2 to the left.
var r0 = field[0][0][i];
var r1 = field[0][0][i+1] << 1;
var r2 = field[0][0][i+2] << 2;
What you will get is:
010000
010000
010000
Apply AND you can have your win detection. To get the other diagonal direction just do it again, but instead of shifting to the left <<, shift to the right >>
I hopes this helps someone.
untested:
int left = max(0, move.x-5), right = min(width-1, move.x+5), top = max(0, move.y-5), bottom = min(width-1, move.y+5);
// check the primary diagonal (top-left to bottom-right)
for (int x = left, y = top; x <= right && y <= bottom; x++, y++) {
for (int count = 0; x <= right && y <= bottom && stones[x][y] == lastPlayer; x++, y++, count++) {
if (count >= 5) return true;
}
}
// check the secondary diagonal (top-right to bottom-left)
// ...
// check the horizontal
// ...
// check the vertical
// ...
return false;
alternatively, if you don't like the nested loops (untested):
// check the primary diagonal (top-left to bottom-right)
int count = 0, maxCount = 0;
for (int x = left, y = top; x <= right && y <= bottom; x++, y++) {
if (count < 5) {
count = stones[x][y] == lastPlayer ? count + 1 : 0;
} else {
return true;
}
}
Related
A small application that I have written allows a user to add various items to two arrays. Some logic calculates a figure from the contents of each array.
Any items in array x can be placed into array y, and back again. Items belonging in array y can never be moved (unless they were moved from array x).
The user can move these items around in two lists using a simple javascript ui. To make things simpler, I originally made a naive script which:
Moved an item from a to y.
Performed some logic using this 'possibility'
If the result was less than before, leave x in y.
If not, then x remains in x.
Move on to next item in x and repeat.
I knew that this was ineffective. I have read around and have been told do this using bitwise math to remember the possibilities or 'permutations' but I am struggling to get my head around this particular problem at this stage.
If anyone would be able to explain (pseudo code is fine) what would be the best way to achieve the following I would be very grateful.
array x = [100,200,300,400,500]
array y = [50,150,350,900]
With these two arrays, for each value from x, push every combination of that value and all the other values from x into array y. For each one I will perform some logic (i.e. test result and store this 'permutation' in an array (an object of two arrays representing x and y). I foresee this as being quite expensive with large arrays, likely to repeat a lot of combinations. I feel like I'm almost there, but lost at this last stage.
Sorry for the long explanation, and thanks in advance!
Use this for creating the power set of x:
function power(x, y) {
var r = [y || []], // an empty set/array as fallback
l = 1;
for (var i=0; i<x.length; l=1<<++i) // OK, l is just r[i].length, but this looks nicer :)
for (var j=0; j<l; j++) {
r.push(r[j].slice(0)); // copy
r[j].push(x[i]);
}
return r;
}
Usage:
> power([0,2], [5,6])
[[5,6,0,2], [5,6,2], [5,6,0], [5,6]]
I have been told do this using bitwise math to remember the possibilities or 'permutations' but I am struggling to get my head around this particular problem at this stage.
It would be iterating to 2n (for an array of length n), using single bits to determine whether an item should be included in the subset. Example for an array [a,b]:
i binary included in set
-----------------------------
0 00 { }
1 01 { b }
2 10 { a }
3 11 { a, b }
We can use bitwise operators in JS for arrays with up to 31 items (which should be enough).
function power(x, y) {
var l = Math.pow(2, x.length),
r = new Array(l);
for (var i=0; i<l; i++) {
var sub = y ? y.slice(0) : [];
for (var j=0; j<x.length; j++)
// if the jth bit from the right is set in i
if (i & Math.pow(2,j)) // Math.pow(2,j) === 1<<j
sub.push(x[j]);
r[i] = sub;
}
return r;
}
I have this image which is completely black except for a white object in the middle (it can be anything but it is always completely white). What I would like to do with nodeJs, is trace the boundary of the object (I would like to find all the white points which are next to black) in the image (performance is key!)
With pngjs I can read an image which gives me an array in which each pixels has 4 values (RGBA). Its a one dimensional array. So, suppose the image is 1000 x 1000 pixels it gives me an array of 1000 x 1000 x 4 = 4000000 entries.
The below expression converts x and y into an array index
var idx = (1000 * y + x) << 2;
data[idx] = 243;
data[idx + 1] = 16;
data[idx + 2] = 16;
Anyway, I could traverse the whole array and register the points where black changes into white, but as I said, performance is very important. I can imagine that some kind of smart iterative search algorithm exists that can follow the boundary somehow :)
Maybe someone knows a library that can help, or an article about how to do this would be great too!!
Check out chain codes like Freeman Code. You need 1 contour point to start with. So just iterate through your lines until you hit your object. Then you walk around your object until you reach your starting point. You will get a code that describes the direction you took for every step. This code can be used to calculate various object features or to just draw the contour of your object.
Btw if your obect is always white and your background is always black you don't have to process 4 channels. Red, green or blue channels contain the same information. Just use either one of them.
My aim is to find a smooth best fit line between this two circuit curvy shapes.
Is there any algorithm betten than mine that can find a set of points (or a curve) between two lines like this example?
The algorithm I have so far takes the inner part and for each point finds the closest, however this doesnt work because (look at the first corner).
(Red is the inner part, green is the outer part, blue is the optimised dots I have found)
Here is my jsfiddle:
http://jsfiddle.net/STLuG/
This is the algorithm:
for (i = 0; i < coords[0].length; i++) {
var currentI = coords[0][i];
j = 0;
var currentJ = coords[0][j];
currentDist = dist(currentI,currentJ);
for (j=1; j < coords[1].length; j++) {
possibleJ = coords[1][j];
possibleDist = dist(currentI, possibleJ);
if (possibleDist < currentDist) {
currentJ = possibleJ;
currentDist = possibleDist;
} else {
}
}
b_context.fillRect(
(currentI.x+currentJ.x)/2+maxX,
(currentI.y+currentJ.y)/2+maxY,
1, 1);
}
Thanks
I would try least-squares-algorithm.
You have a number of points: y0 and x0 and y1 and x1 for the first and the second curve respectively.
You want to find a curve y(t) and x(t) which is smooth and in-between the two given curves.
So there is the distance between the first curve(x0(t), y0(t)) to your to be calculated curve(x(t), y(t)):
S0=SumOverAllT(x0(t)-x(t))^2 + (y0(t) - y(t))^2
The same for the second curve:
S1=SumOverAllT(x1(t)-x(t))^2 + (y1(t) - y(t))^2
The sum of both sums:
S=S0+S1
You will have a set of parameters which you want to determine.
E.g. if you use polynomials:
x(t)=ax+bx*t+cx*t^2+dx*t^3....
y(t)=ay+by*t+cy*t^2+dy*t^3....
You will then calculate
dS/dax, dS/dbx, dS/dcx, ....
for all parameters to be calculated
and set these derivatives to zero:
dS/dax==0
dS/dbx==0
....
This will give you a set of linear equations which can be attacked by the gauss algorithm or whatever method to solve a system of linear equations.
If you're using polynomials it might happen that the curve calculated oscillates strongly.
In this case I would suggest to try to minimize the integral of the square of the second derivative:
I=integral((d^2x/dt^2)^2 + (d^2y/dt^2)^2, dt)
you would calculate the differential of I vs. some additional parameters which you did not use for above system of equation -- adding a parameter rx and calculating dI/drx==0 -- thus you have one more parameter and one more equation.
Anybody with a PHD in mathematics please advice me on any stupidity I mentioned above.
Also search the internet for:
Curve fitting
Spline approximation
A better approach would be to use splines -- piecewise continuous polynomials, so that
the 0 derivative
the first derivative
the second derivative
is continuous.
Look up or buy Numerical recipes to find code which does exactly this.
For spline approximation you would have a set of polynomials:
x0(t)=a0x + b0x*(t - t0) + c0x*(t-t0)^2 + d0x*(t - t0)^3....
x1(t)=a1x + b1x*(t - t0) + c1x*(t-t0)^2 + d1x*(t - t0)^3....
Every polynomial would only be used to cover the matching t=t0..t1 between two given points.
You would then add equations to make certain that the value, first and second derivatives are identical for two neighboring polynomials.
And set the 2 derivative for the first and last polynomial to zero.
Potentially you could calculate two splines -- one for every of the two input curves you have:
x0(t)
y0(t)
x1(t)
y1(t)
And then you could derive the middle of the two splines:
x(t)=(x0(t) + (x1(t)-x0(t))/2
y(t)=(y0(t) + (y1(t)-y0(t))/2
make certain that the distance between any of the given curves and you resulting curve is never zero so that they don't cross each other
To make certain, that your calculated line does not cross one of the given lines, you could minimize (sum(sum(1/(x0-x)^2)) + sum(sum(1/(x1-x)^2)))
I'm using a library (JavaScript-Voronoi) which produces an array of line segments that represent a closed polygon. These segments appear unordered, both the order in which the segments appear as well as the ordering of the points for each end of the segment.
(Edit: As noted in a comment below, I was wrong: the segments from the library are well-ordered. However, the question stands as written: let's assume that the segments do not have any ordering, as this makes it more generally useful.)
For example:
var p1 = {x:13.6,y:13.1}, p2 = {x:37.2,y:35.8}, p3 = {x:99.9,y:14.6},
p4 = {x:99.9,y:45.5}, p5 = {x:33.7,y:66.7};
var segments = [
{ va:p1, vb:p2 },
{ va:p3, vb:p4 },
{ va:p5, vb:p4 },
{ va:p3, vb:p2 },
{ va:p1, vb:p5 } ];
Notice how the first segment links to the last (they share a common point), and to the next-to-last. It is guaranteed that every segment shares an end with exactly one other segment.
I would like to convert this into a list of points to generate a proper SVG polygon:
console.log( orderedPoints(segments) );
// [
// {"x":33.7,"y":66.7},
// {"x":13.6,"y":13.1},
// {"x":37.2,"y":35.8},
// {"x":99.9,"y":14.6},
// {"x":99.9,"y":45.5}
// ]
It doesn't matter whether the points are in clockwise or counter-clockwise order.
The following code is what I've come up with, but in the worst-case scenario it will take n^2+n point comparisons. Is there a more efficient algorithm for joining all these together?
function orderedPoints(segs){
segs = segs.concat(); // make a mutable copy
var seg = segs.pop(), pts = [seg.va], link = seg.vb;
for (var ct=segs.length;ct--;){
for (var i=segs.length;i--;){
if (segs[i].va==link){
seg = segs.splice(i,1)[0]; pts.push(seg.va); link = seg.vb;
break;
}else if (segs[i].vb==link){
seg = segs.splice(i,1)[0]; pts.push(seg.vb); link = seg.va;
break;
}
}
}
return pts;
}
If your polygon is convex, you can pick middle point of each line segment, then use convex hull algorithm to find convex polygon by middle items, after that, because you know what is the arrangement of middles and also you know which middle belongs to which segment, you can find an arrangement in original array.
If you just want to find a convex hull, use convex hull algorithm directly, it's O(n log n), which is fast enough, but also you can find a Quickhull algorithm in javascript here. quickhull is also in O(n logn), but in average, the worst case is O(n^2), but it's fast because of less constant factor.
but in the case of general algorithm:
Set one end of each segment as First, and another end as second (randomly).
Sort your segments by their first x and put it in array First after that in array first sort segments with same first x by their first y and put two extra int into your structure to save start and end position of items with same first x.
Then again sort your segments with the second x value, .... and make array second.
Above actions both are in O(n log n).
Now pick first segment in array First, search for its second x value in both arrays First and second, in the case you find similar values, search for their y values in related subarray (you have start and end position of items with same x). You know there is only one segment with this order (also is not current segment), so finding next segment takes O(log n) and because in all there is n-1 next segment it takes O(n logn) (also preprocessing), which is extremely faster than O(n^2).
It should be possible to turn the points into a (double, unordered?) linked list in linear time:
for (var i=0; i<segments.length; i++) {
var a = segments[i].va,
b = segments[i].vb;
// nexts being the two adjacent points (in unknown order)
if (a.nexts) a.nexts.push(b); else a.nexts = [b];
if (b.nexts) b.nexts.push(a); else b.nexts = [a];
}
Now you can iterate it to build the array:
var prev = segments[0].va,
start = segments[0].vb, // start somewhere, in some direction
points = [],
cur = start;
do {
points.push(cur);
var nexts = cur.nexts,
next = nexts[0] == prev ? nexts[1] : nexts[0];
delete cur.nexts; // un-modify the object
prev = cur;
cur = next;
} while (cur && cur != start)
return points;
If you do not want to modify the objects, an EcmaScript6 Map (with object keys) would come in handy. As a workaround, you could use a JSON serialisation of your point coordinates as keys of a normal object, however you are then limited to polygons that do not contain a coordinate twice. Or just use the unique voronoiId property that your library adds to the vertices for identifying them.
For a convex polygon, you don't even need to know the side segments. You just need a bunch of vertices. The procedure to order the vertices is pretty simple.
average all the vertices together to get a point inside the polygon. note that this doesn't even need to be the centroid. it just needs to be a point inside the polygon. call this point C.
for each vertex V[i], compute the angle the line segment from V[i] to C forms with the line segment from V[i] to V[i]+(1,0). call this a[i].
sort the angles of vertices using the vertices as satellite data.
the sorted vertices are in order around the polygon. there are some redundancies that you can remove. 1 runs in linear time, 2 runs in linear time, 3 runs in n log n.
I've been doing web development for years now and I'm slowly getting myself involved with game development and for my current project I've got this isometric map, where I need to use an algorithm to detect which field is being clicked on. This is all in the browser with Javascript by the way.
The map
It looks like this and I've added some numbers to show you the structure of the fields (tiles) and their IDs. All the fields have a center point (array of x,y) which the four corners are based on when drawn.
As you can see it's not a diamond shape, but a zig-zag map and there's no angle (top-down view) which is why I can't find an answer myself considering that all articles and calculations are usually based on a diamond shape with an angle.
The numbers
It's a dynamic map and all sizes and numbers can be changed to generate a new map.
I know it isn't a lot of data, but the map is generated based on the map and field sizes.
- Map Size: x:800 y:400
- Field Size: 80x80 (between corners)
- Center position of all the fields (x,y)
The goal
To come up with an algorithm which tells the client (game) which field the mouse is located in at any given event (click, movement etc).
Disclaimer
I do want to mention that I've already come up with a working solution myself, however I'm 100% certain it could be written in a better way (my solution involves a lot of nested if-statements and loops), and that's why I'm asking here.
Here's an example of my solution where I basically find a square with corners in the nearest 4 known positions and then I get my result based on the smallest square between the 2 nearest fields. Does that make any sense?
Ask if I missed something.
Here's what I came up with,
function posInGrid(x, y, length) {
xFromColCenter = x % length - length / 2;
yFromRowCenter = y % length - length / 2;
col = (x - xFromColCenter) / length;
row = (y - yFromRowCenter) / length;
if (yFromRowCenter < xFromColCenter) {
if (yFromRowCenter < (-xFromColCenter))--row;
else++col;
} else if (yFromRowCenter > xFromColCenter) {
if (yFromRowCenter < (-xFromColCenter))--col;
else++row;
}
return "Col:"+col+", Row:"+row+", xFC:"+xFromColCenter+", yFC:"+yFromRowCenter;
}
X and Y are the coords in the image, and length is the spacing of the grid.
Right now it returns a string, just for testing.. result should be row and col, and those are the coordinates I chose: your tile 1 has coords (1,0) tile 2 is(3,0), tile 10 is (0,1), tile 11 is (2,1). You could convert my coordinates to your numbered tiles in a line or two.
And a JSFiddle for testing http://jsfiddle.net/NHV3y/
Cheers.
EDIT: changed the return statement, had some variables I used for debugging left in.
A pixel perfect way of hit detection I've used in the past (in OpenGL, but the concept stands here too) is an off screen rendering of the scene where the different objects are identified with different colors.
This approach requires double the memory and double the rendering but the hit detection of arbitrarily complex scenes is done with a simple color lookup.
Since you want to detect a cell in a grid there are probably more efficient solutions but I wanted to mention this one for it's simplicity and flexibility.
This has been solved before, let me consult my notes...
Here's a couple of good resources:
From Laserbrain Studios, The basics of isometric programming
Useful article in the thread posted here, in Java
Let me know if this helps, and good luck with your game!
This code calculates the position in the grid given the uneven spacing. Should be pretty fast; almost all operations are done mathematically, using just one loop. I'll ponder the other part of the problem later.
def cspot(x,y,length):
l=length
lp=length+1
vlist = [ (l*(k%2))+(lp*((k+1)%2)) for k in range(1,y+1) ]
vlist.append(1)
return x + sum(vlist)