Bachelor's thesis Life and death solver in Go

64
Charles University at Prague Faculty of mathematics and physics Bachelor’s thesis Tom´ s Kozelek Life and death solver in Go Department of theoretic informatics and mathematical logic Supervisor: Mgr. Marta Vomlelov´ a, Ph.D. Study program: general informatics 2006

Transcript of Bachelor's thesis Life and death solver in Go

Charles University at PragueFaculty of mathematics and physics

Bachelor’s thesis

Tomas Kozelek

Life and death solver in Go

Department of theoretic informatics and mathematical logic

Supervisor:

Mgr. Marta Vomlelova, Ph.D.

Study program: general informatics

2006

1

On this place I would like to thank to the supervisor of my thesis for all the time spent atconsultations and for racional argumetation. Also I would like to thank to all Go playerswho gave me inspiring ideas.

Prohlasuji, ze jsem svou bakalarskou praci napsal samostatne a vyhradne s pouzitım cito-vanych pramenu. Souhlasım se zapujcovanım prace a jejım zverejnovanım.

V Praze dne 30. kvetna 2006 Tomas Kozelek

2

3

Contents

1 Introduction 81.1 Thesis preview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.2 About the game of Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91.3 Rules of Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91.4 Computers and Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111.5 Motivation for life and death analysis . . . . . . . . . . . . . . . . . . . . . . 121.6 Existing life and death solvers . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2 Known algortihms 142.1 Minimax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142.2 Alpha beta pruning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152.3 Iterative deepening depth first search . . . . . . . . . . . . . . . . . . . . . . 162.4 Heuristics and forward pruning . . . . . . . . . . . . . . . . . . . . . . . . . 172.5 Zobrist hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.6 Transposition Tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202.7 Pattern matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3 Solutions to life and death solving key issues 233.1 Position representation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233.2 Search stopping conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.3 Static eye recognition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243.4 Repetitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263.5 Under the stones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263.6 Ko . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273.7 Pass moves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4 Performance and testing 314.1 Performance analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314.2 Program testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334.3 Program input limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

5 Conclusion 355.1 Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355.2 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

6 Go terms glossary 37

4

A TGA Programming manual 39A.1 About . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39A.2 Documentation hiearchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39A.3 Handling Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40A.4 Tree search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

A.4.1 Tree search implementation description . . . . . . . . . . . . . . . . . 41A.4.2 Transposition tables implementation . . . . . . . . . . . . . . . . . . 45

A.5 Static eye recognition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47A.5.1 Recognizing status of the group . . . . . . . . . . . . . . . . . . . . . 47A.5.2 Recognizing status of the potential eye . . . . . . . . . . . . . . . . . 48

A.6 Starting search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48A.6.1 After the start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48A.6.2 Handling a single problem . . . . . . . . . . . . . . . . . . . . . . . . 48A.6.3 Analysing single position . . . . . . . . . . . . . . . . . . . . . . . . . 49

A.7 Sgf parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50A.7.1 Sgf format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50A.7.2 Parsing automata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51A.7.3 Parsing proccess . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

A.8 Position representation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52A.8.1 How is position represented . . . . . . . . . . . . . . . . . . . . . . . 52A.8.2 Position initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . 53A.8.3 Playing a move . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54A.8.4 Unplaying moves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56A.8.5 Static heuristics methods . . . . . . . . . . . . . . . . . . . . . . . . . 57

B TGA User manual 58B.1 About . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58B.2 Installation & miscellanous . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58B.3 Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

B.3.1 Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59B.3.2 Input sgf file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

B.4 Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61B.4.1 Output sgf files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

5

6

Nazev prace : Life and death solver in GoAutor: Tomas KozelekKatedra (ustav): Katedra teoreticke informatiky a matematicke logikyVedoucı bakalarske prace: Mgr. Marta Vomlelova, Ph.D.e-mail vedoucıho: [email protected]

Abstrakt: V teto praci jsem se zameril na studium problematiky resenı uloh ”zivota a smrti”v Go, jez je jednou ze zakladnıch dovednostı ktere by mel program hrajıcı Go zvladnout. Jakosoucast prace vznikl program TGA, resıcı tyto ulohy. Program je postaven na zakladnıch al-goritmech prohledavanı stavoveho prostoru z teorie her ( napr. alfa beta prorezavanı, trans-pozicnı tabulky ) v kombinaci s metodami vyuzıvajıcımi znalosti hry Go ( heuristiky aorezavacı metody ). Pro ucely programu jsem mimo jine vytvoril ”blokove orientovanou”prezentaci pozice, implementoval jsem zjednodusenou statickou analyzu zivota a smrti sku-piny a navrhl jsem sadu heuristik, ktere nejen vyznamne zrychlujı vypocet programu ale takeumoznujı resit obtızne ulohy typu ”under the stones”. Program je urcen k resenı prevazneuzavrenych uloh a umı si poradit s ruznymi specialitami problematiky zivota a smrti v Go (napr. ruzne druhy ko, seki, ”bent four in the corner” ). Co se vykonnosti tyce, sılu programuv resenı specifikovanych Go uloh odhaduji na 1 dan, coz je srovnatelne s velmi pokrocilymlidskym hracem.Klıcova slova: Go, hra dvou hracu, prohledavanı stromu, zivot a smrt

Title : Life and death solver in GoAuthor: Tomas KozelekDepartment: Katedra teoreticke informatiky a matematicke logikySupervisor: Mgr. Marta Vomlelova, Ph.D.Supervisor’s e-mail address: [email protected]

Abstract: In this thesis I focused myself on problematics of solving life and death problemsin the game of Go, which is one of fundamental skills of a Go playing program. Togetherwith thesis, life and death solving program TGA was created. Program is built upon basicspace search algorithms from the game theory ( e.g. alpha beta pruning, transposition tables) in combination with methods using knowledges about the game of Go ( heuristics andpruning methods ). For program purposes I created ”block oriented” position representation,I implemented simplified static analysis of life and death of the group and I proposed a setof heuristic. These heuristics not only speed up search significantly, moreover they make itpossible to solve difficult problems of ”under the stones” type. Program is designed to solvemostly enclosed problems and it is capable to treat different life and death solving pecularities( e.g. different types of ko, seki, ”bent four in the corner” ). As for performance, I estimateprogram’s strength in solving specified Go problems to be 1 dan. This is comparable with astrong human player.Key words: Go, game of two players, tree search, life and death

7

Chapter 1

Introduction

1.1 Thesis preview

Here is a short description of all chapters:

In the first chapter a short introduction into the game of Go is provided and rules areexplained. Emphasis on life and death principles is put in the rules explanation. Next,there are stated reasons why Go programs are rather weak. At last some existing lifeand death solvers are presented and compared.

Second chapter deals with the known algorithms. Basic tree search algorithms and pruningmethods used in life and death solving in Go are described together with comments ontheir implementation in TGA ( e.g. minimax, alpha beta pruning, transposition tables,zobrist hashing).

Third chapter describes how I dealt with the important issues specific to the game of Go.Here, the ”block oriented” position representation I used is described. Implementedsearch stopping criteria are shown. Ko handling and pass moves handling is explained.

In the fourth chapter overview on the program performance is given. Factors influencingspeed of the search are discussed here. Also potential testing method is presented. Aparagraph devoted to the program limitations in input problems is included.

The fifth chapter summarizes pros and cons of the program. Potential future tasks andimprovements are mentioned here.

Glossary of basic Go terms used in the thesis is included.

First appendix contains chosen parts of TGA programming manual. Here program modulesare described and particular tasks program accomplishes are presented in detail ( e.g.tree search, position representation ).

Second appendix contains user manual to TGA. Here is shown how to operate program andexamples of correct sgf inputs and outputs are provided.

8

1.2 About the game of Go

Go is an ancient board game invented probably in China 4000 years ago. It flourished inEast Asia especially Japan, China and ( in recent time ) in Korea , in these countries it iswidespread and tightly connected with everyday’s life : like football in Europe. It is also here( in East Asia ), where most intensive research in computer Go is being done. Characteristictreit’s of Go are huge strategic deepness, complexity and also tactics (for more informationabout Go itself see [11]).

1.3 Rules of Go

It is said that learning the rules of Go takes at most five minutes, but mastering Go takesthe whole lifetime. I present here the basic rules for reader’s not familiar with Go. I mostlyput emphasis on explaining terms and concepts connected with life and death solving.

Go is played by two players black and white, who are placing stones ( one stone a turn ) onthe intersections of a Go board ( also called goban, usuall size of board is 19 x 19 intersections,but smaller boards are used for special purposes as well). Black player starts the game. Everysingle stone has so called liberties: empty adjacent intersections in horizontal and verticalorientation. Maximum number of liberties of one stone is thus four ( for stone on the edgethree and for stone in the corner two ). Stones connected by lines share their liberties. Theseconnected stones create so called chains ( in computer Go called blocks). When a stone (or a block ) is deprived of all it’s liberties by enemy stones it is captured, removed from theboard and becomes opponent’s prisoner. Stone might not be played on an intersection ifthis action is taking away last liberty of some friendly block ( or itself ) : so called suicide.The only exception to this rule is when some enemy block is captured by this move. Inthat case capturing enemy stones is superior to suicide. There might arise a situation on theboard when both players could be capturing the enemy stone forever ( similar situation mightappear on the edge or in the corner, see Figure 1.1). Therefore ko rule exists ( ko means inJapanese infinity ): a stone cannot be played on the intersection, if same position as after (same player’s ) last turn would appear. Thus when fighting the ko ( trying to capture theenemy stone and then connect or continue with some other move in area ) player who cannottake the ko because of violating the ko rule, plays a ko threat usually in the other part ofthe board and after opponent replies to this ko threat he might retake the ko ( if opponentignores the ko threat and connects the ko, player might apply his ko threat and usually gainssome local advantage).

Stones which might not be connected from definition ( blocks ) but are closely related arecalled groups ( it is difficult to disconnect stones belonging to one group, or they are simplyall near together ). Player whom the group belongs and who is caring for it is called defender

( he is defending it ), his opponent who is trying to attack the group is called attacker. Weare interested in situation when a group is isolated from it’s friendly groups and must takecare of itself alone. If the whole group can be captured ( part by part or whole in one turn) and the defender cannot prevent this then the group is unconditionally dead ( or simplydead ). It still dwells on the board, but both players know that in the end it will be removedfrom the board and it’s stones will be attacker’s prisoners. On the other hand if the groupcannot be captured even if attacker tries to, it is unconditionally alive ( or simply alive ).This is possible because of so called eyes. Eye is a coordinate where attacker is allowed to

9

Figure 1.1: Defender must win and connect the ko to live.

Figure 1.2: Example of the living group with two eyes ( in the left ) and of the dead groupwith only one eye ( in the right).

play only if it is the last liberty of the group - and thus capturing the group - otherwise heis prevented to play there by the suicide rule ( more exact definition of eye is given in §6). If defender’s group has at least two connected eyes then it cannot be captured ( becauseof the suicide rule attacker cannot fill both eyes ). Third type of groups is called unsettled

these groups might be killed if the attacker moves first ( it means that after his move groupis dead ) or it might be saved if the defender moves first ( after his move it is alive). If wetake the ko rule into consideration, situation gets a little bit complicated : if the group isnot unconditionally alive, but attacker has to win some ko inside the group to kill the group,then the group is alive in ko ( attacker or defender starts the ko and then attacker ignoresone or more defender’s ko threats to win the ko - this situation is no more local because bothplayers must consider the board as a whole ). There is one last major complication with lifeand death problems’s statuses: if attacker cannot kill the group in his turn, but there are stillsome attacker’s stone inside the group and every defender’s attempt to capture them leadsto the death of the group, then the group is alive in seki ( it means that group is alive butit posses no territory in final counting, see Figure 1.3 ). Generally there is a simple orderingin group statuses ( from the point of defender ) alive > alive in seki > alive in ko > dead.However, in actual game this ordering must be considered in the context of global situation.

Purpose of the game is surrounding territory ( empty intersections ) for myself and tak-ing away enemy’s territory. Both players are alternating in their turns until both of themconsecutively pass ( special move when no stone is placed and turn goes to opponent, ”emptymove” ). Territory is then counted as a sum of surrounded intersections plus captured enemystones. Because black has an advantage of moving first, white is given compensation in komi:point bonus ( usually 6.5 points ) which is added to his score in the end of the game. As a

10

Figure 1.3: White’s group is alive in seki.

result it is very improbable to achieve a draw in the game of Go. There are several differentrulesets ( e.g. Japanese, AGA, Ing , New Zealand ) which slightly differ in some details ofthe basic rules and mostly in the way territory is counted. Here, rules are explained accord-ing to Japanese ruleset, see [11]. It might be a little confusing how territory is described :”surrounded intersections” is not very exact definition. Actually there is a precise definitionwhat is and what is not territory. Territory ( in Japanese ruleset ) is sum of all intersectionswhere opponent cannot create a living group. Therefore when both players consecutivelypass in the end of the game, they are saying : ”I consider all intersections you surroundedyour territory”.

To distinguish the strength of players ( or computers ) there is a simple ( and approximate) ranking system in Go. Players are divided into amateurs ( they play mostly for fun ) andprofessionals ( their living depends on go, to become a professional player must go throughextremely difficult tournament with other players intending to become professionals ). In Gosimilar system is used as in other east asian sports: 20 kyu < ... < 1 kyu < 1 dan < ... <

7 dan <= 1 dan professional < ... < 9 dan professional. Kyu are ”student” classes, dan are”master” classes.

1.4 Computers and Go

Since Go is the last board game where computers are still on very amateur level ( strongestprogram is around 6 kyu ) it is natural that huge money and effort have been invested intoresearch in computer Go in recent times. Actually, for a long time there was a 1,000,000 $prize for a Go program which could beat a 1 dan professional player. Even thought it expiredin the year 2000 there are many similar prizes for Go playing programs as well as annualchampionships and other activities encouraging research in computer go. Because usuall treesearch techniques ( which are successfull in other board games ) can be applied in computerGo only in a limited way many promising methods were developed especially for computerGo ( see §2 ). However, results are still unsatisfying. Main reasons why it will take a longtime till Go program beats a strong human player are:

High branching factor While in chess average number of possible moves is around 36 ( ina single position ), in Go this number highly exceeds 100. However, it was found thateven on 9x9 board it is difficult to develop a strong program.

Huge Complexity In spite of chess ( where ”search and evaluate” principle is very suc-cessfull ) in Go there has not yet been found sufficient evaluating function which would

11

make ”search and evaluate” method usable. The reason is that in Go two positionswhich differ only in a single stone placement might produce diametrally different results.

Local vs. Global position Attempts to ”parcelize” problem of the board evaluation intoevaluation of local situations are limited by fact that in Go all local situations aretightly related in both tactical ( e.g. ko, ladders ) and also strategic meaning ( e.g.groups strength, territory outlines ).

