Anda di halaman 1dari 145

VRIJE UNIVERSITEIT BRUSSEL Faculty of Science Computer Science Departement

APPLYING THE GENETIC ALGORITHM TO THE GAME OF OTHELLO


By Itamar Faybish

Promoter: Pr. Bernard Manderick

Thesis presented in partial fulfillment of the requirements for the degree of Master of Science in Computer Science

Academic year 1998 - 1999

ACKNOWLEDGMENT

I would like to thank Mr. Bernard Manderick for his valuable time, for all the valuable advices and interesting ideas, and for his kindness at all times.

This work is dedicated to my family, and to all the persons in the world who dedicate their lives to the well-being of others.

CONTENTS
1 INTRODUCTION 2 THE OTHELLO GAME
2.1 INTRODUCTION 2 2.2 THE RULES 2 2.3 GENERAL OTHELLO STRATEGIES 4 2.3.1 Introduction 4 2.3.2 The number of pieces 5 2.3.3 The number of possible moves 7 2.3.4 The corners and stable squares 7 2.3.5 The X squares attack 8 2.3.6 Edge configurations 9 2.4 PROGRAMMING OTHELLO GAMES 11 2.4.1 Introduction 11 2.4.2 Mobility 11 2.4.2.1 Use of possible moves 11 2.4.2.2 Use of empty squares 11 2.4.2.3 Use of non-empty squares 13 2.4.3 Stability 14 2.4.4 Topology 16

1 2

3 THE MIN-MAX AND ALPHA-BETA ALGORITHMS


3.1 INTRODUCTION 17 3.2 MIN-MAX 17 3.2.1 Introduction 17 3.2.2 The algorithm 18 3.2.3 Some notes on the algorithm 19 3.3 ALPHA-BETA 20
4

17

3.3.1 Introduction 20 3.3.2 The algorithm 20 3.3.3 Some notes on the algorithm 22

4 GENETIC ALGORITHMS
4.1 INTRODUCTION 23 4.2 GENETIC ALGORITHM SCOPE 23 4.3 HOW DO GENETIC ALGORITHMS WORK 24 4.3.1 Representation 24 4.3.2 Selection 25 4.3.3 Recombination 25 4.3.3.1 One point Crossover 25 4.3.3.2 Two points Crossover 26 4.3.3.3 N points Crossover 26 4.3.3.4 Uniform Crossover 26 4.3.4 Mutation 27 4.4 THE SCHEMA THEOREM 27 4.5 CRITICISMS OF THE SCHEMA THEOREM 29 4.6 ON PARAMETER VALUES 29 4.7 PRACTICAL EXAMPLE OF A GENETIC ALGORITHM 30

23

5 THE GENETIC ALGORITHM APPLIED TO THE OTHELLO GAME 31


5.1 INTRODUCTION 31 5.2 THE PROGRAMS OPTIONS 32 5.3 THE SIMULATION SCREEN 36 5.4 THE CHOSEN PARAMETERS 40 5.5 ABOUT THE SEARCH DEPTH 44 5.6 RESULTS 45 5.6.1 First serie 45
5

5.6.1.1 Simulations 45 5.6.1.2 Analysis of these results 51 5.6.2 Second serie 52 5.6.2.1 Simulations 52 5.6.2.2 Analysis of these results 52 5.7 MODIFYING THE PROGRAM 53 5.7.1 Changing the evaluation function 53 5.7.2 Changing the optimization problem 55

6 CONCLUSION 7 APPENDIX
7.1 EXAMPLE OF A GENETIC ALGORITHM 58 7.2 THE OTHELLO PROGRAM 63 7.2.1 Main module 63 7.2.2 Otgraph unit 127 7.2.3 Globvar unit 136

57 58

8 BIBLIOGRAPHY

138

1
INTRODUCTION

This thesis shows how a genetic algorithm can be applied to a strategic game. We decided to use the othello game for its very simple rules, but neverless amazing complexity, and the relatively fast speed at which one game can be completed with reasonable quality (about 20 seconds on a Pentium 400); this makes it an ideal game for our purpose. The idea behind using the genetic algorithm is that we suppose that we have some specific parameters in the evaluation function of the othello game, but we do not know how they should interact, or in other words, what should be their coefficients in order to make it a strong/good evaluation function compared to randomly generated coefficients, so we will create a genetic population which is made up of many individual strings, each one representing a set of specific parameters coefficients; then after some generations have passed, all the strings in the population will tend toward a string which will represent a good set of coefficients. First we will introduce the othello game and the major strategies involved when playing this game, and also how can a computer simulate these strategies algorithmitically. Then we will introduce the min-max and alpha-beta algorithm, these are optimized search tree algorithms which are very important in all strategic game programs, in short, these algorithms permits the computer to choose a move according to what happens many moves later. After that we will introduce the genetic algorithm, and also give a short program demonstrating how it can be implemented. And finally we come to the program developed for this thesis.

2
THE OTHELLO GAME

2.1 INTRODUCTION
The game of Othello is a very exciting and easy to learn board game; but this easy to learn refers only to its rules and can be quite misleading, in fact it is a most complex game when it comes to actually trying to play good, and is a most frustrating game when it comes to try not to loose badly against even bad computers J We are also going to introduce the basics strategies and the way they can be implemented in a program.

2.2 THE RULES


The game of Othello is played on an 8 x 8 square board, by two players, one taking white while the other taking black. The initial configuration, before the game starts, is :

Black starts playing first. Each player puts one disc on the board at each turn, unless : either no one can move, or one player cannot move, and then the other player can play again. The game ends when no player can move, and the one who has the most discs of his own colors has won. Now how to put a disc on the board ? The idea is one can put a disc of his own color at a square, if and only if there is at least one disc of the opposite color immediately surrounding

it, and at the extension of this line there is a disc of his own color. For example, at the beginning of the game, black can play at the following squares marked with an X :

Lets follow an opening game: Black plays D3

: White plays C5

Black plays E6

White plays D2:

Black plays C4

We can see from these examples that a player can take more than one disc at once, as seen from the second to last move, and from more than one direction, as seen from the last move.

2.3 GENERAL OTHELLO STRATEGIES 2.3.1 Introduction


Ok, now that you know the rules, you may think it is a YAEGFSC, or a Yet Another Easy Game For Small Childrens.. well.. if I was to start betting money that I win just one game out of 50 against an Othello expert, my banc advisor would resign and send me to his opponent. So learning to play well this game demands a lot of effort, probably similar to playing well the game of Chess, although a bit less maybe. In this section we will review the main strategic themes related to this game, although probably many other exists which we dont know about,
4

but knowing those should suffice to win with quite a high probability (90% or more probably) against those who dont. If this turns out wrong, please dont send us hate mails.

2.3.2 The number of pieces


This is the most well known factor that most beginners in Othello use. It would be ok if the actual factor was to minimize his own discs while maximizing the opposite, but beginners usually reason the other way round, and when they have 30 discs and the opponent has 4 discs, they claim that the opponent, either computer or player, is lost, and is either stupid or doesnt know how to play; computer dont care to hear such things, as they are deaf, but humans.., a small smile on their faces, waiting to see their opponents lost in despair when the entire board becomes full of their own discs at the end of the game. Youre lucky! . Yes, maybe.. So we can see from this that the correct strategy is rather to ha ve the less discs of ones own color, had try to maximize the discs of the opponents color. Only when approaching the end, like about 10-14 half plys (a ply is equal to two half ply, a half ply is one move from either black or white), that one should starts worrying about the number of discs on the board. The idea behind all this is that the less discs one own, the more possibilities one have, the less possibilities for the opponent. At some points, one can end up with for example ten possibilities, while the opponent has none, then the first player has the game under control and can manipulate his opponent as he wish, getting a corner is easy in this case, and once someone has a corner, the opponent chances of surviving are minimal (of course there are exceptions, we are talking in the general case, we will review some of these exceptions in the next sections). Lets look at an example, in here I was black and played badly, and the computer is white, he has 9 discs, I have 22 discs, but I am completely lost. It is my turn and I dont have any good moves, he will easily get a corner soon and beat me in no time.

Counter-example

This is a man made board, just to show that the above rule is not always true. In this position, according to the above said, black is winning, but is he? Actually he is completely lost, the only move he has is G2 :

White plays G1 :

Now the only way for black to stop white from taking the corner at H1 is to play at C6 .

Lets stop the present game but note something. The ending position is probably lost for black for many reasons, white has two good moves at his disposal: C1 and H6 , while black has no good moves, he has to takes the central white pawns and thus he ends up having more discs of his colors than his opponent! (compare this with the initial position) Also black has a disc on G2 a strategic square which in most case, at least in the beginning and middle game, is usually not a good idea to take, and can cost dearly (gives a possibility to take the corner).

2.3.3 The number of possible moves


This factor is closely related to the one above it. But the counter example presented their shows that it is not quite the same. If we look at this counter example, we can note that although black has only two discs, he has only one possible square at his turn! And this square is even a very bad one for him, but he has no choice, while white on the next move has many good moves at his disposal. Lets look at another example, I am playing white, upponent plays black, it is my turn to play, I can play at G2 or at B2 , both are loosing; B2 because he plays H1 , and G2 because soon he will get the A1 corner (note that this is not immediate). So what happened, if we look at the position and counts the number of possibilities he has: 11, me.. :2.

2.3.4 The corners and stable squares


As we have already seen, the corners ( H8 , H1 , A8 , A1 ) are very important in Othello, but why is it so? The reason lies in the fact that corners are what are called stable squares. Stable squares are squares which are inalterable in color, until the end of the game. Once a player got a corner, the opponent can never change the color of this corner again. Now

there are also squares which are stable only for only one player but not for the other at certain time in the game. For example, lets look at the following position:

First note that this is an impossible configuration, it is just used as a tutorial example. So we see that black has the corner H8, given the rules of the game, white will never be able to recapture this corner. Now we can see that because black got this corner, G8 and H7 become stable squares for him, but these are not for white as if he play G8 then black can play F8, and if white play H7 then black can play H6. There are many exceptions to this rule as for example:

In this position, G8 is also a stable square for white. or,

In this case, if it is black to play, he can play G8 and get a total of 8 stable squares, but if it is white to play, he can play G8 and he gets 6 stable squares against 2 for black:

And white is certainly not worse. This can serve as a counter-example to the importance of corners. Now, we have to note that stable squares are not necessarily on edges, they can actually be anywhere on the board, but finding them needs a good algorithm, and they do not play as great a role as the edges stable squares.

2.3.5 The X squares attack


As we have seen before, putting a disc of one own color on one of the X squares ( B7 , B2 , G7 , G2 ) is usually bad at the opening and the middle game. But sometimes these are winning moves! Usually this happens at the end of the game, but not always. Lets look at an example (the following are man made board, and so contains only the relevant theme to illustrate the point):

Now it is black to move, is it good to put a square in B7 ? This can be tricky and actually it depends on the position, if for example white can play (after black plays B7 ) B8

without turning B7 into white, then black putting a square on B7 is a serious mistake, but if not.. : Black plays B7

If white plays B8 and B7 turn white:

And black can probably put a disc in A8:

After black played B7 , white could (maybe) have played A8:

And then black plays in B8:

And black follows with the move H8 , in the process both have won a corner, but black has 7 stable squares against 1 for white. These kind of attacks are called X squares attacks.

2.3.6 Edge configurations


This factor is quite important and is a very tricky one as we shall see. It deals with different configuration of edges, and a good knowledge of them, at least of the ideas involved is important. Lets begin with an example of an edge:

Note that this case is the same for the 4 edges, and also that it is symmetric on the same edge. Now lets say it is white to play, can he play on G8 ? The answer seems.. yes, he can, everything looks calm, but this move loses the corner H8 !

White plays G8 :

Black plays F8:

White must play E8 if he doesnt want to give the corner H8:

And black plays... H8:

So we see that in the initial position, White putting a disc on G8 was a serious mistake. Lets take the opposite position now:

Let it be black to play now, where should he put. Ignoring all the other possibilities he has, a good choice might be putting a disc in D8 , and if white is not careful, he will loose the corner (and probably the game). Once a black disc is put on D8 , white should either play a white disc on F8 , or on E8 , and black has nothing. Lets look at another of this tricky edge treatment, consider the following position:

Is it a good edge configuration for white? If it is white to play, it is ok, he can play either at D8 or at F8 , but if it is black to play, white looses at least a corner, black can play either at: F8 , and if white plays E8 , black play D8 and wins two corners! (of course white should probably give only one of them, but in anyway, once black has one of them, he will have good chances of conquering the other one!), or he can play at D8 , and if white play at E8 , then black plays at F8 winning again both corners. Of course these analysis dont count what happens at the other edges or on X squares, so one has to be careful about fast conclusions.. But in a general way, we can see that edge knowledge (or disknowledge) can lead to wins (or losses) of games.

10

2.4 PROGRAMMING OTHELLO GAMES 2.4.1 Introduction


Now we come to the programming area, the principal question to ask ourselves is, how can we map all the above strategies in programming code and produce an Othello game which plays reasonably well? In this section we are going to review some possible techniques (these do not pretend to be exhaustive, many others, possibly much better may be devised). We are going to divide the techniques mainly in three different areas, first we are going to talk about the mobility aspect, then we are going to review the stability aspect, and finally the topological aspect will be discussed. One has to note that all these technique deals with the evaluation of a particular position. The idea is to use them in conjunction with a lookahead algorithm (like alpha-beta or min-max, we will talk about these in the next chapter), which will look many half-moves ahead and decides on the base of the final positions evaluation which current move seems best.

2.4.2 Mobility
The mobility is most important in Othello, it deals mainly with the number of moves one has at his disposal, with respect to his opponent number of moves. In order to increase ones mobility, many techniques can be used, we are going to review three of them:

2.4.2.1 Use of possible moves


This is the easiest (and maybe best) technique. Simply count the number of possible legal moves one has at his disposal, count the opponent ones, and the player which has the more of them stands better (this is oversimplified of course).

2.4.2.2 Use of empty squares


This technique can still be divided in two: Lets study the first one; In this technique, the program scan the entire board, and at each empty square he sums up the number of white squares around it and puts it in a variable, and sums up the number of black squares around him and puts the result in another variable. For example:

11

Lets start counting for white: A5 1 A4 1 A3 1 B3 1 F3 2 G3 1 G4 1 G5 1 F6 1 D8 1 C8 1 B8 1 B7 1 B6 1 Total: 15 If we do the same thing for black, we get: B3 2 B2 1 C2 2 D2 3 E2 2 F2 1 F3 1 G4 1 G5 1 G6 1 F6 3 F7 2 F8 1 E8 2 D8 2 C8 1 B7 1 B6 3 A6 1 A5 1 A4 1 Total: 33 Now we can have an idea about the position by examining these totals. The idea is to maximize it for the opponent while minimizing it for oneself, so white can be quite happy about his position. The second technique is to count the number of empty squares which has at least a disc of a specific colors, note the subtle difference introduced by the at least compared to the above. Lets study the same example as above:

Lets start counting for white: A5 1 A4 1 A3 1 B3 1 F3 1 G3 1 G4 1 G5 1 F6 1 D8 1 C8 1 B8 1 B7 1 B6 1 Total: 14 And for black: B3 1 B2 1 C2 1 D2 1 E2 1 F2 1 F3 1 G4 1 G5 1 G6 1 F6 1 F7 1 F8 1 E8 1 D8 1 C8 1 B7 1 B6 1 A6 1 A5 1 A4 1 Total: 21

12

Again one tries to maximize the opponent total while minimizing his own, again we see that white is happy.

2.4.2.3 Use of non-empty squares


In this technique, one counts the total number of opponent disc surrounding his own discs, again there are two versions. Lets take the same example as above:

Lets start counting for white: B4 4 E4 5 F4 2 E5 4 D6 6 C7 2 Total: 23 And for black: C3 1 D3 1 E3 2 C4 1 D4 2 B5 1 C5 2 D5 3 F5 3 C6 2 E6 2 D7 2 E7 1 Total: 23 Mm.. strange, both totals are the same? ! Is it possible that in fact this technique is no technique at all? The answer is yes! By studying the process one can see that in fact these two totals will always be the same.. The second technique is to count the number of discs of one owns that have at least one disc of the opponents surrounding them. Again with the same example:

13

Lets start counting for white: B4 1 E4 1 F4 1 E5 1 D6 1 C7 1 Total: 6 And for black: C3 1 D3 1 E3 1 C4 1 D4 1 B5 1 C5 1 D5 1 F5 1 C6 1 E6 1 D7 1 E7 1 Total: 13 The idea is to try to minimize ones total and maximize the opponent, we see that, as before, white still stands better. Note that this is not the same as counting the number of disc each side has, the above example was not a very good one actually. For example, in the following board, these two totals are not equivalent to the number of total disc one own.

Black total: 7 White total: 9 Note that in this example, these totals dont really say that white has a very bad position

2.4.3 Stability
In these techniques, we are going to try to get stable edge squares, and find a way to recognize tricky (and non-tricky) edge configurations. One simple rules we can apply is the following: If a player gets a corner, then he gets a very large bonus points, if his opponent gets one, then he gets a very large minus bonus points; If a player play an X square while the corner just next to him diagonally is unoccupied then it is very bad, if his opponent play an X square while the corner just touching him is unoccupied then it is very good. This rule ends about 16 half-moves before the end. This can be stated as The Golden Rule and, with conjunction of the mobility strategies can give already quite a strong computer. But unfortunately, this rule doesnt take in account any of the tricky edges configurations talked about earlier. And if the computer looks only three

14

half-moves ahead for the entire game, then he has no chance of treating edges configurations well, for example:

If it is white to play, and he thinks about playing G8, all he can see with three half-moves ahead is the following position (after black plays F8 and white plays E8):

Which he has no reason to believe that it is bad for him, when on the next move, black plays H8 and takes the corner. This is called the Horizon effect in game playing programming. Note that with four half-moves ahead, white has good chances of seeing that it is bad to play G8 but he may miss it too, if for example, after he plays G8, black cannot play on F8 directly on next move. One way to try to reduce this problem is to define a notion of a calm position and a turbulent position. For example the just above edge may be seen as turbulent, and the look-ahead algorithm may be programmed in such a way that when he arrives at a turbulent position, he continue to play on this specific branch of the tree until he gets to a calm position. Another solution is to use an hash table of all the edges configuration, telling if such and such edge is good or not. In this way, even looking only one half-move ahead at the position:

White knows that playing on G8 is bad, because if he plays G8, he will look at the evaluation of this edge (Black on D8, White on G8, the six other squares being empty) in the hash table and see that it is very bad for white. Another example of problems partly solved with an hash table are the X square attack. For example:

Black wonders if playing B7 is good. His normal evaluations tell him no, because he gives out a corner, but actually, the following configuration :

is found to be very good for black in the hash table, so even by looking just two half-moves ahead, black would know that playing on B7 is actually quite good. How big is this table? As each edge has 8 squares, each can take three values (white, black, empty), there are in total 38 , but as the edge is symettric, there are actually (38 -1)/2 entries. But is it all? no, lets look at the following edge:

15

If it is black to play, he wins the corner by playing H8, while if it is white to play, he plays E8 and everything is ok for him. From this example we can see that there are actually two tables of (38 -1)/2 entries and not one! One for if it is white to play, and one for if it is black to play; although most of the entries in both tables will be the same.

2.4.4 Topology
In these techniques, we are going to try to form big chunks of discs of our own color at certain strategic regions. This will help us determining that positions as these:

Are bad for white and good for black. The idea is to compare the number of discs of a player in a certain area with all other regions. For example, one way could be to divide the board into four regions: A1, D4 , E1, H4 , A5, D8 , E5, H8 each representing 4 x 4 g rids. Now we take the number of our own discs in a certain region, and we divide it by the number of our own discs in all other regions. If this ration is greater than a certain threshold, then we say it is good, if not then neither good nor bad. Lets study this ration for the above example: For black (for the four regions): 0/12, 0/12, 0/12, 12/0 or 0, 0, 0, 120 (if we replace 0 by 0.1 in order not to have an annoying divide by zero error ) For white 4/5, 0/9, 1/8, 4/5 or 0.8, 0, 0.125, 0.8 So we give black a big bonus because he got a ratio of 120! (the threshold should be about 2 to 4!).

16

3
THE MIN-MAX AND ALPHA-BETA ALGORITHMS

3.1 INTRODUCTION
In this chapter we are going to introduce the min-max and the alpha-beta algorithms. These algorithms are very well known heuristic search algorithms. Most computer games engines for strategic games are using some modified forms of the basic alpha-beta algorithms for their search part. For example Deep Blue is using a very optimized parallel alpha-beta algorithm. Actually alpha-beta is an optimized min-max, so we will start with the latter.

3.2 MIN-MAX 3.2.1 Introduction


The term min-max comes from minimalization-maximalization. Let us look at the following tree: Max 2

Min

Max 1 4 2 6 3 2 100 4 0

From the above tree we can easily understand how min-max is working. At each step each node is taking either the maximum or the minimum value of the immediate nodes below it,
17

depending whether on which level of the tree he is in. The first node is the computer trying to decide which of the three possibilities is better. He tries to choose the possibility which maximize his evaluation and minimize his opponents. So the algorithm starts from the most bottom nodes, or terminals, where the positions are effectively evaluated always in terms of the computer (so the higher the evaluation, the better the computer thinks he is). Then the values are rising to the starting node in a recursive way. If one gets to an opponent node, then the opponent will try to minimize the computers evaluation, and so he will choose the move with the smaller value for the evaluation, if one gets to one of the computers node, then he will try to maximize his evaluation by choosing the mode giving him the highest value.

3.2.2 The algorithm


min-max ( depth, force, color, chosen_move ) variables min, max, mode, note : integer. start min + ; max - ; look_for_playable_moves ( color, ); if odd ( depth ) then mode maximize; else mode minimize; end if for each move do move_piece ( move ) if depth < lowest_upper_limit then note min-max ( depth+1, force, opposite ( color ), chosen_move ) else if turbulant (move) then if depth < highest_upper_limit then note min-max ( depth+1, force, opposite ( color ), chosen_move ) else note evaluate_the_position () end if else note evaluate_the_postion () end if end if if mode = minimize then if note < min then min note chosen_move move end if else if note > max then max note chosen_move move end if end if replace_move_as_before ( move ); end for if depth = 1 then move_piece ( chosen move ) end if destroy_list_of_moves () 18

return ( note ) end.

3.2.3 Some notes on the algorithm


The algorithm given above needs some explanation with respect to the variables lowest_upper_limit, and highest_upper_limit, and to the function: turbulent(). First let us imagine a case where the computer, playing white wants to evaluate the following position:

having a search depth of 3 half plys. He might think that putting a disc at the position H8 is ok because the final position he arrives at in his calculations is:

which is perfectly ok for him, no reason to get alarmed. But if he would have calculate for 4 half plys, he would have seen that now black plays on H8 and won a corner! (and probably the game!):

So although in untricky positions, 3 half plys might be ok, in some it might be catastrophic! The position:

is called a turbulent position, meaning it is still in an important tactical phase, and stopping the search now is not a good idea. One way of avoiding this is to define turbulent positions, and when the computer encounter one at the end of his search, he continue some moves more. So the turbulent function will define the turbulent positions. It can also simply define turbulent moves, these are moves which may, or may not be turbulent, in either case it will be a turbulent move; for example, in othello, the squares G8 or B8 can be define as turbulent due to the fact that they are very important in tactical phases. The lowest_upper_limit will thus define the limit of search when no turbulent moves/positions are encountered, and the highest_upper_limit defines the limit above which, even though we encounter a turbulent move/position, we still stop the search and evaluate the position, otherwise the search can go on forever!

3.3 ALPHA-BETA

19

3.3.1 Introduction
The alpha-beta algorithm is not better than the min-max in the sense that it will give another move, for equal ply search, which will be better than the min-max move. The alpha-beta algorithm is better than the min-max in the sense that it is much faster as it is using some branch cutting when it knows that he doesnt need to evaluate further. It basically works as the min-max algorithm, only min is now called alpha, and max, beta. Again, let us look at an example:

beta

alpha

beta 3 4 5 1 3 2 100 2 0

In this example, we see that once we had that the first node of the second level of the tree is 3, then when we examined the sub-tree of the second node of the second level and found that the first has value 1, then we didnt have to continue to explore the rest of the nodes because we know that the results of this sub-tree is at most 1, and so, knowing that the top node of the tree is a maximum node, we know that he will always prefer the first node of the second level. We saved two evaluations! Same thing happens in the third sub-tree when it reaches 2, and so he knows it is useless to evaluate the last one. Now imagine one of these sub-trees had 10000000 nodes to evaluate, we would have saved the same amount of evaluating time! While min-max would have gone for ages; of course it is a little bit exaggerated, but it shows the point. The code shown below is taken from the book : Echec et C by Yann Takvorian, and was used in the first versions of my program until I discovered another, faster version, at Martin Fiertz webpage (see bibliography).

3.3.2 The algorithm


alpha-beta ( mode, color, value, depth, bound ) variables local_value, temporary : integer; chosen_move : record; start if mode = minimize then local_value + else local_value - end if chosen_move none look_for_playable_moves ( color, ); 20

sorting_moves () for each move in the list do if depth >= highest_upper_limit or ( depth >= lowest_upper_limit and not turbulent ( move ) ) then move_piece ( move ) noted_move evaluate_the_position () else move_piece ( move ) noted_move alpha_beta ( oppositie ( mode ), opposite ( color ), local_value, depth+1, bound ) temporary noted_move end if if value = then if mode = minimize and temporary < local_value then local_value temporary chosen_move move end if if mode = maximize and temporary > local_value then local_value temporary chosen_move move end if else if mode = minimize then if temporary < local_value then replace_move ( move ) destroy ( list_of_moves ) return ( temporary ) end if else if temporary < local_value then local_value temporary chosen_move move end if endif if mode = maximize then if temporary > local_value value then replace_move ( move ) destroy ( list_of_moves ) return ( temporary ) end if else if temporary > local_value then local_value temporary chosen_move move end if end if end if end for replace_move ( move ) destroy_list_of_moves () if depth = 1 then move_piece ( chosen move ) end if return ( local_value ) end. 21

3.3.3 Some notes on the algorithm


Again as for the min-max algorithm, we have the highest_upper_limit and the lowest_upper_limit variables, and the turbulent function, which serves the same purpose as explained above. One important thing to note is the presence of a sorting function: sorting_moves() in the beginning. This is very useful to speed up the search; it sorts the playable moves according to the evaluation of the positions at the search depth the computer is currently in. This is in accordance with the explanation given in 3.3.1 where we can see that the order in which the positions are evaluated are of vital importance to the number of branch of the search tree that the computer has to consider/not consider. Investigating the importance of this function with the othello game, which will be described in chapter 5, I can tell that it can speed up the calculation by a very large amount (up to factors of 10 using high search depths!).

22

4
GENETIC ALGORITHMS

4.1 INTRODUCTION
In this chapter we are going to introduce the genetic algorithm, first theoretically, then in practice by showing how it can be applied to find the optimum of a simple function. The genetic algorithm, or GA in short, is an evolutionary algorithm which can be seen as an algorithm trying to maximize ones chances of survival in a hostile environment. For example, we can take a look at humans versus fishes. Humans were developed in an out of the sea environment where one cannot float in the air, consequently, the human body has legs; fishes on the other hand can float in the water, consequently, they dont have legs. If humans had not developed legs they couldnt survive, while if fishes had legs, it may have hindered their need of mobility in order to survive under the water, and they may have not survived. What can we see from this very simple example? That nature provided a way for both to survive in their respective environment, providing them with the best tools corresponding to their needs. I recall that once, more than 10 years ago, my teacher had shared an interesting idea. He said that as humans dont really need their foot fingers as they used to need it in the old times, the five fingers might just well stick into one in very far away generations. We can see that Mother Nature always tries her best to apply all her knowledge to help sentient being on this earth, using an unimaginable GA, giving everyone a chance to live well during our stay on earth, but somehow the humans thank her by destroying the earth resources, by giving hatred vibrations, making war, while love and good vibrations would, could, can, will? make this earth into a paradise. Ok, talked enough non-sense J let us now see how this Mother Nature algorithm can be mapped into our ridiculous (for her..) apparatus.

4.2 GENETIC ALGORITHM SCOPE


