I am trying to code a Gomoku (five in a row) game in Java as an individual project. For the AI, I understand that the use of a Minimax function with Alpha-beta Pruning is a good way to approach this. However, I'm having a little trouble envisioning how this would work.
My question is this: what is a good representation for a node in a minimax tree?
I'm thinking my evaluation function will "weight" all the empty spaces on the board. It will then take the best value from that board as the node of the minmax decision tree. Am I on the right direction?
And any other tips are also welcome!
Thanks in advance!
The state space search is through the different states of the board. There are a lot of moves, since you can place a stone anywhere unoccupied. Each state can be represented as a e.g. 9x9 matrix, with 3 values -- white, black, or unoccupied. With a 9x9 board, there are therefore 3 ^ 81 possible board states.
From any board state, the number of moves is the number of unoccupied vertices. You can place a stone on any of these vertices. You can only play your own color. So, at most there are 81 possible moves .. 81 for the first move, 80 for the second, and so on. So you can search to depth 5 reasonably, and possibly more .. not too bad.
The proper representation is, as mentioned, a 2D matrix -- this can just be a 2D array of ints, with values e.g. 0 for unoccupied, 1 for white, 2 for black. ... int[9,9].
Your evaluation function doesn't sound very good. Instead, I would give points for the following:
-- get 5 in a row -- basically give it the max score for this one, since it's a win
-- 4 in a row with 2 open ends -- also max score, since opponent can't block you from getting 5.
-- 4 in a row with 1 open end -- still a very threatenning position, since opponent has to play
in one spot to block.
-- 3 in a row with 2 open ends -- very high score again
--- 4, 3, 2, 1 with both closed ends -- 0, since can't ever make 5 in a row.
and so on.
Then, you just apply the standard minimax algorithm -- i.e. alpha beta pruning -- it would be exactly the same as chess, but you have a different state space generator and evaluation function.
I'd consider an evaluation function of the following form: consider each set of, say, 6 positions in a line. (On a 19x19 board there are 14 along each line and varying numbers from 0 to 14 along each diagonal; I think that comes to 742 of these on the whole board. My arithmetic may be wrong.) For each set there are 729 possible arrangements of black, white and empty spaces. Or, er, 378 if you take end-to-end symmetry into account. Or, er, um, fewer than that but I can't be bothered to work out how many fewer if you take black/white symmetry into account too.
So now your evaluation function will consist of a table-lookup for each block of 6 stones, in a 378-or-however-many-element table (or perhaps two of them, one for horizontal and vertical lines, one for diagonal ones). Add up the results and that's your evaluation of the position.
It may turn out that actually a larger table (derived from a longer row of positions) works better.
But what goes in the table? Let your program work that out. Start with arbitrary values in the table (you might, e.g., take eval(line) = #black(line)-#white(line) or something). Let your program play itself, using alpha-beta search. Now update the table entries according to what happens. There are many different ways of doing this; here are a (sketchily-described) few.
During each game, keep track of how many times each pattern occurred in each player's positions. When the game's over, adjust each pattern's score so that patterns seen more often by the winning player look better.
Each time you do a search, adjust the scores for the patterns in the current position to bring the current static score nearer to the score obtained by search.
Each time a move is made, adjust the scores for each pattern in the "before" position to make the "before" score match the "after" score better.
Have lots of different tables (hence lots of different variants of the evaluation function). Let them play against one another. Apply some sort of evolution (e.g., play all against all, then throw out the worst performers and replace them with mutants derived from the better performers).
For a more sophisticated version of these ideas (applied to chess, but the same ideas would work fine for gomoku), take a look at http://cs.anu.edu.au/~Lex.Weaver/pub_sem/publications/knightcap.pdf .
Related
Blue-Walls
Green highlighted cells = open list
Red Highlighted cells = closed list
Hello, can anyone tell me how can i implement backtracking in a a star search algorithm?
I've implemented the a star search according to wiki, but it does not backtrack, what i mean by backtrack is that the open list(green cells) contains 2,0 and 3,3 as shown in the picture, upon reaching 2,0 the current node would "jump" to 3,3 since the cost is now more than 3,3 and continue the search from there, how can it be done so that it would backtrack from 2,0->2,1->2,2... all the way back to 3,3 and start the search from there?
your image is like 2d grid map
But your text suggest graph approach which is a bit confusing.
For 2D grid map the costs must be different between cells on path
You got too much of cost=100 in there and therefore you can not backtrack the path. You have to increase or decrease cost on each step and fill only cells that are near last filled cells. That can be done by recursion on big maps or by scanning whole map or bounding box for last filled number on small maps.
Look here for mine C++ A* implementation
The backtracking
Can be done by scanning neighbors of start/end cells after A* filling moving always to the smallest/biggest cost
In this example start filling from (2,0) until (3,3) is hit and then backtrack from (3,2) cost=8 to the smallest cost (always cost-1 for incremental filling). If you need the path in reverse order then start filling from (3,3) instead ...
speedup
Sometimes double filling speed up the process so: Start filling from both ends and stop when they join. To recognize which cell is filled from which point you can use positive and negative values, or some big enough ranges for costs.
You can follow backpointers from the two nodes until you reach the common ancestor (0,2), then print the nodes you visited when following from (2,0), followed by the the nodes you visited when following from (3,3), printed in reverse.
To find the common ancestor of two nodes in an A* search tree, just maintain the two "current nodes", and follow the backpointer of whichever has the higher g-cost, until the two current nodes are in the same place.
It bears mentioning that this is a weird thing to do, though. A* is not a stack-based traversal, so it doesn't backtrack.
I'm trying to write a tetris clone. My board is a 10x20 array of integers on which 0 means no tile, 1-7 means a tile of specific color. It is constantly translated to graphic interpretation. My shapes are 4x4 arrays of integers. I've just come to a realisation that while making all of the shapes 4x4 makes some things easier, it also causes a problem when moving a piece left and right. Let's say we've got the I shape:
0010
0010
0010
0010
Now, if I move it to left wall there will always be a two units long gap, since the 0s cant move outside of the main board array. What would be the easiest way to allow the 1s to move to the left wall without causing an out of bounds exception?
Using your described method, one way to simply avoid getting the IndexOutOfBoundsException would be to expand your board to be 18 x 24 instead of 10 x 24, and then write in additional code that doesn't let you move a block left/right if there would be any 1's in the object array that leave the middle 10 squares of the grid. By adding this 'padding' to your grid, you avoid the exception and should still be able to implement.
I hope this approach makes sense to you. If not I can provide a more pseudo-code driven answer, but I hope you get the idea. (Just comment if you have any questions.)
BTW, #assylias makes a very good point. It is important to have a good design/plan before you start implementing things to avoid road-bumps like these. It comes with experience, so keep practicing and you will get the hang of it.
NOTE: As Nick pointed out in the comment, another way of doing this is to simply check if any 1's leave the grid before moving any of the arrays. This is certainly possible (and arguably a more elegant/simple solution), although it may be a bit harder to get right.
You need a way of detecting collisions with borders and existing pieces.
You would probably have a fixed handle on each piece, you'll also have an X and Y offest for the piece which indicates it's position as it moves down the grid.
To stop a piece moving out of bounds, loop through the 4*4 matrix of the moving piece and for the bits which are set to 1 simply check
to make sure that the X position + X offset is >= 0 and <=9 and the Y Position is >=0 and <=19 if either of these checks fail
then your piece would be moving outside the limits of the board array so stop the change to x or y offest as appropriate.
Translating the co-ordinates of the set bits in your piece matrix with the board array also allows you to check and see whether your piece has collided with a tile already in the board.
You should be doing these collision checks when a piece rotates as well I would have thought.
Pardon the wall of text. I'll add images later. I need to generate a somewhat realistic map of cubic meter voxels, with water, sand, grasses, trees, minerals, deserts, beaches, islands, etc, without any sort of voronoi cop-out(i.e. be smart about relating these factors to each other). Yes, this is a game.
I figured I'd generate critical points randomly and interpolate them for elevation and humidity readings, but I'm at a loss with random generation. Basically I need a somewhat even distribution of points without having to make the full list at once. I need to generate roughly 20x20x20 at a time, and probably work with approximately 1000x1000x1000 cells of critical points, but I'd expect strange things to happen at the edges of the large cells. Does anyone know of any way to select points in this way? The real trouble is that points should prefer to be in proximity to others in "mountain-range" style chains.
The problem here is that this is happening across these 1-km cells.
I can simply pick points this way within a cell, but since a cell and its neighbor need to be dependent on each other, my trivial algorithm would have encountered the need to head to infinity for one of these cells, or see a grid-like pattern of broken chains. The chains should not break more frequently on cell boundaries. If they do a somewhat problematic wafer-like pattern shows in generation and makes for a poor generation that is unusable for design/gameplay reasons.
n.b. For the purposes of these, the system-level random generator can be seeded and is practically uniform. As far as within a cell, I can select chains just fine.
I also considered having a cell spill over into any ungenerated cells so its chains start connected to existing ones, but that would break the determinism of generation simply based on location and seed, adding order of generation as a factor.
Again, for the purposes of realism and design I'm trying to stay away from using Perlin. Or should I post on gamedev.SE?
The key concept is to create the interfaces between a cell and all six of its neighbors before filling in the cell. Picture creating your entire world as a grid of hollow boxes, but before you do that, create it as a wire-frame outline, but before you do that, create the grid of wire-frame intersections.
Here's a simplistic approach -- you'll have to improve this. First, consider this method of generating the entire world at once:
(1) select all the vertex voxels -- perhaps the upper north west voxel in each cell -- and set its world attributes to reasonable values based entirely on location and seed.
(2) select the lines of voxels connecting the vertices, and fill in all their world attributes, based on location and seed, but constrained to match up with the existing vertex voxel values at each end.
(3) select the planes of voxels describing the faces bounded by the existing lines, and fill in in all their world attributes, based on location and seed, but constrained to match up with the existing line voxel values along the edges.
(4) fill in the cell, based on location and seed, but constrained to match up with the existing bounding six faces.
Now, consider that this method doesn't need to be done all at once. All that is necessary is that you create all six faces of a cell before filling it in, and that you create all four bounding lines of a face before you fill that in, and that you create the two end points of a line before filling that in. After the first cell, some of these will already exist.
The reason I said that you'll have to improve this idea is that it produces noticeable gradient boundaries at the cell boundaries. I'm afraid that each interface voxel will not only need to contain world attributes, but the rate of change of each attribute across the interface at that point. This implies that each line voxel will have to contain two rates of change for each attribute, and each vertex, three.
I'm not going to describe how you would constrain the gradient of a world attribute as it approaches a voxel with a predefined gradient because I'm sure you can handle it, my answer is already too long, and I don't know how.
This is a homework question (sorry, I know these are frowned upon), but neither I nor the teacher actually knows how to solve it efficiently, so I wanted to put it up here to see if the wonderful brains on SO could help us out.
An array of unspecified size is given, containing random numbers. It must be sorted into increasing order. Each element can be either moved to an adjacent empty space, or moved on top of an adjacent larger element. I need to write a method that returns the minimum number of moves needed to sort the given array.
The question has been labeled 'optional', since the teacher realized the problem was far too difficult, but I'm curious how it might be solved. Any suggestions for arrays of any size (it can be for arrays of length 3 for all I care) are appreciated.
EDIT: Thanks for pointing out that this was unclear. I'm using the array to represent a hypothetical real world situation. Let's use the example of coins: they are all laid out on a table in a row, and there are only a specified number of 'spaces' they can be put in. But they can be put on top of adjacent larger coins, or slide to an adjacent empty space (which has been vacated by a coin that had presumably moved on top of a pile).
I decided to examine the problem with a few assumptions/changes, because it made it a more interesting problem to me:
1) You can move elements left or right from any part of a pile.
2) You can stack an element onto a pile no matter if it's bigger, smaller or the same size.
3) The array is considered sorted as long as you never encounter a bigger number before a smaller number no matter how you go through stacks. So _ 11 2 3 is sorted but _ _ 12 3 is not because you could interpret the 2 as being before the 1.
This leads to a very interesting problem, in spite of this axiom:
Axiom A: The order in which you make moves is irrelevant, and can be re-arranged in any way to still achieve the same final result.
Axiom Ab: If the array has no repeats in it, then simply move every element towards its final position.
In particular, I developed a strategy hoping that you could solve it with only local examination and no recursiveness/backtracking, but I've proven that this is fruitless and will show it later.
My strategy is:
1) Proceed from left to right looking for pairs that are flipped the wrong way (a higher number before a lower number).
2a) When you find one, if there is an empty spot or a stack that the right hand value could immediately fill, move it left until it does fill it.
2b) Else, move the left value right one. This creates a situation where you have a stack of indifferent numbers - first, compare the value you moved right to the value on its new right according to the logic of 1) before comparing downwards.
2bii) Treat a downwards comparison the same way as a pair comparison - move the lower value left if it can fit an empty spot or stack, else move the higher value right and continue.
Examples:
1231 -> we shift 1b left because it will fit onto a stack. 11 2 3 _
1321 -> we shift 3 right because 2 will not fit into an empty spot/onto a stack. we shift 1b left twice because it will fit into an empty spot/fit onto a stack, then we shift 3 right because 2 will not fit into an empty spot/onto a stack. 1 1 2 3
1132 -> we shift 3 right as 2 can't go left. we shift 2 left because it will fit in an empty spot. 1 1 2 3
2311 -> we shift 3 right as 1a can't go left. we shift 1b left twice because it will fit in an empty spot. we shift 1a left because it will stack. we shift 2 right because 1a1b can't go left. we shift 1a2b left as it will fill an empty spot. 11 2 3 _
However, we run into a problem with these two starting arrays:
23411 10 moves optimal, 2r 3r 4r 1al*3 1bl*4 to make 11 2 3 4 _.
23111 9 moves optimal, 2r*3 3r*3 1bl 1cl*2 to make _ _ 111 2 3 - the opposite of the 23411 strategy! We move the 1s less and the 23 more because there are more 1s and so we save moves moving them as little as possible.
Which shows that we can't JUST do a simple local comparison to solve this new problem.
I'm still thinking about this problem, it seems non-trivial in an intriguing way, and I hope some of you enjoy contemplating this with me :)
I assume:
"sorted" means there is a single stack left
we can only move a stack onto another stack if the largest number in the source stack is smaller than the smallest number in the destination stack (or equivalently: stacks must be sorted with smaller numbers on top, and moving a stack onto another moves the entire stack on top of the other)
Then, there is obviosly no solution if the array contains a number more than once. Also, the magnitude of the numbers doesn't matter, only their rank does. Without loss of generality, we can therefore assume that the array contains the numbers 1..n in arbitrary order. Also, to determine the possible moves, only the top of bottom of the stacks matter. It is therefore sufficient to store:
int[] bottom;
int[] top;
Since we can not take stacks apart, we may only move stack i onto stack j if bottom[i] + 1 == top[j], or we'll end up in an unsolvable state. If however, we are in a game state where such a move is possible, it is optimal to perform it immediately, because eventually we'll have to combine these stacks, and moving an individual stack around is cheaper than moving two stacks around.
The only degree of freedom therefore is how to move the stacks into position with the least number of moves. At this time, I don't see an obvious greedy algorithm for that, so finding the optimal solution may require encoding the positions of the game into a graph, and apply a shortest path algorithm to that graph to find the shortest sequences of moves.
EDIT: Given that everyone seems to be solving a different problem, I'll state the problem I'm solving (which I believe to be the correct one (don't we all?)): (using disks to hopefully make things easier to understand)
Given n piles, each containing exactly 1 disk each, order these disks in increasing order of size. The end result must have each pile containing 1 disk each. The only allowed operation is to move a single disk from the top of one pile to the top of an adjacent pile, subject to the constraint that no disk may be placed on top of a smaller disk. Moving a disk to an empty pile or moving the last disk from a pile is allowed. There are always n piles, thus: (1) New piles may not be created, so a disk may not be moved outside the bounds of the original sequence. (2) Empty piles remain, so if the adjacent position is empty, the disk may not be moved over that position without first being moved onto that position.
Note:
A disk with a diameter of x = a number x.
This may not be the most efficient solution, but it should work and may give someone something to work off of.
It's a purely iterative process - after every step we end with all stacks having size 1; this may be a fundamental inefficiency with this approach.
Solution:
I'll use 1, 2 and 5 to illustrate the below, but this is just to indicate size ordering, it holds the same for any other numbers with the same ordering.
Repeat until sorted:
Find the biggest element not already in the correct position (5 in this case)
(Actually it doesn't have to be the biggest, just anything in the below form, but be careful not to move elements past where they should be)
We have 2 cases:
It's at the left-most position, we have 2 cases:
512... - move 1 right, move 5 right, move 1 2 left, end with 152
521... - move 1 2 left, move 2 right, move 1 2 right, move 5 right, move 1 2 left, end with 152
It's not at the left-most position, we have 2 cases:
...251... - move 1 2 left, move 5 right, move 1 right, end with 215
...152... - move 2 left, move 1 right, move 1 right, move 2 left, end with 251 (after which we do steps from previous case)
EDIT 2:
A more efficient solution:
Find the smallest disk not in the right position
Put it on top of the disk to the right
Shift all disks to the left of that disk 1 position right
Shift the smallest disk all the way to the left (until you encounter a smaller disk which would already be in the right place)
Possible preprocessing step: Sort to a separate list to efficiently find smallest elements
Optimization: If you were going to move a disk to the right past its target position, instead, don't shift the disk on the right to the right in the previous step, but simply put this disk on that disk. How to do this at the right time efficiently may be a bit complex. It may also be helpful to not perform certain moves to increase efficiency.
Example:
52413
// put smallest disk (1) on disk to the right
1
524_3
// shift disks to the left 1 position right (3 moves - moved 4, then 2, then 5)
1
_5243
// move 1 all the way left (4 moves)
15243
// same as above for 2
2
15_43
2
1_543
12543
When the smallest disk is on the right-most position (as is now the case with 3), this is a bit of a problem. 2 ways of resolving it:
Swap 1 and 2 and put 1 on 2 (1 2 positions right, 2 left, 1 2 positions left). Then you'd have an open spot onto which to move 3. Not an option if there's less than 2 smaller elements. These elements shouldn't be fixed until we've sorted a few more elements, in case we run into the same situation.
If we had 12453, we could simply put 4 on 5 to open a slot for 3 (which sort of delays the problem). If the first non-sorted disk (5 in this case) is bigger than the second one (4) we could use some strategy like I described for my previous solution above to move elements to satisfy this condition.
I want to draw a dendrogram from objects which I have already clustered and saved in a (leave)tree. My Cluster object in Java can be represented by these pictures. Each leaves contains an object and each node contains the distance between its children.
Now, I want to draw a dendrogram stepwise, so first object 1 and 2 should be drawn, then object 3 should be added to those. Then 5 and 6 together etc.. to the end where everything is connected. I already have all the tools to draw this easily, but I cannot find an efficient way in walking through the tree correctly.
This should be the result so far in the example (EDIT: There's a mistake, the distance from 5 to 6 now looks smaller than the distance from 3 to 1&2, but that isn't the case in my example!):
Does anyone have some tips for this recursive algorithm?
you're rendering depth first - you may find you need to keep track of depth to get the layout to work right - also note you have to draw non-terminal notes as well ("*" gets drawn before 3)
basically
draw(node)
{
if(hasleft) draw(left)
if(hasright) draw(right)
drawme()
}
this does assume a binary tree, but that is what you've drawn - and this is going to get a lot more complicated because of layout issues - you really want to consider drawing breadth first from the top down, it makes layout a lot less painful