More on this topic is in [3].

1.5 Motivation for life and death analysis

Solving life and death of the groups is one of the basic skills every Go playing entit ( man orprogram ) should handle. In the actual game, the status of particular groups is one of mainfactors to decide the next move: from the strategic aspect ( the direction of the game ) aswell as from the tactical aspect ( e.g. to kill the opponent’s group, to start the ko inside thegroup ). Human players spend a lot of time studying and solving life and death problems,because the ability to correctly solve ( complicated ) life and death problem might decide agame. For those who are not yet very comfortable with thinking in Go terms, solving lifeand death problems ( also shortened as life and death ) might be compared to solving chessproblems : for instance black mates in three moves , white to gain material advantage.

Actually there are two main different attitudes for a computer program towards life anddeath problematics. These are: solving problem of life and death alone ( usually presentedat some part of the board with optional additional information - this is an attitude the restof this thesis is about ) and ability to solve problem in the actual game . The latter one isnaturally ( much ) more difficult because program has to:

recognize problem This covers distinguishing groups and their optional connections, rec-ognizing urgency of solving life and death for particular groups, etc. For instance someGo playing programs use a simple principle that any group with at least five libertiesis alive and when a group has less liberties the program starts life and death analysis( what might be already too late, see [8]). For humans, after gaining little experience,it becomes quite natural to recognize which groups are in trouble and should deserveplayer’s focus. However, this is such a complex problem that computers are muchbehind here.

solve it in the context of the game Here, many aspects must be taken into account, e.g.ladders across the board, possible connection of defender’s group to outside, ability towin the ko ( number of ko threats ) , the most profitable way to make the group alive( with how many points groups live, if there is a ko who takes it first ).

1.6 Existing life and death solvers

Generally it is usual to divide Go playing programs ( and so it is possible to divide pure lifeand death solvers ) into :

12

heuristic oriented A tree search engine is implemented with the classical minimax algo-rithm supported by alpha beta pruning. Good order of moves for alpha beta is reachedby using loads of heuristics. Transposition tables might be used.

tree search oriented In recent times usually some variants of df-pn ( see [6] ) search isimplemented relying on large transposition tables to speed up search.

Probably the best known life and death solver is Go Tools programmed by Thomas Wolfe( see [1] ). It’s development took more than 15 years and thus it’s performance in life anddeath are comparable to those of very strong amateur players ( around 5d ). Go Tools is anexample of heuristic oriented searcher. There are also many special features included in theprogram, e.g. output to several formats, problems generation ( it is said to generate around100 reasonable problems during a night), possible human co-operative mode ( human andGo Tools are working together on problem solving).There are also several Go programs ( forinstance see [7] ) similar to Go Tools ( in heuristic oriented approach ), howewer quite behindin efficiency.

Explorer ( see [5] ) is a tree search oriented program based on df-pn search ( using up toaround 300MB for transposition tables lookup ). Recently, it showed even better performancethan Go Tools on the set of the most difficult problems generated by Go Tools itself.

TGA is a heuristic oriented life and death solver.

13

Chapter 2

Known algortihms

There has been a lot of research in computer Go in recent years, therefore many effectivealgorithms covering different aspects of the game have been developed. I will focus onlyon algorithms contributing to computer life and death solving and especially on those Iimplemented myself in TGA. I found many useful information about described algorithmsin [13].

2.1 Minimax

Minimax is a very basic algorithm used in the games of two players with full informationand zero sum. Players are called maximizer and minimizer. It is a recursive algorithm forsearching a game tree. It’s idea is quite straight yet very powerfull. There are two types ofnodes in the tree :

In maximizing node maximizer is to play.

In minimazing node minimizer is to play.

Every node in the tree might be evaluated with a number ( expressing result of the gamein actual position ). The higher the number, the better the result for maximizer and viceversa. There are two ways how to evaluate a node:

Use static evaluating function. This function is usually used in deeper levels of the treeand it might not always return an evaluation of the position ( it still might be uncertain). Howewer it must return correct evaluation in a leaf node. In TGA this evaluation isdone by static eye recognition combined with simple capture recognition ( when mostof the defender’s group is captured it is stated dead ). These evaluating functions arevery quick and thus they are started in every node. However, only in deeper levelscertain evaluation is returned.

Retrieve evaluation from sons. This requests all sons to be already evaluated. Actuallyhere lies a spirit of the minimax algorithm. When actual node is maximizing, it’sevaluation is set to a maximum of evaluations of his sons ( maximizer choses the bestmove for him - the one with highest evaluation ). In minimazing node, evaluation is setto a minimum of his sons ( best move for minimizer is the one with lowest evaluation).

14

Minimax algorithm goes through the tree ( usually ) in depth-first order and evaluatesnodes according to two mentioned rules. This way every node in the tree is evaluated. Asmentioned above a node evaluation represents how good/bad the actual position is for eitherof players, evaluation of the root node expresses the result of the input position for actualplayer to move. This evaluation is optimal for both ( neither of players can reach a betterresult when his opponent plays best moves ) .

Famous John Von Neumann’s theorem says that for every game of two players with fullinformation and zero-sum ( ”what one player gains the other looses” ) there is a non - loosingstrategy for one player. Solving Go problems is also of this kind. We might suppose life ofthe group as a victory for maximizer, death of the group for minimizer and ko as a draw.Minimax algorithm is proved to always find this optimal strategy ( or simply the final result). Howewer, sometimes a lot of useless search is performed ( sub trees not influencing the finalresult are searched). To prevent this there are many possibilities how to speed up minimaxsearch a lot by pruning large number of positions. Some of these are used in TGA and theywill be mentioned later ( see §2.2, §2.4, §2.6).

In TGA defender of actual group is always a maximizer and attacker is a minimizer.I didn’t use negamax ( very common modificiation of minimax ) because actions taken indefender’s and attacker’s node are not symetrical ( e.g. they differ in used heuristics and insome group status transformations, see [9] ).

2.2 Alpha beta pruning

Probably the best known pruning method related to the minimax algorithm is alpha betapruning. It is a simple yet very powerful method. Alpha beta principle is following : whenduring a search there is found a node which makes one of the players to avoid the actualbranch ( because he is already proved to achieve a better result in different - previous - partof the tree ) no brothers of this node are searched anymore. This variant is about to beavoided, therefore any further search is a time loss. The algorithm operates with two values,alpha and beta. Alpha represents the minimum score that the maximizing player can get.Beta represents the maximum score that the minimizing player can get. In the beginning ofthe search alpha is set to negative infinity and beta is set to positive infinity. As the searchgoes on this ”window” becomes narrower thus excluding some values and pruning the search.If beta is lesser than alpha in some node, then alpha beta cutoff is performed since in theoptimal play players would avoid this whole branch.

The ( very easy ) practical example from Go problems solving follows ( see Figure 2.1 ).Let’s take a tree where root node ( R ) is maximizing ( defender’s ). First son of the rootnode ( A ) proved group status ko ( defender can live through ko in this variant ). Nowsecond root’s son ( B ) is being searched ( it is minimizing - attacker’s node ). First nodehere ( C - first son of B ) proves status ko as well. At this point alpha beta cutoff on othersons of B is performed and control is returned to the root node and next node in the rootlevel is searched. This is because when C has a status ko then status of B can’t be better ( fordefender ) than ko ( it will be ko or dead ). And since root is defender’s node ( maximizing) he will anyway choose response from A .

Benefits of alpha beta pruning are following:

It loses no information. This is a very valuable characteristics making alpha beta pruningpresent in almost every minimax implementation.

15

Figure 2.1: Reference figure to example of alpha beta pruning. Red color is used to markalpha beta cutoffed nodes.

It speeds up minimax considerably. Statistics show that with alpha beta it is possible tosearch twice as much nodes in the same time as ordinary minimax can.

It is quite easy to implement.

Alfa beta pruning efficiency is very tightly connected with the ordering of moves in theactual node. A good ordering is the one where good moves ( for actual player ) are searchedbefore bad moves. Good ordering causes cutoff earlier than a bad one does, thus cuttingoff larger part of search tree. Perfect ordering is a hypothetical ordering where moves aresorted exactly according to their values for actual player to move. With this perfect orderingcutoff would be performed after first searched son or not at all in every node. Many differentheuristics ( see §2.4 ) were created in order to provide alpha beta with good ordering.

Some complications might arose when combining alpha beta pruning with transpositiontables. The fact is that if alpha beta cutoff was performed in some node then retrieved valueof this node might not represent the exact value of this node ( the one when both players playoptimal moves ), but it rather represents value of the node in relation with previous searches( and thus with previously obtained alpha and beta ). In TGA, this is naturally taken intoaccount and when storing positions into transposition tables these ”estimated” positions aremarked and treated slightly differently ( see [9] ).

2.3 Iterative deepening depth first search

Iterative deepening depth first search ( iddfs ) is a compromise in search attitude betweendepth-first and breadth-first search. It works as a depth-first search with limited depth andthis limited depth increases periodically till the result is not found. So algorithm searches (in depth-first manner ) all one move long variants, then all two move long variants, and soon. It might seem quite wastefull to go through certain moves ( those in upper part of thesearch tree ) multiple times, but actually there are ways how to benefit from these multiplesearches ( see below ). Main advanteges of iddfs are:

16

It is very useful for time limited search. When algorithm must return some solution afterlimited amount of time, for instance in playing chess with a little time limit on everymove. In this case depth-first search algorithms are of little use. Howewer, iddfs simplyreturns solution from the deepest search it has accomplished ( even though this solutionmight not be exact - it is only an estimate ).

It enables to use results from previous deepenings to improve actual search and thus erasepossible search slowdown caused by multiple searches in the upper parts of the tree.Usually profit from previous search results is taken through transposition tables ( see§2.6 ) and various heuristics ( see §2.4 ). In latter searches position is retrieved fromtransposition tables and stored alpha and beta values are used to narrow actual search.Updated alpha and beta values are naturally saved in transposition tables.

Iddfs might be applied to problems where there is a high probability of stucking algorithmin useless variants ( which are however difficult to filter out another way ). Here iddfslimits search in these useless variants and problem solving variants are treated as well.Example of this might be using iddfs to solve under the stones problems ( see §3.5 ).

In chess iddfs is a very successfull and widely used method. Stated aspects are supposedto make effective iddfs superior to both depth-first and bredth-first search. However, theremight also arise complications when solving Go problems with iddfs. In TGA I implementediddfs in order to make program solve ”under the stones” problems ( see §3.5 ). I made iddfscooperate with alpha beta pruning and transposition tables mechanism in order to makeiterative deepening effective and usable. However using classical method of starting withone move long variants and increasing variants length by one in every turn, led to rathercatastrophical result when moderately difficult problems usually ( with classical depth-firstapproach ) solved within thousands of nodes took ( with iddfs method ) tens or even hundredsthousands nodes to solve. Essence of these results lies in the lack of information gained bythe search in shalow levels. Here ( in shalow levels of the problem ) very little positions aresolved ( in majority of positions the status is stated ”unknown” ) and thus extremely littlepruning is done by alpha beta mechanism. This leads to neccessity of searching much widerspace than when using classical depth-first search ( where alpha beta is rather effective ). Themost critical levels are those rather deep and having very high percentage of positions withunknown status. Here a lot of useless search is done. There are many nodes in the searchtree, since search goes rather deep and alpha beta is ineffective. The idea I implemented inTGA is to use iddfs which starts on very deep level ( 20 - 25 ). Also with every iteration thisdepth is increased more rapidly ( by 5 ). In TGA, iddfs is used to solve ”under the stones”problems, what is successfully done by this implementation. However, it’s performance ismuch worse ( even in such unnatural modification ) than performance of classical depth-firstsearch ( see §4.1 ). Since ”under the stones” problems are solved thanks to effective heuristics( see §2.4 ) even without iddfs, this algorithm becomes rather obsolete in TGA.

2.4 Heuristics and forward pruning

Alpha beta pruning causes a significant speed up in search by forcing cutoffs on whole sub-trees. However, it’s efficiency is influenced a lot by ordering of moves in the actual node (see §2.2 for definition of good ordering ). Heuristic is often very simple method that should

17

provide a move ordering which forces earlier alpha beta cutoff than the original ordering.Heuristics are usually based on actual game’s specifics ( here it is Go ). In heuristic orientedsearchers ( see §1.6 ) it is usual to put a lot of emphasis on creating good heuristics. Whena human is solving go problems he relies a lot on his ”solving” experiences and he generallyexcludes majority of problem variants thus going through only a very little part of the searchtree. Heuristics try to simulate this ”experience” factor to reduce search tree. I personallydivide heuristics into two basic categories:

position related heuristics are taking into account only the actual position. Therefore theymight be also called static. These might be compared to general rules humans followwhen solving Go problems ( e.g. don’t fill outside liberties, capture enemy stones ).

search history related heuristics are using information from previous search or from theshape of the search tree. Therefore it is possible to call them dynamic. When ahuman is solving some variant of a Go problem, he is using information he gatheredthrough search in the previous variants ( here belongs shape of stones, which moveswere effective, etc. ). Dynamic heuristics might be compared to this human’s ability.These heuristics might not be even game specific dependent ( e.g. killer heuristic, seeparagraph below ).

Probably the most known and widely used heuristic is a killer heuristic. It is a searchhistory related heuristic which is applicable in chess as well as in Go and other games. Theidea ( quite elegant ) is following : a move which caused alpha beta cutoff in similar positionshould be promoted. By similar position is meant position on the same level in the searchtree.

I faced task of creating heuristics preventing long and silly variants that might appear inthe ”under the stones” problems ( see §3.5 ). This requires a set of static heuristics coveringintuitive human’s attitude ( e.g. connecting important stones, capturing opponents stones).Used heuristics are ( order according to weight significance ):

self atari heuristic A move into self atari is demoted. Moreover self atari on larger blockis cut off ( forward pruning method ).

larger block connection heuristic A move connecting larger blocks is promoted ( onlyin defender’s node ).

save connection heuristic An attacker’s move which connects his unsafe stones to safeones is promoted.

capturing heuristic A move which captures enemy block of stones is promoted.

atari heuristic A move which ataris enemy block of stones is promoted.

connection heuristic A move connecting blocks ( of smaller size ) is promoted.

liberty heuristic A defender’s move which takes away liberty of attacker’s ( unsafe ) blockis promoted. A defender’s move which take’s away liberty of ( safe ) attacker’s block isdemoted.

18

This set of static heuristics succeeded in preventing long variants in the ”under the stones”problems and rapidly reduced number of searched nodes ( see §4.1 ). Besides these staticheuristics also one dynamic heuristic is implemented. It is inspired by killer heuristic and itpromotes moves that made group alive ( or dead in attacker’s nodes ) in similar nodes ( inprevious nodes at the same depth ).