So what are these Genetic Algorithms all about? They can basically be seen as optimization algorithms, enabling us to find maximums and minimum of functions having many parameters by iterative means. One problem with Genetic Algorithms is that it may find maximums and minimums, but these can be local maximums and minimums and not the global ones. As the numbers of parameters and values they can takes increase, they will have more and more of these local extremas, so one has to devise an algorithm which tries to move
23

on if one is stuck in such an extrema, although it may also be ok to stay there, if all we wanted is an acceptably good solution to a problem.

4.3 HOW DO GENETIC ALGORITHMS WORK


Let us now study the basic genetic algorithm layout, the terms in italic are all explained afterward. We must first note that when presented with a problem we want to apply a genetic algorithms to, nothing much has to be changed to this layout, this is called the problem independence and is one of the main strength of genetic algorithms. The only part which build the bridge between the actual problem and the genetic algorithm, is the fitness function, or in other words, the function which we want to maximize or minimize with respects to its coefficients; and the representation of the coefficients of this function, this is called the problem dependent part. So we can say that the following layout, or algorithm, is kind of universal. 1. Choose a Selection scheme, choose initial Crossover, or Recombination probability, Mutation probability, number of strings in population, representation of each string (binary versus multary representation, etc.) 2. Create the initial population giving them random values 3. Repeat 4. 5. 6. The strings are selected according to their fitness. This is called the Selection phase Some pairs of strings are recombined to form new strings, this is called the Recombination phase Some bits of the entire population are inversed, this is called the Mutation phase.

7. Until A certain condition; for example the standard deviation of the entire population can be used, or the number of generations passed. 8. The string having the best value for its fitness is chosen as the winner.

4.3.1 Representation
Let us start the study of this loop by looking at the representation of each string. To find a suitable representation for the strings is not that trivial. For example we may have a problem when we have three parameters <a,b,c>, a, b and c can have values from 0 to 32, what to choose then? If we choose a binary representation, then we have 31 values (33-63) which will never be used, and so it may be seen as a waste of memory space. Another way is to use a multary representation (thesis by Paul Field) of cardinal 32. In the present paper we will assume that we always use binary representation for the strings. For example let us assume that we have a population of 6 strings, each having 1 parameters of 8 bit (or alleles) each. After step 2, we may have the following population: P { (1,0,1,0,0,1,1,0), (0,1,1,0,0,1,0,1), (1,0,1,1,1,0,0,1), (0,0,0,1,1,0,1,0), (1,0,0,0,1,1,1,1), (0,0,1,1,1,1,0,0) }

24

4.3.2 Selection
Now the next step is selection. This is the step where the fittest performing strings are chosen proportionally to their fitness values. There are numerous ways in which this can be done. We are only going to present some of them, for a complete description of selection mechanism, the reader is encouraged to read Extended Selection Mechanisms in Genetic Algorithms by Thomas Back and Frank Hoffmeister. So now we have a population of strings, and for each string we know its fitness through the fitness function, which strings should be chosen for the next step? It would be logical that strings with the highest fitness will have more chance of being picked up, so we need to use some probabilities in here. One way of doing this is by what is called the stochastic sampling selection. The idea is easy, simply divide each fitness of each string by the average of all the fitness values of all strings. The value before the . gives the number of times the string will be selected with a probability of 1, and the value after the . gives the probability that the string will be selected again. For example, if we have a value of 1.37, 2.54, and 0.25 for 3 strings respectively, then the 1st string will be copied once, and he will have a twin with a probability of 37 %, the 2nd string will be copied twice, and will have a triplet with a probability of 54 %, and the last one will survive with a probability of 25%. Another way is what is called the roulette wheel way. There are basically two ways of doing this, it can be either rank based or actual fitness based. For the rank based, each string is assigned a rank according to its fitness value, the higher the rank, the better his fitness is (actually, the stochastic sampling selection can also be based on the rank). Then the sum of all the ranks are made, and each string has a probability of his rank divided by the sum found, times 100, of being chosen for the next step. This is repeated until the size of the chosen strings match the original population size. For the fitness based, the same principle is used, only the rank is replaced by the actual fitness value. We can note that rank based algorithms have a constant selective pressure, that means that there is no concept of very good strings, or very bad strings, the only thing that counts is its rank with respect to the fitness function. While fitness based algorithms have a high selective pressure, if a string is very strong with respect to the rest of the population, then after some time he will be the only lonely one left! This can be quite bad for genetic algorithm and is called premature convergence.

4.3.3 Recombination
Now once the strings have been chosen, they must be recombined somehow, with a certain probability called the recombination probability; we are going to review the most important operators fulfilling this task:

4.3.3.1 One point Crossover


Let us see how this work by looking at an example, let us imagine we want to reproduce the two following strings: I1 = (0,1,1,0,0,1,0,1) I2 = (1,1,0,1,0,0,1,0)

25

The one point Crossover operator consist of first choosing randomly a position inside the string; here we have 7 choices. Let us imaging we got the 3rd position. The offsprings are created by switching the three first bits of I1 with the three first bits of I2 giving: Ic1 = (1,1,0,0,0,1,0,1) Ic2 = (0,1,1,1,0,0,1,0)

4.3.3.2 Two points Crossover


The two points Crossover works similarly to the one point Crossover, except that now 2 points are chosen randomly inside the string, and the bits in between of these 2 points are exchanges to give two new offsprings, example: I1 = (0,1,1,0,0,1,0,1) I2 = (1,1,0,1,0,0,1,0) gives: Ic1 = (0,1,0,1,0,1,0,1) Ic2 = (1,1,1,0,0,0,1,0)

4.3.3.3 N points Crossover


In this scheme, n points are chosen randomly inside the string, and the bits in between the first and second, third and fourth, etc. are exchanged. For example:

I1 = (0,1,1,0,0,1,0,1) I2 = (1,1,0,1,0,0,1,0) gives: Ic1 = (0,1,0,1,0,1,1,0) Ic2 = (1,1,1,0,0,0,0,1)

4.3.3.4 Uniform Crossover


In this scheme each pair of bits of the parents have a 50% probability of being inverted. Example, where the black circle indicate a favorable toss, while the white circle a defavorable toss:

I1 = (0,1,1,0,0,1,0,1) I2 = (1,1,0,1,0,0,1,0) gives:

26

I1 = (1,1,1,0,0,0,1,1) I2 = (0,1,0,1,0,1,0,0)

4.3.4 Mutation
The mutation of the strings consists in inverting each bit of these with a certain probability, typically very low like 1% or 0.1%. This is done to ensure that all the space of solutions has a non-zero probability of being investigated, or in other words, to keeps a certain dynamic in the system.

4.4 THE SCHEMA THEOREM


