Recursion Applied To "AI" =========================== Artificial Intelligence (AI) is the Mount Everest of programming. Every programmer really wants to make a program that learns and shows intelligence, but no one has ever done it. To start off this article, there must be a defining line between actual AI (evaluation and learning) and robotic response (reacting to stimuli). Imagine a space-fighting game. Just a simple 2D will do. There is the player's spaceship, and then there is the computer-driven spaceship, and both are trying to destroy the other. Though the computer-drive ship may be very good at winning the game, it is simple robotic proceedures that govern it: Move towards/Aim at the player's ship If in range, shoot If low shields or power, retreat The robotic enemy typically does not do any evaluation of the player's movements or strategy, and this is almost always good enough to make the game challenging enough...but it is not artificial intelligence. It is responding to the player's ship, and what it is doing at the moment and not learning how to be a better fighter. Some programs that have achieved a half-step towards artifical intelligence, or at least artificial thinking, are chess and checkers games. To take the easier example, let us consider a checkers program. In the game of checkers, each state in the game has a limited number of possibilities. For example, at the beginning of the game, there are only 4 possible moves, since there are only 4 pieces that are allowed to move. However, the artifical thinking has to come into play when there are advantages and disadvantages to each move that is made. This is also where recursion comes into play (no pun intended). Each move that is made in a checkers game creates a "multi-pronged fork-in-the-road" for the possibilies for moves after. With such form of branching, a recursive function is the best way to handle and evaluate each possible move, and eventually come up with a victory-inspired move. Let's start in the simplest form of the recursive function. We have to assume that the graphical interface for the game is already set up, and that each piece is numbered or otherwise at least has an identity. The function must (for the computer player's side): Determine how many possible moves there are Call this recursive routine once for each possibility. Of course, there must be some way of passing the function the layout of the board, without actually changing the layout that the player actually sees. One possible way would be to pass the function 25 variables, one for each piece, plus one more for who's turn it is. Each variable would indicate a piece's location with a number. If the piece has been taken, then that piece's variable could be 0, otherwise a number from 1 to 32, since that is the number of possible places that a piece could be. 64 squares on the board, but only one color is ever used. The recursive function would, upon receiving the "coordinates" of each piece, figure out every possible move that can be done by the player who's turn it is, whether computer of human. The function would call each one of those possibilities by calling itself. The parameters that the function would send itself are the parameters it received, but with one variable changed- the variable of the piece that is to be evaluated. So, in short, the recursive function figures out all possible moves, makes a list of them, then calls the function (itself) for each one of those possible moves. Of course, until the end of the game, the first move in the list that the function has compiled will call another recursion of itself, but when there are no more possible moves, the recursion loops will be broken, and the program flow will eventually return to the main program where the function was first called. Determine the possible moves for the current player Call this function for possible move #1 Call this function for possible move #2 Call this function for possible move #3 Call this function for possible move #n The condition that breaks the loop is when all pieces of one color are taken. If the method mentioned previously is used (24 variables, one for each piece), all variables indicating one players pieces are gone. Now, the way a function works is that it must return a single value. Since there is a single condition that breaks the loop, that is where the return value is set. When the recursive function reaches an "end game" point (when all of either player is gone), if the winner is the computer, then the function would return an indication that this is good, say a 1. If the winner is the player, then it would return a bad indication, say a 0. Determine the possible moves for the current player Call this function for possible moves If there are no possible moves, then If all computer's pieces are taken, return 0 If all player's pieces are taken, return 1 There is only one way that the function would only have one move available in the list of moves. This is if there is a required move, such as a forced capture, or if there is only one move that the player can make, due to physical restraint. In these cases, the recursive routine works, but there are no multiple pathways. When the recursive function has completed evaluating every possible move from its point of view (from its current board layout), then a decision can be made simply. Since each evaluation returns a number, take those numbers and add them together. The total should then be the return value of the function. Determine the possible moves for the current player Call this function for possible moves If there are no possible moves, then If all computer's pieces are taken, return 0 If all player's pieces are taken, return 1 If there are possible moves Add up the numbers returns by the evaluated moves Return that value (instead of 0 or 1) Now you must actually implement this recursive function into the main program, since it must be called. Basically, you must copy the code from the recursive function into the main program in order to call it. The reason for this is because you must start by figuring out how many possible moves there are, then call the function for each possible move from the current layout. The reaosn you cannot simply call the function is because of a little extra coding: The function returns a number for each proposed move. Since one move is bound to produce more possibilities of winning than the others, the move that returns the highest value is the best move to take. This cannot be programmed into the recursive function, since the function should not be allowed to affect the actual playing board. If there is a tie between the numbers returned, just choose the first or last of the moves proposed, it doesn't make a difference. That is the basic way that recursion is used to create a "thinking" computer player for a game of checkers. Obviously this does not include the possibilities of crowning, since the recursive function would have to be sent additional parameters indicating if a piece was crowned or not, so that it knows what possible moves it has. But this is the basis for how what some people call artifical intelligence works. It uses recursion to consider every possible move, consider the best move to make, and make it. Recursion is a wonderful tool for creating this kind of half-intelligence- half-robotic-reaction, but it still is not true AI. What is involved with true artificial intelligence is a combination of extreme evaluation (thinking) and storage and useage of data (learning). So, how is that done? Wait for the next article...! - Written by Jack Thomson for the QBasic Station