Along with heuristics several forward pruning methods are used. These methods performcutoff based on some game oriented knowledge related to the position. This way wholesubtrees might be cutoffed thus speeding search significantly. However, these methods mustbe created very precisely since the danger of potential information loss appears. I used onlyforward pruning methods which are supposed to keep search consistency ( they don’t causeinformation loss ):

Defender is prevented to fill his own full eye.

Self atari on the larger block ( more than 2 stones ) of defender’s stones is prevented. This isa well known and widely used pruning technique in life and death solvers programming.

Too little defender’s stones are recognized as dead. Since TGA has rather weak eye recogni-tion I made up this pruning method to save searching in the deeper levels of the searchtree. When there are too little defender’s stones ( < 4 in attacker’s node and < 3 indefedender’s node ) group is stated dead. Such a little set of defender’s stones cannotcreate a living group thus this pruning might be safely used. This method proved tobe very efficient ( see §4.1 ).

2.5 Zobrist hashing

Zobrist hashing is a well-known method of creating hash code from a given position. It isused a lot and with success in chess programming as well. The idea is based on randomnumbers. A table representing board is created: usually a typical two dimensional array.Under each item ( coordinate ) there might be stored as many keys as there is possible statesfor the coordinate - in go it is three : black , white , empty ( in chess this would be 13,for each color and figure one + one for empty coordinate). This table is initialized withrandom values. Now there is a randomly generated hash key for every possible coordinateand every possible value ( e.g. black stone at [17,4], empty point [10,2] ). Retrieving a hashkey for given position is simple : it is a result of XOR bit operation performed on hash keysrelated to all pairs [ coordinates - it’s state ] in position. Sometimes hash keys for emptycoordinates are all set to zero ( or they are not taken into account at all). This simplifies thewhole situation because zero is neutral towards XOR operation ( I used this improvement aswell ). The last remaining task is considering two equal positions with different players tomove. This is actually very common in solving Go problems. When comparing two positionkeys also color of players to move would have to be considered. This task is in TGA simplysolved by creating two extra random hash keys ( one for black and one for white ). Hash keyfor actual position is xored with hash key for a stone color corresponding to actual player( position hash key is normalized by the color ). This keeps uniformity in operations withposition hash keys.

Now on it is supposed that empty coodinates have hash key equal to zero. When movingfrom one position to another by playing a move, new zobrist hash key is retrieved from the

19

old one by xoring hash key of new move ( e.g. black played [3,3] ) with the hash key ofactual position. When a move captures an enemy stones it is also neccessary to xor out allcaptured stones from the new hash key. This simple way of retreiving new hash key from theold one makes zobrist method extremely quick and easy to use. The longer the hash keysare the lower the probability of collision is ( collision means there apper two equal hash keysfor two different positions). Therefore at least 32 bit types but rather 64 or 96 bit types arerecommended for number type of hash key. One of main areas to use zobrist hashing areTransposition Tables (see §2.6 ).

In TGA zobrist generated hash keys are used in transposition tables and in repetitionchecks as well. Since 64 bit hash keys are used it is possible to treat a hash key ( afternormalization by color ) as a unique representation of position ( probability of collision isgenerally in degree of 1/(2^60), what is imponderanble ). This way quite a lot of memory intransposition tables is saved ( otherwise whole position on some bit tape would have to bestored along with hash key ). Also whole process of position saving and retrieving speeds upa bit.

For more information on Zobrist see [14].

2.6 Transposition Tables

Transposition tables ( or only tables in this chapter ) mean a significant step towards effi-ciency in the tree search. It is natural not to search positions which were already visited inthe previous search and their result was decided. This is exactly the idea of the transposi-tion tables ( transposition means getting into the same position by different ways ). Whenthe algorithm comes to a new node it at first checks whether there is not a record in thetransposition tables with the actual position :

If there is a record in the table , algorithm retrieves information which were found dur-ing the previous search. These might be very accurate ( e.g. exact status of the group) or rather inaccurate ( alpha and beta values used to narrow upcoming search). Itis much more common to store innacurate information ( alpha and beta values ) withstored positions since these are more general and flexible ( in further text I presumestoring these kind of information ).

If there is not such a record , solved position together with related relevant information( group status or mentioned alpha and beta values ) is stored into transposition tables.

By retrieving information stored in the transposition tables to narrow ( or even eliminate) further search in the actual position whole subtrees might be cut off and thus efficiencyof the search increases a lot. There is a general principle about relationship between alphabeta pruning and transposition tables. The more efficient alpha beta is ( good ordering andheuristics ) the less efficient the transposition tables are. For the transposition tables to beeffective there must be a lot of saved positions in them, however when alpha beta is effectiveit prunes a lot of positions itself and transposition tables are lacking. In TGA since alphabeta pruning is of rather mediocre efficiency, transposition tables proved to speed up wholesearching proces very measurebly ( see §4.1 ).

Transposition tables are usually implemented as a hashing table where a hashing keyrepresents actual position. This is appropriate since transposition tables are searched in

20

every node and thus high look up speed is demanded. Moreover very usual is using Zobristhashing ( see §2.5 ) as a method for creating position hash keys. Here Zobrist clearly benefitsfrom ability to create a new position hash key from the old one only by xoring performedmove.

Potential problems with transposition tables arise when position with estimated resultwere stored into the transposition tables. Estimated result doesn’t represent result withoptimal play by both players because of some cutoffs made on base of knowledges fromsearches in previous variants. This happens for instance in case of alpha beta pruning wheninto transposition tables position with narrowed alpha and beta is saved. When retrievingthis position it must be checked whether actual alpha and beta are within the stored ones.Otherwise it is not possible to use stored results due to the possible information loss. Alsopossible information updating must not be forgotten ( accuring information of previouslystored position ). Information updating is especially neccessary when using transpositiontables in interaction with iterative deepening depth-first search ( see §2.3 ), because duringshalow deepenings very inaccurate information are stored into transposition tables.

I implemented transposition tables are written above ( hash tables using Zobrist hashing) and all complications mentioned in previous paragraphs are properly handled ( see [9] ).

2.7 Pattern matching

Pattern matching is a general name for methods where a given position or it’s parts ismatched against some prepared database of positions or shapes. Pattern matching is quitea promising method in Go programming. It is mostly used in the opening. There are Goprograms able to play reasonable opening moves ( around first 20 - 50 moves ) based onlyon pattern matching against large database of professional games. In Go problems solvingthere are two main areas for using pattern matching:

Matching shape of the group against position database. The idea is to retrieve a potentialproblem solving move from the database of stored positions. If the retrieved move atleast partially solves the problem, search is a lot speeded because of much more effectivealpha beta cutoffs. However due to incredible variety of Go problems ( a difference insingle stone might require completely different solution ) this method requires a large( hundred thousands to million positions ) position database to be effective.

Matching eye shape of the group. Here algorithm tries to find actual eye shape ( also stonessurrounding eye shape and their connectivity must be taken into account ) in a databaseof eye shapes to decide whether actual eye shape provides two eyes or not without afurther search. This is a very elegant and effective method of early recognition of livinggroup. Database for this matcher needs usually ten thousands to hundred thousandspositions. This database ( containing evaluations whether the single shape is alive ornot ) might be generated by the program itself. Evaluation of the shapes would bedone by simple tree search.

Organisation of positions database is another important factor. Some sophisticated meth-ods might be used in order to avoid comparing actual position against all positions in thedatabase as for instance : dividing positions into groups according to their similarity ( similarposition are together in the group ) and matching actual position only against a representant

21

of each group. Comparing algorithm might be derived from some standard string comparingalgorithm.

The original outline of TGA contained also pattern matching module ( of the first type). This module was successfully implemented. I used simple database organisation wherematching is done by sequential comparison of the actual position against all positions indatabase. Results were several best fitting positions ready to be used in latter search. How-ever, as I mentioned above this algorithm needs very large database to be effective. SinceI didn’t managed to obtain such a database this modul is useless ( creating database aloneis imposible and existing ones are only for purposes of Go programs they were created for). I didn’t even try to make this module cooperate with tree search module. It was a mis-take in the program designation to implement pattern matching of this kind. Much moreuseful would be an eye shape pattern matcher which would solve one of the main programweaknesses ( see §4.1) - early recognition of the group status based on the eye shape of thegroup.

22

Chapter 3

Solutions to life and death solving keyissues

The most common way of life and death solving is still some tree search algorithm supportedby pruning and heuristic knowledges. Howewer there are some algorithms where tree searchis used only as a secondary encouragment to the results extracted from static analysis ( forinstance see [2] ). These algorithms are useful mostly as a modules in Go playing programsbecause of their speed. In life and death analyses as a separate task they are beaten by thetree searching programs. In this chapter most important issues in tree search approach to lifeand death are presented together with my suggested solutions. These ideas were implementedin TGA ( see [9] ).

3.1 Position representation

Position representation is a ”base stone” for every life and death solving program. Effectivelydesigned position representation with ability for quick playing and unplaying moves is nec-cessary for every tree search algorithm. The usual approach is simple and intuitive. Positionis represented as a two dimensional table. It might be implemented either with ordinarytwo dimensional array or for speeding up as a one dimensional array with simple coordinatesrecalculation algorithm. In this table every item represents single coordinates on the board.Capturing moves and suicide moves must be handled separately. In this approach playingand especially unplaying stones is very straightforward, simple to program ( the only task isto properly handle capturing moves ), but provides no additional information to the position.Especially information about liberties are very important in life and death analysis : howmany liberties a particular block has, which moves are capturing moves, etc.

Fact that it would be useful to posses more information on position structure such asabout liberties led me to create ”block oriented” position representation. Position is storedas a set of blocks ( a block is a set of stones of the same color connected together fromthe definition ) and a two dimensional table representing board where each item of thetable is a pointer to the block occupying this coordinate ( or the NULL if it is an emptycoordinate ). Together with each block useful information are saved: the block color, theblock identification number, the number of the stones in the block ( and list of them ), thenumber of the liberties ( and list of them ). When playing a stone, neighbouring friendlyblocks are merged into a single block. It is checked whether some opponents blocks don’t get

23

captured, and of course simple suicide check is performed. Advantages of such an approachshould be clear :

Additional information about particular blocks might be very helpful in performing opera-tions connected with the actual block structure: for instance setting moves priority in”capture heuristic” ( moves capturing opponent’s stones are preffered to other moves :see §2.4 ) is pretty simple and requires minimal proccessor operations.

When playing stone itself, capture or self capture checks are also simplified in comparisonwith previous approach.

This approach is more closer to the human approach. Humans sees block of stones ( andbears in mind their liberties ) rather than separate stones. Howewer, they are accus-tomed to view the position even more abstractly ( in terms of groups of stones ). Butfor life and death solving, this more structured approach is not relevant.

On the other hand there are also disadvantages ( compared with simple ”two dimensionalarray” representation ):

Unplaying stones is much slower ( and also more complicated for the programmer ). Beforea stone is played all neccessary information for restoring actual position are saved inappropriate data structure at the beginning of a dynamic list. All moves along the pathto the actual position are saved in order to be properly unplayed. When unplaying,first item of the dynamic list is taken and position is updated according to storedinformation.

In some cases this approach might be slower than the intuitive one.

Whole algorithm is more difficult to program.

3.2 Search stopping conditions

Another crucial area of every life and death computer solver are search stopping conditions.A trivial idea is to perform tree search until the whole defender’s group is captured or untilattacker has no possible move to play ( what means that group is alive ). This solutionmust be supported by possibility of playing pass moves, otherwise the defender would alwaysfill his eyes and got captured. However this approach might monsterize even rather simpleproblems. There are much more sophisticated methods to state group’s status without asearch. The most important search stopping condition is presented in following chapter.

3.3 Static eye recognition

A group with two eyes is alive. This is one of the first observations every human Go playermust go through in his beginnings. After some time even beginners develop an accuratesense to distinguish which intersections will produce an eye and which will not. Togetherwith recognizing several dead shapes which produce maximally one eye, they gain a goodequipment to judge upon life or death of groups in rather simplified problems. It is possible to

24

Figure 3.1: Example of living group with diagonally related eyes.

create a life and death solver which recognizes group as alive only when it has two pure eyes.However, efficiency calls for better recognition methods. Actually this task of life and deathis quite well analysed and in successfull Go problems solving programs effective approachesexist. One of the most successfull methods is to recognize eye regions. An eye region is aset of neighbouring intersections surrounded by defender’s stones which are either empty orpossessing unsafe attacker stone. This definition might vary according to understanding ofword ”surrounded” in previous sentence and there might arise several types of eye regions.These eye regions are matched against an eye shape database ( usually ten thousands tohundred thousands positions) in order to reveal how many ( 0 , 1 , > 1 ) eyes the regionproduces. There are many similar methods using the eye shape database, differing in e.g.the way of the database organisation, the comparing function ( for instance, see [7] ). Thereare also methods researching eye shapes from the point of combinatorics, but this is out ofthe scope of this paper.

I used rather simple method paying cost to local ineffectivities in the search. That meansthat clearly ( to the human or sophisticated program ) alive / dead positions must be searcheddeeper to recognize status of the group. The algorithm keeps list of potential eyes. At thebeginning here are all empty intersections and unsafe attacker stones. Upon this list in everynode of the search tree static eye analysis is performed. When two full eyes are recognized (according to the definition in [3] ) and their connectivity is proved ( they must be connectedfrom definition ) then the group is stated alive. On the other side when there is only onepotential eye ( from list of potential eyes false eyes are excluded in every analysis ) group isstated dead. Simple heuristic (see [9] ) is used which makes program recognize some basicdead shapes ( e.g. three in a row with attacker’s stone in the middle, cross with attacker’sstone in the middle ) without any deeper search ( algorithm doesn’t refer to coordinatesadjacent to attacker’s stone as to actually potential eyes - because defender must fill themto capture the attacker’s stone - and thus is the eye shape restricted to a single potential eye- the attacker’s stone ). The algorithm prevents searching variants when defender fills hisown full eye ( this simplification loses no information because playing into completed eye isuseless ). The algorithm is capable of recognizing two diagonally related eyes as well ( seeFigure 3.1 ). Still, eye recognition is the weakest part of the whole algorithm. Because ofseparate approach to the potential eyes ( not treating them as regions but as single points )

25

eye recognition is quite ineffective and slows down the whole process because the tree mustbe searched deeper in order to reveal eye status of the group. Implementing more efficientstatic eye recognition algorithm is one of main tasks for future improvement (see §5.1 ). Thisalgorithm could be possibly based on ideas in [2] or I could create my own eye shape databasewith matching module, see §2.7.

3.4 Repetitions