So the fundamental question that one may ask oneself is: why does it work? The key idea in understanding this is to see strings as combinations of schemata. A schemata is defined by a ternary representation: {0, 1, #} where # is a wild card being either 0 or 1. For example if we have the following string: I = (0,1,1,0,1,1,0,1), a schema for it could be: SI = (0,1,#,#,#,#,#,#) Now we can study the effect of recombination and mutation on the schematas rather than on a particular string. At each generation, good schematas, that is, schematas with a high average fitness, will increase, while the bad ones, that is, those who have a low average fitness, will tend to disappear. It has been proven that if is the size of the population, then 3 schematas are processed at each generation, this is sometimes viewed as the parallelism in genetic algorithms. Now schemata are characterized by two operators, if H represent a schema, then o(H) represent the order of the schema, that is, the number of fixed position in the schema, and (H) represents the distance between the first and last specific string position. For example: A = (0,1,#,#,#,1,0,#), then o(A) = 4 (A) = 6 Let m( H, t ) represent the number of strings having the schema H at a particular time t . Let us use proportional selection where the probability for a string, i, to be chosen in the selection stage : pi = f i / k f k ; where f i represents the fitness of the string i. After choosing n time from the current population, we can expect, for time t +1: m(H, t +1) = m(H, t )n f ( H)

1
k

fk

27

where f (H ) is the average fitness of the strings representing schema H at time t . Using
f = n-1 k f k

representing the average of the entire population, we can write: m(H, t +1) = m(H, t ) let f (H) = f + c f where c is a constant. We can rewrite the above equation as: f +c f f m(H, t +1) = m(H, t )[1+c] m(H, t ) = m(H, 0) [1+c]t m(H, t +1) = m(H, t ) Now we see that if H is a good schemata, the system would give him an exponentially increasing number of trial to reproduce, while if H is a bad schemata, the system would give him an exponentially decreasing number of trials to reproduce. Now let us see what happens when one point-crossover happens. Let us take an example of two representative schema H1 = (1,#,#,#,#,#,#,0) and H2 = (#,#,1,1,#,#,#,#). If crossover is applied at position between 1 and 7, H1 will be disturbed as a1 = 1 and a8 = 0 will be put in another schema. We must note that the fact that the other parent mating contains the same alleles at these positions is not taken in account in the analysis. In H2 , the only way to disrupt this schemata is to choose the position 3 as crossover point. So we see that close together alleles schematas are more likely to stay undisturbed. Using the defining length , we can say that the probability for a schema H to be disturbed is: pd =
f (H) f

(H 1 ) l 1

where l represents the length of the schema. In case of schema H1 , this probability is equal to 1, while in H2 , this probability is equal to 1/7. Now we can rephrase the above equation in term of the survival probability, that is, the probability that the schema wont be disturbed: ps = 1- pd ( H1 ) ps = 1l 1 if the crossover has a certain probability, pc, of actually occurring, then:

28

( H1 ) l 1 Let us see now how mutation disturbs schemas. Assuming that a single bit is altered with probability pm. That means a single bit would survive with probability 1- pm. As the mutations are independent from one another, the probability that a schema survives is equal to (1- pm)o(H). Combining the above results, we get the fundamental theorem of genetic algorithms which state that:
ps = 1- pc m(H, t +1) m(H, t )
f (H) f

[1- pc

(H ) ] [1- pm]o(H) l 1

4.5 CRITICISMS OF THE SCHEMA THEOREM


We must note that the above analysis is an ideal analysis. We assumed that when the crossover points occurs in between two fixed points in a schema then we loose the schema. But this is not always the case as stated above. Also we do not consider the gain of schemas from different ones; for example if I1 = (1,0,0,0,0,0,0,0) is one point crossovered after the first bit with the string I2 = (0,1,0,0,0,0,0,0) then we get Ic(1&2) = (1,1,0,0,0,0,0,0) which belongs to the schema H = (1,1,#,#,#,#,#,#). Also the schema theorem provides an inequality, and can therefore only provide a lower bound that hold for one generation only. There exists a more accurate version of this theorem which takes into account the gains and losses effects of crossover, but we are not going to review it here.

4.6 ON PARAMETER VALUES


The initial values of n, the population size, pc, the crossover probability, and pm, the mutation probability are crucial points if one wants to have a successful search. De Jong (1975) defined the following performance measures: on-line(T) =
1 T n f (d t,i ) T n t =1 i =1

1 T f (d t ) T t =1 where t represents the generation index, and f (d t ) the fitness for the best string in the generation t . We can see that on-line represents the sum of all the fitnesses so far, while offline represents the sum of only the best fitnesses from each generation. De Jong (1975) provided the initial parameter for having good on-line performance:

off-line(T) =

Population size n between n = 50 and n = 100 crossover rate pc = 0.6 and mutation rate pm = 0.001 After having investigated different crossover operators, he recommends using two-point crossover. In 1986, Grefenstette used a genetic algorithm to find the optimal parameters of genetic algorithms. In other words, he used a second level genetic algorithm on a first level one. He
29

found parameter values that significantly improves the on-line performance compared to De Jongs values, but he could not improve off-line performance: Population size n = 30 crossover rate pc = 0.95 and mutation rate pm = 0.01 In 1989, Schaffer and al. found that two point crossover performs no worse and sometimes better than one point crossover, and after a thorough study of the n, pc and pm parameters values, they advocate the following values: Population size n between n = 20 and n = 30 crossover rate pc between pc = 0.75 and pc = 0.95 and mutation rate pm = 0.005 and pm = 0.01

4.7 PRACTICAL EXAMPLE OF A GENETIC ALGORITHM


Let us now study a practical example. We are going to write a program which finds the maximum of the function f(x ) = x , for x going from 0 to 255, using a genetic algorithm. This may seem like a stupid thing to do, after all, the answer is 255 no?! Yes, but remember that a genetic algorithm is problem independent, only the fitness and the representation of the coefficients affecting the fitness are problem dependent; so the program we are going to develop here can actually be modified very easily to solve almost any kind of problems. The reason for writing it in the first place is to show how easy it is to code a genetic algorithm. The program can be found in appendix 1. Running the program yields the expected results: all the strings tends to 255 after only a few generations.

30

5
THE GENETIC ALGORITHM APPLIED TO THE OTHELLO GAME

5.1 INTRODUCTION
In this chapter we are going to see how the genetic algorithm can be applied to the othello game. We are going to do an in-depth study of a program developed for this purpose. This program was named Genetic Othello for some unknown and mysterious reasons.. J This program is a real genetic laboratory, it can be used to analyse and study the effect, or impact, that various changes in the genetic parameters and schemes have on the efficiency of finding a particular high level string. But what does a high level string actually means? First, one must note that contrary to function optimization where the fitness can be calculated directly from the function, othello does not have such function. So there is no direct and clear way of precising the fitness of a string. This fact has many important consequences. First, how can we calculate the fitness of a particular string during the simulation? Second, once we find a so called good string, how can we really be sure that it is as good as it is supposed (and prayed) to be? The answer is not clear cut, and gave rise to some new and interesting genetic schemes. A very interesting feature of this program is that it can actually be modified very easily to tackle practically any function/game optimization, and not only the othello game! I will explain at the end of this chapter how and where the program has to be modified for this.

31

5.2 THE PROGRAMS OPTIONS

The first think that strikes when one looks at this interface, is that, well.. it is not a window interface. As the program was written with Turbo Pascal 7.0 for DOS, developing an interface is far from easy contrary to Delphi or Visual C++, and a very small change to it and the entire code has to be changed! This has the consequence that one must think very carefully about it before doing it. All the options seen above were grouped in a more or less logical way. To modify anyone of this option one simply has to push on the character to the left of the option. Let us now analyze each one of them (the values in brackets for each option are the possible values for it) Super Championship [ Yes, No]: This option is for the one who wants to make a simulation with only the best of the best of strings. It can be compared to super championship in chess where 10 top players participate. In order to choose these 10 top players, the organizer held 10 other tournaments, and the winners were chosen. This option works in conjunction with the option Standard deviation stop criteria, this last one define the minimum mean standard deviation of all the parameters where the population is supposed to have converged to the solution. So if this option is set to Yes, and, for example, the population size is 32, then there will be 32 different tournaments, finding the 32 best strings, then a thirty-third tournament which will include all these 32 experts. Although a very interesting option in view of finding a quality string, it is also a very painful one in term of time taken for it to take place, unless one has two or three free lifetimes, it is a very long process J . It is included here because some may have very powerful computers which these two or three lifetimes could be reduced to maybe a week
32

or two.. Note that the state space being 28*(number of parameters) (each parameter is coded on 8 bits), a global maxima is not something easy to find; this means that the strings we find are probably local maximum, and we are looking for the highest of them all. This last statement needs some thoughts, since, as said earlier, we cannot define a clear-cut fitness for a string! So actually it is even more complicated as a global maximum may not exist at all! We will spare the reader of philosophical discussions.. J Style [Match style algorithm, Classic style algorithm, Simple mutation style algorithm, Rank style mutation algorithm, Test algorithm, Player against computer, player starts , Player against computer, computer starts , Two players ]: This option is the central option, specifying the scheme used by the genetic algorithm. Let us review each one of these schemes : Match style algorithm: This scheme is a logical scheme to use for any game. It works as follow: First the population is randomly generated. Then the first string will play a match against the second string, the third string will play a match against the fourth string, and so on; the winners (half of the population) is used to generate the next generation in some way, according to the values of some other options as we are going to see. In any case, these winners will stay in the next generation. So if there is some very good string, like a Kasparov string, then he will win and stay for many generations, until a Kasparovs modified clone comes, or a Karpov string comes and finally succeed to win. Classic style algorithm: This scheme was named classical because it resembles a classical GA as known by most people. It works as follow: First the population is randomly generated, and a random string serves as a fitness finder for the entire population. This string will play each and every string from the population, and the system will assign a fitness to this last string with respect to his performance in this match. Then the next generation is built according to these fitnesses, with a certain crossover probability, and some mutation; the previously random string (the fitness giver) is now one of the populations string, this can be seen as a cooperation scheme, having a population of 1 on one side, and another population on another side. The fitness giver can also be a random string for each generation. Simple mutation style algorithm: This scheme is quite special. It works as follow: First, as above, the population is generated randomly. Then, as in the match style algorithm, the first string is playing the second, the third is playing the fourth, and so on, and the winner are those string which will construct the next generation. How is the next generation constructed? Simple, each winner is duplicated! The trick now is that all the strings, which were now constructed, are mutated by a very big amount in the beginning, like for example 1 bit out of 2 has a 50% of being mutated. This amount will slowly decrease with time. This scheme is very flexible; it could be compared to an artist who works on a statue that is made out of a very special compound which slowly strengthens with time. In the beginning he can mold it very easily, and when he finally has some kind of art done, it hardens, and finally he can say he finished his statue, and this statue will be solid as a rock (of course he should not get asleep while his hands are inside the compound!). Rank style mutation algorithm: This scheme is very similar to the previous one. The only difference is that in this scheme, at each generation, the mutation factor affects more those strings that are considered to have a small fitness than those that are considered to have a higher fitness.

33

Test algorithm: This scheme is very important. This is the algorithm that will tell us if a particular string (the test string ), defined by another option, is actually good or not. This string will play against an entire population, and the number of wins/losses/draws are given, permitting one to make statistics, to cry out of despair, or to smile out of satisfaction. Player against computer, player starts . This option enable one to test the test string by playing against it, only this time, the player makes the first move. Player against computer, computer starts : This option enables one to test the test string by playing against it, the computer plays the first move. Two players . This option enables one to play against another human, if he is too disgusted by playing against the computer.

Search depth [1-9]: This option defines how many half- plys the computer uses for its alpha-beta algorithm. Population size [8-32]: This option defines the size of the population. Parameters [2-10]: This option defines how many parameters are used in the evaluation function of the computer. Number of games per match [1- 4]: This option specifies how many games are played during a match between two strings. As computer always play the same line, two different sequences of initial moves had to be imposed. Recombination [only uniform crossover, uniform+one point crossover, average+one point crossover, only one point crossover, N-point crossover]: This option define the type of recombination used when the system creates a population each generation. One has to note that this option works only for the Match style algorithm and the Classic style algorithm; also, in the case of Match style algorithm, it is only used to create one half of the population, the first half remaining unchanged, in case of the Classic style algorithm, the entire population is affected. Also, only the best strings are used for each type, until saturation (no more possible new strings to create). Let us see each one of these types of recombinations: Only uniform crossover: This is the usual uniform crossover, bit by bit. Uniform+one point crossover: One half will be created using a uniform crossover, while the other half will be created using a one-point crossover. Average+one point crossover: This is a special recombination. Half of it will be the average of some strings with respect to each parameter represented in decimal, while the other half will be created using a one-point crossover. Only one point crossover: This is the usual one point crossover. N-point crossover: Work in conjunction with the option N-point crossover, it defines the usual N-point crossover.

N-point crossover [1- 49]: This option defines how many crossover points are used during N-point recombination. Crossover Probability [0- 100]: This option defines the probability that crossover will occur, it works only with conjunction the classic style algorithm.
34

Mutation rate [0- 1000]: This option defines the rate of mutation in terms of the number of bits that have a 50% probability of being mutated. It works in conjunction with the classic style algorithm. Average recombination factor [2- 10]: This option works only with the Average+one point crossover sub-option of the Recombination option. What it does is to define how much the new strings created out of two other strings is going to approach the best string. For example if it is 2, then it will be the mean of the two strings, if it is 4, then the new string will be: new_strings = best_string -( best_string - worst_string )/4. Mutation algorithm start [2-40 ]: This option works in conjunction with the simple mutation style algorithm, and the rank style mutation algorithm, it defines the first rate of mutation of the population after the first generation. It is expressed in number of bits that have a 50% probability of being modified. Mutation algorithm rate [1-10]: This option works in conjunction with the simple mutation style algorithm, and the rank style mutation algorithm, it defines how much the mutation is decreased each generation. For example, if it is 1, and the mutation algorithm start is 2, then after 4 generation, the rate will be 1 out of (2+4*1=6 bits). Mutation algorithm string [1- 15]: This option works in conjunction with the simple mutation style algorithm, and the rank style mutation algorithm, it defines from which string the mutation should start. For example if it is 1, then it means the entire population should be mutated each generation, if it is 3, then only from the third string should the population be mutated. Random [No random string , One completely random string , Two completely random strings , One semi-random string , Two semi-random strings ]: This option defines if one desire to see some random strings at each generation. This could be useful to avoid premature convergence, and to assure a certain diversity in the population. Let us analyze each of the possible type: No random string : No random string is used. One completely random string : One completely random string will be added as the last string in the population Two completely random string : Two completely random strings will be added as the last, and 2 before the last, in the population One semi-random string : This is an interesting type. It works in conjunction with the option Semi-random value, the string created is not totally random, but based on the best string from the last generation; each parameter will either decrease or increase by a random amount which is defined by the option with which it works in conjunction. This type creates one such string and places it as the last string in the population. Two semi-random string : Same as above, only two such strings are created and placed as the last and 2 before the last. Semi-random value [8-64]: This option works in conjunction with the sub-options One semi-random string and Two semi-random string of the Random option. It defines how much random amount will be subtracted/added to each parameter of a new string, which is a duplicate of some high fitness string.
35

Classic style fitness [Use best string , Use average string , Use worst string , Use random string ]: This option work only with the classic style algorithm. It defines the fitness giver string at each generation. It can either be: Use best string : The string is the best string from the last generation. Use average string : The string is an average string from the last generation. Use worst string : The string is the worst string from the last generation. Use random string : The string is a random string. Special sorting in the selection function [ Yes, No]: This option works only with the classical style algorithm, it defines if one wants to sort the strings according to their fitness, in some special manner, after the selection of the strings from the last generation. Use fitness sorting every generation [ Yes, No]: This option defines if one wants to sort the strings of the population according to their fitness, in some manners, just before all the mutation/recombination at each generation actually takes place. Generations [-1, ]: This option defines how many generations to simulate. The value 1 means infinite. Standard deviation stop criteria [-1,19]: This option defines the minimum mean standard deviation of the strings below which the simulation should end. 1 means there is none defined. Save string on exit [ Yes, No]: This option works in conjunction with the test string option. It defines if one wants the test string to be saved when one quits the program. The file used for storage is always param.txt and if it doesnt exist then the program will create it. Rewrite history file [ Yes, No]: This option defines if one wants to rewrite the history file, which is always genetic.txt. If it is defined as Yes, then the file will be erased and rewritten, otherwise all the information will be appended to it. This file stores the entire results/data of the simulation at each generation, enabling one to make a thorough analysis afterward. Test string []: This option is used to create/view a string which will serve as a testing string for the computer. In case one chooses the test algorithm style, this string will be tested against the entire population and statistics can be drawn, in case one chooses the player against computer style, the computer will take the form of this string. Begin []: This option starts the simulation. Quit []: This option quit the program.

5.3 THE SIMULATION SCREEN


Now that we have seen the programs options, we may still lack a proper picture of what this program is all about. This section will show how the simulation is actually working. First one must note that the screens (the term screen is used instead of interface because their is no
36

interaction with the user) of the Match style algorithm, the Classic style algorithm, and the Testing algorithm are slightly different. Second, in order to come back to the option screen, one must simply click on the <Escape> button. Let us first see the Match style algorithm screen:

Lets analyse the different part of this screen:

First there is the othello board on which the computer is playing against himself. There is
also the amount of white/black discs present on the board for each player, and a small disc which says whose turn it is to play. The evaluation box represents how much the computer which has just played a move, thinks he is winning/losing; it goes from 0 to 1 and can be seen as the probability, the computer thinks, of winning, 0 meaning 0% of winning chances (a hopeless position), and 1 meaning 100% of winning chances (a won position).

There is a listing of all the strings of the population. In this example we see that the
genetic population is composed of 16 strings, each one has 7 parameters (5 on the left of the board, and 2 on the right). Each parameters being defined by 8 bits, they go from 0 to 255. What is above the red line is the winning string in the previous match.

There is a listing of all the fitnesses of relevance to us, each row corresponds to a string
(the 1st row corresond to the 1st string, the 2nd row to the 2nd string, etc.). What is above the red line is the fitness of the winning string in the previous match. There are 3 values here:
37

The 1st value represent the total amount of wins/losses/draws of a particular string
(mapped by the index of the row) during his entire life. A win is defined as +2, a draw as +1 and a loss as 2; this way of defining the scores was prefered over a scheme where a win is +1, a draw is 0 and a loss is 1, as it gives lost games more importance (negative influence); for example, two draws would score higher than a win and a loss.

The 2nd value represent the total amount of difference between the strings number of
discs and his opponents at the end of each game for a particular match.

The 3rd value represent the total amount of wins/losses/draw of a particular string
during one generation.

There are some gray boxes around two strings (genetic population+fitness). This defines
the two strings that are now competing.

Match players: defines who plays who, and who has white/black. Generation: defines how many generations the computer has simulated up to now. Game: defines which games of a particular match two strings are playing. Deviation: define the true standard deviation of each parameter for the entire population.
The closer it is to 0, the more the population is converging toward a solution. Note that if a random string was chosen in the options, this would not take in account when calculating this deviation.

Average deviation: define the mean of all deviations. This can be used as a criteria of
convergence (see the option Standard deviation stop criteria )

Super championship: define how many tournaments has the championship played up to
now (see the option Super championship).

Average string: defines an average string which is supposed to be the string to which the
population converges, it is defined as being the average of the first half of the population.

Right graph (above the genetic population): draws a graph of the average deviation versus
the number of generation passed. This enables us to see if there is some convergence or not, and to not always fix our eyes at the simulation screen, which would turn us nuts. It draws the graph for 100 generations, then it erases the graph and starts from 101 until 200, and so on.

Left graph (above the genetic population): as the right graph, except that here it draws as
many graph (superimposed) as there are parameters in the simulation (deviation of a
38

parameter versus number of generations passed). Each graph is drawn in another color. This can help us to see a convergence for only some parameters, while others are still in big confusion. For the classic style algorithm, the screen looks like:

Not much difference as said before, just we see that now the population size is 17, this is not a bug, the 17 is the fitness finder string, and so a blue box is always around him. The gray box now shows the string we are interested in finding its fitness.

39

For the Testing algorithm, the screen looks like:

Again, not much different than before. We can see that out test string is the last one in the genetic population list. The addition is at the right, above the genetic population title. We can the number of wins /losses/draws of the test string playing against the population (randomly generated).

5.4 THE CHOSEN PARAMETERS


In order to make all the simulations, a common set of parameters must be chosen for all of them. This is far from easy and took a lot of time. Finally seven parameters have been chosen, they are: For each empty square of the board, look if there is at least one disc of a certain color in its immediate surrounding (one square away); if yes then add one to the variable related to the color found. As examples are better than words, let us see one:

40

In this example, whites variable would be equal to: A5 + A4 + A3 + B3 + F3 + G3 + G4 + G5 + F6 + B8 + C8 + D8 + B7 + B6 = 14, and blacks variable: A5 + A4 + B3 + B2 + C2 + D2 + E2 + F2 + F3 + G4 + G5 + G6 + F6 + F7 + F8 + E8 + D8 + C8 + B7 + B6 + A6 = 21 The computer tries to minimize his own variable, and to maximize the opponents. For each square of a certain color in the central 7x7 board, add the number of empty squares surrounding it. Example:

For white: B4: 4 E4: 1 F4: 4 E5: 1 D6: 0 C7: 5 Total: 15 For black:
41

Total: 29 As previously, the computer will try to minimize his own variable, and to maximize the opponents. Same as the previous parameter, only now we consider the entire 8x8 board. For each square of a certain color, look if there is at least one empty square around it, if yes then add one to the colors variable. Example:

For white: B4: 1 E4: 1 F4: 1 E5: 1 D6: 0 C7: 1 Total: 5 For black: Total: 11 The computer will try to minimize his own variable, and to maximize the opponents. For each square of a certain color, add the number of squares of the same colors surrounding it. Example:

42

For white: B4: 0 E4: 2 F4: 2 E5: 3 D6: 2 C7: 1 Total : 10 For black: Total: 45 The computer tries to minimize his total, and to maximize his opponents For each square of a certain color, look if there is at least one disc of the same color in its immediate surrounding (one square away); if yes then add one to the variable related to the color found. Example:

For white: B4: 0 E4: 1 F4: 1


43

E5: 1 D6: 1 C7: 1 Total : 5 For black: Total : 13 The computer tries to minimize his variable, and to maximize the opponents The seventh parameter is a little bit special. It specifies the importance of empty squares, which are completely surrounded by discs of any color, and where only one player can play in this square. These regions have a great importance in othello, the main reason being that the last player to put a disc on the board has a big advantage; if the computer has one (or many) such square(s), it increases his chances of playing the last move. Example:

In this example, the square E8 can only be occupied by a black disc. Note that not all the discs surrounding it are white.

5.5 ABOUT THE SEARCH DEPTH


One major problem with this program is that it takes a lot of time for a simulation to take place assuming one would like to simulate for at least 100 generations. A solution would be to take a very small search depth, like one, two or three half- plys. This is the solution I took in the beginning but it is not very satisfying; the reason is simple, the quality of the games diminish by quite some margin, blunders are very frequent, even with lots of rules trying to avoid them, and this destroys all the fun. Then I tried to implement an improvement in the alpha-beta algorithm, namely the sorting function (using the quicksort algorithm) discussed in a previous chapter, and miracle o miracle, it worked much faster and I could increase to a search depth of five half- plys! This is already very good. To speed up the endgame search (14 half-plys ahead), I used an hash table which memorized the positions and their alpha-beta return values in order not to analyze them again; unfortunately, due to compiler memory problems, this table is only about 32000 entries big. Also, in the endgame search, I calculated the difference in total discs of each side in an incremental fashion, meaning that the final evaluation of the position is not done at the end of the game but rather at each step (half-ply), this technique speeds it up a little bit. Finally I implemented an increase by one half-ply after the game passed 40 moves. For this to take reasonable time, I took away practically all of the rules I have implemented in the evaluation function in order to avoid certain not obvious blunders. This remind me of a saying some programmer said, but I dont remember who or where I have read/heard it, he said that it is best to look very far ahead in the search depth
44

using very simple rules, than having lots of rules and not to look very far; the hope being that looking very far ahead will, in a way, avoid the blunders. I also tried to cut some branches at every depth, this can be done as the sorting function will sort the current depth moves according to the evaluation function, and so, a certain percentage of the last moves can be discarded. Using this technique I could use a 10 half-plys search depth in a reasonable time! And even higher! Unfortunately, the quality of the games decreased sensibly, as it is no more a brute force search, some branches may be cut out, when they actually should not be as they could be deceptive positions, meaning positions which looks bad using a direct evaluation of them, but turns out winning if just looked 1 (or more) half-ply(s) ahead, as these positions are quite hard to define in othello, I decided not to use this branch cutting technique. As a feeling of how much time it takes for one half-ply at different search depth, let us see the following graph (on a Pentium 150):
14 12 10 Time (s) 8 6 4 2 0 Series1 1 0.04 2 0.05 3 0.09 4 0.2 5 0.44 6 1.56 7 4.19 8 12.5

Depth (half-plys)

, we can see that the time taken for each move increase exponentially with the search depth!

5.6 RESULTS
In this section I will present many results of simulation done with the program. I will give the initial setting, and the number of wins/draws/losses that the converged strings scored against the same set of initially randomly generated opponents (the file random.txt contains this set and is used to initialize the appropriate vector).

5.6.1 First serie 5.6.1.1 Simulations Simulation 1


Style: Match play Population:16 Mutation rate: 1 out of 1000 bits Recombination: 7-point crossover Random string: No random string used Games per match: 2
45

Converged string: 232.136.251.190.042.224.224 Games: Wins: 35 Losses: 27 Draws: 2 Matches: Wins: 9 Losses: 7 Draws: 0 Total points scored: 131

Simulation 2
Style: Match play Population:16 Mutation rate: 1 out of 1000 bits Recombination: 7-point crossover Random string: 1 semi-random ( 48 ) Games per match: 2

Converged string: 220.222.038.004.065.023.092 Games: Wins: 44 Losses: 18 Draws: 2 Matches: Wins: 13 Losses: 3 Draws: 0 Total points scored: 781

Simulation 3
Style: Match play Population:16 Mutation rate: 1 out of 1000 bits Recombination: 7-point crossover Random string: 1 semi-random ( 48 ) Games per match: 4

Converged string: 254.243.000.253.254.032.205


46

Games: Wins: 47 Losses: 17 Draws: 0 Matches: Wins: 16 Losses: 0 Draws: 0 Total points scored: 1057

Simulation 4
Style: Match play Population:16 Mutation rate: 1 out of 1000 bits Recombination: 7-point crossover Random string: 1 complete random Games per match: 4

Converged string: 190.228.069.251.028.123.020 Games: Wins: 45 Losses: 18 Draws: 1 Matches: Wins: 13 Losses: 3 Draws: 0 Total points scored: 604

Simulation 5
Style: Match play Population:16 Mutation rate: 1 out of 1000 bits Recombination: average+1 point crossover Random string: 1 semi-random ( 48 ) Games per match: 2

Converged string: 194.196.044.178.027.102.097 Games:


47

Wins: 53 Losses: 9 Draws: 2

Matches: Wins: 15 Losses: 1 Draws: 0 Total points scored: 1150

Simulation 6
Style: Match play Population:16 Mutation rate: 1 out of 1000 bits Recombination: only uniform crossover Random string: 1 semi-random ( 48 ) Games per match: 2

Converged string: 168.255.006.213.004.015.050 Games: Wins: 44 Losses: 19 Draws: 1 Matches: Wins: 15 Losses: 1 Draws: 0 Total points scored: 974

Simulation 7
Style: Simple mutation Population:16 Mutation rate: 1 out of 1000 bits Random string: no random Games per match: 2

Converged string: 173.209.043.165.028.051.072 Games: Wins: 53 Losses: 11 Draws: 0


48

Matches: Wins: 14 Losses: 2 Draws: 0 Total points scored: 1188

Simulation 8
Style: Rank style mutation Population:16 Mutation rate: 1 out of 1000 bits Random string: no random Games per match: 2

Converged string: 195.222.108.046.012.074.074 Games: Wins: 45 Losses: 16 Draws: 3 Matches: Wins: 15 Losses: 1 Draws: 0 Total points scored: 790

Simulation 9
Style: Rank style mutation Population:16 Mutation rate: 1 out of 1000 bits Random string: semi random ( 48 ) Games per match: 2

Converged string: 255.222.255.125.000.000.077 Games: Wins: 46 Losses: 17 Draws: 1 Matches: Wins: 15 Losses: 1
49

Draws: 0

Total points scored: 981

Simulation 10
Style: Classic Population:16 Mutation rate: 1 out of 50 bits Recombination: 7-point crossover Random string: no random Games per match: 2

Converged string: 237.173.189.210.045.245.228 Games: Wins: 43 Losses: 21 Draws: 0 Matches: Wins: 11 Losses: 5 Draws: 0 Total points scored: 526

Simulation 11
Style: Classic Population:16 Mutation rate: 1 out of 100 bits Recombination: average + 1-point crossover Random string: two semi-random ( 48 ) Games per match: 2

Converged string: 142.130.003.168.031.191.021 Games: Wins: 45 Losses: 17 Draws: 2 Matches: Wins: 14 Losses: 2 Draws: 0
50

Total points scored: 582

5.6.1.2 Analysis of these results


So how can the actual values of the strings found help us in any way to improve the evaluation function? Are these values of the strings linked in any way? After careful analysis of all strings, one can note that there are clear correlations between them! Some of the parameters tend to 0, others tend to 256, and still others tend to 76 or something similar. This is most helpful, it enables us to know which parameters are most important, and which one of them can be left out of the evaluation function. So for othello programmers, this can be of great help since one can find countless ideas for improving the evaluation function, but without the assurance that these ideas are correct; a simulation will then show if the idea was correct or not! Lets see a graph which look at first sight completely chaotic (to say the least..), but when looked closely, shows clearly the correlation existing between the strings:

Simulation results
300 250 200 Value 150 100 50 0 1 2 3 4 Parameter 5 6 7

The surprising (or not) thing also is that all the strings were found with different style/recombination/random sets, proving the viability of the program/genetic algorithms used. We can note that most of the results are around 45 wins/19 losses, the highest being 53 wins and the lowest being 36 wins, which is quite low but is also the only one like that. One thing I did not put in the results is the rate of convergence of each simulation, I worked on a Pentium 150 on which I wrote the program, and made the simulation on another computer which is quite faster (a Pentium 400), still, each simulation took about a day. I will try to summarize the results. For most of the styles, if one chooses no random, then the population converges quite rapidly, in about 25 generations. If one chooses 1 semi-random, then the rate of convergence is decreasing slowly, then gets to a certain limit, jumps up and down around
51

this limit, and finally converges to some string, this takes about 100 generations. If one chooses 1 complete random, then the rate of convergence is much slower, going down sometimes and suddenly goes up like a rocket; this is due to the fact that the population first converges to some good string, then a random string comes which beats this good string, but.. if he beat this string, he can beat a large proportion of the population! Ouch.. So actually using 1 complete random, one can never really be sure that the string, which now looks to be the convergent string, is the convergent string forever.. after all, if this was the case for all random strings, it would mean that this string is the global maximum, which as said does not probably exist, this is quite of a paradox.. J For a comparision between the styles, the match style and the mutation styles are more or less the same in term of convergence speed, the classic style being quite faster to converge. Personally, after hundreds of simulations (the simulations above are the official results with the finally chosen seven parameters), I would give a preference to match style with 1 semi-random of 48, and with a 7 point crossover recombination algorithm. I like the 1 semi-random most because it is like a Hill-climbing technique, first the population converges to some good string, meaning we are near to a local maximum, then the 1 semi-random string will slowly rise the population to this maximum.

5.6.2 Second serie 5.6.2.1 Simulations


11 simulations were run with the same following setting: Style: Match play Population:16 Mutation rate: 1 out of 1000 bits Recombination: 7-point crossover Random string: 1 semi-random ( 48 ) Games per match: 2

Converged strings: 190.255.177.246.003.046.100 148.254.180.255.002.041.130 214.201.136.221.004.075.001 078.187.125.187.053.239.077 076.155.005.154.001.009.004 000.218.037.000.011.006.035 147.254.075.112.000.000.088 042.240.011.007.022.000.090 134.237.165.167.008.021.094 154.255.000.045.000.088.070 254.255.158.000.000.084.085

5.6.2.2 Analysis of these results


Lets make the same graph as before with these new found values:

52

Simulation results
300 250 200 Value 150 100 50 0 1 2 3 4 Parameter 5 6 7

This graph is nicer than the previous ones. It clearly shows existing correlations between the different results. For example we can see that none of the eleven simulations gave a value of less than 155 to the second parameter, also none gave a value of more that 53 to the fifth parameter! These could be called value correlations, but we can even find topological ones, for example for most of the simulations, there is a minimum between the second and the fourth parameter, even though the third and fourth parameter do not agree much! This can explained by the fact that although the actual values of the parameters are important (due to some external rules which are not governed by the set of parameters), the relationship between the parameters may be even more important. As said before, these graphs can be of great help for othello (or any other game) programmers as they clearly show existing relationships between (in study) parameters, they show which are important, which are not, etc. this can save months of try, test, and pray procedures J .

5.7 MODIFYING THE PROGRAM 5.7.1 Changing the evaluation function


As discussed earlier, an interesting use of the program is to test new aspects of othello and study their importance, how they interact with other aspects, etc. In this section we are going to see how this can be done. The function which should be modified is eval_position, its function is to evaluate the present position, coded in the vector board. This vector is 8x8 long. An empty square is coded 0, a square belonging to the player, by 1, and a square belonging to the computer, by 2 (when two computers are playing each other, this rule still holds, the computer in this case always represents the string whose turn it is to play, and the player, his opponent). There is one variable move , which represents how many moves have been made up till now, so it is equal to the total number of non-empty squares minus 4. Also, there is a genetic vector which is the base on which everything lies; its first index represents a
53

particular string in the population, and its second index represents the set of parameters of this particular string. For example, genetic [4][1] means the first parameter of the 4th string in the population. The number of parameters is set in the menu screen, there can be up to 10 different, 8 bit integers, parameters. And finally, there is a variable gennum, which represents the index of the particular string which is currently playing. As an example lets create a very basic (and very bad from an othello point of view) evaluation function which tries to maximize the difference in number of the computers discs and of his opponents, the code would look something like that:
function eval_position(board:vector1):integer; var r1, r2, i, o, total: integer; begin r1 := 0; { r1 -> The computers opponent total number of discs ) r2 := 0; { r2 -> The computer total number of discs } total := 0; for i := 1 to 8 do for o := 1 to 8 do begin if board[i][o] = 1 then inc(r1) else if board[i][o] = 2 then inc(r2); end; total := r2-r1; eval_position := total; end;

Lets try now a function which uses the genetic vector. This function (combined with the simulation) will search for the 2 optimal coefficients of 2 parameters, one representing the opposite of the above rule, and the other one, the difference in total number of possibilities between the computer and his opponent:
function eval_position(board:vector1):integer; var r1, r2, r3, r4, i, o, total: integer; begin r1 := 0; { r1 -> the computers opponent total number of discs ) r2 := 0; { r2 -> the computer total number of discs ) r3 := 0; { r3 -> the computers opponent total number of possibilities } r4 := 0; { r4 -> the computer total number of possibilities } total := 0; for i := 1 to 8 do for o := 1 to 8 do begin if board[i][o] = 1 then inc(r1) else if board[i][o] = 2 then inc(r2) else begin { empty square } if (choice(board,i,o,1,0,no_u) = 1) then inc(r3); if (choice(board,i,o,2,0,no_u) = 1) then inc(r4); end; end; total := ((r1-r2) * genetic[gennum][1]) + ((r4-r3) * genetic[gennum][2]);

54

eval_position := total; end;

We have introduced a new function, choice , which, if called as choice (board,i,o,1,0,no_u) will return 1 if the computers opponent can play in the position [i][o] (if he cannot play there, the function will return 0), and if called as choice (board,i,o,2,0,no_u), will return 1 if the computer can play in the position [i][o]; no_u being a global variable and should not be modified. Note that this function is much better than the previous one, and that it is somewhat correct from an othello perspective. This is basically all that there is to know in order to create your own evaluation function! Now you are ready to analyze more closely the actual one used in the program. Although modifying only this function is enough for most purposes, you could be interested in modifying the alpha-beta algorithm represented by the function algol3. Also, as said before, the program is using a brute force search where the number of half- plys searched are equal to what is set in the menu screen, and after move 40, this search depth in increase by one; this can be modified in the procedure computerplay, in the place where the algol3 function is called, the variable hb representing the search depth set in the menu screen.

5.7.2 Changing the optimization problem


In the beginning of this chapter, it was mentioned that the program can be easily modified to tackle practically any optimization problem, let us see now how this can be done. We will go step by step. First, the constant use_othello in the globvar.pas unit file, must be set to 0. Then recompile. Second, go in the file genelab.pas and look for the procedures: othello_proc, and any_proc. The othello_proc procedure is like a module which species the problem, it is used if and only if the constant use_othello is set to 1. The any_proc procedure is the one which should be modified according to the optimization problem one wants to investigate. Let us study from an example of a simple function optimization problem. We wish to find the maximum of f(x ) = x , for x going from 0 to 256. The procedure any_proc would look something like:
procedure any_proc(var se1, se2:integer); var xh:real; ch:char; label 2; begin if keypressed then begin ch := readkey; if ch = chr(27) then begin endp := 1; super_champion := 2; super_champ := 1; goto 2; end; end; gennum := op1; xh := genetic[gennum][1]; se2 := round(xh); gennum := op2; xh := genetic[gennum][1]; 55

se1 := round(xh); 2: end;

The section marked with the line should in principle always be there. Now how does it work? First, lets note that in the othello case, we could choose up to 4 games per match, meaning that the same sets of parameters are competing up to 4 times, in different circomstences (a set of parameter first plays black, then plays white, then again the same, but with another initial board configuration). To represent these 4 states, we have three parameters: op1 , op2 , and next_gen; op1 represents the index of the first set of parameters in the genetic vector, and op2, the index of the second set of parameters in the same genetic vector. If we compete 4 times, then the first time, op1 will play against op2 (op1 will play first), at the second turn, the values of op1 and op2 are swapped, so op2 will play against op1 , at the third turn, again, the values of op1 and op2 are swapped, but also the value of the variable next_gen becomes 1 (it was 0), this permits to put some conditions in the procedure which takes this in account in order to start with a different initial board configuration, finally, the values of op1 and op2 are swapped again. All these turns are done automaticely, one just has to think of the implementation, also how op1 and op2 are changed afterward, and how the population is evolved each generation is also done automaticely. In the case we are interested now, the simple function optimization, there is only 1 game per match as there is no concept of who starts/finish like in othello or chess. Now we can start to understand how the above code works. First gennum is given the index of one of the string pointed out by op1 . Then a variable, xh, receives the value of the 1st parameter of this string, we assume here that each string is represented by one and only one parameter. Then se2 is given the value of this variable; se2 represents the fitness of op1 . Then the same thing is done for the string op2 , and se1 is given the value of the function for this string. Thats it. J (why do I hear you sing Allelouia?!..J ). Hope it doesnt sound too chaotic, it may in the first reading.. This code can be mutated as much as one likes. For example one can try a more complicated function (just modify s e2 := round(xh)and s e1 := round(xh) to any desired function), or to use more parameters. This program can be viewed as two programs in one; first there is the othello game, then the genetic laboratory, both are somewhat independent of one another thus the power of modifying it for any purpose. Of course the interface of the program is that of an othello program as this was the central theme of the thesis, thus, if you want to use the genetic part for another game, you will have to rewrite part of the interface (not all, just the board/pawns, etc.), and solve some problems, but it should be feasible J Also, the othello module, othello_proc, can be analyzed more closely to get more intuition how all this works. One last thing: although this code was tested, and seems to work reasonalby well, it is far from being garanteed to be 100% bug free, any feedback would be most welcomed!

56

6
CONCLUSION

This thesis demonstrated how genetic algorithms can be applied to a practial case, more specifically, to the game of othello. It showed that the genetic algorithm paradigm is a very powerfull one and can be applied without much difficulties to a non-trivial problem. The genetic othello program developed here succeded in finding sets of parameters, which, if not global maximums, at least seemed somewhat optimal! I argued before that a global maximum probably doesnt exist in this case, as the fitness calculation method is somewhat abstract compared to a simple function optimization problem, but from the last graph given in the previous chapter, one can start to ponder seriously about this question! Maybe we should not talk about finding a global maximum as usual, but rather introduce some new concepts, like finding a global, or optimum, parameter topology. This program can still be extended with new ideas, also new parameters can be tested. The code will soon be availabe at: http://members.xoom.com/ifaybish/othello.html, for any questions related to this thesis or the code, the readers are most welcomed to send an email. I hope reading it didnt cause too many headaches, but rather gave some inspiration for exploring further this subject!

57

7
APPENDIX

7.1 EXAMPLE OF A GENETIC ALGORITHM


*************************************************************************** * Program written by Itamar Faybish, 24/10/98, used as an example of * coding genetic algorithms *************************************************************************** program ex_genetic_algorithm; uses crt, dos; *************************************************************************** * Lets start with a population of 100, Crossover probability of 60%, and * mutation probability of 1%, we define three constants: *************************************************************************** const population_size = 200; crossover_probability = 60; mutation_probability = 1; *************************************************************************** * Now we define two types: * - pop, being an array of integers and of population_size size, so our * initial population consists of population_size strings, and we will * initialize them such that each string is a byte. * - arrayg, being an array of integer of size 8, this will represent a * byte. *************************************************************************** type pop = array[1..population_size] of integer; arrayg = array[1..8] of integer; *************************************************************************** * We define 7 variables: * - arrayb and arrayb2 represents two bytes * - population represents our population of strings * - fitness1 represents the fitness value of each string * - temp1, temp2, temp3 are temporary variables *************************************************************************** var arrayb,arrayb2 : arrayg; population,fitness1 : pop;

58

temp1, temp2, temp3:integer; *************************************************************************** * The following procedure prints the entire population of strings *************************************************************************** procedure look_population(population:pop); var temp1, temp2:integer; begin for temp1 := 1 to population_size do begin write(population[temp1],' '); end; end; *************************************************************************** * The following function returns the fitness of a string *************************************************************************** function fitness(string1 : integer):integer; begin fitness := string1; end; *************************************************************************** * The following procedure initialize the population with random values * taken in the range 0-255, and assigns to the variable fitness1 the * initial fitness of the strings. *************************************************************************** procedure init_population(var population:pop); var temp1, temp2:integer; begin for temp1 := 1 to population_size do begin population[temp1] := random(256); fitness1[temp1] := fitness(population[temp1]); end; end; *************************************************************************** * The following procedure takes a byte and gives its binary representation * to the variable ar *************************************************************************** procedure binary(var ar:arrayg;i:integer); var temp:integer; begin temp := 1; while i <> 0 do begin if i mod 2 = 0 then begin ar[temp] := 0; inc(temp); end else begin ar[temp] := 1; inc(temp); end; i := i div 2; end; while temp < 9 do begin ar[temp] := 0; inc(temp); end; end; *************************************************************************** * The following function calculate a b ***************************************************************************

59

function exp(a,b:integer) : integer; var temp1,temp2:integer; begin temp1 := 1; temp2 := a; while temp1 < b do begin temp2 := temp2 * a; inc(temp1); end; if b = 0 then exp := 1 else exp := temp2; end; *************************************************************************** * The following function gets a binary representation of a byte and gives * out its decimal value *************************************************************************** function decimal(ar:arrayg):integer; var temp1,temp2:integer; begin temp1 := 0; for temp2 := 1 to 8 do begin if ar[temp2] = 1 then temp1 := temp1 + exp(2,temp2-1); end; decimal := temp1; end; *************************************************************************** * The following procedure makes a selection of strings from the population * and gives out this selection, it uses a roulette wheel, rank based * algorithm *************************************************************************** procedure selection(var population:pop); var fitness_sum, temp1, temp2, temp3, temp4, ii, oo : integer; population_tmp, fitness_tmp : pop; begin fitness_sum := 0; temp1 := 1; *************************************************************************** * The following algorithm transforms the real fitness to rank based * fitness *************************************************************************** for ii := population_size downto 1 do begin for oo := 2 to population_size do begin if (fitness1[temp1] <= fitness1[oo]) then begin temp1 := oo; end; end; fitness1[temp1] := -2000; fitness_tmp[temp1] := ii; temp1 := 1; end; fitness1 := fitness_tmp; *************************************************************************** * End of algorithm *************************************************************************** for temp1 := 1 to population_size do begin fitness_sum := fitness_sum + fitness1[temp1]; end; *************************************************************************** * The following code is the roulette wheel algorithm repeated

60

* population_size times *************************************************************************** for temp4 := 1 to population_size do begin temp2 := random(fitness_sum)+1; temp3 := 1; temp1 := 0; while (temp1 < temp2) do begin temp1 := temp1 + fitness1[temp3]; inc(temp3); end; population_tmp[temp4] := population[temp3-1]; end; population := population_tmp; end; *************************************************************************** * The following procedure receives two strings in their byte * representation, and apply a one point crossover at the point i . *************************************************************************** procedure crossover(var a1,a2:arrayg;i:integer); var t1,t2 : integer; temp : arrayg; begin for t1 := i+1 to 8 do begin t2 := a1[t1]; a1[t1] := a2[t1]; a2[t1] := t2; end; end; *************************************************************************** * The followin procedure receives a strings in its byte reprentation, and * inverse its i bit. *************************************************************************** procedure inverse(var a1:arrayg;i:integer); var t1,t2 : integer; temp : arrayg; begin if a1[i] = 0 then a1[i] := 1 else a1[i] := 0; end; *************************************************************************** * Start of the Main part *************************************************************************** begin clrscr; randomize; *************************************************************************** * Initialize initial population *************************************************************************** init_population(population); *************************************************************************** * Start of the main loop *************************************************************************** repeat *************************************************************************** * Selection of strings in the population *************************************************************************** selection(population); ***************************************************************************

61

* Look the strings in the population *************************************************************************** look_population(population); *************************************************************************** * Start of Recombination algorithm to all pairs of strings in the * population with a probability of crossover_probability. *************************************************************************** for temp1 := 0 to (population_size div 2)-1 do begin temp2 := random(100); temp3 := random(6)+2; if temp2 < crossover_probability then begin binary(arrayb,population[2*temp1+1]); binary(arrayb2,population[2*temp1+2]); crossover(arrayb,arrayb2,temp3); population[temp1] := decimal(arrayb); population[temp1] := decimal(arrayb2); end; end; *************************************************************************** * End of Recombination algorithm *************************************************************************** *************************************************************************** * Start of the Mutation algorithm *************************************************************************** for temp1 := 1 to population_size do begin for temp2 := 1 to 8 do begin temp3 := random(100); if (temp3 < mutation_probability) then begin binary(arrayb,population[temp1]); inverse(arrayb,temp2); population[temp1] := decimal(arrayb); end; end; end; *************************************************************************** * End of the mutation algorithm *************************************************************************** *************************************************************************** * Recalculate the fitness of the new generation *************************************************************************** for temp1 := 1 to population_size do begin fitness1[temp1] := fitness(population[temp1]); end; delay(10); clrscr; until temp1 = 1000; *************************************************************************** * End of loop : repeat infinitely many time *************************************************************************** end.

62

7.2 THE OTHELLO PROGRAM 7.2.1 Main module


{ ********************************************************* * Genetic Othello V1.01 (c) * * Written by Itamar Faybish * * Date: 10/06/99 * * Compiler used: Borland Pascal 7.0 * ********************************************************* } { ********************************************************* * Main module. * ********************************************************* } program Othello_genetic; uses newdelay, crt, graph, globvar, otgraph; { *********************************************************** * The following procedure initialize some variables and * * shows the initial screen. * *********************************************************** } procedure start; var oo,ii,uu,x,y,a,x1,a1,x2,y2,a2: integer; begin graphdriver := vga; graphmode := vgahi; initgraph(graphdriver, graphmode, 'f:\turbo7'); setbkcolor(8); setcolor(15); cleardevice; settextjustify(centertext,centertext); settextstyle(3, horizdir, 4); outtextxy(320,140,'Genetic Othello V1.01' ); settextstyle(3, horizdir, 1); outtextxy(320,200,'by' ); settextstyle(3, horizdir, 2); outtextxy(320,240,'Itamar Faybish' ); settextstyle(3, horizdir, 1); outtextxy(320,295,'10 june 99' ); settextjustify(lefttext,toptext); setcolor(white); rectangle(280,20,360,100); rectangle(279,19,361,101); setcolor(lightblue); rectangle(0,0,639,479); rectangle(3,3,636,476); setfillstyle(solidfill, lightblue); floodfill(2,2,lightblue); setcolor(blue); rectangle(0,0,639,479); rectangle(3,3,636,476); moveto(0,0); lineto(3,3); moveto(636,476); lineto(639,479); moveto(3,476); lineto(0,479); moveto(636,3); lineto(639,0); setcolor(white); setfillstyle(solidfill, green); floodfill(320,80,white); circle(325,55,3); setfillstyle(solidfill, white); floodfill(325,55,white); circle(315,65,3); setfillstyle(solidfill, white);

63

floodfill(315,65,white); setcolor(8); circle(325,65,3); setfillstyle(solidfill, 8); floodfill(325,65,8); circle(315,55,3); setfillstyle(solidfill, 8); floodfill(315,55,8); setcolor(white); aaa := 280; bbb := 20; ccc := 360; ddd := 20; setcolor(7); for eee := 1 to 9 do begin moveto(aaa,bbb); lineto(ccc,ddd); bbb := bbb + 10; ddd := ddd + 10; end; aaa := 20; bbb := 280; ccc := 100; ddd := 280; setcolor(7); for eee := 1 to 9 do begin moveto(bbb,aaa); lineto(ddd,ccc); bbb := bbb + 10; ddd := ddd + 10; end; ua := 2; tz := 1; uh := 1; x := 180; y := 274; x1 := 460; a := 1; a1 := 0; x2 := 0; y2 := 0; a2 := 0; repeat if a = 1 then setcolor(green) else setcolor(yellow); circle(x,y-102,3); circle(x1,y-102,3); if a = 1 then setcolor(yellow) else setcolor(green); circle(x,y,3); circle(x1,y,3); if x > 320 then begin setcolor(green); a := 1; end; if a = 1 then dec(x) else inc(x); delay(1); if x < 180 then begin setcolor(yellow);a := 0;end; if x1 < 320 then begin setcolor(yellow); a1 := 1; end; if a1 = 1 then inc(x1) else dec(x1); if x1 > 460 then begin setcolor(green);a1 := 0;end; until keypressed; end; { *********************************************************** * The following procedure initialize some variables. * *********************************************************** } procedure start1; var oo,ii,uu,x,y,a,x1,a1,x2,y2,a2: integer; begin new(hash_alpha); new(hash_beta); new(hash_gamma); new(hash_delta); for ii := 1 to 32000 do begin

64

hash_alpha^[ii] := 0; hash_beta^[ii] := 0; hash_gamma^[ii] := 0; hash_delta^[ii] := 0; end; for ii := 1 to 30 do begin min[ii] := 0; min2[ii] := 0; end; for oo := 1 to 64 do for uu := 1 to 20 do xyglob[oo][uu] := 0; min[1] := 1; min[2] := 9; min[3] := 5; min[4] := 7; min[5] := 3; min[6] := 7; min[7] := 11; min[8] := 7; min[9] := 7; min[10] := 9; min[11] := 5; min[12] := 7; min[13] := 3; min[14] := 5; min[15] := 7; min[16] := 3; min[17] := 3; min[18] := 5; min[19] := 9; min[20] := 7; min[21] := 7; min[22] := 7; min[23] := 11; min[24] := 13; min[25] := 7; min[26] := 11; min[27] := 9; min[28] := 5; min2[1] := 0; min2[2] := 6; min2[3] := 8; min2[4] := 10; min2[5] := 4; min2[6] := 4; min2[7] := 2; min2[8] := 12; min2[9] := 10; min2[10] := 10; min2[11] := 8; min2[12] := 6; min2[13] := 10; min2[14] := 4; min2[15] := 8; min2[16] := 2; min2[17] := 4; xyglob[1][1] := xyglob[3][1] := xyglob[5][1] := xyglob[7][1] := xyglob[9][1] := xyglob[11][1]:= xyglob[13][1]:= 3453; 6435; 5646; 2526; 3647; 1624; 3715;

xyglob[9][2] := 4315; xyglob[11][2]:= 4752; xyglob[13][2]:= 2432; xyglob[15][2] := 1336; xyglob[5][3] := 4663; xyglob[7][3] := 3624;

65

xyglob[9][3] := 4333; xyglob[11][3] := 5256; xyglob[13][3] := 4225; xyglob[7][4] := 5633; xyglob[9][4] := 4352; xyglob[11][4] := 2526; xyglob[13][4] := 2416; xyglob[15][4] := 1514; xyglob[3][5] := 6556; xyglob[5][5] := 6635; xyglob[7][5] := 6476; xyglob[7][6] := 3324; xyglob[9][6] := 4332; xyglob[11][6] := 2342; xyglob[13][6] := 3626; xyglob[15][6] := 4613; xyglob[11][7] := 1312; xyglob[7][8] := 4376; xyglob[9][8] := 6746; xyglob[11][8] := 5775; xyglob[13][8] := 6463; xyglob[15][8] := 3732; xyglob[7][9] := 6486; xyglob[9][9] := 6775; xyglob[9][10] := 5763; xyglob[5][11] := 6435; xyglob[7][11] := 5274; xyglob[9][11] := 3662; xyglob[11][11] := 6746; xyglob[13][11] := 5737; xyglob[15][11] := 7566; xyglob[7][12] := 4436; xyglob[3][13] xyglob[5][13] xyglob[7][13] xyglob[9][13] := := := := 6335; 3664; 4333; 6562;

xyglob[5][14] := 4323; xyglob[7][14] := 3646; xyglob[7][15] := 5665; xyglob[3][16] := 6236; xyglob[5][16] := 5663; xyglob[3][17] := 6656; xyglob[5][17] := 4636; xyglob[7][17] := 6433; xyglob[5][18] := 6535; xyglob[7][18] := 4633; xyglob[9][18] := 6364; xyglob[11][18] := 4342; xyglob[13][18] := 5261; xyglob[15][18] := 3141; xyglob[9][19] := 2444; xyglob[11][19] := 1312; xyglob[7][20] := 4376; xyglob[9][20] := 6746; xyglob[11][20] := 5775; xyglob[13][20] := 6463; xyglob[7][21] := 6346; xyglob[7][22] := 6476;

66

xyglob[9][22] := 6775; xyglob[11][22] := 7457; xyglob[13][22] := 4663; xyglob[15][22] := 6885; xyglob[17][22] := 3643; xyglob[19][22] := 8648; xyglob[21][22] := 5878; xyglob[23][22] := 8384; xyglob[25][22] := 7737; xyglob[27][22] := 7323; xyglob[29][22] := 4733; xyglob[31][22] := 2462; xyglob[33][22] := 3214; xyglob[35][22] := 3888; xyglob[37][22] := 8742; xyglob[39][22] := 5225; xyglob[11][23] xyglob[13][23] xyglob[15][23] xyglob[17][23] xyglob[19][23] xyglob[21][23] xyglob[23][23] xyglob[25][23] xyglob[27][23] xyglob[29][23] xyglob[31][23] xyglob[33][23] xyglob[35][23] xyglob[37][23] xyglob[39][23] xyglob[41][23] xyglob[43][23] xyglob[45][23] xyglob[13][24] xyglob[15][24] xyglob[17][24] xyglob[19][24] xyglob[21][24] xyglob[23][24] xyglob[25][24] xyglob[27][24] xyglob[29][24] xyglob[31][24] := := := := := := := := := := := := := := := := := := := := := := := := := := := := 6357; 4374; 8483; 8673; 8587; 4636; 2568; 3726; 1538; 4762; 5241; 5848; 7232; 7124; 1433; 4223; 3122; 1311; 8668; 4684; 3685; 8333; 8774; 7347; 4862; 3825; 2443; 1416;

xyglob[7][25] := 3336; xyglob[9][25] := 4342; xyglob[11][25] := 6323; xyglob[13][25] := 1364; xyglob[15][25] := 4652; xyglob[17][25] := 3224; xyglob[19][25] := 6147; xyglob[21][25] := 1473; xyglob[23][25] := 5125; xyglob[25][25] := 6267; xyglob[27][25] := 8375; xyglob[29][25] := 3757; xyglob[31][25] := 4858; xyglob[33][25] := 6876; xyglob[35][25] := 8638; xyglob[37][25] := 2874; xyglob[11][26] xyglob[13][26] xyglob[15][26] xyglob[17][26] xyglob[19][26] xyglob[21][26] xyglob[23][26] xyglob[25][26] xyglob[27][26] xyglob[29][26] xyglob[31][26] xyglob[33][26] xyglob[35][26] := := := := := := := := := := := := := 3264; 7323; 2463; 5174; 2546; 5283; 7675; 6257; 8584; 8213; 1526; 8631; 6777;

67

xyglob[37][26] xyglob[39][26] xyglob[41][26] xyglob[43][26]

:= := := :=

5841; 2147; 1614; 3717;

xyglob[9][27] := 6443; xyglob[11][27] := 3263; xyglob[13][27] := 5224; xyglob[15][27] := 6261; xyglob[5][28] := 6335; xyglob[7][28] := 4333; xyglob[9][28] := 2423; xyglob[11][28] := 2513; xyglob[0][1] := 3434; xyglob[6][2] := 3564; xyglob[8][2] := 7563; xyglob[10][2] := 4342; xyglob[12][2] := 3332; xyglob[14][2] := 5262; xyglob[16][2] := 6123; xyglob[18][2] := 7341; xyglob[20][2] := 5726; xyglob[22][2] := 3674; xyglob[24][2] := 8483; xyglob[26][2] := 8286; xyglob[28][2] := 3124; xyglob[30][2] := 7125; xyglob[32][2] := 5146; xyglob[8][3] := 7667; xyglob[10][3] := 5886; xyglob[12][3] := 6373; xyglob[10][4] xyglob[12][4] xyglob[14][4] xyglob[16][4] xyglob[18][4] xyglob[20][4] xyglob[22][4] xyglob[24][4] xyglob[26][4] xyglob[28][4] xyglob[30][4] xyglob[32][4] xyglob[34][4] xyglob[36][4] := := := := := := := := := := := := := := 4657; 6375; 7447; 4336; 6883; 8485; 8687; 3837; 2832; 4241; 2542; 2615; 3316; 5161;

xyglob[4][5] := 3533; xyglob[6][5] := 3656; xyglob[8][5] := 2443; xyglob[10][5] := 4226; xyglob[12][5] := 1664; xyglob[14][5] := 5273; xyglob[16][5] := 7574; xyglob[18][5] := 4625; xyglob[20][5] := 6313; xyglob[22][5] := 6523; xyglob[4][6] := 3635; xyglob[2][7] := 3343; xyglob[4][7] := 5364; xyglob[6][7] := 3556; xyglob[8][7] := 6362; xyglob[10][7] := 7352; xyglob[12][7] := 4151; xyglob[14][7] := 6136; xyglob[12][8] := 6566; xyglob[14][8] := 7546; xyglob[10][9] := 7573; xyglob[12][9] := 6646;

68

xyglob[14][9] := 8386; xyglob[10][10] := 6546; xyglob[12][10] := 7352; xyglob[8][10] := 7425; xyglob[10][10] := 3623; xyglob[6][12] := 6563; xyglob[8][12] := 7542; xyglob[10][12] := 2423; xyglob[12][12] := 7374; xyglob[14][12] := 3231; xyglob[10][13] := 5152; xyglob[4][14] := 3546; xyglob[6][14] := 5325; xyglob[8][14] := 5662; xyglob[10][14] := 6324; xyglob[8][15] := 1564; xyglob[10][15] := 6624; xyglob[2][16] := 3546; xyglob[4][16] := 3356; xyglob[6][16] := 6453; xyglob[8][16] := 6543; xyglob[10][16] := 3225; xyglob[4][17] := 3747; xyglob[6][17] := 3356; glu := 1; tv ua tz uh end; := := := := 2; 2; 1; 1;

{ *********************************************************** * The following 2 procedures are used to draw nice * * colored boxes at specific places. * *********************************************************** } procedure box1(xx1,xx2,xx3,xx4,yy1,yy2,yy3,yy4:integer); begin setcolor(blue); setfillstyle(solidfill, blue); rectangle(xx1,xx2,xx3,xx4); floodfill(xx1+1,xx2+1,blue); setcolor(lightblue); setfillstyle(solidfill, lightblue); rectangle(yy1,yy2,yy3,yy4); floodfill(yy1+1,yy2+1,lightblue); setcolor(8); moveto(yy1,yy2); lineto(xx1,xx2); moveto(yy3,yy2); lineto(xx3,xx2); moveto(yy3,yy4); lineto(xx3,xx4); moveto(yy1,yy4); lineto(xx1,xx4); rectangle(yy1,yy2,yy3,yy4); rectangle(xx1,xx2,xx3,xx4); setcolor(white); end; procedure box2(xx1,xx2,xx3,xx4,yy1,yy2,yy3,yy4:integer); begin setcolor(lightgreen); setfillstyle(solidfill, lightgreen); rectangle(xx1,xx2,xx3,xx4); floodfill(xx1+1,xx2+1,lightgreen); setcolor(green);

69

setfillstyle(solidfill, green); rectangle(yy1,yy2,yy3,yy4); floodfill(yy1+1,yy2+1,green); setcolor(7); moveto(yy1,yy2); lineto(xx1,xx2); moveto(yy3,yy2); lineto(xx3,xx2); moveto(yy3,yy4); lineto(xx3,xx4); moveto(yy1,yy4); lineto(xx1,xx4); rectangle(yy1,yy2,yy3,yy4); rectangle(xx1,xx2,xx3,xx4); setcolor(white); end; { *********************************************************** * The following procedure is used to set the colors of * * the "options buttons" in the option screen. * *********************************************************** } procedure abc(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x:integer); var y: integer; begin y := 0; setcolor(a); outtextxy(59,70+y,'A)'); setcolor(b); outtextxy(59,85+y,'B)'); setcolor(c); outtextxy(59,100+y,'C)'); setcolor(d); outtextxy(59,115+y,'D)'); setcolor(e); outtextxy(59,130+y,'E)'); setcolor(f); outtextxy(59,145+y,'F)'); setcolor(g); outtextxy(59,160+y,'G)'); setcolor(h); outtextxy(59,175+y,'H)'); setcolor(i); outtextxy(59,190+y,'I)'); setcolor(j); outtextxy(59,205+y,'J)'); setcolor(k); outtextxy(59,220+y,'K)'); setcolor(l); outtextxy(59,235+y,'L)'); setcolor(m); outtextxy(59,250+y,'M)'); setcolor(n); outtextxy(59,265+y,'N)'); setcolor(o); outtextxy(59,280+y,'O)'); setcolor(p); outtextxy(59,295+y,'P)'); setcolor(q); outtextxy(59,310+y,'Q)'); setcolor(r); outtextxy(59,325+y,'R)'); setcolor(s); outtextxy(59,340+y,'S)'); setcolor(t); outtextxy(59,355+y,'T)'); setcolor(u); outtextxy(59,370+y,'U)'); setcolor(v); outtextxy(59,385+y,'V)'); setcolor(w); outtextxy(59,400+y,'W)'); setcolor(x); outtextxy(59,415+y,'X)'); setcolor(11);

70

outtextxy(59,430+y,'Y)'); outtextxy(59,445+y,'Z)'); end; { *********************************************************** * The following procedure is used to set the initial * * permutation of the "options buttons". * *********************************************************** } procedure assign_place(var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x:integer); begin a := 70; b := 85; c := 100; d := 115; e := 130; f := 145; g := 160; h := 175; i := 190; j := 205; k := 220; l := 235; m := 250; n := 265; o := 280; p := 295; q := 310; r := 325; s := 340; t := 355; u := 370; v := 385; w := 400; x := 415; end; { *********************************************************** * The following procedure shows the menu screen with all * * the options, and handle the desired changes. * *********************************************************** } procedure start2; var nomb_,nomb: string; hh,a,xx1,ya,gg,ff,xx2,y,xx3,xx4,yy1,yy2,yy3, yy4,x2,y2,y3, alex,y4,y5,y6,y7,ii,oo,ll, temp1, temp2, a1,b1,c1,d1,e1,f1,g1,h1,i1,j1,k1,l1,m1,n1,o1,p1,q1,r1,s1,t1,u1, v1,w1,x1,y1,z1: integer; ch, ch2: char; begin if generations = -2 then generations := -1; if styl = 5 then generations := -1; if endp <> 1 then begin lib := 1; last := 1; tv := 8; globe := tv; num_games := 4; styl := 1; random_var := 1; recombination := 1; back := 8; hb := 3; save_string := 1; rewrite_history := 1; crossover_prob := 60; population_size := 16; mutation_start := 1; classic_fitness := 1; standard_stop := -1; parameters := 7; ncrossover := 3; generations := -1;

71

mutation_rate := 2; sort_selection := 1; super_champion := 2; super_champ := 1; sort_generation := 1; random_value := 48; average_recombination := 2; hash_table := 2; mutation := 1000; mutation_algo := 2; assign(param_file,'param.txt'); reset(param_file); temp1 := 1; while not eof(param_file) do begin read(param_file,genetic[population_size+1][temp1]); genetic_save[temp1]:= genetic[population_size+1][temp1]; inc(temp1); end; end; endp := 0; setbkcolor(back); setcolor(11); cleardevice; box1(122,10,540,57,126,16,536,52); settextstyle(7, horizdir, 4); outtextxy(130,10,'GENETIC OTHELLO V1.01' ); settextstyle(2, horizdir, 5); setcolor(lightblue); rectangle(0,0,639,479); rectangle(3,3,636,476); setfillstyle(solidfill, lightblue); floodfill(2,2,lightblue); setcolor(blue); rectangle(0,0,639,479); rectangle(3,3,636,476); moveto(0,0); lineto(3,3); moveto(636,476); lineto(639,479); moveto(3,476); lineto(0,479); moveto(636,3); lineto(639,0); setcolor(lightblue); moveto(3,66); lineto(636,66); moveto(3,69); lineto(636,69); setfillstyle(solidfill, lightblue); floodfill(4,67,lightblue); setcolor(blue); moveto(3,66); lineto(636,66); moveto(3,69); lineto(636,69); a1 b1 d1 e1 f1 g1 h1 i1 j1 k1 l1 m1 n1 o1 p1 q1 r1 := := := := := := := := := := := := := := := := := 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0;

72

s1 t1 u1 v1 w1 x1 y1 z1

:= := := := := := := :=

0; 0; 0; 0; 0; 0; 0; 0;

y := 0; ya:= 60; assign_place(w1,c1,d1,e1,f1,s1,h1,x1,a1,b1,n1,j1,k1,o1,i1,l1,u1,p1,q1,g1,v1,m1,t1,r1); y1 := 430; z1 := 445; setcolor(white); outtextxy(77,a1+y,'Crossover probability : outtextxy(77,b1+y,'Mutation rate : 1 out of %'); bits');

outtextxy(77,c1+y,'Style : '); outtextxy(77,d1+y,'Search depth : '); outtextxy(77,e1+y,'Population size : '); outtextxy(77,f1+y,'Parameters :' ); outtextxy(77,g1+y,'Generations :' ); outtextxy(77,h1+y,'Recombination :' ); outtextxy(77,i1+y,'Random :' ); outtextxy(77,j1+y,'Mutation algorihtm start : 1 out of bits'); outtextxy(77,k1+y,'Mutation algorithm rate :'); outtextxy(77,l1+y,'Semi-random value :'); outtextxy(77,m1+y,'Save test string on exit :'); outtextxy(77,n1+y,'Average recombination factor :'); outtextxy(77,o1+y,'Mutation algorithm starting string :'); outtextxy(77,p1+y,'Special sorting in the selection function :'); outtextxy(77,q1+y,'Use fitness sorting every generation :'); outtextxy(77,r1+y,'Test string :'); outtextxy(77,s1+y,'Number of games per match :'); outtextxy(77,t1+y,'Rewrite history file :'); outtextxy(77,u1+y,'Classic style fitness :'); outtextxy(77,v1+y,'Standard deviation stop criteria :'); outtextxy(77,w1+y,'Super Championship :'); outtextxy(77,x1+y,'N-Point crossover :'); outtextxy(77,y1+y,'Begin ' ); outtextxy(77,z1+y,'Quit ' ); setcolor(lightred); outtextxy(95,460+y,'During the simulation press <Escape> to come back to this menu' ); setcolor(yellow); case styl of 1: begin outtextxy(135,c1+y,'Match style algorithm'); abc(11,11,11,11,11,11,11,11,4,11,11,4, 4,4,11,11,4,4,11,11,11,11,11,11); end; 2: begin outtextxy(135,c1+y,'Classic style algorithm'); abc(11,11,11,11,11,11,11,11,11,11,11,4, 4,4,11,11,11,11,4,11,11,11,11,11); end; 3: begin outtextxy(135,c1+y,'Simple Mutation style algorithm'); abc(11,11,11,11,11,11,4,11,4,4,4,11, 11,11,11,11,4,4,11,11,11,11,11,11); end; 4: begin outtextxy(135,c1+y,'Rank style Mutation algorithm'); abc(11,11,11,11,11,11,4,11,4,4,4,11, 11,11,11,11,4,4,11,11,11,11,11,11); end; 5: begin outtextxy(135,c1+y,'Testing algorithm'); abc(4,11,11,11,11,11,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,11,11,11); end;

73

6: begin outtextxy(135,c1+y,'Player against computer, player starts'); abc(4,11,11,11,11,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,11,11,11); end; 7: begin outtextxy(135,c1+y,'Player against computer, computer starts'); abc(4,11,11,11,11,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,11,11,11); end; 8: begin outtextxy(135,c1+y,'Two players'); abc(4,11,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,11,4,4,4,4,4,4); end; end; setcolor(yellow); case recombination of 1:outtextxy(200,h1+y,'only uniform crossover'); 2:outtextxy(200,h1+y,'uniform+one point crossover'); 3:outtextxy(200,h1+y,'average+one point crossover'); 4:outtextxy(200,h1+y,'only one point crossover'); 5:outtextxy(200,h1+y,'n-point crossover'); end; case random_var of 1:outtextxy(145,i1+y,'no random string'); 2:outtextxy(145,i1+y,'one completely random string'); 3:outtextxy(145,i1+y,'two completely random string'); 4:outtextxy(145,i1+y,'one semi-random string'); 5:outtextxy(145,i1+y,'two semi-random string'); 6:outtextxy(145,i1+y,'best string from previous generation'); 7:outtextxy(145,i1+y,'average string from previous generation'); 8:outtextxy(145,i1+y,'worst string from previous generation'); end; case save_string of 1:outtextxy(277,m1+y,'Yes'); 2:outtextxy(277,m1+y,'No'); end; case sort_selection of 1:outtextxy(400,p1+y,'Yes'); 2:outtextxy(400,p1+y,'No'); end; case sort_generation of 1:outtextxy(372,q1+y,'Yes'); 2:outtextxy(372,q1+y,'No'); end; case rewrite_history of 1:outtextxy(244,t1+y,'Yes'); 2:outtextxy(244,t1+y,'No'); end; case super_champion of 1:outtextxy(240,w1+y,'Yes'); 2:outtextxy(240,w1+y,'No'); end; case classic_fitness of 1:outtextxy(250,u1+y,'Use 2:outtextxy(250,u1+y,'Use 3:outtextxy(250,u1+y,'Use 4:outtextxy(250,u1+y,'Use end; best string'); average string'); worst string'); random string');

for ii := 1 to parameters do begin oo := genetic_save[ii]; str(oo div 100,g_string); outtextxy(150+ii*30+7,r1,g_string); oo := oo - (oo div 100)*100;

74

str(oo div 10,g_string); outtextxy(150+ii*30+14,r1,g_string); oo := oo - (oo div 10)*10; str(oo,g_string); outtextxy(150+ii*30+21,r1,g_string); genetic[population_size+1][ii] := genetic_save[ii]; end; a := 1; y2 := 0; u := 0; setcolor(yellow); str(crossover_prob,nomb_); outtextxy(260,a1+y,nomb_); str(mutation,nomb_); outtextxy(265,b1+y,nomb_); str(hb,nomb_); outtextxy(192,d1+y,nomb_); str(population_size,nomb_); outtextxy(210,e1+y,nomb_); str(parameters,nomb_); outtextxy(175,f1+y,nomb_); str(generations,nomb_); outtextxy(185,g1+y,nomb_); str(mutation_algo,nomb_); outtextxy(340,j1+y,nomb_); str(mutation_rate,nomb_); outtextxy(267,k1+y,nomb_); str(random_value,nomb_); outtextxy(232,l1+y,nomb_); str(average_recombination,nomb_); outtextxy(310,n1+y,nomb_); str(mutation_start,nomb_); outtextxy(347,o1+y,nomb_); str(num_games,nomb_); outtextxy(294,s1+y,nomb_); str(standard_stop,nomb_); outtextxy(338,v1+y,nomb_); str(ncrossover,nomb_); outtextxy(230,x1+y,nomb_); gg := 0; ff := 0; hh := 0; repeat if (keypressed) then begin my := 0; mx := 0; ch := readkey; if (ch = 'i') then begin setcolor(back); str(crossover_prob, nomb_); outtextxy(260,a1+y,nomb_); crossover_prob := crossover_prob + 1; if crossover_prob = 101 then crossover_prob := 0; setcolor(yellow); str(crossover_prob, nomb_); outtextxy(260,a1+y,nomb_); end; if (ch = 'j') then begin setcolor(back); str(mutation, nomb_); outtextxy(265,b1+y,nomb_); mutation := mutation + 10; if mutation = 1010 then mutation := 0; setcolor(yellow); str(mutation, nomb_); outtextxy(265,b1+y,nomb_); end; if (ch = 'b') then begin delay(100);

75

styl := styl + 1; if styl = 9 then styl := 1; case styl of 1: begin tz := 1; setcolor(back); outtextxy(135,c1+y,'Two players'); setcolor(yellow); outtextxy(135,c1+y,'Match style algorithm'); abc(11,11,11,11,11,11,11,11,4,11,11,4, 4,4,11,11,4,4,11,11,11,11,11,11); end; 2: begin setcolor(back); outtextxy(135,c1+y,'Match style algorithm'); setcolor(yellow); outtextxy(135,c1+y,'Classic style algorithm'); abc(11,11,11,11,11,11,11,11,11,11,11,4, 4,4,11,11,11,11,4,11,11,11,11,11); end; 3: begin setcolor(back); outtextxy(135,c1+y,'Classic style algorithm'); setcolor(yellow); outtextxy(135,c1+y,'Simple Mutation style algorithm'); abc(11,11,11,11,11,11,4,11,4,4,4,11, 11,11,11,11,4,4,11,11,11,11,11,11); end; 4: begin setcolor(back); outtextxy(135,c1+y,'Simple Mutation style algorithm'); setcolor(yellow); outtextxy(135,c1+y,'Rank style Mutation algorithm'); abc(11,11,11,11,11,11,4,11,4,4,4,11, 11,11,11,11,4,4,11,11,11,11,11,11); end; 5: begin setcolor(back); outtextxy(135,c1+y,'Rank style Mutation algorithm'); setcolor(yellow); outtextxy(135,c1+y,'Testing algorithm'); abc(4,11,11,11,11,11,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,11,11,11); end; 6: begin ua := 1; setcolor(back); outtextxy(135,c1+y,'Testing algorithm'); setcolor(yellow); outtextxy(135,c1+y,'Player against computer, player starts'); abc(4,11,11,11,11,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,11,11,11); end; 7: begin ua := 2; setcolor(back); outtextxy(135,c1+y,'Player against computer, player starts'); setcolor(yellow); outtextxy(135,c1+y,'Player against computer, computer starts'); abc(4,11,11,11,11,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,11,11,11); end; 8: begin tz := 2; setcolor(back); outtextxy(135,c1+y,'Player against computer, computer starts'); setcolor(yellow); outtextxy(135,c1+y,'Two players'); abc(4,11,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4); end; end; end; if (ch = 'c') or ((my < 198) and (my > 164) and (mx < 300) and (mx > 60)) then begin delay(100);

76

setcolor(back); str(hb, nomb_); outtextxy(192,d1+y,nomb_); setcolor(yellow); hb := hb + 1; if hb = 16 then begin setcolor(yellow); hb := 1; end; str(hb, nomb_); outtextxy(192,d1+y,nomb_); end; if (ch = 'd') then begin setcolor(back); str(population_size, nomb_); outtextxy(210,e1+y,nomb_); population_size := population_size + 2; if population_size = 34 then population_size := 8; setcolor(yellow); str(population_size, nomb_); outtextxy(210,e1+y,nomb_); end; if (ch = 'e') then begin setcolor(back); str(parameters, nomb_); outtextxy(175,f1+y,nomb_); parameters := parameters + 1; if parameters = 11 then parameters := 1; setcolor(yellow); str(parameters, nomb_); outtextxy(175,f1+y,nomb_); end; if (ch = 't') then begin setcolor(back); str(generations, nomb_); outtextxy(185,g1+y,nomb_); generations := generations + 1; setcolor(yellow); str(generations, nomb_); outtextxy(185,g1+y,nomb_); end; if (ch = 'g') then begin delay(100); recombination := recombination + 1; if recombination = 6 then recombination := 1; case recombination of 1: begin setcolor(back); outtextxy(200,h1+y,'n-point crossover'); setcolor(yellow); outtextxy(200,h1+y,'only uniform crossover'); end; 2: begin setcolor(back); outtextxy(200,h1+y,'only uniform crossover'); setcolor(yellow); outtextxy(200,h1+y,'uniform+one point crossover'); end; 3: begin setcolor(back); outtextxy(200,h1+y,'uniform+one point crossover'); setcolor(yellow); outtextxy(200,h1+y,'average+one point crossover'); end; 4: begin setcolor(back); outtextxy(200,h1+y,'average+one point crossover');

77

setcolor(yellow); outtextxy(200,h1+y,'only one point crossover'); end; 5: begin setcolor(back); outtextxy(200,h1+y,'only one point crossover'); setcolor(yellow); outtextxy(200,h1+y,'n-point crossover'); end; end; end; if (ch = 'o') then begin delay(100); random_var := random_var + 1; if random_var = 9 then random_var := 1; case random_var of 1: begin setcolor(back); outtextxy(145,i1+y,'worst string from previous generation'); setcolor(yellow); outtextxy(145,i1+y,'no random string'); end; 2: begin setcolor(back); outtextxy(145,i1+y,'no random string'); setcolor(yellow); outtextxy(145,i1+y,'one completely random string'); end; 3: begin setcolor(back); outtextxy(145,i1+y,'one completely random string'); setcolor(yellow); outtextxy(145,i1+y,'two completely random string'); end; 4: begin setcolor(back); outtextxy(145,i1+y,'two completely random string'); setcolor(yellow); outtextxy(145,i1+y,'one semi-random string'); end; 5: begin setcolor(back); outtextxy(145,i1+y,'one semi-random string'); setcolor(yellow); outtextxy(145,i1+y,'two semi-random string'); end; 6: begin setcolor(back); outtextxy(145,i1+y,'two semi-random string'); setcolor(yellow); outtextxy(145,i1+y,'best string from previous generation'); end; 7: begin setcolor(back); outtextxy(145,i1+y,'best string from previous generation'); setcolor(yellow); outtextxy(145,i1+y,'average string from previous generation'); end; 8: begin setcolor(back); outtextxy(145,i1+y,'average string from previous generation'); setcolor(yellow); outtextxy(145,i1+y,'worst string from previous generation'); end; end; end; if (ch = 'l') then begin setcolor(back); str(mutation_algo, nomb_); outtextxy(340,j1+y,nomb_); mutation_algo := mutation_algo + 1; if mutation_algo = 41 then mutation_algo := 2; setcolor(yellow);

78

str(mutation_algo, nomb_); outtextxy(340,j1+y,nomb_); end; if (ch = 'm') then begin setcolor(back); str(mutation_rate, nomb_); outtextxy(267,k1+y,nomb_); mutation_rate := mutation_rate + 1; if mutation_rate = 11 then mutation_rate := 1; setcolor(yellow); str(mutation_rate, nomb_); outtextxy(267,k1+y,nomb_); end; if (ch = 'p') then begin setcolor(back); str(random_value, nomb_); outtextxy(232,l1+y,nomb_); random_value := random_value + 1; if random_value = 65 then random_value := 8; setcolor(yellow); str(random_value, nomb_); outtextxy(232,l1+y,nomb_); end; if (ch = 'v') then begin delay(100); save_string := save_string + 1; if save_string = 3 then save_string := 1; case save_string of 1: begin setcolor(back); outtextxy(277,m1+y,'No'); setcolor(yellow); outtextxy(277,m1+y,'Yes'); end; 2: begin setcolor(back); outtextxy(277,m1+y,'Yes'); setcolor(yellow); outtextxy(277,m1+y,'No'); end; end; end; if (ch = 'k') then begin setcolor(back); str(average_recombination, nomb_); outtextxy(310,n1+y,nomb_); average_recombination := average_recombination + 1; if average_recombination = 11 then average_recombination := 2; setcolor(yellow); str(average_recombination, nomb_); outtextxy(310,n1+y,nomb_); end; if (ch = 'n') then begin setcolor(back); str(mutation_start, nomb_); outtextxy(347,o1+y,nomb_); mutation_start := mutation_start + 1; if mutation_start = population_size then mutation_start := 1; setcolor(yellow); str(mutation_start, nomb_); outtextxy(347,o1+y,nomb_); end; if (ch = 'r') then begin delay(100); sort_selection := sort_selection + 1;

79

if sort_selection = 3 then sort_selection := 1; case sort_selection of 1: begin setcolor(back); outtextxy(400,p1+y,'No'); setcolor(yellow); outtextxy(400,p1+y,'Yes'); end; 2: begin setcolor(back); outtextxy(400,p1+y,'Yes'); setcolor(yellow); outtextxy(400,p1+y,'No'); end; end; end; if (ch = 's') then begin delay(100); sort_generation := sort_generation + 1; if sort_generation = 3 then sort_generation := 1; case sort_generation of 1: begin setcolor(back); outtextxy(372,q1+y,'No'); setcolor(yellow); outtextxy(372,q1+y,'Yes'); end; 2: begin setcolor(back); outtextxy(372,q1+y,'Yes'); setcolor(yellow); outtextxy(372,q1+y,'No'); end; end; end; if (ch = 'x') then begin delay(100); for ii := 1 to parameters do begin setcolor(back); str(genetic_save[ii] div 100,g_string); outtextxy(150+ii*30+7,r1,g_string); genetic_save[ii] := genetic_save[ii] - (genetic_save[ii] div 100)*100; str(genetic_save[ii] div 10,g_string); outtextxy(150+ii*30+14,r1,g_string); genetic_save[ii] := genetic_save[ii] - (genetic_save[ii] div 10)*10; str(genetic_save[ii],g_string); outtextxy(150+ii*30+21,r1,g_string); setcolor(yellow); genetic[population_size+1][ii] := 0; end; for ii := 1 to parameters do begin for oo := 1 to 3 do begin ch2 := readkey; val(ch2,ll,ll); if oo = 1 then genetic[population_size+1][ii] := genetic[population_size+1][ii] + 100*ll else if oo = 2 then genetic[population_size+1][ii] := genetic[population_size+1][ii] + 10*ll else genetic[population_size+1][ii] := genetic[population_size+1][ii] + ll; genetic_save[ii] := genetic[population_size+1][ii]; str(ll,g_string); outtextxy(150+ii*30+oo*7,r1,g_string); end; end; end; if (ch = 'f') then

80

begin setcolor(back); str(num_games, nomb_); outtextxy(294,s1+y,nomb_); num_games := num_games + 1; if num_games = 5 then num_games := 1; setcolor(yellow); str(num_games, nomb_); outtextxy(294,s1+y,nomb_); end; if (ch = 'w') then begin delay(100); rewrite_history := rewrite_history + 1; if rewrite_history = 3 then rewrite_history := 1; case rewrite_history of 1: begin setcolor(back); outtextxy(244,t1+y,'No'); setcolor(yellow); outtextxy(244,t1+y,'Yes'); end; 2: begin setcolor(back); outtextxy(244,t1+y,'Yes'); setcolor(yellow); outtextxy(244,t1+y,'No'); end; end; end; if (ch = 'q') then begin delay(100); setcolor(white); classic_fitness := classic_fitness + 1; if classic_fitness = 5 then classic_fitness := 1; case classic_fitness of 1: begin setcolor(back); outtextxy(250,u1+y,'Use random string'); setcolor(yellow); outtextxy(250,u1+y,'Use best string'); end; 2: begin setcolor(back); outtextxy(250,u1+y,'Use best string'); setcolor(yellow); outtextxy(250,u1+y,'Use average string'); end; 3: begin setcolor(back); outtextxy(250,u1+y,'Use average string'); setcolor(yellow); outtextxy(250,u1+y,'Use worst string'); end; 4: begin setcolor(back); outtextxy(250,u1+y,'Use worst string'); setcolor(yellow); outtextxy(250,u1+y,'Use random string'); end; end; end; if (ch = 'u') then begin setcolor(back); str(standard_stop, nomb_); outtextxy(338,v1+y,nomb_); standard_stop := standard_stop + 1; if standard_stop = 20 then standard_stop := -1; setcolor(yellow); str(standard_stop, nomb_); outtextxy(338,v1+y,nomb_); end;

81

if (ch = 'a') then begin delay(100); setcolor(white); super_champion := super_champion + 1; if super_champion = 3 then super_champion := 1; case super_champion of 1: begin setcolor(back); outtextxy(240,w1+y,'No'); setcolor(yellow); outtextxy(240,w1+y,'Yes'); end; 2: begin setcolor(back); outtextxy(240,w1+y,'Yes'); setcolor(yellow); outtextxy(240,w1+y,'No'); end; end; end; if (ch = 'h') then begin setcolor(back); str(ncrossover, nomb_); outtextxy(230,x1+y,nomb_); ncrossover := ncrossover + 1; if ncrossover = 50 then ncrossover := 1; setcolor(yellow); str(ncrossover, nomb_); outtextxy(230,x1+y,nomb_); end; if (ch = 'z') or ((my < 278) and (my > 254) and (mx < 300) and (mx > 60)) then begin delay(100); if save_string = 1 then begin rewrite(param_file); for temp1 := 1 to parameters do begin write(param_file,genetic_save[temp1],' '); end; end else reset(param_file); close(moves_file); close(param_file); reset(file_genetic); close(file_genetic); closegraph; clrscr; textmode(bw80); highvideo; textcolor(white); textbackground(blue); writeln(' '); writeln(' Hello, '); writeln(' '); writeln(' '); writeln(' If you want to make suggestions or comments on this program, you can contact '); writeln(' me by email at: '); writeln(' '); writeln(' '); writeln(' '); writeln(' '); As I pass quite some time on internet, I have made some personal web email: ifaybish@ulb.ac.be This is a Freeware program and can be distributed freely with no charge.

82

writeln(' pages about subjects which interest me: '); writeln(' '); writeln(' - About spiritual practices and religions: '); writeln(' '); writeln(' '); writeln(' - About fractals images I have created using wonderful freeware programs: '); writeln(' '); writeln(' '); writeln(' - About chess endgames and studies which I found fascinating: '); writeln(' '); writeln(' '); writeln(' '); writeln(' '); normvideo; halt; end; if tu = 2 then u := 0; if ((my < 248) and (my > 224) and (mx < 300) and (mx > 60)) then ch := 'b'; end; until (ch = 'y'); delay(80); if rewrite_history = 2 then append(file_genetic) else rewrite(file_genetic); writeln(file_genetic,'*********** Header of simulation ***********'); writeln(file_genetic,''); writeln(file_genetic,'Text file created from the program: Genetic Othello V1.01'); writeln(file_genetic,''); writeln(file_genetic,'Crossover probability: ',crossover_prob); writeln(file_genetic,'Mutation rate: ',mutation); writeln(file_genetic,'Style: ',styl); writeln(file_genetic,'Force: ',hb-2); writeln(file_genetic,'Population_size: ',population_size); writeln(file_genetic,'Parameters: ',parameters); writeln(file_genetic,'Generations: ',generations); writeln(file_genetic,'Recombinations: ',recombination); writeln(file_genetic,'Random: ',random_var); writeln(file_genetic,'Mutation algorithm start: ',mutation_algo); writeln(file_genetic,'Mutation algorithm rate: ',mutation_rate); writeln(file_genetic,'Semi-random value: ',random_value); writeln(file_genetic,'Use Hash table: ',hash_table); writeln(file_genetic,'Average recombination factor: ',average_recombination); writeln(file_genetic,'Mutation starting string: ',mutation_start); writeln(file_genetic,'Special sorting in the selection function: ',sort_selection); writeln(file_genetic,'Use fitness sorting every generation: ',sort_generation); writeln(file_genetic,'Classic style fitness: ',classic_fitness); write(file_genetic,'Test string: '); for oo := 1 to parameters do begin write(file_genetic,genetic_save[oo]:3,' '); end; writeln(file_genetic,''); writeln(file_genetic,''); writeln(file_genetic,'*********** Start of simulation ***********'); writeln(file_genetic,''); if styl = 5 then generations := 0; if generations = -1 then generations := -2; end; { *********************************************************** * The following function calculates the power of a number * * (of double type). * *********************************************************** } function expdouble(a,b:integer) : double; var See you soon! http://members.xoom.com/ifaybish/chess.html http://members.xoom.com/ifaybish/fractals.html http://members.xoom.com/ifaybish

83

temp1: integer; temp2: double; begin temp1 := 1; temp2 := a; while temp1 < b do begin temp2 := temp2 * a; inc(temp1); end; if b = 0 then expdouble := 1 else expdouble := temp2; end; { *********************************************************** * The following function calculates the power of a number * * (of integer type). * *********************************************************** } function exp(a,b:integer) : integer; var temp1,temp2: integer; begin temp1 := 1; temp2 := a; while temp1 < b do begin temp2 := temp2 * a; inc(temp1); end; if b = 0 then exp := 1 else exp := temp2; end; { *********************************************************** * The following function returns the evaluation of the * * position represented in the <board> vector. * *********************************************************** } function eval_position(board:vector1):integer; var i, o, b, p1, r7,r8,r3,r4,p2, c,t1,t2,total,tot1,tot2,local,local1,y3,y4,y5,y6, local2,local3,local4,r5,r6,border,lr1,ll1,ur1,ul1,lr2,ll2,ur2,ul2, olr1,oll1,our1,oul1,olr2,oll2,our2,oul2,lr3,ll3,ur3,ul3,lr4,ll4,ur4,ul4, temp1,oo,ii,ooo,iii,r9,r10,r11,r14,r15,mean_1_o,mean_1_i, mean_1_o_2,mean_1_i_2,mean_2_o,mean_2_i,mean_2_o_2,mean_2_i_2,r16,r17, direction, save_i, save_o, empty_1, empty_2, modify_1, modify_2, emp_1, emp_2, r18, r19, r20, r21, border1, border2, aa: integer; secondp1,secondp2,sum1i,sum1o,sum2i,sum2o,r12,r13,sumtot_1,sumtot_2: real; stop, ok: boolean; ta: vector1; label 3; begin tot1 := 0; tot2 := 0; b := 0; c := 0; total := 0; y3 := 0; y4 := 0; y5 := 0; y6 := 0; r1 := 0; r2 := 0; r3 := 0; r4 := 0; t1 := 0; t2 := 0; p1 := 0; p2 := 0; r5 := 0; r6 := 0; r7 := 0; r8 := 0; r9 := 0; r10 := 0; r11 := 0; r12 := 0;

84

r13 := 0; r14 := 0; r15 := 0; r16 := 0; r17 := 0; r20 := 0; r21 := 0; empty_1 := 0; empty_2 := 0; emp_1 := 0; emp_2 := 0; modify_1 := 0; modify_2 := 0; border1 := 0; border2 := 0; if board[1][1] = 1 then begin i := 2; while (board[i][1] = 1) and (i<5) do begin total := total - 50; inc(i); end; i := 2; while (board[1][i] = 1) and (i<5) do begin total := total - 50; inc(i); end; end; if board[8][1] = 1 then begin i := 7; while (board[i][1] = 1) and (i>4) do begin total := total - 50; dec(i); end; i := 2; while (board[8][i] = 1) and (i<5) do begin total := total - 50; inc(i); end; end; if board[8][8] = 1 then begin i := 7; while (board[i][8] = 1) and (i>4) do begin total := total - 50; dec(i); end; i := 7; while (board[8][i] = 1) and (i>4) do begin total := total - 50; dec(i); end; end; if board[1][8] = 1 then begin i := 2; while (board[i][1] = 1) and (i<5) do begin total := total - 50; inc(i); end; i := 7; while (board[1][i] = 1) and (i>4) do begin total := total - 50; dec(i);

85

end; end; if board[1][1] = 2 then begin i := 2; while (board[i][1] = 2) and (i<5) do begin total := total + 50; inc(i); end; i := 2; while (board[1][i] = 2) and (i<5) do begin total := total + 50; inc(i); end; end; if board[8][1] = 2 then begin i := 7; while (board[i][1] = 2) and (i>4) do begin total := total + 50; dec(i); end; i := 2; while (board[8][i] = 2) and (i<5) do begin total := total + 50; inc(i); end; end; if board[8][8] = 2 then begin i := 7; while (board[i][8] = 2) and (i>4) do begin total := total + 50; dec(i); end; i := 7; while (board[8][i] = 2) and (i>4) do begin total := total + 50; dec(i); end; end; if board[1][8] = 2 then begin i := 2; while (board[i][1] = 2) and (i<5) do begin total := total + 50; inc(i); end; i := 7; while (board[1][i] = 2) and (i>4) do begin total := total + 50; dec(i); end; end; for i := 1 to 8 do for o := 1 to 8 do begin if board[i][o] = 1 then tot2 := tot2 + 1 else if board[i][o] = 2 then tot1 := tot1 + 1; if move < 50 then begin if (board[i][o] = 0) then begin direction := 1;

86

repeat save_i := i; save_o := o; case direction of 1: if save_i < 8 then inc(save_i); 2: if save_o < 8 then inc(save_o); 3: if save_i > 1 then dec(save_i); 4: if save_o > 1 then dec(save_o); 5: if (save_i < 8) and (save_o < 8) 6: if (save_i < 8) and (save_o > 1) 7: if (save_i > 1) and (save_o < 8) 8: if (save_i > 1) and (save_o > 1) end;

then then then then

begin begin begin begin

inc(save_i); inc(save_i); dec(save_i); dec(save_i);

inc(save_o); dec(save_o); inc(save_o); dec(save_o);

end; end; end; end;

if ((save_i <> i) or (save_o <> o)) and (board[save_i][save_o]=0) then begin direction := 10; end else inc(direction); until direction > 8; if direction = 9 then begin if (choice(board,i,o,2,0,no_u) = 1) and (choice(board,i,o,1,0,no_u) = 0) then total := total + genetic[gennum][7]*2 else if (choice(board,i,o,1,0,no_u) = 1) and (choice(board,i,o,2,0,no_u) = 0) then total := total - genetic[gennum][7]*2; end; if if if if if if if if if if if if if if if if (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) and and and and and and and and and and and and and and and and (board[i-1][o] = 2) then inc(y5) else (board[i][o-1] = 2) then inc(y5) else (board[i][o+1] = 2) then inc(y5) else (board[i+1][o] = 2) then inc(y5) else (o>1) and (board[i-1][o-1] = 2) then inc(y5) else (o<8) and (board[i-1][o+1] = 2) then inc(y5) else (o<8) and (board[i+1][o+1] = 2) then inc(y5) else (o>1) and (board[i+1][o-1] = 2) then inc(y5); (board[i-1][o] = 1) then inc(y6) else (board[i][o-1] = 1) then inc(y6) else (board[i][o+1] = 1) then inc(y6) else (board[i+1][o] = 1) then inc(y6) else (o>1) and (board[i-1][o-1] = 1) then inc(y6) else (o<8) and (board[i-1][o+1] = 1) then inc(y6) else (o<8) and (board[i+1][o+1] = 1) then inc(y6) else (o>1) and (board[i+1][o-1] = 1) then inc(y6);

end else if (board[i][o] = 1) then begin if (i>1) and (i<8) and (o>1) and (o<8) then begin if (i>1) and (board[i-1][o] = 0) then inc(r14); if (o>1) and (board[i][o-1] = 0) then inc(r14); if (o<8) and (board[i][o+1] = 0) then inc(r14); if (i<8) and (board[i+1][o] = 0) then inc(r14); if (i>1) and (o>1) and (board[i-1][o-1] = 0) then if (i>1) and (o<8) and (board[i-1][o+1] = 0) then if (i<8) and (o<8) and (board[i+1][o+1] = 0) then if (i<8) and (o>1) and (board[i+1][o-1] = 0) then end else inc(border2); if if if if if if if if if if if if if if (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) and and and and and and and and and and and and and and (board[i-1][o] = 1) then inc(r4); (board[i][o-1] = 1) then inc(r4); (board[i][o+1] = 1) then inc(r4); (board[i+1][o] = 1) then inc(r4); (o>1) and (board[i-1][o-1] = 1) then (o<8) and (board[i-1][o+1] = 1) then (o<8) and (board[i+1][o+1] = 1) then (o>1) and (board[i+1][o-1] = 1) then

inc(r14); inc(r14); inc(r14); inc(r14);

inc(r4); inc(r4); inc(r4); inc(r4);

(board[i-1][o] = 1) then inc(r6) else (board[i][o-1] = 1) then inc(r6) else (board[i][o+1] = 1) then inc(r6) else (board[i+1][o] = 1) then inc(r6) else (o>1) and (board[i-1][o-1] = 1) then inc(r6) else (o<8) and (board[i-1][o+1] = 1) then inc(r6) else

87

if (i<8) and (o<8) and (board[i+1][o+1] = 1) then inc(r6) else if (i<8) and (o>1) and (board[i+1][o-1] = 1) then inc(r6); if if if if if if if if if if if if if if if if (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) and and and and and and and and and and and and and and and and (board[i-1][o] = 0) then inc(r21); (board[i][o-1] = 0) then inc(r21); (board[i][o+1] = 0) then inc(r21); (board[i+1][o] = 0) then inc(r21); (o>1) and (board[i-1][o-1] = 0) then (o<8) and (board[i-1][o+1] = 0) then (o<8) and (board[i+1][o+1] = 0) then (o>1) and (board[i+1][o-1] = 0) then

inc(r21); inc(r21); inc(r21); inc(r21);

(board[i-1][o] = 0) then inc(r10) else (board[i][o-1] = 0) then inc(r10) else (board[i][o+1] = 0) then inc(r10) else (board[i+1][o] = 0) then inc(r10) else (o>1) and (board[i-1][o-1] = 0) then inc(r10) else (o<8) and (board[i-1][o+1] = 0) then inc(r10) else (o<8) and (board[i+1][o+1] = 0) then inc(r10) else (o>1) and (board[i+1][o-1] = 0) then inc(r10);

if move < 40 then temp1 := 750 else temp1 := 375; if ((i ((i ((i ((i = = = = 2) 7) 7) 2) and and and and (o=2) (o=2) (o=7) (o=7) and and and and (board[1][1] (board[8][1] (board[8][8] (board[1][8] = = = = 0)) 0)) 0)) 0)) or or or then total := total + temp1;

if ((i = 1) and (o=1)) or ((i = 1) and (o=8)) or ((i = 8) and (o=8)) or ((i = 8) and (o=1)) then total := total - temp1*2; end else if (board[i][o] = 2) then begin if (i>1) and (i<8) and (o>1) and (o<8) then begin if (i>1) and (board[i-1][o] = 0) then inc(r15); if (o>1) and (board[i][o-1] = 0) then inc(r15); if (o<8) and (board[i][o+1] = 0) then inc(r15); if (i<8) and (board[i+1][o] = 0) then inc(r15); if (i>1) and (o>1) and (board[i-1][o-1] = 0) then if (i>1) and (o<8) and (board[i-1][o+1] = 0) then if (i<8) and (o<8) and (board[i+1][o+1] = 0) then if (i<8) and (o>1) and (board[i+1][o-1] = 0) then end else inc(border1); if if if if if if if if if if if if if if if if if if if if if if if if (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) (i>1) (o>1) (o<8) (i<8) (i>1) (i>1) (i<8) (i<8) and and and and and and and and and and and and and and and and and and and and and and and and (board[i-1][o] = 2) then inc(r5); (board[i][o-1] = 2) then inc(r5); (board[i][o+1] = 2) then inc(r5); (board[i+1][o] = 2) then inc(r5); (o>1) and (board[i-1][o-1] = 2) then (o<8) and (board[i-1][o+1] = 2) then (o<8) and (board[i+1][o+1] = 2) then (o>1) and (board[i+1][o-1] = 2) then

inc(r15); inc(r15); inc(r15); inc(r15);

inc(r5); inc(r5); inc(r5); inc(r5);

(board[i-1][o] = 2) then inc(r7) else (board[i][o-1] = 2) then inc(r7) else (board[i][o+1] = 2) then inc(r7) else (board[i+1][o] = 2) then inc(r7) else (o>1) and (board[i-1][o-1] = 2) then inc(r7) else (o<8) and (board[i-1][o+1] = 2) then inc(r7) else (o<8) and (board[i+1][o+1] = 2) then inc(r7) else (o>1) and (board[i+1][o-1] = 2) then inc(r7); (board[i-1][o] = 0) then inc(r20); (board[i][o-1] = 0) then inc(r20); (board[i][o+1] = 0) then inc(r20); (board[i+1][o] = 0) then inc(r20); (o>1) and (board[i-1][o-1] = 0) then (o<8) and (board[i-1][o+1] = 0) then (o<8) and (board[i+1][o+1] = 0) then (o>1) and (board[i+1][o-1] = 0) then

inc(r20); inc(r20); inc(r20); inc(r20);

if (i>1) and (board[i-1][o] = 0) then inc(r11) else if (o>1) and (board[i][o-1] = 0) then inc(r11) else

88

if if if if if if

(o<8) (i<8) (i>1) (i>1) (i<8) (i<8)

and and and and and and

(board[i][o+1] = 0) then inc(r11) else (board[i+1][o] = 0) then inc(r11) else (o>1) and (board[i-1][o-1] = 0) then inc(r11) else (o<8) and (board[i-1][o+1] = 0) then inc(r11) else (o<8) and (board[i+1][o+1] = 0) then inc(r11) else (o>1) and (board[i+1][o-1] = 0) then inc(r11);

if move < 40 then temp1 := 750 else temp1 := 375; if ((i ((i ((i ((i = = = = 2) 7) 7) 2) and and and and (o=2) (o=2) (o=7) (o=7) and and and and (board[1][1] (board[8][1] (board[8][8] (board[1][8] = = = = 0)) 0)) 0)) 0)) or or or then total := total - temp1;

if ((i = 1) and (o=1)) or ((i = 1) and (o=8)) or ((i = 8) and (o=8)) or ((i = 8) and (o=1)) then total := total + temp1*2; end; end; end; if move < 30 then begin if (board[2][1]=1) if (board[1][2]=1) if (board[2][8]=1) if (board[1][7]=1) if (board[7][8]=1) if (board[8][7]=1) if (board[8][2]=1) if (board[7][1]=1) if if if if if if if if end; (board[2][1]=2) (board[1][2]=2) (board[2][8]=2) (board[1][7]=2) (board[7][8]=2) (board[8][7]=2) (board[8][2]=2) (board[7][1]=2)

and and and and and and and and and and and and and and and and

(board[1][1]=0) (board[1][1]=0) (board[1][8]=0) (board[1][8]=0) (board[8][8]=0) (board[8][8]=0) (board[8][1]=0) (board[8][1]=0) (board[1][1]=0) (board[1][1]=0) (board[1][8]=0) (board[1][8]=0) (board[8][8]=0) (board[8][8]=0) (board[8][1]=0) (board[8][1]=0)

then then then then then then then then then then then then then then then then

total total total total total total total total total total total total total total total total

:= := := := := := := := := := := := := := := :=

total total total total total total total total total total total total total total total total

+ + + + + + + + -

50; 50; 50; 50; 50; 50; 50; 50; 50; 50; 50; 50; 50; 50; 50; 50;

if tot1 = 0 then total := total - 10000; stop := false; if move > 49 then begin if tot1 > tot2 then total := + 100*tot1 else if tot2 > tot1 then total := - 100*tot2; end; if (abs(y5)+abs(y6) = 0) then begin y6 := 1; y5 if (abs(r14)+abs(r15) = 0) then begin r14 := 1; if (abs(r8)+abs(r9) = 0) then begin r8 := 1; r9 if (abs(r10)+abs(r11) = 0) then begin r10 := 1; if (abs(r18)+abs(r19) = 0) then begin r18 := 1; if (abs(r1)+abs(r2) = 0) then begin r1 := 1; r2

:= 1; end; r15 := 1; end; := 1; end; r11 := 1; end; r19 := 1; end; := 1; end;

if ((style = 1) and (move < 50)) then begin total := total + 2*(round(( round(((y6-y5) / (abs(y6)+abs(y5))) * (genetic[gennum][1])) + round(((r14-r15) / (abs(r14)+abs(r15))) * (genetic[gennum][2])) + round(((r10-r11) / (abs(r10)+abs(r11))) * (genetic[gennum][3])) + round(((r21-r20) / (abs(r20)+abs(r21))) * (genetic[gennum][4])) + round(((r4-r5) / (abs(r4)+abs(r5))) * (genetic[gennum][5])) + round(((r6-r7) / (abs(r6)+abs(r7))) * (genetic[gennum][6])) ))) end; eval_position := total; end; { *********************************************************** * The following function returns a simplified evaluation * * of the position represented in the <board> vector. * *********************************************************** } function eval_position2(board:vector1):integer; var

89

r20,r21,tot1,tot2,b,c,total,i,o: integer; begin tot1 := 0; tot2 := 0; b := 0; c := 0; total := 0; r20 := 0; r21 := 0; for i := 1 to 8 do for o := 1 to 8 do begin if (board[i][o] = 1) then begin if (i>1) and (board[i-1][o] = 0) then inc(r21); if (o>1) and (board[i][o-1] = 0) then inc(r21); if (o<8) and (board[i][o+1] = 0) then inc(r21); if (i<8) and (board[i+1][o] = 0) then inc(r21); if (i>1) and (o>1) and (board[i-1][o-1] = 0) then if (i>1) and (o<8) and (board[i-1][o+1] = 0) then if (i<8) and (o<8) and (board[i+1][o+1] = 0) then if (i<8) and (o>1) and (board[i+1][o-1] = 0) then if move < 40 then temp1 := 750 else temp1 := 375; if ((i ((i ((i ((i = = = = 2) 7) 7) 2) and and and and (o=2) (o=2) (o=7) (o=7) and and and and (board[1][1] (board[8][1] (board[8][8] (board[1][8] = = = = 0)) 0)) 0)) 0)) or or or then total := total + temp1;

inc(r21); inc(r21); inc(r21); inc(r21);

if ((i = 1) and (o=1)) or ((i = 1) and (o=8)) or ((i = 8) and (o=8)) or ((i = 8) and (o=1)) then total := total - temp1*2; end else if (board[i][o] = 2) then begin if (i>1) and (board[i-1][o] = 0) then inc(r20); if (o>1) and (board[i][o-1] = 0) then inc(r20); if (o<8) and (board[i][o+1] = 0) then inc(r20); if (i<8) and (board[i+1][o] = 0) then inc(r20); if (i>1) and (o>1) and (board[i-1][o-1] = 0) then if (i>1) and (o<8) and (board[i-1][o+1] = 0) then if (i<8) and (o<8) and (board[i+1][o+1] = 0) then if (i<8) and (o>1) and (board[i+1][o-1] = 0) then if move < 40 then temp1 := 750 else temp1 := 375; if ((i ((i ((i ((i = = = = 2) 7) 7) 2) and and and and (o=2) (o=2) (o=7) (o=7) and and and and (board[1][1] (board[8][1] (board[8][8] (board[1][8] = = = = 0)) 0)) 0)) 0)) or or or then total := total - temp1;

inc(r20); inc(r20); inc(r20); inc(r20);

if ((i = 1) and (o=1)) or ((i = 1) and (o=8)) or ((i = 8) and (o=8)) or ((i = 8) and (o=1)) then total := total + temp1*2; end; end; if (abs(r20)+abs(r21) = 0) then begin r20 := 1; r21 := 1; end; total := total + round(((r21-r20) / (abs(r20)+abs(r21))) * 500); eval_position2 := total; end; { *********************************************************** * The following procedure is doing an ascending sort on a * * vector of numbers. The algorithm used is quicksort * * which is well known for its speed. * *********************************************************** }

90

procedure quicksort_ascending(var numb:vector6;var v:vector2;low,high:integer); var i, j, g, h: integer; begin i := low; j := high; g := numb[(low+high) div 2]; while i<j do begin while numb[i] < g do inc(i); while numb[j] > g do dec(j); if i <= j then begin h := v[i][1]; v[i][1] := v[j][1]; v[j][1] := h; h := v[i][2]; v[i][2] := v[j][2]; v[j][2] := h; h := numb[i]; numb[i] := numb[j]; numb[j] := h; inc(i); dec(j); end; end; if low < j then quicksort_ascending(numb, v, low, j); if i < high then quicksort_ascending(numb, v, i, high); end; { *********************************************************** * The following procedure is doing a descending sort on a * * vector of numbers. The algorithm used is quicksort. * *********************************************************** } procedure quicksort_descending(var numb:vector6;var v:vector2;low,high:integer); var i, j, g, h: integer; begin i := low; j := high; g := numb[(low+high) div 2]; while i<j do begin while numb[i] > g do inc(i); while numb[j] < g do dec(j); if i <= j then begin h := v[i][1]; v[i][1] := v[j][1]; v[j][1] := h; h := v[i][2]; v[i][2] := v[j][2]; v[j][2] := h; h := numb[i]; numb[i] := numb[j]; numb[j] := h; inc(i); dec(j); end; end; if low < j then quicksort_descending(numb, v, low, j); if i < high then quicksort_descending(numb, v, i, high); end; { *********************************************************** * The following procedure calculate the number of * * possibilities one has in a specific position. * *********************************************************** } procedure artificial(board:vector1;color: integer;var resultat:integer);

91

var v: vector2; ii,oo,a,answer: integer; begin a:=0; for ii:=1 to 8 do begin for oo:=1 to 8 do begin if (board[ii][oo] = 0) then begin answer:= 0; answer:= choice(board,ii,oo,color,0,no_u); if (answer = 1) then begin a:= a + 1; end; end; end; end; resultat := a; end; { *********************************************************** * The following function is an alpha-beta algorithm. The * * optimizations used are: incremental calculation in the * * endgame, use of hash table in the endgame, sorting the * * moves in each iteration using a simplified evaluation * * function+using quicksort for fast sorting. * *********************************************************** } function algol3(var board:vector1;prof, force, color:integer;var vv1,vv2:integer;alpha, beta, end_alpha:integer; var vect_coul:vector_color):integer; var tabsave: vector1; v: vector2; vv: vector3; ev_po: vector6; p1,p2,valeur_local,r3,r4,hhh,coup_note,tampon,note,g,y3,t4,y4,y5,y6,r1,r2,a,ii,oo,i,o ,temp1,temp2,temp3,temp4,temp5,enter,b,meilleur,valeur,value,best1,best2, total_alpha,total_alpha2: integer; vect_save: vector_color; alphat,total_alpha2b,total_alpha2c,ooo,bb: real; label 10,11; begin if (prof = force+1) then begin if stop = 1 then algol3 := eval_position(board) else algol3 := vect_coul[2]-vect_coul[1]; end else begin a := 1; for oo := 1 to numempty-1 do begin if (board[vw[oo][1],vw[oo][2]] = 0) then begin if (choice(board,vw[oo][1],vw[oo][2],color,0,no_u) = 1) then begin v[a][1]:=vw[oo][1]; v[a][2]:=vw[oo][2]; a:= a + 1; end; end; end; if ((stop = 2) and (prof < force-6)) or ((stop <> 2) and (prof < force)) then begin temp5 := a; if a > 2 then begin tabsave := board; for oo := 1 to a-1 do begin choice(board,v[oo][1],v[oo][2],color,1,no_u); ev_po[oo] := eval_position2(board);

92

board := tabsave; end; if color = 2 then begin quicksort_descending(ev_po,v,1,a-1); end else begin quicksort_ascending(ev_po,v,1,a-1); end; end; a := temp5; end; if a = 1 then begin if end_alpha = 2 then begin if stop = 1 then algol3 := eval_position(board) else algol3 := vect_coul[2]-vect_coul[1]; end_alpha := 0; end else begin algol3 := algol3(board,prof,force,3-color,best1,best2,alpha,beta,2,vect_coul) end; goto 10; end; g := 1; end_alpha := 0; while (g < a) do begin tabsave := board; vect_save := vect_coul; choice(board,v[g][1],v[g][2],color,1,vect_coul); if (stop = 2) then begin total_alpha := 1; total_alpha2b := 1; total_alpha2c := 1; total_alpha2 := 1; if (move > 59-endgame) and (move < 52) and (prof < 8) then begin for oo := 1 to 8 do begin for b := 1 to 8 do begin ooo := oo; bb := b; alphat := ((ooo-1)*8+bb); if color_global = 1 then begin if board[oo][b] = 0 then begin total_alpha2b := total_alpha2b+alphat+98; total_alpha2c := total_alpha2c+((oo-1)*8+b)*13+1; end else if board[oo][b] = 1 then begin total_alpha2b := total_alpha2b+alphat*37+1; total_alpha2c := total_alpha2c+oo*b*43+7; end else if board[oo][b] = 2 then begin total_alpha2b := total_alpha2b+(alphat)*27+2; total_alpha2c := total_alpha2c+((oo-1)*18+b)*35+1; end; end else begin if board[oo][b] = 0 then begin total_alpha2b := total_alpha2b+alphat+98; total_alpha2c := total_alpha2c+((oo-1)*8+b)*13+1; end else if board[oo][b] = 2 then begin total_alpha2b := total_alpha2b+alphat*37+1;

93

total_alpha2c := total_alpha2c+oo*b*43+7; end else if board[oo][b] = 1 then begin total_alpha2b := total_alpha2b+(alphat)*27+2; total_alpha2c := total_alpha2c+((oo-1)*18+b)*35+1; end; end; end; case prof of 1:total_alpha2c := (round(total_alpha2c) mod 50); 2:total_alpha2c := (round(total_alpha2c) mod 50)+50; 3:total_alpha2c := (round(total_alpha2c) mod 500) + 100; 4:total_alpha2c := (round(total_alpha2c) mod 5000) + 600; 5:total_alpha2c := (round(total_alpha2c) mod 7000) + 5600; 6:total_alpha2c := (round(total_alpha2c) mod 8000) + 12600; 7:total_alpha2c := (round(total_alpha2c) mod 11000) + 20600; end; total_alpha2b := (round(total_alpha2b) mod 64000)-32000+1; total_alpha := round(total_alpha2c); total_alpha2 := round(total_alpha2b); end; end; end; if (move < 60-endgame) or (prof > 7) or (move > 51) or (hash_alpha^[total_alpha] <> total_alpha2) or (hash_gamma^[total_alpha] > alpha) or (hash_delta^[total_alpha] < beta) or (stop <> 2) {or (g=a-1)} then begin value := algol3(board,prof+1,force,3-color,best1,best2,alpha,beta,1,vect_coul) ; hash_alpha^[total_alpha] := total_alpha2; hash_beta^[total_alpha] := value; hash_gamma^[total_alpha] := alpha; hash_delta^[total_alpha] := beta; end else begin value := hash_beta^[total_alpha]; inc(vvv1); if vvv1 = 32000 then vvv1 := 1; if vvv1 mod 20 = 0 then begin setcolor(white); circle(320,10,2); circle(320,10,1); end; if vvv1 mod 40 = 0 then begin setcolor(yellow); circle(320,10,2); circle(320,10,1); end; end; if (color=2) then begin if (value>=beta) then begin algol3 := value; if prof = 1 then begin vv1:=v[g][2]; vv2:=v[g][1]; maxx:= alpha; end; goto 10; end; if (value>alpha) then begin alpha:=value; if prof = 1 then begin vv1:=v[g][2]; vv2:=v[g][1]; maxx:= alpha; end; end; end

94

else begin if (value<=alpha) then begin if prof = 1 then begin vv1:=v[g][2]; vv2:=v[g][1]; maxx:= alpha; end; algol3 := value; goto 10; end; if(value<beta) then begin beta:=value; if prof = 1 then begin vv1:=v[g][2]; vv2:=v[g][1]; maxx:= beta; end; end; end; 11: board := tabsave; vect_coul := vect_save; inc(g); end; if (color = 2) then algol3 := alpha else algol3 := beta; end; 10: end; { *********************************************************** * The following procedure initialise the board to an * * empty board. * *********************************************************** } procedure init1(var board:vector1); var i, o: integer; begin for i := 1 to 8 do for o := 1 to 8 do board[i][o] := 0; end; { *********************************************************** * The following procedure draws the actual board on the * * screen. * *********************************************************** } procedure print2(board:vector1); var x,y: integer; begin grid; for x := 1 to 8 do begin for y := 1 to 8 do begin if board[x][y] = 1 then putimage(126+(40*y),46+(40*x),sprite2,copyput) else if board[x][y] = 2 then putimage(126+(40*y),46+(40*x),sprite1,copyput) else end; end; end; { *********************************************************** * The following procedure shows the actual score of the * * players. * *********************************************************** } procedure print_res(board:vector1;color:integer); var i, o, score1, score2: integer; text_, nombre, text2_: string; begin

95

score1 := 0; score2 := 0; for o := 1 to 8 do for i := 1 to 8 do if ua = 2 then begin if board[o][i] = 1 then score2 := score2 if board[o][i] = 2 then score1 := score1 end else begin if board[o][i] = 1 then score1 := score1 if board[o][i] = 2 then score2 := score2 end; setcolor(8); str(cc,text_); str(dd,nombre); setcolor(green); if move < 62-endgame then begin if ee > 1500 then ee := 1500 else if ee < -1500 then ee := -1500; { str(ee,text2_)} str(((ee/3000)+0.5):2:2,text2_); end else begin str(ee,text2_); end; if move < 2 then begin tv := globe; setcolor(8); cc := 2; dd := 2; str(cc,text_); str(dd,nombre); outtextxy(225 ,35,text_); outtextxy(410 ,35,nombre); end; if eval = 1 then outtextxy(375 ,440,text2_); setcolor(tv); outtextxy(225 ,35,text_); outtextxy(410 ,35,nombre); cc := score1; dd := score2; ee := maxx; ff := move; str(score1,text_); str(score2,nombre); setcolor(7); outtextxy(180, 10, 'player 1'); outtextxy(225 ,35,text_); setcolor(15); outtextxy(365, 10,'player 2'); outtextxy(410 ,35,nombre); setcolor(white); if move < 61-endgame then begin if maxx > 1500 then maxx := 1500 else if maxx < -1500 then maxx := -1500; str((((maxx)/3000)+0.5):2:2,text2_); { str(maxx,text2_)} end else begin str(maxx,text2_); end; if (maxx <> 30000) and (eval = 1) then begin outtextxy(208,440,' Evaluation : '); outtextxy(375,440,text2_); box2(207,438,433,472,209,440,431,470); end; setcolor(8); end;

+ 1 else + 1; + 1 else + 1;

{ ***********************************************************

96

* The following procedure handles the computer. If there * * is a move in its move library corresponding to the * * present situation then he will play it directly, * * otherwise he will do a search using the alpha-beta * * algorithm seen before to find the "best" move. * *********************************************************** } procedure computerplay(var tablea:vector1;color:integer); var res, vsave1,vsave2,test,temp,maxsave,x1,x2,x3,x4,local1, a,oo,ii,maxx_save,temp5,temp1,temp2,temp3,test2,temp6: integer; vvv: vector2; tabsave2: vector1; ok,out: boolean; ev_po: vector6; coco: string; begin artificial(tablea,color,res); if (move = 0) then begin var1 := 1; xglob := 3; yglob := 4; end; if var1 <> 6 then begin ok := false; out := false; temp1 := last; while (not ok) and (not out) and (temp1 < 29) do begin x1 := trunc(xyglob[move][temp1]/1000); x2 := trunc(xyglob[move][temp1]/100)-x1*10; if styl < 6 then begin if next_gen = 0 then begin if move = 0 then begin x1 := 3; x2 := 4; end; if move = 2 then begin x1 := 3; x2 := 3; end; end else begin if move = 0 then begin x1 := 3; x2 := 4; end; if move = 2 then begin x1 := 5; x2 := 3; end; if move = 4 then begin x1 := 5; x2 := 6; end; end; end; case var1 of 1 : begin if (x1 = xglob) and (x2 = yglob) then begin ok := true; last := temp1; end else if move mod 2 = 1 then begin if min[temp1+1] < move then out := true;

97

end else if min2[temp1+1] < move then out := true end; 2 : begin if (x2 = xglob) and (x1 = yglob) then begin ok := true; last := temp1; end else if min[temp1+1] < move then out := true; end; 3 : begin if (x2 = 8-xglob+1) and (x1 = 8-yglob+1) then begin ok := true; last := temp1; end else if min[temp1+1] < move then out := true; end; 4 : begin if (x1 = 8-xglob+1) and (x2 = 8-yglob+1) then begin ok := true; last := temp1; end else if min[temp1+1] < move then out := true; end; end; inc(temp1); end; if not ok then var1 := 6; if ok = true then begin xxglob := trunc(xyglob[move][temp1-1]/10)-100*x1-10*x2; yyglob := xyglob[move][temp1-1]-1000*x1-100*x2-10*xxglob; case var1 of 1 : begin end; 2 : begin temp := xxglob; xxglob := yyglob; yyglob := temp; end; 3 : begin temp := xxglob; xxglob := 8-yyglob+1; yyglob := 8-temp+1; end; 4 : begin xxglob := 8-xxglob+1; yyglob := 8-yyglob+1; end; end; move := move + 1; colo := 1; putimage(225,10,sprite4,xorput); putimage(410,10,sprite5,xorput); choice(tablea,yyglob,xxglob,color,1,no_u); write(moves_file,'Move ',move,' : ',xxglob,' ',yyglob); writeln(moves_file); x := xxglob; y := yyglob; end; end; if var1 = 6 then begin if (res <> 0) then begin finish := 0; colo := 0; vect_coul[1] := 0; vect_coul[2] := 0; numempty := 1; for ii := 1 to 8 do for oo := 1 to 8 do

98

begin if tablea[ii][oo] = 0 then begin vw[numempty][1] := ii; vw[numempty][2] := oo; inc(numempty); end else if tablea[ii][oo] = 1 then begin inc(vect_coul[1]); end else if tablea[ii][oo] = 2 then begin inc(vect_coul[2]); end; end; maxx:= 0; for ii := 1 to 32000 do begin hash_beta^[ii] := 0; hash_alpha^[ii] := 0; hash_gamma^[ii] := 0; hash_delta^[ii] := 0; end; if move < 40 then begin stop := 1; test2 := algol3(tablea,1,hb,2,v1,v2,-30000,30000,1,vect_coul); end else if move < 60-endgame then begin stop := 1; test := algol3(tablea,1,hb+1,2,v1,v2,-30000,30000,1,vect_coul); end else begin stop := 2; test := algol3(tablea,1,endgame,2,v1,v2,-30000,30000,1,vect_coul); end; putimage(137+v1*40,57+v2*40,sprite6,1); save_one := 0; save_two := 0; colo := 1; if board[v2,v1] = 0 then begin putimage(225,10,sprite4,xorput); putimage(410,10,sprite5,xorput); tabsave[move] := tablea; colors[move] := 2; move := move + 1; choice(tablea,v2,v1,color,1,no_u); write(moves_file,'Move ',move,' : ',v1,' ',v2); writeln(moves_file); x := v1; y := v2; tabla[move][1] := v2; tabla[move][2] := v1; end; end else begin finish := finish + 1; putimage(225,10,sprite4,xorput); putimage(410,10,sprite5,xorput); end; end; yglob := y; xglob := x; end; { *********************************************************** * The following procedure handles the player. *

99

*********************************************************** } procedure humanplay(var tabl:vector1;var color:integer;var ch:char); var res{,x,y},xa,ya,a,answer2,answer,temp,temp1,temp2,xx,yy: integer; could: string; begin tv := globe; a := 1; maxxx := 8; maxy := 8; minx := 8; miny := 8; for xa := 1 to 8 do begin temp := 0; for ya := 1 to 8 do begin if tabl[ya][xa] = 0 then temp := temp + 1; end; if (temp > 0) and (a = 2) and (xa > maxxx) then begin maxxx := xa; end; if (temp > 0) and (a = 1) and (xa < minx) then begin minx := xa; maxxx := xa;a := 2; end; end; a := 1; for ya := 1 to 8 do begin temp := 0; for xa := 1 to 8 do begin if tabl[ya][xa] = 0 then temp := temp + 1; end; if (temp > 0) and (a = 2) and (ya > maxy) then begin maxy := ya; end; if (temp > 0) and (a = 1) and (ya < miny) then begin miny := ya; maxy := y;a := 2; end; end; artificial(tabl,color,res); if (res <> 0) then begin putimage(137+x*40,57+y*40,sprite3,xorput); xx := 3; yy := 5; repeat b1 := false; b2 := false; b3 := false; if b1 or b2 or b3 then begin delay(70); yy := ((my - 80) div 40 )+1; xx := ((mx - 160) div 40 )+1; answer2 := choice(tabl,yy,xx,color,0,no_u); end else answer2 := 0; if keypressed and (not b1) and (not b2) begin ch := readkey; if ch = chr(60) then begin uc := uc + 1; if uc = 16 then uc := 0; cleardevice; print2(tabl); print_res(tabl,color); end; if ch = chr(59) then begin tv := tv + 1; setbkcolor(tv); cleardevice; print2(tabl); if tv = 16 then tv := 0; print_res(tabl,color); end; if (ch = chr(77)) and (x<8) then x := if (ch = chr(80)) and (y<8) then y := if (ch = chr(75)) and (x>1) then x := if (ch = chr(72)) and (y>1) then y := and (not b3) then

x y x y

+ + -

1; 1; 1; 1;

100

if (ch = 'n') then begin eval := 3-eval; cleardevice; print2(tabl); print_res(tabl,color); putimage(137+x*40,57+y*40,sprite3,xorput); end; if (ch = 't') and (move > 2) then begin if tu <> 2 then begin if move > coup_save then coup_save := move; repeat dec(move); until colors[move] = 1; tabl := tabsave[move]; cleardevice; print2(tabl); print_res(tabl,color); putimage(137+x*40,57+y*40,sprite3,xorput); end else begin dec(move); tabl := tabsave[move]; cleardevice; print2(tabl); print_res(tabl,color); putimage(137+x*40,57+y*40,sprite3,xorput); color := 3-color; end; end; putimage(137+x*40,57+y*40,sprite3,xorput); if (ch in ['a'..'z','1'..'9']) or (ch = chr(32)) or (ch = chr(13)) then putimage(137+x*40,57+y*40,sprite3,xorput); answer := choice(tabl,y,x,color,0,no_u); end; until ((((ch = chr(32)) or (ch = chr(13))) and (answer = 1)) and (tabl[y][x] = 0)) or ((answer2 = 1) and (b1 or b2 or b3)) or (ch = chr(27)); finish := 0; if (answer2 = 1) and (b1 or b2 or b3) then begin putimage(137+x*40,57+y*40,sprite3,xorput); y := yy; x := xx; b1 := false; b2 := false; b3 := false; end; if (tabl[y,x] = 0) and (ch <> chr(27)) then begin putimage(225,10,sprite4,xorput); putimage(410,10,sprite5,xorput); tabsave[move] := tabl; colors[move] := color; move := move + 1; colo := 1; coup_save := 0; choice(tabl,y,x,color,1,no_u); tabla[move][1] := y; tabla[move][2] := x; yglob := y; xglob := x; if lib = 1 then begin if (x = 3) and (y = 4) then var1 if (x = 4) and (y = 3) then var1 if (x = 5) and (y = 6) then var1 if (x = 6) and (y = 5) then var1 lib := 0; end; end; end else begin

:= := := :=

1 else 2 else 3 else 4;

101

putimage(225,10,sprite4,xorput); putimage(410,10,sprite5,xorput); finish := finish + 1; end; if ch = chr(27) then finish := 2; b1 := false; b2 := false; b3 := false; end; { *********************************************************** * The following set of procedures and functions deals * * with the genetic algorithm implementation. * *********************************************************** } procedure swap(var swap1, swap2:integer); var temp: integer; begin temp := swap1; swap1 := swap2; swap2 := temp; end; procedure binary(var ar:arrayg;i:integer); var temp: integer; begin temp := 1; while i <> 0 do begin if i mod 2 = 0 then begin ar[temp] := 0; inc(temp); end else begin ar[temp] := 1; inc(temp); end; i := i div 2; end; while temp < bit_used+1 do begin ar[temp] := 0; inc(temp); end; end; function decimal(ar:arrayg):integer; var temp1,temp2: integer; begin temp1 := 0; for temp2 := 1 to bit_used do begin if ar[temp2] = 1 then temp1 := temp1 + round(exp(2,temp2-1)); end; decimal := temp1; end; procedure selection(var genetic:genes;fitness:vector4;var fitness_save:vector4); var fitness_sum, temp1, temp2, temp3, temp4, save_fit: integer; fitness_save2, fitness2: vector4; save_gen: genes; genetic_tmp: genes; begin fitness_sum := 0; fitness2 := fitness; for temp1 := 1 to population_size do begin fitness_sum := fitness_sum + fitness[temp1]; end; for temp1 := 1 to population_size+2 do

102

begin for temp4 := 1 to 10 do begin genetic_tmp[temp1][temp4] := 0; end; end; for temp4 := 1 to population_size do begin temp2 := random(fitness_sum)+1; temp3 := 1; temp1 := 0; while (temp1 < temp2) do begin temp1 := temp1 + fitness[temp3]; inc(temp3); end; genetic_tmp[temp4] := genetic[temp3-1]; overall_fitness[temp4] := fitness2[temp3-1]; end; if sort_selection = 1 then begin for temp3 := population_size downto 2 do begin for temp4 := 2 to temp3 do begin if temp3 mod 2 = 0 then begin if (overall_fitness[temp4] > overall_fitness[temp4-1]) then begin save_gen[1] := genetic_tmp[temp4]; genetic_tmp[temp4] := genetic_tmp[temp4-1]; genetic_tmp[temp4-1] := save_gen[1]; save_fit := overall_fitness[temp4]; overall_fitness[temp4] := overall_fitness[temp4-1]; overall_fitness[temp4-1] := save_fit; end; end else begin if (overall_fitness[temp4] < overall_fitness[temp4-1]) then begin save_gen[1] := genetic_tmp[temp4]; genetic_tmp[temp4] := genetic_tmp[temp4-1]; genetic_tmp[temp4-1] := save_gen[1]; save_fit := overall_fitness[temp4]; overall_fitness[temp4] := overall_fitness[temp4-1]; overall_fitness[temp4-1] := save_fit; end; end; end; end; end; genetic := genetic_tmp; end; procedure crossover(var a1,a2:arrayg;i:integer); var t1,t2: integer; temp: arrayg; begin for t1 := i+1 to 8 do begin t2 := a1[t1]; a1[t1] := a2[t1]; a2[t1] := t2; end; end; procedure set_bit(var ar:arrayg;i:integer;o:integer); begin ar[i] := o; end; function get_bit(ar:arrayg;i:integer):integer; begin get_bit := ar[i];

103

end; procedure inverse(var a1:arrayg;i:integer); var t1,t2: integer; temp: arrayg; begin if a1[i] = 0 then a1[i] := 1 else a1[i] := 0; end; procedure random_strings(var genetic:genes); var temp6: integer; begin for temp6 := 1 to parameters do begin case random_var of 1: {nothing}; 2: genetic[population_size][temp6] := random(exp(2,bit_used)); 3: begin genetic[population_size-2][temp6] := random(exp(2,bit_used)); genetic[population_size][temp6] := random(exp(2,bit_used)); end; 4: begin genetic[population_size][temp6] := genetic[1][temp6]random_value+random(random_value*2+1); if genetic[population_size][temp6] > exp(2,bit_used)-1 then genetic[population_size][temp6] := exp(2,bit_used)-1 else if genetic[population_size][temp6] < 0 then genetic[population_size][temp6] := 0; end; 5: begin genetic[population_size-2][temp6] := genetic[1][temp6]random_value+random(random_value*2+1); if genetic[population_size-2][temp6] > exp(2,bit_used) then genetic[population_size-2][temp6] := exp(2,bit_used)-1 else if genetic[population_size-2][temp6] < 0 then genetic[population_size-2][temp6] := 0; genetic[population_size][temp6] := genetic[2][temp6]random_value+random(random_value*2+1); if genetic[population_size][temp6] > exp(2,bit_used)-1 then genetic[population_size][temp6] := exp(2,bit_used)-1 else if genetic[population_size][temp6] < 0 then genetic[population_size][temp6] := 0; end; 6: genetic[population_size][temp6] := best[temp6]; 7: genetic[population_size][temp6] := middle[temp6]; 8: genetic[population_size][temp6] := worst[temp6]; end; end; end; procedure classic_strings(var genetic:genes); var temp6: integer; begin for temp6 := 1 to parameters do begin case classic_fitness of 1: genetic[population_size+1][temp6] := 2: genetic[population_size+1][temp6] := 3: genetic[population_size+1][temp6] := 4: genetic[population_size+1][temp6] := end; end; end;

best[temp6]; middle[temp6]; worst[temp6]; random(256);

procedure mutation_strings(var genetic:genes); var oo, ii, temp1: integer; begin for oo := 1 to population_size do begin for ii := 0 to bit_used*parameters-1 do begin temp1 := random(mutation); if temp1 = 1 then begin binary(binc1,genetic[oo][(ii div bit_used)+1]);

104

inverse(binc1,(ii mod bit_used)+1); genetic[oo][(ii div bit_used)+1] := decimal(binc1); end; end; end; end; procedure init_values(var overall_fitness2:vector4; var fitness:vector4); var oo: integer; begin for oo := 1 to population_size+2 do begin overall_fitness2[oo] := 0; fitness[oo] := 0; end; end; procedure fill_file_genetic(genetic:genes); var temp1, temp2, temp3: integer; begin writeln(file_genetic,'generation: ',generation); writeln(file_genetic,''); case styl of 1: temp3 := population_size; 2: temp3 := population_size+1; 3: temp3 := population_size; 4: temp3 := population_size; 5: temp3 := population_size+1; end; for temp1 := 1 to temp3 do begin for temp2 := 1 to parameters do begin write(file_genetic,genetic[temp1][temp2]:3,' '); end; writeln(file_genetic,''); end; writeln(file_genetic,''); end; { *********************************************************** * The following procedure is the othello "module". * *********************************************************** } procedure othello_proc(board:vector1;var color,se1,se2:integer); var tt, rr, ii, oo: integer; ch: char; label 2; begin if (ua = 2) and (tz = 1) then begin color_global := 2; color := 2 end else begin color := 1; color_global := 1; end; if tz = 2 then begin color := 2; color_global := 2; end; maxx := 0; print_res(board,color); glob := 0; repeat if keypressed then begin ch := readkey; if ch = chr(27) then begin endp := 1;

105

super_champion := 2; super_champ := 1; goto 2; end; end; if next_gen = 0 then xyglob[1][1] := 3453 else xyglob[1][1] := 3433; if (finish < 2) then begin if color = 1 then begin if var1 = 6 then begin color := 2; color_global := 2; tempim := sprite1; sprite1 := sprite2; sprite2 := tempim; for tt := 1 to 8 do for rr := 1 to 8 do begin if board[tt][rr] = 2 then board[tt][rr] := 1 else if board[tt][rr] = 1 then board[tt][rr] := 2; end; gennum := op1; computerplay(board,color); gennum := op2; tempim := sprite1; sprite1 := sprite2; sprite2 := tempim; for tt := 1 to 8 do for rr := 1 to 8 do begin if board[tt][rr] = 1 then board[tt][rr] := 2 else if board[tt][rr] = 2 then board[tt][rr] := 1; end; color := 1; color_global := 1; end else computerplay(board,color); end else begin if tz = 1 then begin computerplay(board,color) end else humanplay(board,color,ch) end; color := 3 - color; color_global := 3-color_global; print_res(board,3-color); end; until (finish = 2) or (ch = chr(27)); for ii := 1 to 8 do for oo := 1 to 8 do begin if board[ii][oo] = 2 then inc(se1) else if board[ii][oo] = 1 then inc(se2); end; 2: for ii := 1 to 32000 do begin hash_beta^[ii] := 0; hash_alpha^[ii] := 0; hash_gamma^[ii] := 0; hash_delta^[ii] := 0; end; end; { *********************************************************** * The following procedure is the module you can modify * * for your own use. You can thus use all the power of the * * genetic "laboratory" for any optimization task you * * decide! See the reference thesis for how to actually *

106

* use it. * *********************************************************** } procedure any_proc(var se1, se2:integer); var xh: real; ch: char; label 2; begin if keypressed then begin ch := readkey; if ch = chr(27) then begin endp := 1; super_champion := 2; super_champ := 1; goto 2; end; end; gennum := op1; xh := genetic[gennum][1]; se2 := round(xh); gennum := op2; xh := genetic[gennum][1]; se1 := round(xh); 2: end; { *********************************************************** * The following 3 procedures are the heart of the genetic * * laboratory. They are different implementation of * * genetic algorithms. For a complete explanation, please * * refer to the thesis. * *********************************************************** } procedure play_match(var board:vector1); var x, y, test, temp, tt,rr, color, res, answer, ii, se1, se2, oo, temp_fitness, temp1, temp2, temp4, temp5, temp6, save_fit, temp7, temp8, temp9,uu: integer; temp3: vector4; ok: boolean; save_gen: genes; xh: real; ch: char; label 2; begin if mem_genetic = 1 then begin overall_fitness[1] := save_fit1; overall_fitness[2] := save_fit2; end; settextstyle(2, horizdir, 4); setcolor(7); str(op2, op2_string); outtextxy(530,265,op2_string); setcolor(white); str(op1, op1_string); outtextxy(600,265,op1_string); settextstyle(8, horizdir, 2); se1 := 0; se2 := 0; if use_othello = 1 then begin othello_proc(board,color,se1,se2); end else begin any_proc(se1,se2); end; if endp = 1 then goto 2; finish := 2; if (finish = 2) then begin;

107

setcolor(11); y := 370; x := -3; if se2-se1>0 then begin fitness[op1] := fitness[op1] + se2-se1; fitness[op2] := fitness[op2] - se2+se1; overall_fitness2[op1] := overall_fitness2[op1] + 2; overall_fitness2[op2] := overall_fitness2[op2] - 2; end else if se2-se1=0 then begin fitness[op1] := fitness[op1]; fitness[op2] := fitness[op2]; overall_fitness2[op1] := overall_fitness2[op1] + 1; overall_fitness2[op2] := overall_fitness2[op2] + 1; end else begin fitness[op1] := fitness[op1] + se2-se1; fitness[op2] := fitness[op2] - se2+se1; overall_fitness2[op2] := overall_fitness2[op2] + 2; overall_fitness2[op1] := overall_fitness2[op1] - 2; end; settextstyle(2, horizdir, 4); setcolor(back); str(op1, op1_string); outtextxy(505,40,op1_string); setcolor(back); str(op2, op2_string); outtextxy(525,40,op2_string); settextstyle(8, horizdir, 2); if next_gen = 1 then begin vvv1 := 1; if num_games = 1 then swap(op1,op2); if op1 > op2 then begin inc(game); swap(op1,op2); end else begin swap(op1,op2); if (fitness[op1]+overall_fitness2[op1]*20) >= (fitness[op2]+overall_fitness2[op2]*20) then begin overall_fitness[op2] := 0; fitness[mem_genetic] := fitness[op1]; overall_fitness2[mem_genetic] := overall_fitness2[op1]; overall_fitness[mem_genetic] := overall_fitness[op1] + 1; if mem_genetic = 1 then save_fit1 := overall_fitness[1]; if mem_genetic = 2 then save_fit2 := overall_fitness[2]; for ii := 1 to parameters do genetic[mem_genetic] := genetic[op1]; end else begin overall_fitness[op1] := 0; fitness[mem_genetic] := fitness[op2]; overall_fitness2[mem_genetic] := overall_fitness2[op2]; overall_fitness[mem_genetic] := overall_fitness[op2] + 1; if mem_genetic = 1 then save_fit1 := overall_fitness[1]; if mem_genetic = 2 then save_fit2 := overall_fitness[2]; for ii := 1 to parameters do genetic[mem_genetic] := genetic[op2]; end; inc(mem_genetic); op2 := op2 + 2; op1 := op1 + 2; inc(turng); inc(turng2); inc(turng); inc(turng2); if (num_games = 4) or (num_games = 3) then next_gen := 0 else next_gen := 1; game := 1;

108

end; end else if next_gen = 0 then begin if num_games = 3 then swap(op1,op2); if op1 > op2 then begin inc(game); swap(op1,op2); end else begin inc(game); swap(op1,op2); inc(next_gen); end; end; if (op1 = population_size+2) and (op2 = population_size+1) then begin if sort_generation = 1 then begin for oo := (population_size div 2) downto 2 do begin for ii := 2 to oo do begin if (fitness[ii]+overall_fitness2[ii]*20) > (fitness[ii-1]+overall_fitness2[ii-1]*20) then begin save_gen[1] := genetic[ii]; genetic[ii] := genetic[ii-1]; genetic[ii-1] := save_gen[1]; save_fit := overall_fitness[ii]; overall_fitness[ii] := overall_fitness[ii-1]; overall_fitness[ii-1] := save_fit; save_fit := overall_fitness2[ii]; overall_fitness2[ii] := overall_fitness2[ii-1]; overall_fitness2[ii-1] := save_fit; save_fit := fitness[ii]; fitness[ii] := fitness[ii-1]; fitness[ii-1] := save_fit; end; end; end; end; for ii := 1 to parameters do begin best[ii] := genetic[1][ii]; worst[ii] := genetic[population_size div 2][ii]; middle[ii] := genetic[population_size div 4][ii]; end; save_fit1 := overall_fitness[1]; save_fit2 := overall_fitness[2]; turng2 := 3; turng := 1; inc(generation); calculate := 1; temp1 := 1; mem_genetic := 1; overall_fitness[1] := save_fit1; overall_fitness[2] := save_fit2; op1 := 2; op2 := 1; for oo := 0 to ((population_size div 2)-1) do begin for ii := 1 to parameters do begin genetic[((population_size div 2) + 1 + oo)][ii] := 0; end; end; if (recombination = 2) or (recombination = 3) then begin temp6 := population_size div 8; for oo := 0 to temp6-1 do

109

begin main_temp1 := random(bit_used*parameters-1)+1; temp1 := (main_temp1 div bit_used) + 1; if (main_temp1 mod bit_used) <> 0 then inc(temp1); if temp1 > parameters then temp1 := parameters; for temp2 := 1 to temp1 do begin genetic[2*(oo+temp6)+((population_size div 2) + 1)][temp2] := genetic[2*oo+1][temp2]; genetic[2*(oo+temp6)+((population_size div 2) + 2)][temp2] := genetic[2*oo+2][temp2]; end; for temp2 := temp1 to parameters do begin genetic[2*(oo+temp6)+((population_size div 2) + 1)][temp2] := genetic[2*oo+2][temp2]; genetic[2*(oo+temp6)+((population_size div 2) + 2)][temp2] := genetic[2*oo+1][temp2]; end; if (main_temp1 mod bit_used) <> 0 then begin binary(binc1,genetic[oo*2+2][temp1-1]); binary(binc2,genetic[oo*2+1][temp1-1]); crossover(binc1,binc2,main_temp1 mod bit_used); genetic[2*(oo+temp6)+((population_size div 2) + 1)][temp1-1] := decimal(binc1); genetic[2*(oo+temp6)+((population_size div 2) + 2)][temp1-1] := decimal(binc2); end; end; end; if (recombination = 4) then begin temp6 := population_size div 4; for oo := 0 to temp6-1 do begin main_temp1 := random(bit_used*parameters-1)+1; temp1 := (main_temp1 div bit_used) + 1; if (main_temp1 mod bit_used) <> 0 then inc(temp1); if temp1 > parameters then temp1 := parameters; for temp2 := 1 to temp1 do begin genetic[2*oo+((population_size div 2) + 1)][temp2] := genetic[2*oo+1][temp2]; genetic[2*oo+((population_size div 2) + 2)][temp2] := genetic[2*oo+2][temp2]; end; for temp2 := temp1 to parameters do begin genetic[2*oo+((population_size div 2) + 1)][temp2] := genetic[2*oo+2][temp2]; genetic[2*oo+((population_size div 2) + 2)][temp2] := genetic[2*oo+1][temp2]; end; if (main_temp1 mod bit_used) <> 0 then begin binary(binc1,genetic[oo*2+2][temp1-1]); binary(binc2,genetic[oo*2+1][temp1-1]); crossover(binc1,binc2,main_temp1 mod bit_used); genetic[2*(oo+temp6)+((population_size div 2) + 1)][temp1-1] := decimal(binc1); genetic[2*(oo+temp6)+((population_size div 2) + 2)][temp1-1] := decimal(binc2); end; end; end; if (recombination = 1) or (recombination = 2) then begin for oo := 0 to ((population_size div 4) - 1) do begin for ii := 0 to 8*parameters-1 do begin main_temp1 := random(100)+1; if main_temp1 < 50 then begin if recombination = 1 then begin binary(binc1,genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+1][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1);

110

binary(binc1,genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); end else if recombination = 2 then begin binary(binc1,genetic[((population_size div 2) + 1 + oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+1][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + oo)][(ii div bit_used) + 1] := decimal(binc1); end; end else begin if recombination = 1 then begin binary(binc1,genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); binary(binc1,genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+1][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); end else begin binary(binc1,genetic[((population_size div 2) + 1 + oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + oo)][(ii div bit_used) + 1] := decimal(binc1); end; end; end; end; end; if (recombination = 5) then begin for oo := 0 to ((population_size div 4) - 1) do begin temp7 := 1; temp9 := bit_used*parameters; temp8 := random(temp9-(ncrossover+2))+1; main_temp1 := 1; for uu := ncrossover+1 downto 1 do begin for ii := temp7 to temp8 do begin if main_temp1 < 2 then begin binary(binc1,genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+1][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1);

111

binary(binc1,genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); end else begin binary(binc1,genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); binary(binc1,genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+1][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); end; end; temp7 := temp8+1; if uu = 2 then temp8 := temp9 else temp8 := temp7+random(temp9-temp7-uu+2); inc(main_temp1); if main_temp1 = 3 then main_temp1 := 1; end; end; end;

if recombination = 3 then begin for oo := 0 to ((population_size div 4) - 1) do begin for ii := 1 to parameters do begin genetic[((population_size div 2) + 1 + oo)][ii] := genetic[2*oo+1][ii]+((genetic[2*oo+2][ii]-genetic[2*oo+1][ii]) div average_recombination); end; end; end; mutation_strings(genetic); init_values(overall_fitness2,fitness); random_strings(genetic); fill_file_genetic(genetic); in_file := 1; end; end; 2: end; procedure play_classic(var board:vector1); var x, y, test, temp, tt,rr, color, res, answer, ii, se1, se2, oo, temp_fitness, temp1, temp2, temp4, temp5, temp6, temp7, temp8, temp9, uu: integer; temp3: vector4; ok: boolean; ch: char; label 2; begin settextstyle(2, horizdir, 4); setcolor(7); str(op2, op2_string); outtextxy(530,265,op2_string); setcolor(white); str(op1, op1_string); outtextxy(600,265,op1_string); settextstyle(8, horizdir, 2); se1 := 0;

112

se2 := 0; if use_othello = 1 then begin othello_proc(board,color,se1,se2); end else begin any_proc(se1,se2); end; if endp = 1 then goto 2; if (finish = 2) then begin; setcolor(11); y := 370; x := -3; if se2-se1>0 then begin if (styl = 5) then begin if (op1 = population_size+1) then inc(wins) else inc(losses); end; fitness[op1] := fitness[op1] + se2-se1; fitness[op2] := fitness[op2] - se2+se1; overall_fitness2[op1] := overall_fitness2[op1] + 2; overall_fitness2[op2] := overall_fitness2[op2] - 2; end else if se2-se1=0 then begin if (styl = 5) then begin inc(draws); end; fitness[op1] := fitness[op1]; fitness[op2] := fitness[op2]; overall_fitness2[op1] := overall_fitness2[op1] + 1; overall_fitness2[op2] := overall_fitness2[op2] + 1; end else begin if (styl = 5) then begin if (op2 = population_size+1) then inc(wins) else inc(losses); end; fitness[op1] := fitness[op1] + se2-se1; fitness[op2] := fitness[op2] - se2+se1; overall_fitness2[op2] := overall_fitness2[op2] + 2; overall_fitness2[op1] := overall_fitness2[op1] - 2; end; settextstyle(2, horizdir, 4); setcolor(back); str(op1, op1_string); outtextxy(505,40,op1_string); setcolor(back); str(op2, op2_string); outtextxy(525,40,op2_string); settextstyle(8, horizdir, 2); if next_gen = 0 then begin if num_games = 3 then swap(op1,op2); if op1 > op2 then begin inc(game); swap(op1,op2); end else begin inc(game); swap(op1,op2); inc(next_gen); end; end else begin if num_games = 1 then swap(op1,op2); if op1 > op2 then begin

113

inc(game); swap(op1,op2); end else begin swap(op1,op2); if (op1 = population_size+1) then begin op1 := population_size; op2 := op2 + 1; turng2 := population_size + 1; inc(turng); end; op1 := op1 + 1; inc(turng2); if (num_games = 4) or (num_games = 3) then next_gen := 0 else next_gen := 1; game := 1; end; end; if (op1 = population_size+1) and (op2 = population_size+1) then begin turng2 := population_size + 2; turng := 1; inc(generation); temp1 := 1; calculate := 1; wins := 0; losses := 0; draws := 0; fitness_save := fitness; for ii := population_size downto 1 do begin for oo := 2 to population_size do begin if (fitness[temp1] <= fitness[oo]) then begin temp1 := oo; end; end; fitness[temp1] := -200; fitness_tmp[temp1] := ii; if (ii = population_size) then begin for temp6 := 1 to parameters do best[temp6] := genetic[temp1][temp6]; end; if (ii = 1) then begin for temp6 := 1 to parameters do worst[temp6] := genetic[temp1][temp6]; end; if (ii = population_size div 2) then begin for temp6 := 1 to parameters do middle[temp6] := genetic[temp1][temp6]; end; temp1 := 1; end; fitness := fitness_tmp; genetic_memory[passes] := genetic[1]; op1 := population_size+1; op2 := 1; inc(passes); selection(genetic,fitness,fitness_save); if recombination = 4 then begin for oo := 0 to ((population_size div 2) - 1) do begin main_temp1 := random(100); if main_temp1 < crossover_prob then begin main_temp1 := random(bit_used*parameters-1)+1; temp1 := (main_temp1 div bit_used) + 1; if (main_temp1 mod bit_used) <> 0 then inc(temp1); for temp2 := temp1 to parameters do begin

114

temp5 := genetic[2*oo+2][temp2]; genetic[2*oo+2][temp2] := genetic[oo*2+1][temp2]; genetic[2*oo+1][temp2] := temp5; end; if (main_temp1 mod bit_used) <> 0 then begin binary(binc1,genetic[oo*2+2][temp1-1]); binary(binc2,genetic[oo*2+1][temp1-1]); crossover(binc1,binc2,main_temp1 mod bit_used); genetic[oo*2+1][temp1-1] := decimal(binc1); genetic[oo*2+2][temp1-1] := decimal(binc2); end; end; end; end else if recombination = 1 then begin for oo := 0 to ((population_size div 2) - 1) do begin main_temp1 := random(100); if main_temp1 < crossover_prob then begin for ii := 0 to bit_used*parameters-1 do begin main_temp1 := random(100)+1; if main_temp1 < 50 then begin binary(binc1,genetic[2*oo+1][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); main_temp3 := get_bit(binc1,(ii mod bit_used) + 1); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); set_bit(binc2,(ii mod bit_used) + 1,main_temp3); genetic[2*oo+1][(ii div bit_used) + 1] := decimal(binc1); genetic[2*oo+2][(ii div bit_used) + 1] := decimal(binc2); end else begin end; end; end; end; end; if (recombination = 5) then begin for oo := 0 to ((population_size div 2) - 1) do begin temp7 := 1; temp9 := bit_used*parameters; temp8 := random(temp9-(ncrossover+2))+1; main_temp1 := 1; for uu := ncrossover+1 downto 1 do begin for ii := temp7 to temp8 do begin if main_temp1 < 2 then begin binary(binc1,genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+1][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); binary(binc1,genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); end else begin binary(binc1,genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used)+1]);

115

binary(binc2,genetic[2*oo+2][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 1 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); binary(binc1,genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used)+1]); binary(binc2,genetic[2*oo+1][(ii div bit_used)+1]); set_bit(binc1,(ii mod bit_used) + 1,get_bit(binc2,(ii mod bit_used) + 1)); genetic[((population_size div 2) + 2 + 2*oo)][(ii div bit_used) + 1] := decimal(binc1); end; end; temp7 := temp8+1; if uu = 2 then temp8 := temp9 else temp8 := temp7+random(temp9-temp7-uu+2); inc(main_temp1); if main_temp1 = 3 then main_temp1 := 1; end; end; for ii := 1 to population_size do begin for oo := 1 to parameters do begin genetic[ii][oo] := genetic[ii+population_size div 2][oo]; end; end; end; mutation_strings(genetic); init_values(overall_fitness2,fitness); random_strings(genetic); fill_file_genetic(genetic); classic_strings(genetic); in_file := 1; end; end; 2: end; procedure play_mutation(var board:vector1); var x, y, test, temp, tt,rr, color, res, answer, ii, se1, se2, oo, temp_fitness, temp1, temp2, temp4, temp5, temp6, save_fit: integer; temp3: vector4; ok: boolean; save_gen: genes; ch: char; label 2; begin if mem_genetic = 1 then begin overall_fitness[1] := save_fit1; overall_fitness[2] := save_fit2; end; settextstyle(2, horizdir, 4); setcolor(7); str(op2, op2_string); outtextxy(530,265,op2_string); setcolor(white); str(op1, op1_string); outtextxy(600,265,op1_string); settextstyle(8, horizdir, 2); if use_othello = 1 then begin othello_proc(board,color,se1,se2); end else begin any_proc(se1,se2); end; if endp = 1 then goto 2;

116

if (finish = 2) then begin; setcolor(11); y := 370; x := -3; if se2-se1>0 then begin fitness[op1] := fitness[op1] + se2-se1; fitness[op2] := fitness[op2] - se2+se1; overall_fitness2[op1] := overall_fitness2[op1] + 2; overall_fitness2[op2] := overall_fitness2[op2] - 2; end else if se2-se1=0 then begin fitness[op1] := fitness[op1]; fitness[op2] := fitness[op2]; overall_fitness2[op1] := overall_fitness2[op1] + 1; overall_fitness2[op2] := overall_fitness2[op2] + 1; end else begin fitness[op1] := fitness[op1] + se2-se1; fitness[op2] := fitness[op2] - se2+se1; overall_fitness2[op2] := overall_fitness2[op2] + 2; overall_fitness2[op1] := overall_fitness2[op1] - 2; end; settextstyle(2, horizdir, 4); setcolor(back); str(op1, op1_string); outtextxy(505,40,op1_string); setcolor(back); str(op2, op2_string); outtextxy(525,40,op2_string); settextstyle(8, horizdir, 2); if next_gen = 1 then begin if num_games = 1 then swap(op1,op2); if op1 > op2 then begin inc(game); swap(op1,op2); end else begin swap(op1,op2); if (fitness[op1]+overall_fitness2[op1]*20) >= (fitness[op2]+overall_fitness2[op2]*20) then begin overall_fitness[op2] := 0; fitness[mem_genetic] := fitness[op1]; overall_fitness2[mem_genetic] := overall_fitness2[op1]; overall_fitness[mem_genetic] := overall_fitness[op1] + 1; if mem_genetic = 1 then save_fit1 := overall_fitness[1]; if mem_genetic = 2 then save_fit2 := overall_fitness[2]; for ii := 1 to parameters do genetic[mem_genetic] := genetic[op1]; end else begin overall_fitness[op1] := 0; fitness[mem_genetic] := fitness[op2]; overall_fitness2[mem_genetic] := overall_fitness2[op2]; overall_fitness[mem_genetic] := overall_fitness[op2] + 1; if mem_genetic = 1 then save_fit1 := overall_fitness[1]; if mem_genetic = 2 then save_fit2 := overall_fitness[2]; for ii := 1 to parameters do genetic[mem_genetic] := genetic[op2]; end; inc(mem_genetic); op2 := op2 + 2; op1 := op1 + 2; inc(turng); inc(turng2); inc(turng); inc(turng2); if (num_games = 4) or (num_games = 3) then next_gen := 0 else next_gen := 1;

117

game := 1; end; end else if next_gen = 0 then begin if num_games = 3 then swap(op1,op2); if op1 > op2 then begin inc(game); swap(op1,op2); end else begin inc(game); swap(op1,op2); inc(next_gen); end; end; if (op1 = population_size+2) and (op2 = population_size+1) then begin if sort_generation = 1 then begin for oo := (population_size div 2) downto 2 do begin for ii := 2 to oo do begin if (fitness[ii]+overall_fitness2[ii]*20) > (fitness[ii-1]+overall_fitness2[ii-1]*20) then begin save_gen[1] := genetic[ii]; genetic[ii] := genetic[ii-1]; genetic[ii-1] := save_gen[1]; save_fit := overall_fitness[ii]; overall_fitness[ii] := overall_fitness[ii-1]; overall_fitness[ii-1] := save_fit; save_fit := overall_fitness2[ii]; overall_fitness2[ii] := overall_fitness2[ii-1]; overall_fitness2[ii-1] := save_fit; save_fit := fitness[ii]; fitness[ii] := fitness[ii-1]; fitness[ii-1] := save_fit; end; end; end; end; for ii := 1 to parameters do begin best[ii] := genetic[1][ii]; worst[ii] := genetic[population_size div 2][ii]; middle[ii] := genetic[population_size div 4][ii]; end; save_fit1 := overall_fitness[1]; save_fit2 := overall_fitness[2]; turng2 := 3; turng := 1; inc(generation); calculate := 1; temp1 := 1; mem_genetic := 1; overall_fitness[1] := save_fit1; overall_fitness[2] := save_fit2; op1 := 2; op2 := 1; for oo := 1 to 8 do begin fitness_rank[oo] := oo; end; for oo := 0 to ((population_size div 4)-1) do begin for ii := 1 to parameters do begin

118

genetic[((population_size div 2) + 1 + oo)][ii] := genetic[2*oo+1][ii]; fitness_rank[((population_size div 2) + 1 + oo)] := 2*oo+1; end; end; for oo := 0 to ((population_size div 4)-1) do begin for ii := 1 to parameters do begin genetic[((population_size div 2) + 1 + oo + population_size div 4)][ii] := genetic[2*oo+2][ii]; fitness_rank[((population_size div 2) + 1 + oo + population_size div 4)] := 2*oo+2; end; end; for oo := mutation_start to population_size do begin for ii := 0 to bit_used*parameters-1 do begin if styl = 3 then temp1 := random(mutation_algo+mutation_rate*generation) else temp1 := random(mutation_algo+(population_size div 2)fitness_rank[oo]+mutation_rate*generation); if temp1 = 1 then begin binary(binc1,genetic[oo][(ii div bit_used)+1]); inverse(binc1,(ii mod bit_used)+1); genetic[oo][(ii div bit_used)+1] := decimal(binc1); end; end; end; init_values(overall_fitness2, fitness); random_strings(genetic); fill_file_genetic(genetic); in_file := 1; end; end; 2: end; { *********************************************************** * The following procedure handle the human versus human, * * or human versus computer case. * *********************************************************** } procedure play_human(board:vector1); var x, y, test, temp, color, res, answer: integer; ok: boolean; ch: char; begin gennum := population_size + 1; if (ua = 2) and (tz = 1) then color := 2 else color := 1; if tz = 2 then color := 2; maxx := 0; print_res(board,color); glob := 0; repeat if (finish < 2) then begin if color = 1 then humanplay(board,color,ch) else begin if tz = 1 then begin computerplay(board,color) end else humanplay(board,color,ch) end; color := 3 - color; print_res(board,3-color); end; until (finish = 2) or (ch = chr(27)); if (finish = 2) or (ch = chr(27)) then begin;

119

repeat until keypressed; endp := 1; end; end; { *********************************************************** * Start of the program. * *********************************************************** } begin PatchCrt (Crt.Delay); endp := 0; save_color := 0; color_global := 1; nosound; bit_used := 8; assign(file_genetic,'genetic.txt'); start; start1; super_champion := 2; assign(moves_file,'moves.txt'); rewrite(moves_file); vvv1 := 1; repeat in_file := 1; passes := 1; turng := 1; mem_genetic := 1; test_mode := 1; game := 1; calculate := 1; wins := 0; losses := 0; draws := 0; randomize; gennum := 1; x := 5; y := 3; eval := 1; last := 1; style := 1; uc := 10; coup_save := 0; generation := 1; lib := 1; tv := 8; globe := tv; back := 8; setbkcolor(back); setcolor(11); cleardevice; if super_champion = 2 then begin start2; end else begin inc(super_champ); if super_champ < population_size then begin for main_temp1 := 1 to parameters do genetic_binome[super_champ-1][main_temp1] := genetic[1][main_temp1]; end else begin for main_temp1 := 1 to parameters do genetic_binome[super_champ-1][main_temp1] := genetic[1][main_temp1]; genetic := genetic_binome; for main_temp2 := 1 to parameters do begin genetic[population_size][main_temp2] := random(exp(2,bit_used)); end; end;

120

end; if (num_games = 4) or (num_games = 3) then next_gen := 0 else next_gen := 1; for main_temp1 := 1 to population_size+2 do begin fitness[main_temp1] := 0; fitness_tmp[main_temp1] := 1; overall_fitness[main_temp1] := 0; overall_fitness2[main_temp1] := 0; end; turng2 := population_size + 2; assign(rand_file,'random.txt'); reset(rand_file); if super_champ < population_size then begin if styl > 4 then begin for main_temp1 := 1 to population_size do begin for main_temp2 := 1 to parameters do begin if styl <> 5 then genetic[main_temp1][main_temp2] := random(exp(2,bit_used)) else begin read(rand_file,genetic[main_temp1][main_temp2]); write(genetic[main_temp1][main_temp2]); end; end; end; end else begin for main_temp1 := 1 to population_size+2 do begin for main_temp2 := 1 to parameters do begin genetic[main_temp1][main_temp2] := random(exp(2,bit_used)); end; end; end; end; if styl < 6 then fill_file_genetic(genetic); vvv1 := 0; if (styl begin op2 := op1 := end else if (styl begin op1 := op2 := end; repeat color_global := 1; for main_temp1 := 1 to 32000 do begin hash_alpha^[main_temp1] := 0; hash_beta^[main_temp1] := 0; hash_gamma^[main_temp1] := 0; hash_delta^[main_temp1] := 0; end; temp_hb := 0; temp_random := random(24)+8; minx := 1; miny := 1; maxxx := 8; maxy := 8; init_vars; = 1) or (styl = 3) or (styl = 4) then 1; 2; = 2) or (styl = 5) then population_size+1; 1;

121

setbkcolor(tv); grid; settextstyle(2, horizdir, 4); outtextxy(520,60,'Genetic Population'); outtextxy(20,60,'Genetic Population'); outtextxy(40,260,'Fitness'); outtextxy(530,250,'Match players'); outtextxy(540,280,'Generation'); outtextxy(560,310,'Game'); outtextxy(550,340,'Deviation'); outtextxy(525,370,'Average Deviation'); outtextxy(522,400,'Super Championship'); outtextxy(532,430,'Average String'); outtextxy(40,450,'Move'); for main_temp1 := 1 to parameters do begin average_string[main_temp1] := 0; for main_temp2 := 1 to population_size div 2 do begin average_string[main_temp1] := average_string[main_temp1]+genetic[main_temp2][main_temp1]; end; end; for main_temp1 := 1 to parameters do begin average_string[main_temp1] := round(average_string[main_temp1]/(population_size div 2)); end; if styl = 5 then begin outtextxy(540,10,'Wins:'); outtextxy(540,25,'Losses:'); outtextxy(540,40,'Draws:'); setcolor(8); str(wins_save,g_string); outtextxy(590,10,g_string); str(losses_save,g_string); outtextxy(590,25,g_string); str(draws_save,g_string); outtextxy(590,40,g_string); setcolor(white); str(wins,g_string); outtextxy(590,10,g_string); str(losses,g_string); outtextxy(590,25,g_string); str(draws,g_string); outtextxy(590,40,g_string); wins_save := wins; losses_save := losses; draws_save := draws; end; if calculate = 1 then begin calculate := 0; for main_temp1 := 1 to parameters do begin mean[main_temp1] := 0; mean2[main_temp1] := 0; average[main_temp1] := 0; end; total_deviation := 0; main_temp3 := population_size-1; for main_temp1 := 1 to parameters do begin for main_temp2 := 1 to main_temp3 do begin mean[main_temp1] := mean[main_temp1] + genetic[main_temp2][main_temp1]; end; mean[main_temp1] := mean[main_temp1] / main_temp3; end;

122

for main_temp1 := 1 to parameters do begin for main_temp2 := 1 to main_temp3 do begin average[main_temp1] := average[main_temp1]+sqr(mean[main_temp1]genetic[main_temp2][main_temp1]); end; average[main_temp1] := sqrt(average[main_temp1] / (main_temp3)); deviation_mem[main_temp1][generation] := average[main_temp1]; end; for main_temp1 := 1 to parameters do begin total_deviation := total_deviation+average[main_temp1]; end; total_deviation := total_deviation / parameters; average_mem[generation] := total_deviation; end; if in_file = 1 then begin in_file := 0; write(file_genetic,'parameter deviation: '); for main_temp1 := 1 to parameters do begin write(file_genetic,average[main_temp1]:3:0,' '); end; writeln(file_genetic,''); writeln(file_genetic,' ********************'); writeln(file_genetic,''); end; setcolor(7); moveto(520,5); linerel(0,50); linerel(100,0); if (generation mod 100) > 1 then begin moveto(520,55-round(average_mem[(generation div 100)*100+1]/2)); for main_temp1 := 2+(generation div 100)*100 to generation do begin setcolor(14); lineto(519+main_temp1-(generation div 100)*100,55-round(average_mem[main_temp1]/2)); end; end; setcolor(7); moveto(20,5); linerel(0,50); linerel(100,0); if (generation mod 100) > 1 then begin for main_temp2 := 1 to parameters do begin setcolor(main_temp2+2); moveto(20,55-round(deviation_mem[main_temp2][(generation div 100)*100+1]/2)); for main_temp1 := (generation div 100)*100+2 to generation do begin setcolor(main_temp2); lineto(19+main_temp1-(generation div 100)*100,55round(deviation_mem[main_temp2][main_temp1]/2)); end; end; end; for main_temp1 := 1 to 5 do begin str(average[main_temp1]:3:0,g_string); setcolor(white); outtextxy(485+25*main_temp1,350,g_string); end; for main_temp1 := 6 to parameters do begin str(average[main_temp1]:3:0,g_string); setcolor(white); outtextxy(485+(main_temp1-5)*25,360,g_string);

123

end; for main_temp1 := 1 to 5 do begin str(average_string[main_temp1],g_string); setcolor(white); outtextxy(485+25*main_temp1,440,g_string); end; for main_temp1 := 6 to parameters do begin str(average_string[main_temp1],g_string); setcolor(white); outtextxy(485+(main_temp1-5)*25,450,g_string); end; str(total_deviation:3:0,g_string); setcolor(white); outtextxy(560,385,g_string); if super_champ = population_size then begin setcolor(yellow); end else begin setcolor(white); end; str(super_champ,g_string); outtextxy(570,415,g_string); setcolor(white); str(game,g_string); setcolor(white); outtextxy(570,325,g_string); settextstyle(2, horizdir, 4); if (styl = 1) or (styl = 3) or (styl = 4) then main_temp3 := population_size else main_temp3 := population_size+1; for main_temp1 := 1 to main_temp3 do for main_temp2 := 1 to 5 do begin if main_temp2 mod 2 = 0 then setcolor(white) else setcolor(yellow); str(genetic[main_temp1][main_temp2], g_string); outtextxy(15+trunc(25*(main_temp2-1)),70+main_temp1*10,g_string); end; for main_temp1 := 1 to main_temp3 do for main_temp2 := 6 to 10 do begin if main_temp2 mod 2 = 0 then setcolor(white) else setcolor(yellow); str(genetic[main_temp1][main_temp2], g_string); outtextxy(485+trunc(25*(main_temp2-5)),70+main_temp1*10,g_string); end; if (styl = 1) or (styl = 3) or (styl = 4) then begin setcolor(4); moveto(507,61+10*((mem_genetic+1) )); lineto(628,61+10*((mem_genetic+1) )); moveto(12,61+10*((mem_genetic+1) )); lineto(133,61+10*((mem_genetic+1) )); moveto(12,261+10*((mem_genetic+1) )); lineto(133,261+10*((mem_genetic+1) )); setcolor(7); moveto(12,261+10*((turng+1))); lineto(133,261+10*((turng+1))); lineto(133,281+10*((turng+1))); lineto(12,281+10*((turng+1))); lineto(12,261+10*((turng+1)));

124

moveto(507,61+10*((turng+1) lineto(628,61+10*((turng+1) lineto(628,81+10*((turng+1) lineto(507,81+10*((turng+1) lineto(507,61+10*((turng+1)

)); )); )); )); ));

moveto(12,61+10*((turng+1) )); lineto(133,61+10*((turng+1))); lineto(133,81+10*((turng+1) )); lineto(12,81+10*((turng+1) )); lineto(12,61+10*((turng+1) )); end else begin setcolor(7); moveto(507,61+10*((turng+1) )); lineto(628,61+10*((turng+1) )); lineto(628,71+10*((turng+1) )); lineto(507,71+10*((turng+1) )); lineto(507,61+10*((turng+1) )); moveto(12,61+10*((turng+1) )); lineto(133,61+10*((turng+1))); lineto(133,71+10*((turng+1) )); lineto(12,71+10*((turng+1) )); lineto(12,61+10*((turng+1) )); moveto(12,261+10*((turng+1))); lineto(133,261+10*((turng+1))); lineto(133,271+10*((turng+1))); lineto(12,271+10*((turng+1))); lineto(12,261+10*((turng+1))); setcolor(3); moveto(507,61+10*((turng2) lineto(628,61+10*((turng2) lineto(628,71+10*((turng2) lineto(507,71+10*((turng2) lineto(507,61+10*((turng2) )); )); )); )); ));

moveto(12,61+10*((turng2) )); lineto(133,61+10*((turng2))); lineto(133,71+10*((turng2) )); lineto(12,71+10*((turng2) )); lineto(12,61+10*((turng2) )); moveto(12,261+10*((turng2))); lineto(133,261+10*((turng2))); lineto(133,271+10*((turng2))); lineto(12,271+10*((turng2))); lineto(12,261+10*((turng2))); end; settextstyle(2, horizdir, 4); for main_temp1 := 1 to main_temp3 do begin setcolor(white); str(fitness[main_temp1], g_string); outtextxy(40,270+main_temp1*10,g_string); setcolor(yellow); str(overall_fitness[main_temp1],fitness_string); outtextxy(15,270+main_temp1*10,fitness_string); setcolor(white); str(overall_fitness2[main_temp1],fitness_string); outtextxy(65,270+main_temp1*10,fitness_string); end; setcolor(white); str(generation, generation_string); outtextxy(568,295,generation_string); settextstyle(8, horizdir, 2); if (ua = 2) or (tz = 2) then begin putimage(286,206,sprite2,copyput); putimage(326,206,sprite1,copyput);

125

putimage(286,246,sprite1,copyput); putimage(326,246,sprite2,copyput); putimage(225,10,sprite4,xorput); end else begin putimage(326,206,sprite2,copyput); putimage(286,206,sprite1,copyput); putimage(326,246,sprite1,copyput); putimage(286,246,sprite2,copyput); putimage(225,10,sprite4,xorput); end; w := 3; z := 4; init1(board); move := 0; globr := 1; finish := 0; if (ua = 2) or (tz = 2) then begin board[4][4] := 1; board[5][5] := 1; board[4][5] := 2; board[5][4] := 2; end else begin board[4][4] := 2; board[5][5] := 2; board[4][5] := 1; board[5][4] := 1; end; case styl of 1:play_match(board); 2:play_classic(board); 3,4:play_mutation(board); 5:play_classic(board); 6,7,8:play_human(board); end; uc := 10; until (generation = (generations+2)) or (endp = 1) or (total_deviation <= standard_stop); if (generation = (generations+2)) then begin endp := 1; repeat until keypressed; end; if (total_deviation < standard_stop) then begin for main_temp1 := 1 to parameters do begin genetic_save[main_temp1] := genetic[1][main_temp1]; end; endp := 1; if super_champion = 1 then begin if super_champ = population_size then begin repeat until keypressed; super_champ := 1; super_champion := 2; end; end else begin repeat until keypressed; end; end; until endp = 111; end.

126

7.2.2 Otgraph unit


{ ********************************************************** * A unit with some procedures. This seperation had to be * * done because of the size limit the pascal compilers * * I have (Turbo Pascal 7.0 and Borland Pascal 7.0) * * impose for each module/unit. * ********************************************************** } unit otgraph; interface uses globvar,crt,graph; procedure grid; procedure init_vars; function choice(var board:vector1;i, o, color, change:integer;var vect_coul:vector_color):integer; implementation { ********************************************************* * The following procedure draws an 8x8 board on the * * screen. * ********************************************************* } procedure grid; var a, b, c ,d, e, f, k: integer; graphdriver, graphmode: integer; nombre,text_: string; begin setcolor(uc); setbkcolor(green); a := 160; b := 80; c := 480; d := 80; setcolor(white); rectangle(0,0,640,480); rectangle(140,65,500,430); setcolor(tv); setfillstyle(solidfill, tv); floodfill(100,100,white); setcolor(8); rectangle(139,64,501,431); rectangle(138,63,502,432); setcolor(uc); rectangle(0,0,639,479); rectangle(2,2,637,477); setfillstyle(solidfill, lightgray); floodfill(1,1,uc); setcolor(7); a := 160; b := 80; c := 160; d := 400; for f := 1 to 9 do begin moveto(a+1,b+1); lineto(c+1,d+1); moveto(a+2,b+1); lineto(c+2,d+1); a := a + 40; c := c + 40; end; a := 160; b := 80; c := 480; d := 80; setcolor(7); for e := 1 to 9 do begin moveto(a,b+2); lineto(c,d+2);

127

moveto(a,b+1); lineto(c,d+1); b := b + 40; d := d + 40; end; a b c d := := := := 160; 80; 480; 80;

setcolor(uc); for e := 1 to 9 do begin moveto(a,b); lineto(c,d); b := b + 40; d := d + 40; end; setcolor(uc); a := 160; b := 80; c := 160; d := 400; for f := 1 to 9 do begin moveto(a,b); lineto(c,d); a := a + 40; c := c + 40; end; setcolor(7); moveto(481,80); lineto(481,402); moveto(482,80); lineto(482,402); setcolor(white); settextstyle(8, horizdir, 2); for k := 8 downto 1 do begin str(k, nombre); outtextxy(145,45+((k)*40),nombre); end; text_ := 'ABCDEFGH'; for k := 1 to 8 do outtextxy(175+((k-1)*40),400,text_[k]); settextstyle(7, horizdir, 1); setcolor(11); settextstyle(8, horizdir, 2); end; { ********************************************************* * The following procedure initializes some variables * ********************************************************* } procedure init_vars; begin cleardevice; setcolor(8); setfillstyle(solidfill, 8); circle(100,100,13); floodfill(100,100,8); if (ua = 1) and (tz = 1) then getimage(114,114,86,86,sprite2) else getimage(114,114,86,86,sprite1); setcolor(white); setfillstyle(solidfill, white); circle(100,100,13); floodfill(100,100,white); if (ua = 1) and (tz = 1) then getimage(114,114,86,86,sprite1) else getimage(114,114,86,86,sprite2); cleardevice; setcolor(11); setfillstyle(solidfill, 11); circle(100,100,2); floodfill(100,100,11); getimage(103,103,97,97,sprite3);

128

cleardevice; setcolor(yellow); setfillstyle(solidfill, yellow); circle(100,100,2); floodfill(100,100,yellow); getimage(103,103,97,97,sprite6); cleardevice; setcolor(15); setfillstyle(solidfill, 15); circle(100,100,2); floodfill(100,100,15); getimage(103,103,97,97,sprite4); cleardevice; setcolor(7); setfillstyle(solidfill, 7); circle(100,100,2); floodfill(100,100,7); getimage(103,103,97,97,sprite5); cleardevice; end; { *********************************************************** * The following function is the same as the one following * * it, only in a much more compact form. Its disadvantage * * is that it is just a little bit slower, don't ask me * * why.. i have no clue :) Must be some compiler * * subtelties.. It has several functions: either test if * * a certain position is accessible for a particular * * player, or actually put a disc in a specific place and * * thus flip some dics, this can be done virtually, * * meaning the screen doesn't change, or it can be done * * in draw mode, meaning it will draw the new board. * *********************************************************** } (* function choice(var board:vector1;i, o, color, change:integer):integer; var tabsave: vector1; direction, save_i, save_o, save_i2, save_o2, save_i3, save_o3, enter: integer; begin direction := 1; save_i := i; choice := 0; save_o := o; save_i2 := i; save_o2 := o; enter := 0; tabsave := board; repeat case direction of 1: inc(save_i); 2: inc(save_o); 3: dec(save_i); 4: dec(save_o); 5: begin inc(save_i); inc(save_o); end; 6: begin inc(save_i); dec(save_o); end; 7: begin dec(save_i); inc(save_o); end; 8: begin dec(save_i); dec(save_o); end; end; if (save_i < 1) or (save_i > 8) or (save_o < 1) or (save_o > 8) then begin inc(direction); save_i := i; save_o := o; enter := 0; board := tabsave; end else if (board[save_i][save_o] = color) and (enter = 1) then begin if change = 0 then begin direction := 9; choice := 1; board := tabsave; end else begin tabsave := board; inc(direction);

129

save_i := i; save_o := o; enter := 0; end; end else if ((board[save_i][save_o] = 0) and (enter = 0)) or ((board[save_i][save_o] = 0) and (enter = 1)) or ((board[save_i][save_o] = color) and (enter = 0)) then begin board := tabsave; inc(direction); save_i := i; save_o := o; enter := 0; end else if (board[save_i][save_o] = 3-color) then begin enter := 1; if change = 1 then begin if colo = 1 then begin save_i3 := save_i; save_o3 := save_o; while (save_i3 < 9) and (save_i3 > 0) and (save_o3 < 9) and (save_o3 > 0) and (board[save_i3][save_o3] = 3-color) do begin case direction of 1: inc(save_i3); 2: inc(save_o3); 3: dec(save_i3); 4: dec(save_o3); 5: begin inc(save_i3); inc(save_o3); end; 6: begin inc(save_i3); dec(save_o3); end; 7: begin dec(save_i3); inc(save_o3); end; 8: begin dec(save_i3); dec(save_o3); end; end; end; if (save_i3 < 9) and (save_i3 > 0) and (save_o3 < 9) and (save_o3 > 0) and (board[save_i3][save_o3] = color) then begin if color = 1 then begin putimage(126+(40*save_o2),46+(40*save_i2),sprite2,copyput); putimage(126+(40*save_o),46+(40*save_i),sprite2,copyput); end else begin putimage(126+(40*save_o2),46+(40*save_i2),sprite1,copyput); putimage(126+(40*save_o),46+(40*save_i),sprite1,copyput); end; end; end; end; board[save_i][save_o] := color; board[save_i2][save_o2] := color; end; until direction = 9; end; *) { *********************************************************** * Same as previous function, only a tinny faster. * * *********************************************************** } function choice(var board:vector1;i, o, color, change:integer;var vect_coul:vector_color):integer; var tempi, tempo, pass, tempf: integer; Ok, found: boolean; label 1; begin Ok := true; found := false;

130

tempi := i; tempo := o; pass := 0; gla := 0; uh := 0; if tempi < 7 then while ((Ok) and (tempi < 8)) do begin if (board[tempi+1][tempo] = 3-color) then begin tempi := tempi + 1; pass := 1; end else if ((board[tempi+1][tempo] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempi:=i; tempf:=0; while ((board[tempi][tempo] <> color) or (tempf = 0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color; inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end; tempi := tempi + 1; end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; Ok:=true; tempi:=i; tempo:=o; pass:=0; if tempi > 2 then while ((Ok) and (tempi > 1)) do begin if (board[tempi-1][tempo] = 3-color) then begin tempi := tempi - 1; pass := 1; end else if ((board[tempi-1][tempo] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempi:=i; tempf:=0; while ((board[tempi][tempo] <> color)or(tempf=0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color;

131

inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end; tempi := tempi - 1; end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; Ok:=true; tempi:=i; tempo:=o; pass:=0; if tempo > 2 then while ((Ok) and (tempo > 1)) do begin if (board[tempi][tempo-1] = 3-color) then begin tempo := tempo - 1; pass := 1; end else if ((board[tempi][tempo-1] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempo:=o; tempf:=0; while ((board[tempi][tempo] <> color) or (tempf = 0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color; inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end; tempo:= tempo - 1; end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; Ok:=true; tempi:=i; tempo:=o; pass:=0; if tempo < 7 then while ((Ok) and (tempo < 8)) do begin if (board[tempi][tempo+1] = 3-color) then begin

132

tempo := tempo + 1; pass := 1; end else if ((board[tempi][tempo+1] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempo:=o; tempf:=0; while ((board[tempi][tempo] <> color) or (tempf=0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color; inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end; tempo := tempo + 1; end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; Ok:=true; tempi:=i; tempo:=o; pass:=0; if (tempi < 7) and (tempo < 7) then while ((Ok) and (tempi < 8) and (tempo < 8)) do begin if (board[tempi+1][tempo+1] = 3-color) then begin tempi := tempi + 1; tempo := tempo + 1; pass := 1; end else if ((board[tempi+1][tempo+1] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempo:=o; tempi:=i; tempf:=0; while ((board[tempi][tempo] <> color) or (tempf = 0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color; inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end;

133

tempo := tempo + 1; tempi := tempi + 1; end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; Ok:=true; tempi:=i; tempo:=o; pass:=0; if (tempi < 7) and (tempo > 2) then while ((Ok) and (tempi < 8) and (tempo > 1)) do begin if (board[tempi+1][tempo-1] = 3-color) then begin tempi := tempi + 1; tempo := tempo - 1; pass := 1; end else if ((board[tempi+1][tempo-1] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempo:=o; tempi:=i; tempf:=0; while ((board[tempi][tempo] <> color)or(tempf=0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color; inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end; tempo := tempo - 1; tempi := tempi + 1; end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; Ok:=true; tempi:=i; tempo:=o; pass:=0; if (tempi > 2) and (tempo > 2) then while ((Ok) and (tempi > 1) and (tempo > 1)) do begin if (board[tempi-1][tempo-1] = 3-color) then begin tempi := tempi - 1; tempo := tempo - 1; pass := 1; end else

134

if ((board[tempi-1][tempo-1] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempo:=o; tempi:=i; tempf:=0; while ((board[tempi][tempo] <> color)or(tempf=0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color; inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end; tempo := tempo - 1; tempi := tempi - 1; end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; Ok:=true; tempi:=i; tempo:=o; pass:=0; if (tempi > 2) and (tempo < 7) then while ((Ok) and (tempi > 1) and (tempo < 8)) do begin if (board[tempi-1][tempo+1] = 3-color) then begin tempi := tempi - 1; tempo := tempo + 1; pass := 1; end else if ((board[tempi-1][tempo+1] = color) and (pass = 1)) then begin Ok := false; found := true; if (change=1) then begin dec(gla); tempo:=o; tempi:=i; tempf:=0; while ((board[tempi][tempo] <> color)or(tempf=0)) do begin inc(vect_coul[color]); dec(vect_coul[3-color]); tempf:=1; board[tempi][tempo] := color; inc(gla); if colo = 1 then begin delay(uh*16); if color = 1 then putimage(126+(40*tempo),46+(40*tempi),sprite2,copyput) else putimage(126+(40*tempo),46+(40*tempi),sprite1,copyput); end; tempo := tempo + 1; tempi := tempi - 1;

135

end; dec(vect_coul[color]); inc(vect_coul[3-color]); end else goto 1; end else begin Ok := false; end; end; 1:if (found = true) then choice := 1 else choice := 0; if (found = true) then inc(vect_coul[color]); end; begin end.

7.2.3 Globvar unit


{ ********************************************************* * Global variables unit. * ********************************************************* } { ********************************************************* * This unit defines all the global variables of the * * program. * ********************************************************* } unit globvar; interface uses crt, dos, graph; type genes = array[1..34,1..10] of integer; vector1 = array[1..8, 1..8] of integer; vector2 = array[1..64, 1..2] of integer; vector3 = array[1..10] of integer; vector4 = array[1..16] of integer; vector5 = array[1..64] of vector1; vector6 = array[1..64] of integer; vector7 = array[1..10] of real; vector8 = array[1..500] of real; vector9 = array[1..10,1..500] of real; vectortest = array[1..32000] of integer; vector_color = array[1..2] of integer; typeimage = array[1..1000] of byte; arrayg = array[1..12] of integer; min1 = array[1..30] of integer; globb = array[0..64,1..30] of integer; const use_othello = 1; endgame = 14; var genetic, temp_genetic, genetic_memory, genetic_binome: genes; board: vector1; tabla, vw, save_algol: vector2; save_mid_string, genetic_save, best, worst, middle, save_genetic, average_string: vector3; fitness, overall_fitness, fitness_tmp, fitness_save, overall_fitness2: vector4; tabsave: vector5; colors, fitness_rank: vector6; mean, mean2, average: vector7; average_mem: vector8; deviation_mem: vector9; vect_coul, no_u: vector_color; sprite1,sprite2, sprite3, sprite4, sprite5, sprite6, tempim: typeimage; hash_alpha, hash_beta, hash_gamma, hash_delta: ^vectortest; binc1, binc2: arrayg; min, min2: min1; xyglob: globb;

136

b1, b2, b3: boolean; total_deviation: real; maxx: longint; mx, my: word; g_string, g_string2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, op1_string, op2_string, fitness_string, generation_string: string; eval_glob, yglob, xglob, var1, lib, glu, u, ua, ub, ug, uc, uh, hb, tu, tz, minx, miny, maxxx, maxy, style, gennum, eval, hb_save, gla, p3, p4, r1, r2, r3, r4, y7, y8, y9, y10, temp1, yyglob, xxglob, x, y, aaa, bbb, ccc, ddd, eee, globe, op1, op2, passes, main_temp1, main_temp2, generation, temp_hb, temp_random, cccc, turng, turng2, mem_genetic, save_fit1, save_fit2, next_gen, game, main_temp3, calculate, styl, mutation, crossover_prob, population_size, parameters, wins, losses, draws, test_mode, wins_save, losses_save, draws_save, generations, recombination, random_var, mutation_algo, mutation_rate, random_value, hash_table, average_recombination, mutation_start, sort_selection, sort_generation, endp, num_games, in_file, rewrite_history, rank_fitness, classic_fitness, save_string, standard_stop, super_champion, super_champ, ncrossover, eval_gla, bit_used, one, two, one2, two2, one3, two3, one4, two4, one5, two5, one6, two6, numempty, save_one, save_two, value, color_global, save_color, stop, end_alpha, vvvv1, vvvv2, coup_save, v1_save, v2_save, back, last, tv, v1, v2, ff, glob, cc, dd, ee, w, z, globr, move, finish, gg, colo, graphdriver, graphmode, colm, vvv1, vvv2: integer; file_genetic, param_file, rand_file, indr, moves_file: text; implementation begin end.

137

8
BIBLIOGRAPHY

BAYART, FREDERIC : Complexit de lalgorithme alpha-bta Ecole Normale Suprieure de Cachan, Dpartement de mathmatiques et informatique, thse 1996-1997 CHRISTOPHER, D. ROSIN, and, RICHARD K. BELEW : Methods for Competitive Co-evolution: Finding Opponents Worth Beating, Cognitive Computer Science Research Group, Department of Computer Science and Engineering, submitted to ICGA 95. DARRELL WHITLEY : A Genetic Algorithm Tutorial Computer Science Department, Colorado State University. DIRK, EDDELBUTTEL : A Genetic Algorithm for Passive Management Diplme des Etudes Approfondies en Economie Mathmatique et Economtrie, September 1992. FRANK, HOFFMEISTER, and, THOMAS BACK : Genetic Self-Learning University of Dortmund, Department of Computer Science XI. FRANK, HOFFMEISTER, and, THOMAS BACK : Extended Selection Mechanisms in Genetic Algorithms University of Dortmund, Department of Computer Science XI. GABRIEL, J. FERRER : Using Genetic Programming to Evolve Board Evaluation Functions MSc, August 1996, Faculty of the School of Engineering and Applied Science, University of Virginia. HSIAO-LAN, FANG: Investigating Genetic Algorithms for Scheduling MSc Dissertation, Department of Artificial Intellignece, University of Edinburgh, 1992. JOSE, RIBEIRO FILHO, and, CESARE ALIPPI, and, PHILIP TRELEAVEN : Genetic Algorithm Programming Environments Computer Science Department, University College London.

138

LAZARD, EMMANUEL, and, F.F.O. TEAM : Introduction to Othello, March 1993, http://www.wavefront.com/~colin/gr_ffo.html. MARTIN FIERTZ : Strategy Game Programming, an introduction, http://ourworld.compuserve.com/homepages/fierz/strategy.htm YANN, TAKVORIAN : Echecs et C Editions Radio, mars 1991

139

Anda mungkin juga menyukai