Repetition means repeating the position which already appereared in a search tree along apath to an actual node ( actually this includes pass moves and ko managing as well, but sincethese aspects are handled separately I don’t include them into the problem here ). I avoidrepetitions by storing positions along the path to the actual node. Very efficient way how todo this is to associate position with a hash key and then use some appropriate data structurefor storing position’s hash keys - I use simple associative map with the lookup in logarithmictime. Actual position is always compared with all the stored ones. When an equality isfound the actual node is no more expanded and the control returns to it’s father ( statusof the son is stated as ”unknown” ). For hashing positions in order to reveal repetitions,Zobrist hashing method ( see §2.5 ) is used. Repetition checks are inspired by the principleof transposition tables ( see §2.6).

3.5 Under the stones

In computer Go applied on the life and death problems many complications arise after someblock of stones gets captured. Not only number of empty intersections increases at once (and thus increases further complexity of the problem ), but also possibility of repetitions (see §3.4 ) or extremely long useless variants appears. To avoid these complications, problemis considered not solved when an actual variant’s lenght reaches 100 ( ”total depth cutoff”terminates the program ). This is reasonable since even the most difficult problems are usuallysolved within 30 moves long variants. When no countermeasure is taken peculiar variants ofincredible length ( even more than hundred moves ) are likely to arise when both players areplaying into the captured area and possibly recapturing each other( even without repetitions).Area where defender’s stones were captured is meant here, attacker’s stones don’t cause thatmuch trouble when they get captured. Actually very often it is neccessary part of the problemto capture larger attacker’s group in order to obtain eye shape for defender. A simple idea isto prevent the algorithm from playing into such areas ( where more than three defedender’sstones were captured ). However, this fails since there is a special class of problems whereessence of solution lies in playing under captured defender’s stones. These are ( together withproblems where it is neccessary to play under attacker’s stones ) sometimes called ”underthe stones problems” and belong to rather difficult ones even for humans ( for example seeFigure 3.2 ). There are several ways to deal with complication of playing under the stones (ordered from worst to best in meaning of consistency and efficiency ) :

Force a depth cutoff when actual node in the search tree is deeper than some stated depth.Status of such cutoffed variant is set as unknown and the search continues. This causesstupid variants to cutoff but also ”problem solving” variants might be cutoff leading tothe information loss.

26

Figure 3.2: Example of very simple ”under the stones” problem. Black can’t kill white. Afterblack captures five white’s stones, his three stones are trapped and white is alive

Refuse to play on intersections where a larger defender’s block of stones was capturedprevents all silly variants but the information loss still remains in solving ”under thestones” problems.

Implement clever ”under the stones” heuristics helping to cutoff useless variants.

Use iterative deepening depth first search algorithm. Iddfs goes through all variants ( thuslosing no information ) and iterative increasing of the search depth prevents stuckingat long useless variants as with the ordinary depth first search. Moreover previousmethods might be used as supporting heuristics for alpha beta ordering ( see §2.3 ).

Actually the proper handling of playing under the stones was a big challenge to implement.At first, it is possible to ignore playing under the stones by setting an appropriate parameter( see [10] ). In that case the algorithm prevents playing under any larger captured defender’sgroup and thus it is unable to solve the ”under the stones” problems. However this approachprovides better performance in other problems. I implemented iterative depth first search inorder to solve this complication but due to very poor results ( see §4.1 and §2.3 ) this solutionmight be ( in TGA ) used in practice only for easier problems and thus it is switched ononly when explicitly demanded by a program parameter. As a final solution playing underthe stones ( more than three captured defender’s stones ) is allowed and supported by a setof different heuristics ( see §2.4 ) which ( together with alpha beta pruning ) prevent thoselong ”stucking” variants. Generally performance went down ( see §4.1 ) but this slowdownis bearable and ”under the stones” problems ( even some very difficult ones ) are correctlysolved. However, a little possibility that algorithm might fail on some very difficult ”underthe stones” problem still exists ( and then ”total depth cutoff is performed”).

3.6 Ko

Ko handling is tightly connected with most of the Go problems ( even those not resulting in ko). Program has to properly identify every ko and finnd out results of the problem when eitherside wins the ko. Ko detection itself is quite easy. It is sufficient to check whether possiblemove on a given coordinates captures exactly one stone and after the capture the playedstone will be surrounded by three opponents stones or edges of the board. Complications inthe proccess of ko evaluation are obvious:

27

Figure 3.3: In the left ( multistep ko ) black has to win two ko in the row to capture white.In the right ( approach move ko ) both players have to make an approach move to win a ko.

Not every ko in the problem influences status of the group ( group might live/be killedwithout this ko ).

Double ko ( see Figure 3.4 ) means unconditional live of the group if winning one of thesetwo ko makes white alive .

Sometimes ( with interaction with pass moves ) ko status must be transformed to the deadstatus ( see §3.7 ).

There are different kinds of ko ( direct ko see Figure 1.1, approach move ko and multistepko see Figure 3.3 ) and each of these must be recognized.

I handle these obstacles in an actual node in a following way. As for the different kindsof ko, there might be a dramatical difference in the number of neccessary ko threats betweenwinning a two-step ko or winning a direct ko. However, from the local point of view thesebecome rather equal ( one side needs more ko threats than the other to win the ko ) andso I don’t distinguish them in the algorithm. Let’s now take for example defender’s node (I will call him ”observed node” ). Defender is to move and he chooses the most profitablevariant to him. Usual ko is analysed now, not the double ko. During the proccess of applyingheuristics and moves sorting, those moves capturing stone in ko are marked and the orderingis changed as follows: Ordinary moves sorted according to heuristics ( searched first ) - passmove - moves taking the ko. When the ko is not relevant to the status of the group in theobserved node, then the result is found in the first two ”sections” of moves. Moreover, if thethe best status for the defender found in the first two sections is ko status and it is provedthat attacker can kill the group after retaking the ko ( simulated by pass ) then the ko takingmoves ( last section ) are cut off without information loss ( defender cannot get better resultthan life in ko from taking the ko). When defender can live without playing the ko, theresult is decided among ”ordinary” moves( group is alive ). Pass move represents situationwhen during playing the ko attacker ignores defender’s threat and then tries to kill the group.At last the ko taking move is analysed. If defender couldn’t live with previous moves andattacker was able to kill after defender passed, then this ko might be relevant to the life and

28

Figure 3.4: White is alive, he might always capture one ko securing him life.

death of the group ( if defender can live after winning it ). After defender takes the ko it isattacker’s move and he is not allowed to recapture the ko. The status of the group in theobserved node depends on the status of the group after taking the ko ( it is attacker’s turnto move), possibilities are:

Status is dead after defender takes the ko means that group is unconditionally dead in theobserved node ( this ko was not relevant to the group status).

Status is alive after defender takes the ko means that group is alive in ko in the observednode. Thus status alive is transformed into status ko.

Status is ko after defender takes the ko means that status of the group in the observed nodeis either alive ( double ko ) or ko ( approach move ko or multistep ko ). Simple methodto recognize double ko is given below.

Actually it is neccessary to recognize two slightly different types of double ko positionssecuring life for the group:

Both ko are taken by the attacker and defender is to move and he is allowed to capture anyko.

Both ko are taken by the attacker, but one was taken last turn therefore defender cancapture only one ko.

Both of these types are solved in the defender’s node. When defender can start twoindependent ko both leading to life of the group then the group is stated alive ( first type ofdouble ko ). When defender can start ko for life and attacker took ko last move second typeof double ko is recognized and the group is stated alive as well.

3.7 Pass moves

Simulating pass moves in a process of the tree search has a special purpose to reveal twotypes of positions:

seki positions ( see Figure 1.3 ).

29

Figure 3.5: Example of simple ”bent four in the corner” problem. Black to move can get ako whenever he wants and every white’s move leads to his death, thus white is dead.

positions of ”bent four in the corner” type ( see Figure 3.5 ).

In the program pass move is also applied in the nodes where possible ko appears. However,this situation is not of our interest ( it is discussed in §3.6 ) and I will refer to nodes whereno ko taking move appears. Pass moves are always tried as the last moves because they leadto the solution in the minority of the problems and placing them to the end of the possiblemoves list increases chances to avoid them ( by alpha beta cutoff ) and thus speed up thesearch. In defender’s node pass move is tried when neither of sons return status alive ( seekingpotential seki). And if it’s result is alive then group is alive in seki ( neither defender canlive nor attacker can capture ). Howewer, I consider seki status to be equal to alive status( in both cases group cannot be captured by the attacker ) since they differ only in amountof the territory group creates. If it’s result ( of the pass move ) is ko then this status istransformed to the status dead, because when the attacker might get the ko whenever hewants ( defender passed ) then the group is dead ( this principle comes from ”bent four inthe corner”, see below ). It is important to do this transformation already in the son node inorder to keep transposition tables consistency. On the other hand in attacker’s node whenall sons return status alive and at least one returns status ko, pass move is tried in order touncover possible ”bent four in the corner” position. When pass move proves the group to bedead ( defender cannot live in his turn ) than the status of the group in attacker’s node isdead. This is because attacker can make a move and get a ko, but he can also pass, wait tothe end of the game, then remove all ko threats and simply win the ko ( therefore such typeof position is in the Japanese rules stated dead as it stands, see [11] ).

Simple check is performed to prevent two passes in a row ( creating an endless loop ). Itmight seem that applying pass moves slows down the search speed significantly. Howewer,using transposition tables in general prevents any significant differences in the search treesizes, because the more search is done in some particular branch the more probable is trans-position table cutoff in another branch. There is applied a simple pruning technique: passmoves are tried only when number of legal moves ( not suicides ) in the node is lesser thangiven constant. I set this constant to 4 according to own empirical results from solving Goproblems. So far I have met only one go problem where this number was 4 otherwise in allseki problems and ”bent four in the corner” problems this number equals 2.

30

Chapter 4

Performance and testing

There is a little curiosite connected with the program testing. I found a very nice ( and verydifficult: approximately for 5 dan ) problem in magazine Go world ( see [4] ). The title of theproblem was ”white to live” and on the second side there was a solution where white couldlive after around 17 moves long variant containing many under the stones moves. I gavethis problem ( ./data/solved very slow/pos 1.sgf ) to TGA and it outputed ( after searchingaround 200 000 nodes what was done under 30s on my AMD64) ”black can kill” ( what iscorrect ) and ”white can get a ko”. This dissappointed me and I started to search in thesgf output where the mistake is. But after seeing several variants to my surprise I found outthat program found correct solution and white could not get more than a ko. Thus TGAproved mistake in a well established Go magazine. This problem is provided in Figure 4.1 (already marked according to TGA input demands ).

4.1 Performance analysis

Program was tested on a set of approximately 30 problems representing many different kindsof problems and different levels ( 20k - 6d ). Generally on this set of problems program did (with all speeding up arguments switched on ) very well solving all of them in under 300 000searched nodes ( what represents around 35s of search time on my AMD64 processor). Thisis very incomparable with human’s performance. I estimate that it would take at least 20minutes to solve all the problems in the set for a very strong human player. As mentionedabove the pillar of this ( relative ) efficiency is cooperation between alpha beta pruning andtransposition tables. Alpha beta should never be switched off since for instance ”under thestones” analysis is based on the proper move ordering resulting in early alpha beta cutoff.However, with alpha beta pruning alone algorithm was capable to reasonably solve only sim-ple problems ( category ”solved fast” ). It was transposition tables implementation togetherwith saving transposition tables from the first search for the second one, that increasedsearch speed at simple and mediocry difficult problems up to ten times and opened the doorto the difficult problems. I didn’t want to copy known alpha beta heuristics ( I implementedonly those I made up by myself ) therefore TGA’s alpha beta is rather mediocry efficient.However, this is compensated by transposition tables which receive more information ( morecached positions ) and thus can cause more later cutoffs. Next factor mostly influencingspeed of the search is a set of static heuristics. They make it possible to solve ”under thestones” problems with the very acceptable slowdown. Program searches all problems in the

31

Figure 4.1: White can get ko. Difficult problem from Go World magazine.

set approximately 15% slower than when ”under the stones” analysis is toggled off ( thusprogram doesn’t try to play under larger captured defender’s groups - see §3.5 ). Withoutthese heuristics ( and ”under the stones” analysis toggled on ) many problems end in toolong variants and are not solved at all ( total depth cut off is performed, see §3.5 ). Moreoverwhen this heuristic’s set is switched off ( and ”under the stones” analysis is swithed off too) whole search time is doubled and one problem in the set was not solved at all ( too longvariant occured ). It should be clear that this static heuristics set is a very valuable instru-ment for life and death analysis. On the other hand used dynamic heuristic doesn’t havemuch influence on the search speed ( time of the search was shortented by less than 5% ).However it is implemented as a secondary add on to the static heuristics thus this result isnot suprising.

Two most important forward pruning techniques ( see §2.4 ) used in the program are :

Recognizing too little defender’s stones as dead. This method saves a lot of the search inposition’s that are clearly dead ( but are not yet recognized dead by the eye analysis ).Because TGA’s eye analyzes is rather weak, this pruning method is extremely powerfull.If it would be switched off searching time at some ( mediocry difficult ) problems mightmonsterize.

Preventing self atari on the larger block of defender’s stones. This good and well knownpruning technique saved approximately 20% of the search time.

The most slowing factor in TGA’s current implementation is a poor static life and deathrecognition. Program is not able to recognize clearly ( for mediocry experienced player ) deador living groups without further search. Rough estimate is that with the efficient eye analysis( based probably on some eye shape database ) program’s search speed could increase by (up to ) 40%.

Naturally there are a lot of problems where program’s performance is rather poor. Thiscategory fit problems with a lot of empty intersections in the beginning or problems wheresome large group is captured and many new empty intersections arise. An example of sucha problem is given in Figure 4.2. The program searched over 1,300,000 nodes to solve thisproblem ( it took over three and half minute on my AMD64 ). However, strong humanplayers are able to solve this problem under one minute ( it is problem of 4 dan strength ).

32

Figure 4.2: This problem took quite a long time to solve. Result is unsettled and estimateddifficulty is 4 dan.

4.2 Program testing

In Go programming, especially in programming life and death solver there are always com-plications with program testing. Since the outputs are rather very big ( ten thousands orhundred thousands searched nodes are usual ) and to reveal bugs in the algorithm outputmust be searched manually. There are also methods how to shorten the output. For instanceI used method when all uninteresting positions ( together with their subtrees ) are prunedout from the sgf output. ”Uninteresting” position is one of following:

Position which obtained uncertain status ( i.e. it is uncertain due to some alpha beta cutoff).

Position in which defender played the last stone and it obtained dead status. This isuninteresting since from defender it is expected to live thus a move which neithermakes him alive creates a ko is uninteresting.

Position in which attacker played the last stone and it obtained alive status. This is unin-teresting since from attacker it is expected to kill thus a move which neither kills norcreates a ko is uninteresting.

In order to get more effective testing results sometimes ”simultaneous method” is used.Two Go problems solving programs are given the same set ( usually very large ) of problems( standardized to their input formats ) and those which are solved differently are furtherlyanalysed ( this however doesn’t cover all potential bugs in programs ). TGA was not testedthis way, since arrangment of this testing is very complicated. For instance Dave Dyer’sprogram ( see [7] ) was tested this way against Go Tools ( see [1] ).

4.3 Program input limitations

Each life and death solving program has it’s own specific limitations either to the formatof input position ( for instance Go tools accepts only problems where defender’s groups isabsolutely - from side to side - blocked by attacker’s wall, see [1] ) or to the class of problems

33

it solves ( for instance Dave Dyer’s Go problems solver was in original version limited by thedepth of the problem ). TGA’s input format limitations lies in neccessity of marking positionto be solved. It must be shown to the algorithm where are attacker’s stones that might becaptured and where are coordinates which are relevant to the problem ( see [10] ). Actuallythese limitations could be easily removed by adopting similar input format condition as Gotools did, but there are also benefits of this approach:

Program is capable to solve practical problems ( from actual games ), which are not fullyenclosed.

Neccessity to properly mark the position changes given problem in minimal way and is donevery quickly.

Especially the first item I consider to be very important and gives TGA advantage incomparison with Go tools ( see [1] ), when by using TGA player might analyse some life anddeath situations that arose in his game. As to the class of the problems accepted by TGA,it solves all common problem types including solutions like ko ( direct , approach move ,multi step , ), double ko ( means alive ), seki. It is capable to recognize pecularities of ”bentfour in the corner” problems. Most problematic type of problems are so called ”under thestones” problems. However in the end program is able to solve these properly as well withacceptable slowdown ( see §3.5 ). Thus the only remaining major limitation is the size of theproblem. Since TGA has no ”experience support” ( doesn’t profit from previous searches insimilar problems ) large problems ( many coordinates to play on and possibly many stonescapturing resulting in new coordinates to play on ) which might be solved in a while by veryexperienced human are very difficult for TGA ( see §4.1 ). Also TGA’s rather simplified eyerecognition algorithm contributes to this problematic.

34

Chapter 5

Conclusion

Since ( as mentioned above ) research in computer Go is on very high level, I focused myselfrather then trying to bring up some brand new algorithm on implementing some well knownone’s and on observing their cooperation. I think that as an contribution to Computer Gomight be following features of TGA: I created my own position representation ( see §3.1 ),program provides simplified ( and thus ) readable sgf output of the problem, I created set oftree search heuristics ( see §2.4 ) making it possible to solve under the stones problems andgenerally speeding up search, I had to find out my own ways ( see §3 ) to treat peculiaritieslike ko, double ko, seki, bent four in the corner.

5.1 Future work

As a most important ( potential ) future improvements I see these tasks :

Improve static life and death recognition ( as mentioned in §3.3 ) to make algorithm staticallyrecognize living/dead groups as early as possible and thus avoid further ( almost useless) search. Further search is helping only in cooperation with transposition tables, butthis profit is insignificant compared to profit taken by early life and death recognitioncutoff.

Implement support for ife and death problems containing potential connection to the outsidedefender’s safe group. Program structure and organisation should allow this to becomfortably done. However, complications arise with large increase of new potentialcoordinates to play on.

To improve performance of used heuristics especially dynamic heuristic.

To improve sgf output pruning to be capable of showing only several ”interesting” variants.Program should be able to pick up only one or two best answers to the attacker’s killingmove ( or defender’s living move ). So far sgf pruning is efficient, however to attacker’skilling move at worst all answers are provided ( symetrically for defender’s living move).

To perform large scale testing of the program ( see §4.2 ).

35

5.2 Summary

Following generalized tasks were successfully accomplished in TGA ( tasks to be done orimproved are in §5.1 ):

A functional life and death solver was programmed along with this thesis. TGA is fullyworking solver having very good performances ( see §4.1 ) in the representative set ofthe testing problems. It is able to handle many different issues in life and death solving( see §3 ). It has looser demands on type of input problems than other Go solvers (see §4.3 ) thus might be better applicable on the problems from practical games.

Basic algorithms ( see §2 ) were applied, combined and tuned ( especially difficult task,see §4.2 ) in TGA, together with their modifications for special program purposes.

Personal elements were incorporated to the program as well. Almost whole game knowledgeoriented parts were created without being familiar with implementation specifics ofother Go problems solving programs ( e.g. position representation, ko handling, sekihandling, see §3 ). Set of ( in the result ) effective static heuristics was designed (see §2.4 ) and simple ( yet in TGA very efficient ) forward pruning method was created( see §2.4 ).

General overview on life and death solving problematics commented with personal opinionsand ideas was provided in this thesis what should be a good study material to every (rather ) beginner in Go programming problematics.

36

Chapter 6

Go terms glossary

Here are explained all important Go terms used in the text.

atari is a move which threatens to capture some opponent’s block of stones in the nextmove. This block thus has the last liberty after atari move.

approach move ko is a situation when player must make an approach move while fightingthe ko to win it. Thus this ko is much harder to win ( for player making an approachmove ) than the direct ko. Example of the approach move ko is at Figure 3.3.

bent four in the corner is a name for situation when defender cannot live in his turn andattacker can create a ko by playing a ”bent four” shape ( see Figure 3.5 ). This situationis stated dead in Japanese rules because attacker can start this ko whenever he wants,thus wait to the end of the game, remove all white’s ko threats and then start and winthis ko.

block is a set of stones which are ( transitively ) connected along horizontal and verticallines.

double ko is a peculiar situtation when two independent ko appear in a single problem and( winning ) any of them secures life for defender. Then defender is unconditionally alive( attacker can never win both of them, see Figure 3.4 ).

eye is an empty coordinate which fullfills following conditions:

• At adjacent coordinates there are defenders stones or these coordinates are on theedge of the board.

• If no adjacent coordinate is on the edge of the board then at least at three ( of four) diagonal coordinates there must be occupied by defender’s stones ( or anothereyes ).

• If some adjacent coordinates are on the edge of the board then at all remainingdiagonal coordinates ( which are not on the edge ) there must be defender’s stones( or another eyes ).

For example of eyes and false eyes ( points that cannot become eyes ) see Figure 1.2.

eye shape is a set of coordinates which might produce eyes.

37

ko is a situation when both players could be (re)capturing a single stone forever ( see Fig-ure 1.1 ). Therefore there is a simple rule preventing player to recapture the ko whichwas captured last move by the opponent.

ko threat is a special move which threatens some action against opponent and forces himanswer. If he does answer player who played the threat retakes the ko and then oppo-nent looks for the threat.

ladder is a tactical manoeuvre for capturing the block of stones with two liberties in sequenceof atari moves.

liberty of a stone is an empty coordinate adjacent to this stone ( horizontally or vertically). Stones in single block share their liberties. If all liberties of the stone ( block ) arefilled by attacker’s stones ( or by edges of the board ), the stone ( block ) is captured( for more detailed rules explanation see §1.3 ).

multi step ko is a situation when there are several ( usually two ) back-to-back ko andplayer who wants to win the whole ko ( and thus for instance kill the group ) must winall of them consecutively ( thus fighting ko in ”steps” ). Example of multi step ko is atFigure 3.3.

pass is a special kind of move when no stone is played and turn goes to the opponent. Inthe game usually pass is played only in the very end of the game. But in the computergo, especially Go problems solving it has incredibly important role and is played veryoften ( see §3.7 ). For instance to simulate playing a ko threat or to recognize seki.

seki is a name for situation when two groups ( of opposite color ) are living in ”symbiosis”( they cannot kill each other ). Both of them are considered alive but they possess noterritory ( example of simple seki situation is at Figure 1.3).

38

Appendix A

TGA Programming manual

A.1 About

This is TGA’s manual main page. TGA is GNU/GPL life and death solver running underLinux operating systems. TGA was programmed in C++ using standard liberies and stlcontainers.

A.2 Documentation hiearchy

TGA’s documentation consists of:

• Source code, which is legibly written and thoroughly commented.

• Doxygen generated documentation about main classes, data structures, source files andfunctions.

• These manual pages

In these manual pages general relations among program modules are described. Moreoverused programming techniques and algorithms are shown together with short source codeexamples. These code examples are always related to the closest paragraph above them.TGA’s documentation is built for purpose of future changes in program ( not neccessarilydone by author ). Therefore after reading this manual, then examining classes descriptionand spending little time in particular code reading an average programmer should be ableto comfortably operate program sources. Similarly when there is some problem to solve,programmer at first finds adequate topic in manual pages and then follows links to functionsand classes managing this topic in the source code ( functions are shortly described in doxygengenerated documentation as well ).

Most important manual pages ( and related source files ) are:

• Handling Parameters(p. 40) : p manager.cc p manager.h

• Starting search(p. 48) : main.cc main.h

• Tree search(p. 40) : t search.cc t search.h

• Position representation(p. 52) : board.cc board.h

39

• Static eye recognition(p. 47) : eyes.cc eyes.h

• Sgf parsing(p. 50) : sgf read.cc sgf read.h

A.3 Handling Parameters

Parameters are handled by class Params manager declared in p manager.h and implementedin p manager.cc. The idea behing is quite simple. At first ( in Params manager::Params -

manager() ) a table where each record represents one parameter ( Param record ) is created.This table is filled with particular parameters and their records ( parameter token, parameteralternative token, parameter type, a pointer to the corresponding variable ).

• Parameter token is the name of parameter itself ( e.g. ”-i”, ”-ntt” )

• Parameter alternative token is the second name of parameter itself, in GNU style ( e.g.”–input”, ”–no t tables”)

• Parameter type tells whether the parameter value is of type bool ( here two values forfilling adequate variable with true and false are distinguished - see Param type ) orstring or int

• Pointer to the corresponding variable that serves for quick filling this variable with anew value. The latter algortihm deals only with these variables ( they are defined inParams manager itself )

After the program is started,the algorithm goes through the given parameters ( Startingsearch(p. 48) ) and according to Params manager::param table sets ( done by Params -

manager::set variable() ) variables corresponding to particular parameters to their propervalues ( usually only sets the variable to true : most of parameters are ”switchers” ).

A.4 Tree search

Tree search is the most interesting section of the whole program. It is mainly implemented int search.cc and t search.h, search itself is managed by class T searcher ( especially func-tions T searcher::search and T searcher::search son which creates an indirect recursionrelationship ). Other classes defined in t search.h have supporting tasks :

• Sgf printer takes care of creating sgf output of the problem.

• Hash table item represents item stored in transposition tables or for repetition recog-nition purposes.

• Hash table manager conducts transposition tables.

• Hash repset manager guards possible repetitions.

40

A.4.1 Tree search implementation description

Tree search is ( as mentioned above ) done in two functions ( T searcher::search andT searcher::search son ). Let’s see how is the tree search implemented ( I will describemost important things happening in a single node ). After some neccessary initializations itis checked ( only if option -ntt is not switched on ) whether actual position doesn’t matchsome position stored in transposition tables. In case that iddfs is active it is important toinclude transposition table cutoff ( for gs unknown status ) even when status stored in thetables is gsa depth uncertain ( if cutoff would not be performed same search as in the savedposition would be done and gs unknown status would be gained again ).

Group_status act_status_ = hash_table_manager.get_item_status(

Hash_table_item( board_manager.hash_board_manager.get_position_hash(),

stone_color_to_move_, board_manager.ko_manager.get_retake_coordinates(),

alfa, beta)); // retrieveng status from transposition tables

... // adjusting alpha and beta according to saved values

// iddfs, this node was already searched ( and gsa_depth_uncertain status was obtained )

if ( same_depth_limit && act_accuracy_ == gsa_depth_uncertain

&& params_manager.get_iddfs())

return Search_result( gs_unknown,gsa_depth_uncertain, act_tree_size_);

// act_status_ is set from t_tables if stored alpha == stored beta

if ( act_status_ != gs_unknown) {

sgf_printer.buff << "Transposition table cutoff for status: " << act_status_ << endl;

...

return act_status_; // control is returned to father

}

If this test is negative ( position hash key was not found in Hash table manager::hash -

table ), static life and death analyses together with simple capture recognition cutoff ( toolittle defender’s stones cannot create a living group ) is performed:

if (( stone_color_to_move_ == sc_black &&

board_manager.number_stones_de < TOO_LITTLE_TO_LIVE) || // TOO_LITTLE_TO_LIVE is 4

( stone_color_to_move_ == sc_white &&

board_manager.number_stones_de < TOO_LITTLE_TO_LIVE -1))

act_status_ = gs_dead ; //too little stones to live

else

act_status_ = eye_manager.is_alive( board_manager.get_point_board(),

board_manager.get_pot_eyes_map_pt()); // static life and death analysis

If this analyses doesn’t succeed in recognizing the group neither alive ( act status -

== gs alive ) nor dead ( act status == gs dead ), then status of actual node willbe decided from statuses of his sons. At first different static heuristics are applied. Eachheuristic might change moves priority by specified margin. The lesser the priority the moreimportant move becomes ( and thus more probable is that it will be tried first ). Effectsof different heuristics might be cumulated ( some move might be recognized as very goodby more heuristics ). Moves are sorted ( according to their priority decided by heuristics) in order to make pruning techniques ( especially alpha beta ) more effective. Self ataripruning technique is applied as well ( larger defender’s blocks are not allowed to self atari ).Application of some heuristics is shown below:

41

//checking whether a played move is a self atari

if (( board_manager.check_self_atari( stone_color_to_move_, last_item.first,

&stone_in_atari_num))) {

if ( stone_color_to_move_ == sc_black && stone_in_atari_num <= 2 )

last_item.second.increase_act_heuristic_pp( 5); // this selfatari is mediocry demoted

else

last_item.second.increase_act_heuristic_pp( 10); // this move is very much demoted

if ( stone_in_atari_num >= 3 && stone_color_to_move_ == sc_white ) {

last_item.second.set_act_main_pp ( pp_none ); // this prevents move to be played

continue; // no search when atari on more than 2 defender’s stones

//capture heuristic

if (( node_type_ == nt_max ) &&

( board_manager.check_capture( stone_color_to_move_, last_item.first)))

last_item.second.increase_act_heuristic_pp( -5); // this move is promoted

... // Other heuristics. The lesser the evaluation of the move the more it is promoted

Pass moves are included in the list of moves as well. Generally moves are ordered inthe following way ( this is a primar ordering, secondary ordering is represented by heuristicresults ):

• At first ”ordinary” moves” ( not pass, not ko taking moves ) sorted by heuristic evalu-ation.

• A pass move.

• At last ko taking moves ( there is a possibility of starting the ko for life of the group ).

After the moves are sorted (according to their priority), they are stored in Playable -

vector. Then the program goes through this vector of possible moves and tries to play themout. The line of algorithm slightly differs according to the type of the node ( nt min , nt max

). Howewer the principle is the same ( I will give code examples for defender’s node sincethat one is a bit more complicated ):

• At first some checks related to playing a pass are performed. Pass is always involved inthe list of the moves to be played, however whether it is really played is decided rightbefore playing it). Pass is played ( this is expressed by variable play pass allowed )only in following cases :

– Ko taking move ( potentially making group status ko ) is present. Then pass isneccessary to decide whether the ko taking move is relevant to life and death ofthe group ( pass simulates situation when opponent ignores a ko threat and triesto yield from the ko ).

– In defender’s node, none of the searched sons didn’t reach status gs alive andthere are maximally 4 legal moves ( then pass is looking for possibility of seki ).

if ( stone_color_to_move_ == sc_white ) {

//next condition is neccessary for seki status

if ( act_status_ != gs_alive && legal_moves_num <= 4 )

play_pass_allowed = true; } // pass is allowed

42

• In attacker’s node, at least one son have reached ko status and none of sons havereached dead status. Then ( if there are maximally 4 legal moves - pruning technique )pass move is tried in order to reveal potential dead group status because of ”bent fourin the corner”.

• Afterwards, the actual move is played ( Board manager::play a stone ). When it isillegal ( Board manager::play a stone returns false ) next move is tried.

if ( ! board_manager.play_a_stone ( sc_white ,(*it).first))

continue;

else

legal_moves_num++;

• Son of the actual node is searched recursively by calling T searcher::search son

function ( however recursion is indirect since method T searcher::search is calledfrom T searcher::search son ). In T searcher::search son there is also handledpart of the sgf output for the son ( e.g. outputting move’s coordinates, potential outputpruning ). Moreover repetitions are tested and possible repetition cutoff is done. Reasonfor this indirect recursion is that actions related with searching the son is in defender’sand attacker’s pretty identical ( the only practical difference is the color of the playedstone printed to the sgf output).

son_result = search_son( (*it).first, nt_min, level, alfa, beta, play_ko_for_life);

• Possible special handling for ko/double ko is performed. This is a very interesting ( andalso was quite difficult to programm ) section of the algorithm. As mentioned above,when there is a ko taking move it is being searched as the last one ( because this raisesprobability of cutoff before searching the ko taking move). After searching this move,it’s status might be transformed ( see below ) to gs ko ( transformation takes placewhen variable play ko for life is true). When value of play ko for life is false

( in the moment when son where ko was taken should be searched ) then the ko is notrelevant to the result and thus outcoming status of the son is not transformed ( to thegs ko status ). Varibale play ko for life is set ( actually this is done before the sonis searched ) as follows ( in defender’s node):

if ( play_ko

&& after_pass_group_status != gs_unknown //unconditionaly alive

&& after_pass_group_status <= gs_ko //after pass black can get at least ko

&& ( act_status_ != gs_alive ) ) //defender cannot live without ko

play_ko_for_life = true;

Here even when act status is gs ko it is still possible to get unconditional live throughdouble ko ( therefore taking the ko is tried ). In attacker’s node, conditions ( last threein the ”if” statement ) for stating play ko for life are almost symetrical with theexception that gs ko value of act status causes cutoff ( if attacker can get the kothere is no reason to try taking the ko because he cannot get a better result ).

43

• After searching the ko taking move, son’s status ( son result.group status ) is trans-formed as follows ( before passing it to section of comparing against actual status :

see comparing(p. 44)): When play ko for life is true ( all preliminary conditionsfor gs ko status are fulfilled ) then value gs alive ( gs dead in attacker’s node ) ofson’s status is transformed to gs ko. This operation says: when defender can live aftertaking the ko ( attacker in son’s node is not allowed to recapture the ko, but in his turnpass move ”simulating” possible defender’s ignoration of ko threat is naturally involved) then he is alive in ko. And symetrically for the attacker taking the ko. Moreover indefender’s node double ko test is performed :

// double ko handling, ko was taken this turn - possible double ko check

if (( ! params_manager.get_no_ko_handling() ) &&

(play_ko_for_life && son_result.group_status == gs_ko )) {

if ( possible_double_ko ) { // two independent ko’s which white can start -> life

sgf_printer.buff << "Alive in double ko" ; sgf_printer.print_comment(true);

son_result.group_status = gs_alive;

}

else

possible_double_ko = true;

if ( last_move_ko ) {

// last move started ko for life and defender found another ko => double ko

son_result.group_status = gs_alive;

sgf_printer.buff << "Alive in double ko" ; sgf_printer.print_comment(true);

}

}

• Gathered status of the son is compared to actual status ( the best fitting statusso far ) and possible alpha beta cutoff is performed. In this part it is neccessary totake into account accuracy of the actual group status as well ( expresed by act -

accuracy of Group status accuracy type ). Value gsa certain accuracy expressesthat status represents best play of both players. On the other hand value gsa alfa -

beta uncertain means that since alpha beta cutoff was performed retrieved groupstatus might be incorrect, however in relation to the previous searches and previouslyset alpha and beta it is satisfying. It is clear that gsa certain accuracy is preferedto gsa alpha beta uncertain. Thus, algorithm tries to propagate upwards the treegroup statuses with gsa certain accuracy. Therefore, if in one node there are two sonswith same group status ( usually this is gs ko ) and with different accuracies ( gsa -

certain and gsa alfa beta uncertain ) than the one with gsa certain surpassesthe second one. As a side note: it is clear that group statuses with uncertain accuracy( gsa alfa beta uncertain, gsa depth uncertain ) are not included in the prunedsgf output ( -ps is switched on ), because they might provide confusing information( human doesn’t take into account alpha beta values when browsing search results).This natural part of minimax algorithm with alpha beta pruning is in defender’s node( attacker’s node is symmetrical ) programmed as follows:

if ( son_result_.group_status != gs_unknown &&

act_status_ <= son_result_.group_status || act_status_ == gs_unknown)) {

if ( act_status_ == son_result_.group_status ) {

if ( act_accuracy_ != gsa_certain ) //better accuracy ( certain )

44

act_accuracy_ = son_result_.group_status_accuracy;

}

else // act_status_ < son_result_.group_status, accuracy of better solution

act_accuracy_ = son_result_.group_status_accuracy;

act_status_ = son_result_.group_status;

if ( ab_pruning && alfa < act_status_) // alpha update

alfa = act_status_;

}

// alpha beta cutoff

if ( ab_pruning && ( act_status_ >= beta ) ) { //pruning

...

board_manager.unplay_last_stone(); //unplays son’s move

... // storing actual position to transposition table

return act_status_;

}

• Actual move is unplayed and another one is tried. This is done by simply callingfunction Board manager::unplay last stone() and continuing to cycle through thepossible moves.

• Returning status of the actual node. Two things must not be forgotten before returninga status ( result of the node ) of the group in the actual node:

– Keeping consistency of the position. This is done by calling function Board -

manager::unplay last stone() in cases when status of the node is returnedbefore all son’s are resolved ( due to some pruning, usually alpha beta ).

– Storing actual position to transposition tables ( only when option -ntt is switchedon ) with appropriately set input and output alpha beta values together withaccuracy of actual result and other information. This is done by following sourcecode:

hash_table_manager.store_item (

Hash_table_item ( board_manager.hash_board_manager.get_position_hash(),

stone_color_to_move_,

board_manager.ko_manager.get_retake_coordinates(),

in_alfa, in_beta, act_accuracy_, act_status, act_status_));

A.4.2 Transposition tables implementation

Very close to the tree search is the implementation of transposition tables. It is done in t -

search.cc and t search.h. Transposition tables are handled by class Hash table manager

which contains a table representing saved positions and provides functions to perform op-erations upon the table. Transposition table itself ( Hash table manager::hash table ) isimplemented as an array of pointers to the Hash table item ( representing one saved posi-tion ). Since today’s computers don’t struggle with memory shortage as it was usual in thepast I decided for fixed-size table with over two million entries ( therefore an empty tabletakes around 8MB RAM on 32 bit architecture ). During the search process table grows asthere are new positions being stored in. Size ( number of items ) of the hash table doesn’tchange in the process of solving Go problem. When a table becomes rather full ( number

45

of stored position equals 1/2 of the sum of pointers from Hash board manager::hash table

) then saving mode is toggled on and new positions are stored on the place of old ones.Hashing function is ordinary modulo on the higher bits of position’s hash key ( representingthe position ). Reason for this is that random generators ( hash key’s are made up of randomnumbers : see Position representation(p. 52) ) are ”more random” in the higher bits. Forcollision solving typical method of separate chaining is used : each item in the table is a headof a chain containing items with same modulo ( result of hashing function ) of hash key. Newitems are stored to the end of the chain. When searching for a particular item whole chain (in the worst case ) must be searched. The key operations upon the table are:

• Inserting new item into the table ( Hash table manager::store item ) At first, posi-tion’s hash key must be normalized by the color of the player to move. This is done ac-cording to Zobrist method in function Hash table manager::get normalized hash -

key(). Then hashing function is applied on the normalized hash key :

unsigned long pos = ( item.hash_key >> shift_num ) % hash_table_size;

If saving mod is toggled on ( there are too many positions in the hash table ) thenactual position is stored to the first place in the appropriate chain ( replacing someprevious position ). Usually ( not in saving mod ) algorithm goes through chain andtries to update some already stored item ( actual item is a ”new version” of a positionstored previously ) with function hash board manager::actualize item(). When noactualization is done new position is simply stored to the end of the chain.

for ( act_row = 1 ; act_item->next != NULL ; act_row++ ) {

if ( actualize_item ( act_item , item ) )

return; // successfull actualization

act_item = act_item->next; // next item in the chain

}

act_item->next = new Hash_table_item ( item); // saving actual position to the end

• Retrieving information from the table ( Hash table manager::get item status ) Asin the previous method position’s hash key is normalized and position in the table ( pos) is retrieved. Then cycle goes through chain of the stored items until it reaches the endor it founds corresponding item ( this is decided by applying function corresponds()

to both actual and stored item ). Function coorresponds() compares whole ( notshortened by modulo ) hash keys and ko retake coordinates ( this is important : positionis determined by it’s hash key and position of potential ko ). Moreover it comparesinput alpha and beta values of both stored position and the actual position ( in theprocess of tree search ) and takes into account saved position accuracy to decide whethersaved output alpha and beta values might be provided for actual position.

//certain status provides more general condition to apply transposition table cutoff

if ( saved_.accuracy == gsa_certain ) {

if (!((( saved_.node_type == nt_max ) && (( new_.beta <= saved_.beta )

|| ( saved_.new_alfa < saved_.beta ))) ||

(( saved_.node_type == nt_min ) && (( saved_.alfa <= new_.alfa )

|| ( saved_.alfa < saved_.new_beta )))))

return false;

46

}

else // more resolute conditions, because group status is not certain

if (!(( new_.beta <= saved_.beta) && ( saved_.alfa <= new_.alfa)))

return false;

A.5 Static eye recognition

Static eye recognition is dealt in quite a simple way in TGA. It is implemented in eyes.h

and eyes.cc and the most important class handling static analysis is Eye manager.

A.5.1 Recognizing status of the group

Generally, the method of static analysis is performed in ( almost ) every node ( callingEye manager::get group status() method ) and can be described as follows:

• For every potential eye it’s Eye status is found. Meaning of possible Eye status

values is clear, maybe except for es produces no eye. This status expresses that givencoordinate is not a potential eye ( usually adjacent point to attacker’s unsafe stones- defender must play here to capture the attacker and therefore it is not a potentialeye), but it is not clear false eye ( it might become a potential eye in the future whenunsafe attacker plays here and then gets captured ). Therefore it deserves an ownEye status and particular treatment ( it is not excluded from the potential eyes set,but in actual static analysis it is not counted as a potential eye ). Points that obtainstatus es false eye are expelled from the list of potential eyes.

• If there are two or more full eyes their connectivity is checked and ( if they are connected) group is stated alive

if ( full_eyes_num >= 2 )

if ( board_manager.check_eyes_connection ( full_eyes_set))

return gs_alive; // At least two full eyes are connected

• If there are less than two potential eye producing points, group is stated dead.

if ( pot_eye_map_pt_->size() - produces_no_eye_num < 2)

return gs_dead;

• If none of the previous happens, simple heuristic is tried: if group has only two poten-tial eyes ( not counting points that gained status es produces no eye) and these areadjacent, then group is dead ( there cannot arise two eyes ).

if (( pot_eye_map_pt_->size() - produces_no_eye_num) == 2)

if ( board_manager.check_coordinates_adjacency( coordinates_1, coordinates_2))

return gs_dead; //two potential eyes and they are adjacent => group is dead

• Otherwise ( what happens in most cases ) group is stated unknown.

47

A.5.2 Recognizing status of the potential eye

This is not very algorithmically difficult operation but quite a lot of cases must be handled.Here very important role is played by point board which is used to find out whether aparticular coordinates is empty ( ps empty ) or is occupied by defender’s stone ( ps de )or attacker’s stone ( safe attacker ps at x unsafe attacker ps uns at ). Checking what eyestatus potential eye has consist of three steps:

• checking whether the eye point itself is empty or ocuppied by unsafe attacker

• checking adjacent coordinates

• checking diagonal coordinates

First two items are very easy and the only problem is how to perform checking of thediagonal coordinates. Since there is a possibility of so called ”diagonal related eyes” ( twoeyes which are diagonal points to each other ). Here is neccessary piece of recursion: whenan eye seems to be potential diagonal eye ( all adjacent neighbours are defender’s stonesand there is an empty diagonal point which occupied by safe attacker destroys the eye andoccupied by defender ( or becoming ”diagonal related eye” ) creates full eye ) the recursivecalling of Eye manager::get eye status() with check diagonal eye parameter set to trueis performed. When this recursive calling ( parametr check diagonal eye prevents cyclingand also states full eye on actual eye point when there is a potential diagonal eye ( the onerecursive calling was perfrormed from )) returns gs full eye than both diagonal related eyesare full eyes.

A.6 Starting search

All program actions start in main() function defined in main.cc.

A.6.1 After the start

At first, program arguments are parsed and variables corresponding to parameters are setto their proper values ( by calling Params manager::set variable() ). After this, programchooses one of two possible continuations :

• Single position search mode. This is the most commonly used mode. The algorithmsimply calls handle single problem() function, which takes care of proper actions ina single problem tree search.

• Multiple positions search mode. The program opens a file with problem names and thengoes through them and on each one of them calls function handle single problem().The difference with single position mode is also in the output: e.g. the informationabout group status, time of search are differently formated.

A.6.2 Handling a single problem

This task is done by function handle single problem() and consists of:

48

• the initialization of possible persistent transposition tables ( when nor -ntt neither-nst options are given )

if ( params_manager.get_transposition_tables() && params_manager.get_save_tt()) {

t_searcher.hash_table_manager.hash_table_activate(); // hash table is initialized

board_manager.hash_board_manager.init_hash_board();

}

• covering neccessary prints

• analysing the single position for the attacker moving first and then for the defendermoving first ( if their moves aren’t blocked by given argument ). Example of ”defendermoving first” section:

if ( params_manager.get_defender_moves_first() ) { // defender moving first

... // output prints

status_de = analyze_single_position ( filename , sc_white);

print_group_status ( sc_white , status_de );

}

A.6.3 Analysing single position

By the term analysing single position is meant to search a problem with a specified color tomove first ( attacker or defender ). This task is performed by function analyze single -

position() and consists of three main steps :

• initialization of data structures:

sgf_parser.init();

board_manager.init();

... // potential init of transposition tables if these are switched on

• parsing sgf source of the input position

sgf_parser.parse_sgf( pos_name.c_str()); // parsing sgf input

//board initialization from temporar data structure follows

board_manager.init_from_temp_board_manager(

sgf_parser.get_temp_board_manager());

• performing search itself ( for a certain player playing first - see parameters of functionanalyze single position()) Here must be distinguished whether the option -id (iterative deepening depth first search ) is switched on. If it is, search is performed inthe cycle with iteratively increasing maximal depth of the search, until certain status (the accuracy of the status is gsa certain ) is not obtained.

49

if ( params_manager.get_iddfs() ) { // iddfs activated

while (( result.group_status_accuracy != gsa_certain)){ // iterative search

...

switch ( color_to_move ) {

case sc_white:

sgf_printer.set_output (save_filename);

result = t_searcher.search(nt_max, 0 , gs_dead, gs_alive, false,false);

break;

.. //symetrically for sc_black

}

t_searcher.increase_max_depth(); // increase maximal depth of the next search

}

If no iddfs is demanded only a single search is performed and afterwards the informationon the number of searched nodes together with search time is printed.

A.7 Sgf parsing

Parsing sgf input is handled in sgf read.cc and sgf read.h.

A.7.1 Sgf format

The sgf inner structure is very logical and easy to parse. Plain text in sgf source might beinterpreted as one of followings:

• Special characters ( as for instance ”[”, ”]” , ”;” , ... ) create structure of the sgf source( e.g. outline variants, show where data and tokens start )

• Tokens are plain text not enclosed in square brackets. Their function is to correctlyinterpret data that are connected with them.

• Data are plain text enclosed in square brackets and they provide information about theposition.

Example 1:

B[aa]

• ”B” in this example is a token for ”Black’s move” ( that means that data related tothis section are interpreted as a move played by black )

• ”[” and ”]” are special characters to mark up data section

• ”aa” are data related to ”B” token and express coordinates on which black played (here it is [0,0] ).

Example 2:

(;GM[1]FF[4]CA[UTF-8]AP[CGoban:2]ST[2]RU[Japanese]SZ[11]KM[0.00]

CR[ga]AW[bb][cb][eb][fb][ac][bc][cc][dc]

AB[da][db][gb][ec][fc][gc][ad][bd][cd][dd][fe] SQ[aa][ba][ca][da][ea][fa][ab][db])

This is an example of the whole position written in sgf.

50

A.7.2 Parsing automata

Class Sgf parser represents finite automata managing sgf parsing. Set of possible au-tomata’s states is represented by Parser state enum. State function is simulated by avector ( State function vector ) where each item is instance of State function record.Automata changes state when there comes a special character in sgf input and there existsan adequate record in the state function ( Sgf parser::state function ). Moving fromone state to another is implemented in a straigt way: if there is a record for actual state ofautomata and actual character in sgf input ( this is found out with simple operator == forState function record ) then new state is resolved from actual record in state function.

for ( State_function_vector::iterator it = state_function.begin();

it != state_function.end(); it++) // search state function vector

//checking existence of the record in the table. Third parameter is unimportant

if ( State_function_record( actual_state, act_char , actual_state ) == (*it) ) {

found_ = true; // a record was found

new_state_ = (*it).get_new_state(); // retrieve new state

break;

}

if ( found_ ) {

actual_state = new_state_; //set up a new state

...

A.7.3 Parsing proccess

The result of sgf parsing is Sgf parser::temp board manager which serves for setting upa position in Board manager ( that is done by Board manager::init from temp board -

manager() function ).Process of sgf input parsing is conducted by Sgf parser::parse sgf. Function goes

through input sgf file character by character ( white characters are skipped ) and actualvalues are worked out by function Sgf parser::resolve.

if ( sgf_parser.resolve(act_char, act_text ) )

act_text.clear(); // it is significant character [ , ] , ; , ...

else

act_text += act_char; // it is not significant character- part of token or data

Variable act text represents actually read part of text ( actual token name, actual data,... ). If actual character is a significant one, act text is worked up in Sgf parser::resolve

and thus it is cleared and ready for the next use.Function Sgf parser::resolve manages automata transitions and Sgf parser::temp -

board manager filling. At first it founds adequate transition in Sgf parser::state function

vector ( see resolving new state(p. 51) ). When there is no such a transition it means thatactual character act char has no significant meaning in the sgf structure and function re-turns false ( thus reading sgf input proceeds ). Otherwise ( possible transition was found )new state of automata is set and actions are taken according to this state.

if ( found_ ) { // new transition found

actual_state = new_state_; // setting up a new state

51

switch ( new_state_ ) {

case aw_data : // new state is awaiting data -> actual token must be updated

if ( ! act_text.empty() )

actual_token = act_text; // updating actual token

break;

case aw_next_token : // awaiting next token -> it old token must be handled

if ( actual_token == token_adjust_b_stone ) { // putting black stone on the board

Coordinates coordinates_ = Coordinates ( act_text );

// storing coordinates to temporar data structure

temp_board_manager.stone_vector.push_back(Stone(sc_black, sm_none, coordinates_));

}

if ( actual_token == token_comments ) {

temp_board_manager.comments = act_text; // potential comments to the problem

}

... // other possible actions for different values of actual_token

Actions when handling aw data state should be clear. In aw next token state last token( Sgf parser::actual token ) together with it’s data ( act text ) is interpreted. ThusSgf parser::temp board manager is here filled with information about position such asin example code. Comments in the sgf file are extracted ( and saved in temp board -

manager.comments ) and they are printed when problem is being solved ( here might befor instance information about difficulty of the problem or problem’s special features ). Noother states of automata needs such a handling.

A.8 Position representation

Module handling position representation is implemented in board.h and board.cc and itis a largest one in the whole program. Tree search module requires an abstract Go boardwhere move might be played ( and unplayed ) according to the valid rules of go. This moduleprovides such an abstraction. It fullfills following functions :

• Position initialization from the results of sgf parsing.

• Playing a move on given coordinates together with all neccessary actions.

• Unplaying the last played move.

• Providing methods supporting static heuristics.

• Managing position hash keys.

The whole functionality the module offers is managed by class Board manager. However,there are many classes fulfilling specialized tasks ( e.g. Ko manager, Unplay manager, ... )but all of these are included in Board manager, board manager is the only instance of thisclass defined in main.h.

A.8.1 How is position represented

Position is understood as a set of block of stones ( block is a set of connected stones of thesame color and in program is represented by instance of class Block ). Go board is abstractedby a two dimensional array Board manager::board where each item of this array is a pointer

52

to the block occupying actual coordinates ( represented by class Coordinates ) or to NULL

if there is no stone. Along with this ”block oriented” representation simple two dimensionalarray where every item represents actual coordinate ( possible values for single coordinateare listed in Point status enum ) is held in Board manager::point board for purposes ofEye manager. For tree search purposes it is neccessary to keep a list of coordinates whereit is possible to play in actual turn ( Board manager::playable map ) and list of potentialeyes in actual turn ( Board manager::pot eyes map ).

A.8.2 Position initialization

After a successfull sgf parsing process all information neccessary for the position initialization( position of stones and position of marks ) are stored in Sgf parser::temp board manager.The position initialization is done by function Board manager::init from temp board -

manager(). It is neccessary to create blocks ( and pointers to them in Board manager::board

) according to the position structure. This is done by simply playing every stone on the board( see below ) as if they were played in the game.

for ( Stone_vector::iterator it = temp_board_manager.stone_vector.begin();

it != temp_board_manager.stone_vector.end(); it++) //cycle through stored data

// if item is a stone it is played out

if ( ( (*it).get_stone_color() == sc_black) || ( (*it).get_stone_color() == sc_white))

play_a_stone( (*it).get_stone_color() , (*it).get_coordinates() ); // playing

Afterwards sgf marks are handled:

• Some attacker’s blocks are stated safe ( Block::safe ) if it’s stones doesn’t have a”playable mark” ( sm playable ).

• According to ”playable mark” ( sm playable ), appropriate coordinates are added tothe Board manager::playable map ( empty points marked ) and Board manager::pot -

eyes map ( all marked - even unsafe attacker’s stones are potential eyes). Moreoverattacker’s stones marked with ”playable mark” are stated unsafe.

• Coordinates on the ”edge” of the problem ( marked with sm at playable ) are addedto the map of playable moves with priorities pp none for defender ( he cannot playthere ) and pp low for attacker ( attacker’s playing priority here is low ).

Finally, Board manager::Point board is initialized by calling Board manager::fill -

point board().Besides some trivial variables initialization in Board manager::init it is also neccessary

to initialize mechanism for producing Zobrist hashing keys. This is done by Hash board -

manager::init hash board. In this function for every possible position on the Go boardan it’s every possible state ( containing black or white stone - empty points are not takeninto account when hashing ) a long random number is generated. Unsigned long long type( 64 bit ) is used for these numbers. These random numbers ( organized in Hash board -manager::hash board ) provide base for generating Zobrist hash keys for every position onthe Go board and thus make engine in transposition tables and repetitions checks work ( seeTree search(p. 40) ). This initialization function is run from main.cc for every position tobe solved.

53

A.8.3 Playing a move

This is the most complicated operation in the whole position representation. It is done byfunction Board manager::play a stone ( this function is ”a heart” of the whole module )supported by other Board manager functions. The general process can be described in threebasic steps ( in function they are not strictly sequential, rather they interfere ):

• creating Unplay manager record for future stones unplaying

• adding the played stone into the block structure and updating it ( adjusting liberties,merging blocks, some block might get captured, ... )

• updating other data structures ( e.g. Board manager::point board )

If move is a pass move, only shortened record is saved for unplaying purposes and functionreturns true ( playing a move was successfull ). Some trivial checks are performed afterwardsit was found move is not a pass ( e.g. there is no stone on desired coordinates, it is notviolating ko rule ). A complete record for unplaying is created and filled with the actualinfromation.

Unplay_node * act_node = new Unplay_node(); // creating new node of dynamic list

act_node->next = unplay_node_handle; // adding a node to the beginning of list

unplay_node_handle = act_node;

//node is filled with unplaying information

unplay_node_handle->unplay_manager.stone_to_unplay = coordinates_;

unplay_node_handle->unplay_manager.number_stones_de = number_stones_de;

... // saving other datastructures: playable_man, pot_eyes_map, ko_manager, position_hash

If the move is a ko taking move ( checked by function Board manager::takes ko ),Board manager::ko manager is activated and filled with actual ko information ( where ittakes place, which are retake coordinates, which player took the ko ). Now it comes time toadd a stone into the block structure. First, new empty block is created and actual stone isadded into it together with updating position hash key.

Block * act_block_ = board_manager.create_new_block( stone_color_); // creates empty block

act_block_->add_stone ( coordinates_ ); //

board[coordinates_.get_co_row()] // sets a pointer from a board to the block

[coordinates_.get_co_column()].set_present_block_pt( act_block_);

hash_board_manager.xor_with_position_hash( coordinates_, stone_color_); // update hash key

Now, more complicated part comes. At first cycle through the new block’s neighbouringcoordinates is performed and liberties are added to the block as well as neighbour blocksof stones are saved for unplaying purposes ( see Unplaying moves(p. 56) ). Afterwardsanother cycle managing block structure updating is performed. It goes through new block( actual ) neighbours again and merges actual block with neighbouring friendly blocks ( bycalling Board manager::merge blocks ) into a single block of stones. This cycle also removesliberty from enemy neigbouring blocks and possibly performs function capturing this enemyblock ( Board manager::capture block )

for ( int i = 0; i < 4 ; i++ ) {

...

// getting pointer to adjacent block

54

adjacent_block_ = board[adjacent_coordinates_[i].get_co_row()]

[adjacent_coordinates_[i].get_co_column()].get_present_block_pt();

adjacent_block_->remove_liberty( coordinates_ ); // played stone removed block’s liberty

if( adjacent_block_->stone_color == stone_color_) { //adjacent block is friendly one

merge_blocks( act_block_ , adjacent_block_ ); // merging adjacent and actual

}

else { // adjacent block is an enemy one

if ( adjacent_block_->is_captured() ) { //last liberty was removed => capturing block

capture_block( adjacent_block_ );

}

Capturing a block ( Board manager::capture block ) is connected with updating the listof potential coordinates to play ( Board manager::playable map ) and the list of potentialeyes ( Board manager::pot eyes map - coordinates under captured stones become potentialeyes ). Liberties are added to the neigbouring ( opponent’s ) blocks and the position hashkey is updated as well ( stones of the captured block are xored out ). At the end the blockis removed by function Board manager::remove block ( updates pointers from Board -

manager::board and items in Board manager::point board and destroys the block ). Itis neccessary to take appropriate actions when ”under the stones” treatment is switched off( -nus parameter ). In that case when a defender block of at least 3 stones is captured (expressed by variable possible under the stones ), coordinates under the captured stonesare not added to the Board manager::playable map nor Board manager::pot eyes map.Thus, the algorithm doesn’t try to play under them. When no ”under the stones” analysisis demanded, unsafe attacker’s stones that captured this defender’s block are excluded fromthe list of potential eyes ( they cannot be captured thus are not potential eyes ). Backboneof capturing function is programmed as follows:

for ( Coordinates_vector::iterator it = block_->stone_vector.begin();

it < block_->stone_vector.end() ; it++ ) { //cycle through captured stones

// if playing under the stones is not forbidden, data structures are actualized

if (( ! possible_under_the_stones ) || ( ! params_manager.get_no_under_the_stones())) {

playable_map.insert ( make_pair ((*it) , Playable_data())); //playing under the stones

pot_eyes_map.insert ( make_pair ((*it) , 0 ));

}

hash_board_manager.xor_with_position_hash((*it), block_->stone_color); //hash key update

... // adding liberties to adjacent blocks

}

remove_block( block_); // destroying the block

Finally in the process of playing a stone, Board manager::point board and the list ofpotential coordinates to play ( Board manager::playable map ) are updated according tothe played stone. In the end it must be checked whether a played stone was not a suicide. Ifit was a suicide, stone is immediately unplayed ( thus keeping consistence of playing function) and function returns false marking that move was illegal.

if ( act_block_->is_captured() ) {

unplay_last_stone(); // stone must be prevented to commit a suicide

return false; // stone was not played

}

55

A.8.4 Unplaying moves

A single instance of Unplay manager is capable of storing all information neccessary for anunplay of a single move. These instances are organized into a dynamic list in order to properlyunplay all moves along the path to the actual position. Dynamic list thus consists of instancesof Unplay node ( containig instance of Unplay manager and pointer to next node ). Actuallythis dynamic list is backward organized ( new nodes are added to the beginning) thus nodesrelated to last moves are in the beginnning and those related to moves played earlier are in theend. Pointer Board manager::Unplay node handle marks the beginning of this dynamic list.As shown above a new record ( instance of Unplay node ) is added to the dynamic structurein function Board manager::play a stone. Process of unplaying last played move ( anothermove cannot be unplayed ) is covered by function Board manager::unplay last stone.

Unplaying pass moves is handled at first. There are created special ( almost empty )unplaying records for pass moves containing only information that pass was played and copyof Board manager::ko manager (this is neccessary because after pass it is possible to retakesome ko which was forbidden before). If move is not pass, at first some data structures( playable map , ko manager, number stones de, pot eyes map, position hash) are justcopied from the unplay record to the data structures in Board manager.

playable_map = unplay_node_handle->unplay_manager.playable_map;

ko_manager = unplay_node_handle -> unplay_manager.ko_manager;

... // copying other data structures

Now the block structure has to be restored. In the process of playing actual stone ( the oneto unplay ) algorithm stored copies of blocks neighbouring to coordinate on which stone wasplayed ( see Playing a move(p. 54) ). Actual block and block’s neigbouring to coordinate onwhich stone was played are simply removed ( using function Board manager::remove block

). Now position on the board is updated by adding stored blocks into the block structure.This is done by the cycle through all stones in every of these blocks ( maximally four neigh-bouring blocks ) and setting pointers from adequate coordinates in Board manager::board

to the actual block, moreover Board manager::point board is updated.

for ( int i =0 ; i < FOUR ; i++ ) {

at_block_ = unplay_node_handle->unplay_manager.neigbour_blocks[i];

...

for ( Coordinates_vector::iterator it = act_block_->stone_vector.begin();

it < act_block_ ->stone_vector.end() ; it++ ) { //cycle through

// setting a pointer from Board_manager::board

board[(*it).get_co_row()][(*it).get_co_column()].set_present_block_pt( act_block_ );

// updating Board_manager::point_board according to actual stone( color,safe/unsafe )

if ( act_block_->stone_color == sc_white )

point_board[(*it).get_co_row()][(*it).get_co_column()] = ps_de; // defender’s stone

else { // handling variants for ps_at and ps_uns_at

if ( act_block_-> safe )

...

}

Last obstacle is that some of neighbouring blocks might got captured by playing an actualstone ( in Board manager::play a stone ). In that case all stones of this block are cycledthrough and liberties representing these stones ( block was captured, therefore it provided

56

liberties for adjacent blocks ) must be removed from all adjacent blocks. Finally handler toactual stone to unplay ( Board manager::unplay node handle ) is forwarded to the nextitem in the dynamic list ( thus list gets ready for the next unplay ) and actual Unplay node

is destroyed.

A.8.5 Static heuristics methods

Board manager contains set of methods supporting static heuristics neccessary for efficienttree search. They are quite similar and clearly benefit from chosen ”block oriented” positionrepresentation. As an example I provide detailed description of Board manager::check -

take liberty. This function is used to check whether played move captures, ataris or simplytakes away liberty of opponent’s unsafe block. These different actions are distinguished bydemanded number of opponent’s block’s liberties : 1 for capture , 2 for atari , 0 stands formore than 2 and represents simply taking away the liberty. At first it is checked whether inputCoordinates are valid ( not equal to Coordinates (-1,-1 ) ). Then in the cycle neighbouringcoordinates of coordinate in check are retrieved and so are the blocks of stones covering them.After neccessary trivial checks ( negihbouring coordinates are on the board, neighbouringblock is present ) a simple test whether neighbouring block matches the conditions is doneIt matches the conditions if it is an unsafe opponent’s block with an appropriate number ofliberties.

for ( int i = 0; i < FOUR ; i++ ) {

// obtaining neighbouring coordinates

adjacent_coordinates_ = get_adjacent_coordinates ( coordinates_ , Direction ( i ) );

if ( Coordinates ( -1, -1 ) == adjacent_coordinates_ ) // out of the board

continue;

adjacent_block_ = board[adjacent_coordinates_.get_co_row()] // getting neighbour block

[adjacent_coordinates_.get_co_column()].get_present_block_pt();

// if all conditions of taking the liberty away are fulfilled ...

if ( adjacent_block_ != NULL && adjacent_block_-> stone_color != stone_color_ &&

! adjacent_block_->safe &&

( adjacent_block_->liberty_count == liberty_num_ || liberty_num_ == 0 ) ) {

... // here function returns pointer to block whose liberty is taken

return true; // test was successfull, move takes away liberty_num_ liberties

}

}

All static heuristic methods are declared within Board manager and begin with a keyword”check”.

57

Appendix B

TGA User manual

B.1 About

TGA is a program solving life and death problems in Go ( for more information about Gosee [11] ). Program is designed to solve problems where one group ( defender’s ) is ratherclosed from the rest of the board by the opponent’s secure group ( attacker’s ) and it’s life isin question ( it means that it cannot connect outside ). Search algorithm is able to deal withsuch peculiarities as for example: seki, ko ( direct ko, approach move ko, multi step ko ),double ko, bent four in the corner, under the stones problems. I estimate program’s overallsolving strenght to be at low dan level ( around 1d ). However, it showed some pleasantresults in solving rather very difficult ( 5d, 6d ) problems. Program might be very useful inGo problems analyses even for low dan players and for all kyu players.

B.2 Installation & miscellanous

So far program is accessible to Linux/Unix users only. Installation is pretty simple : copyarchive file tga.tar.gz to desired location on your computer, then extract it and followinstructions in ./INSTALL file ( classical make command covers whole installation). Pro-gram uses no special libraries and for successfull installation is sufficient the presence of gcccompiling program with standard libraries. Program is distributed under GNU/GPL free ofcharge.

After the installation the directory structure ( besides others ) contains:

tga is a program itself.

clear sgf is a bash script removing all sgf files from tga directory ( useful after TGA is ranin multiple position mode ).

src is a directory with program source code.

obj is a directory with program object modules.

data is a directory with a small Go problems collection on which program was tested (problems are categorized according to difficulty for the program).

license is a directory containing general information on GNU/GPL.

58

sgf files list is a list of problems on which was program tested ( this file was used as inputin TGA multiple position mode ).

ABOUT is a simple text file with information about project.

B.3 Input

B.3.1 Options

Since there is no need of interactive communication between user and program, TGA is a”command line” program. It is launched by typing ./tga ...-i input position filename

in program directory , where . . . represents program options and input position filename rep-resents problem to be solved in SGF format ( see [12] ) with appropriate marking ( see §B.3.2). There is no configuration file, therefore requested non-default options must be includedin every launch. Options might be written as usual ( e.g. -nab ) or in GNU format ( e.g.–no alpha beta ). Here is a list of all possible options and a detailed explanation of theirmeanings:

-nab ( –no alpha beta ) Deactivates alpha beta prunning ( slows down the search ). Alphabeta prunning significantly reduces number of nodes neccessary to be searched in orderto retrieve a result, thus reducing the search time.

-af ( –attacker first ) Searches only for the result when attacker moves first in the givenposition.

-df ( –defender first ) Searches only for the result when defender moves first in the givenposition.

-to ( –testing output ) Useful testing informations are included in every node’s info of thesgf output ( testing ).

-nkh ( –no ko handling ) If this option is switched on, algorithm doesn’t try to solve ko. Ithas mainly testing purposes and given results might be wrong ( testing ).

-ta ( –testing area ) No search is made and main program line is switched to the testingarea, therefore program won’t give any reasonable results (testing ).

-ps ( –pruned sgf ) Sgf output is pruned significantly. If attacker moves first, only variantsending with death of a defenders group or with a ko are included in sgf output. Ifdefenders moves first, only variants ending with live of his group or with a ko areincluded.

-mp ( –multiple pos mode ) Switches on multiple position mode. Instead of one input posi-tion given after -i option program expects list of positions ( path to single position onsingle line ). This is useful for statistic testing and comparison with other programs.Naturally sgf output is generated for every problem into adequate file ( transformedinput filename).

-ntt ( –no t tables ) Deactivates transposition table ( slows down the search ). Transpositiontables cause repeated positions in search to be solved in instant time from cachedprevious results thus shortening search time a lot.

59

-nst ( –no save t tables ) Deactivates saving transposition tables afrer first search ( slowsdown the search ). Saving transposition tables after first part of search ( i.e. defender tomove in the position ) speeds up second part of the search because of larger informationbase. When either -af or -df are switched ( not both together ) then saving transpositiontables has naurally no effect.

-id ( –iterative deepening ) Activates iterative deepening depth first search ( slows downthe search ). This is rather obsolete ( in TGA ) method for solving under the stonesproblems. Generally problems are solved in longer time with this option.

-ndh (–no dynamic heuristics) Prevents using dynamic heuristics. This option has mainlytesting purposes ( testing ).

-nsh (–no static heuristics) Prevents using static heuristics. This option has mainly testingpurposes ( testing ).

-nus (–no under the stones) No under the stones analysis is performed and algorithm isprevented to play under larger captured blocks of defender’s stones. This might causesome problems to be solved incorectly ( testing ).

-od (–sgf output depth ) number No sgf output is produced when actual position is ( in thesearch tree ) deeper than number. This helps to significantly reduce sgf output in verylarge problems. Also this option is useful when user is interested only in several firstmoves of the solution.

-h ( –help ) Shows short help about program input and options.

Program’s variables are implicitly set to perform the most effective search ( all follow-ing speeding mechanism’s are used: alpha beta , transposition tables, transposition tablessaving, dynamic and static heuristics ). Most often are used following cofigurations of inputparameters.

./tga -i problem name Classical cofiguration with all speeding mechanisms switched on.

./tga -ps -i problem name Here moreover pruned sgf output is demanded ( for larger prob-lems ).

./tga -ps -od 10 -mp -i problem list Here program is in multiple position mode and everysingle sgf output is pruned and it’s depth is limited by constants 10.

B.3.2 Input sgf file

As an input file for the search ( after -i option ), legal sgf ( see [12] ) file is expected. Moreoverthere are special requirements on the input format. Algorithm expects given Go position tobe on the Go board of 19x19 size in the root node of sgf tree. That means that positionmust be created ( by editing stones positions ) not played out ( as it is usual in the game).It is moreover expected that black’s stones represent attacker and white’s defender. Alsofollowing marks are required :

60

Figure B.1: Example of input position

Square to mark empty coordinates on which are both defender and attacker allowed to playand also to mark attacker’s unsafe stones i.e. stones that might be captured by thedefender ( non marked attacker’s stones are expected to be safe ).

Circle to mark empty coordinates on which is attacker allowed to play but defender is not.By these are meant coordinates on the ”edge” of the problem ( see Figure B.1).

Moreover some comments ( according to sgf format ) might be added to the problem ( inthe root node ), these will be included in the output. Example of input position is shown inFigure B.1.

Sgf source of problem given in Figure B.1:

(;GM[1]FF[4]CA[UTF-8]AP[CGoban:2]ST[2]

RU[Japanese]SZ[11]KM[0.00]

CR[ga]AW[bb][cb][eb][fb][ac][bc][cc][dc]

AB[da][db][gb][ec][fc][gc][ad][bd][cd][dd][fe]

SQ[aa][ba][ca][da][ea][fa][ab][db])

B.4 Output

B.4.1 Output sgf files

Besides testing messages which are switched off for usual user, program generates shortinforming messages. Before the search shows information stored at the problem as comments.After the search informs user of a result of the search, number of searched nodes and timetaken in the search. The most important output of the search are output sgf files. Namesof output sgf files is generated from transformed input position filename ( ’/’ and ’.’ aretransformed to ’ ’ ) by adding suffix at.sgf ( solution with attacker to move ) or de.sgf (solution with defender to move ). In every sgf node there is a short information about depthand status of the node. In nodes where transposition tables cutoff was performed ( sameposition in transposition tables was found ) or repetition cutoff was performed ( same positionalong the path to actual node was found ) there is a short information about this action. If-to is switched on node’s description are longer containing information about e.g. potentialeyes, possible ko. When -ps option is switched on, sgf output is pruned to show only relevantinformation. Thus when attacker moves first all nodes resulting in the life of the group are

61

Figure B.2: Position in the sgf output.

pruned and on the other hand when defender moves first all nodes proving the death of thegroup are pruned. This restricts the number of nodes a lot and together with marking theoutput and possible output depth limitation ( option -od number ) significantly increasesoutput’s legibility. There are three marks used in sgf output:

Circle marks the last played move.

Cross marks the opponent’s moves answering last played move.

Triangle marks the coordinate where ko arose in the last turn, therefore opponent cannotplay there now.

Example of position in the single node in the sgf output is shown in Figure B.2.

62

Bibliography

[1] T. Wolf. The program gotools and its computer-generated tsume go database. InH. Matsubara, editor, Game Programming Workshop in Japan ’94, pages 84–96, Com-puter Shogi Association, Tokyo, Japan, 1994.

[2] Z.Chen K.Chen. Static analyzis of life and death in the game of go. Information sciences,121, 1999.

[3] M. Mueller. Computer go. Artificial Intelligence, 134, 2002.

[4] Go world, 2004.

[5] A. Kishimoto M. Mueller. Search versus knowledge for solving life and death problemsin go. In Twentieth national conference on Artificial Intelligence, 2005.

[6] Kishimoto Uller Department. Df-pn in go: An application to the one eye problem.citeseer.ist.psu.edu/634812.html.

[7] D. Dyer. Go problems solving program. http://www.andromeda.com/people/ddyer/go-program.html.

[8] Gnugo documentation. http://www.gnu.org/software/gnugo/.

[9] Tomas Kozelek. TGA programmer’s manual, 2006. Partially included in appendix.

[10] Tomas Kozelek. TGA user’s manual, 2006. Included in appendix.

[11] International server dedicated to the game of go. http://senseis.xmp.net/.

[12] Official specification of the sgf format. www.red-bean.com/sgf.

[13] The free internet encyklopedia. www.wikipedia.org.

[14] Zobrist hashing. http://www.seanet.com/ brucemo/topics/zobrist.htm.

63