\n");
73 |
74 | return (wr.ToString ());
75 | }
76 |
77 | public static void Main (string[] args)
78 | {
79 | int[,] board = new int[15, 15];
80 |
81 | board[7,8] = 1;
82 | board[8,8] = -1;
83 | board[6,8] = 1;
84 | board[6,7] = -1;
85 |
86 | StreamWriter wr = new StreamWriter ("test.dot");
87 | wr.WriteLine ("digraph board {");
88 | wr.WriteLine (" graph [rankdir=TB];");
89 | PrintBoard (wr, "test1", board);
90 | PrintBoard (wr, "test2", board);
91 | wr.WriteLine ("test1 -> test2");
92 | wr.WriteLine ("}");
93 | wr.Close ();
94 | }
95 | }
96 |
97 |
98 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 | Stahlfaust - a Gomoku AI
4 | ========================
5 |
6 | Copyright (C) 2004-2005
7 | Marco Kunze ,
8 | Sebastian Nowozin .
9 |
10 |
11 | This package contains binaries and source code for a small two week project of
12 | ours, an artificial intelligence player for the standard Gomoku game ("five
13 | wins").
14 |
15 | An in-depth documentation is available in the doc/ folder. All files in
16 | this package are licensed under the GNU General Public License, as included in
17 | the "LICENSE" file. Read and accept this license before running or modifying
18 | this program.
19 |
20 | If you have any question relating to this program, please write us at
21 | makunze@cs.tu-berlin.de and nowozin@cs.tu-berlin.de.
22 |
23 | Maybe we will remove updated version of the game at
24 | http://cs.tu-berlin.de/~nowozin/stahlfaust/
25 |
26 |
27 | FAQ
28 | ===
29 |
30 | Q: Why is this game named "Stahlfaust"?
31 | A: Its german and means "iron fist". We thought this AI would squash all
32 | human and AI players in a competition, but actually we turned out to be
33 | second and we both easily win against the AI now.
34 |
35 | Q: Can you give any playing advice?
36 | A: Read the chapter about Gomoku in Victor Allis thesis and understand the
37 | idea of building threat trees. If you practise well after some days you
38 | will have no problems building threat trees of maybe 10-15 moves in your
39 | mind while playing. This is good enough to beat Stahlfaust and for most
40 | human players, too.
41 |
42 | Q: Who should start?
43 | A: Your choice, however Victor Allis proved the standard Gomoku to be a sure
44 | win for the starting player. Hence, you can be more proud to win against
45 | this program in case you played the second move.
46 |
47 | Q: Why is it so slow?
48 | A: It just is. No, really, we coded this in a hurry and the dbsearch - hah -
49 | we were happy it actually worked at all ;-) So, don't complain, we know
50 | the dbsearch can be speed up by a factor of 50-100 in a week's work. The
51 | alpha-beta search is quite fast though, using optimized lookup.
52 |
53 | Q: How to compile it?
54 | A: We developed it using both Mono (http://www.mono-project.com/) and Visual
55 | Studio 2003 .NET. You can compile the VS.NET solution. If you are
56 | familiar with Mono, you can get the gtksharp-gui to work, I am sure.
57 |
58 | Q: Where can I read up on Gomoku?
59 | A: http://en.wikipedia.org/wiki/Gomoku
60 |
61 | Q: I want to become a better Gomoku player, how to?
62 | A: Except playing it a lot (obvious) with many different programs, you should
63 | program a Gomoku AI yourself. It teaches you how to value different
64 | situations on the board and an algorithmic approach to evaluating moves.
65 |
66 |
67 | Contact
68 | =======
69 |
70 | Marco Kunze ,
71 | Sebastian Nowozin .
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/newaiplayer/GoBang.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | class GoBang {
5 | private static bool winning (int[,] board, int i)
6 | {
7 | int maxx = board.GetLength(0);
8 | int maxy = board.GetLength(1);
9 |
10 | int[] pd1 = new int[maxx + maxy];
11 | int[] pd2 = new int[maxx + maxy];
12 | int[] pdy = new int[maxx];
13 |
14 | for (int y = 0 ; y < maxy ; ++y)
15 | {
16 | int pdx = 0;
17 |
18 | for (int x = 0 ; x < maxx ; ++x)
19 | {
20 | int pd1_idx = x - y + (maxy - 1);
21 | int pd2_idx = x + y;
22 | // pdy_idx is x
23 |
24 | if (board[x, y] == i)
25 | {
26 | if (++pd1[pd1_idx] == 5 || ++pd2[pd2_idx] == 5 || ++pdy[x] == 5 || ++pdx == 5)
27 | return (true);
28 | }
29 | else
30 | {
31 | // reset numbers
32 | pd1[pd1_idx] = 0;
33 | pd2[pd2_idx] = 0;
34 | pdy[x] = 0;
35 | pdx = 0;
36 | }
37 | }
38 | }
39 |
40 | return (false);
41 |
42 |
43 | }
44 |
45 | private static bool valid(int[,] board, Coordinate move)
46 | {
47 | if (board[move.X, move.Y] != 0) return false;
48 | return true;
49 | }
50 |
51 | private static void printBoard(int[,] board)
52 | {
53 | Console.WriteLine("Board:");
54 | for (int y = 0; y < board.GetLength(0); ++y) {
55 | for (int x = 0; x < board.GetLength(1); ++x) {
56 | Console.Write("{0} ", board[x, y] == -1?"O":board[x, y] == 0?".":"X");
57 | }
58 | Console.WriteLine();
59 | }
60 | }
61 |
62 | public static void Main(string[] args) {
63 | Console.WriteLine("GoBang 0.001");
64 | string host;
65 | int port;
66 | if (args.Length < 2)
67 | {
68 | host = "localhost";
69 | port = 7777;
70 |
71 | }
72 | else
73 | {
74 | host = args[0];
75 | port = int.Parse(args[1]);
76 | }
77 | Player one = new NewAiPlayer();
78 | //Player two = new NewAiPlayer();
79 | Player two = new Communicator(host, port);
80 |
81 | string color = two.AskColor();
82 | int size = two.AskSize();
83 |
84 | if (color == "white")
85 | {
86 | one.SetColor("white");
87 | two.SetColor("black");
88 | }
89 | else
90 | {
91 | Player tmp = one;
92 | one = two;
93 | two = tmp;
94 | one.SetColor("white");
95 | two.SetColor("black");
96 | }
97 | one.SetSize(size);
98 | two.SetSize(size);
99 |
100 |
101 | int[,] board = new int[size, size];
102 | Coordinate move;
103 | while (true) {
104 | move = one.GetMove();
105 | Console.WriteLine("Got move from player 1: {0}", move);
106 | if (!valid(board,move))
107 | throw new Exception("Invalid move " + move);
108 | board[move.X, move.Y] = 1;
109 | printBoard(board);
110 | if (winning(board, 1)) throw new Exception("white has won");
111 | two.RegOppMove(move);
112 | move = two.GetMove();
113 | Console.WriteLine("Got move from player 2: {0}", move);
114 | if (!valid(board,move))
115 | throw new Exception("Invalid move " + move);
116 | board[move.X, move.Y] = -1;
117 | printBoard(board);
118 | if (winning(board, -1)) throw new Exception("black has won");
119 | one.RegOppMove(move);
120 | }
121 | }
122 | }
123 |
124 |
--------------------------------------------------------------------------------
/src/Gomocup/Gomocup.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {ACE0963E-FC24-435E-9C5C-79EAF24FA37E}
8 | Exe
9 | Properties
10 | Gomocup
11 | pbrain-Stahlfaust
12 | v4.0
13 | 512
14 | Client
15 |
16 |
17 | AnyCPU
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | AnyCPU
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 | GomocupInterface
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
72 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | Stahlfaust - Gomoku AI player
2 | by Marco Kunze and Sebastian Nowozin (makunze@cs.tu-berlin.de, nowozin@cs.tu-berlin.de)
3 |
4 | Introduction
5 | Gomoku, sometimes known as "five wins" is a game played on a 15x15 board by two players. The goal is simple: to be the first player to have a line of five or more stones. While the rules are as simple as that, the game can become very complex and challenging.
6 |
7 | Stahlfaust is an AI player for the standard Gomoku game. It uses alpha-beta search, threat tree dbsearch and defensive search to provide a challenge to you. However, its not very fast... :-(
8 |
9 | The game was developed as term project for the Artificial Intelligence course of Professor Zhang Liqing at the Shanghai Jiaotong University by Marco Kunze and Sebastian Nowozin.
10 | Screenshots
11 | Everybody likes screenshots, so here is one. debugging output windows you can view:
12 |
13 | 
14 |
15 | Technology
16 | The guts of the AI is an intelligent definition of threats, used in both the alpha-beta search and the dependency based search. This definition has been given by L. Victor Allis in his PhD thesis titled " Searching for Solutions in Games and Artificial Intelligence". (By the way, he invented db-search).
17 |
18 | We implemented some extra stuff though and we also try to find the threat trees of the opponent player in order to establish a defense.
19 | Download
20 | The whole program is opensource and free software. It is released under the conditions of the GNU General Public License. The license is included in the distribution in the 'LICENSE' file. Please read and acknowledge this license before using this program. On Linux you will need Mono, a free implementation of the Microsoft .NET framework and compilers. The source is written almost completely in C#.
21 |
22 | Stahlfaust 1.0, stahlfaust-1.0.tar.gz (410kb)
23 |
24 | Thanks
25 | We would like to thank Professor Zhang Liqing for his excellent AI course and the challenge posed to us to write a competitive Gomoku player. We easily defeated the previous' years champion program and landed a respectable second place in this years competition.
26 |
27 | We would also like to thank Victor Allis for sharing insights into the game of Gomoku.
28 |
29 | Feedback
30 | We are curious about user-, developer- and mathematicans feedback, so please mail us your thoughts to makunze@cs.tu-berlin.de and nowozin@cs.tu-berlin.de.
31 | last update: Friday, 18 Mar 2005
32 |
33 | Original author: by Marco Kunze and Sebastian Nowozin.
34 | Original website: http://www.nowozin.net/sebastian/tu-berlin-2006/stahlfaust/
35 | Email: makunze@cs.tu-berlin.de and nowozin@cs.tu-berlin.de
36 | Country: Deutsche
37 | Programming language: C#
38 | IDE: Microsoft .NET framework
39 | Year: 2005
40 | Notes: First time on any source code repository.
41 |
42 | Tags: Five in a Row, Tic Tac Toe, TicTacToe, 5 in a Row, Go-Moku, Connect, Connect5, Connect6, Caro, Noughts and Crosses, Gomoku, Renju, Pente, Piskvork, Amoba, Kółko i Krzyżyk, Gomocup, AI, Engine, Artificial Intelligence, Brain, Pbrain, Gra, Game, Source Code Files, Program, Programming, Github, Board, Coding.
43 |
--------------------------------------------------------------------------------
/src/server/Server.java:
--------------------------------------------------------------------------------
1 | import java.net.*;
2 | import java.io.*;
3 | import java.util.*;
4 | class Server {
5 |
6 | int size;
7 |
8 | int port;
9 |
10 | int turn;
11 | int noMoves;
12 |
13 | BufferedWriter[] out;
14 | BufferedReader[] in;
15 |
16 | char[][] board;
17 |
18 | Server(int size, int port) {
19 | this.size = size;
20 | this.port = port;
21 | turn = 0;
22 | noMoves = 0;
23 |
24 | try {
25 |
26 | System.out.println("Server coming up...");
27 | out = new BufferedWriter[2];
28 | in = new BufferedReader[2];
29 |
30 |
31 | board = new char[size][size];
32 |
33 | for (int x = 0; x < size; ++x)
34 | for (int y = 0; y < size; ++y)
35 | board[x][y] = ' ';
36 |
37 | ServerSocket ss1 = new ServerSocket(port);
38 | Random r = new Random();
39 | int x = r.nextInt(11);
40 | int pl;
41 | for (int i = 0; i < 2; ++i) {
42 | pl = (x+i)%2;
43 | System.out.println("Waiting for player " + (pl + 1));
44 | Socket s1 = ss1.accept();
45 | out[pl] = new BufferedWriter(new OutputStreamWriter(s1.getOutputStream()));
46 | in[pl] = new BufferedReader(new InputStreamReader(s1.getInputStream()));
47 |
48 | System.out.println("Connection to " + (pl+1) + " player established");
49 | out[pl].write("Hello, you are player " + (pl+1) +"\n");
50 | out[pl].flush();
51 | }
52 | } catch (IOException e) {System.out.println("error");};
53 | System.out.println("Server running");
54 | }
55 |
56 | void printBoard() {
57 | System.out.println("Current Board:");
58 | for (int y = 0; y < size; ++y) {
59 | for (int x = 0; x < size; ++x)
60 | System.out.print(board[x][y]);
61 | System.out.println("");
62 | }
63 | System.out.println("Next turn: player " + turn);
64 | System.out.println("Moves played: " + noMoves);
65 | }
66 | void sendBoard() {
67 | try {
68 | out[turn].write("Current Board:\n ");
69 | out[turn].flush();
70 | for (int x = 0; x < size; ++x) {
71 | out[turn].write(x + " ");
72 | }
73 | out[turn].write("\n");
74 | out[turn].flush();
75 | for (int y = 0; y < size; ++y) {
76 | out[turn].write(y + " ");
77 | for (int x = 0; x < size; ++x) {
78 | out[turn].write(board[x][y] + " ");
79 | out[turn].flush();
80 | }
81 | out[turn].write("\n");
82 | out[turn].flush();
83 | }
84 | out[turn].write("Next turn: player " + turn + "\n");
85 | out[turn].flush();
86 | out[turn].write("Moves played: " + noMoves + "\n");
87 | out[turn].flush();
88 | } catch(IOException e) {System.out.println("sendBoard() : error");};
89 | }
90 |
91 | void sendInitialData() {
92 | System.out.println("Sending initial data");
93 | try {
94 | out[0].write("" + size + "\n");
95 | out[0].flush();
96 | out[1].write("" + size + "\n");
97 | out[1].flush();
98 | out[1].write("black\n");
99 | out[0].write("white\n");
100 | out[0].flush();
101 | out[1].flush();
102 | turn = 0;
103 | } catch(IOException e) {System.out.println("sendInitialData() : error");};
104 | }
105 |
106 | boolean validMove(String move) {
107 | try {
108 | System.out.println("Received string from Player: " + move);
109 | int x = Integer.parseInt(move.substring(0, move.indexOf("/")));
110 | int y = Integer.parseInt(move.substring(move.indexOf("/")+1));
111 | System.out.println("Got move: " + x + "/" + y);
112 | if (x > size || y > size || x < 0 || y < 0) {
113 | System.out.println("Move out of the board.");
114 | return false;
115 | }
116 | if (board[x][y] != ' ') {
117 | System.out.println("Field not empty");
118 | return false;
119 | }
120 | return true;
121 | } catch(Exception e) {
122 | System.out.println("Invalid move (Syntax)");
123 | return false;
124 | }
125 |
126 | }
127 |
128 | void makeMove(String move) {
129 | int x = Integer.parseInt(move.substring(0, move.indexOf("/")));
130 | int y = Integer.parseInt(move.substring(move.indexOf("/")+1));
131 |
132 | if (turn == 0) board[x][y] = 'X'; else board[x][y] = 'O';
133 | noMoves++;
134 | turn = 1 - turn;
135 |
136 |
137 | }
138 |
139 | String readMove() {
140 | String move = "";
141 | try {
142 | move = in[turn].readLine();
143 | } catch(IOException e) {};
144 | return move;
145 | }
146 |
147 | boolean existMove() {
148 | return (noMoves < size * size);
149 | }
150 |
151 | int winner() {
152 | for (int x = 0; x < size; ++x) {
153 | for (int y = 0; y < size; ++y) {
154 | try {
155 | if (board[x][y] == 'X' &&
156 | board[x+1][y+1] == 'X' &&
157 | board[x+2][y+2] == 'X' &&
158 | board[x+3][y+3] == 'X' &&
159 | board[x+4][y+4] == 'X')
160 | return 0;
161 | } catch (Exception e) {};
162 | try {
163 | if (board[x][y] == 'X' &&
164 | board[x+1][y] == 'X' &&
165 | board[x+2][y] == 'X' &&
166 | board[x+3][y] == 'X' &&
167 | board[x+4][y] == 'X')
168 | return 0;
169 | } catch (Exception e) {};
170 | try {
171 | if (board[x][y] == 'X' &&
172 | board[x][y+1] == 'X' &&
173 | board[x][y+2] == 'X' &&
174 | board[x][y+3] == 'X' &&
175 | board[x][y+4] == 'X')
176 | return 0;
177 | } catch (Exception e) {};
178 | try {
179 | if (board[x+4][y] == 'X' &&
180 | board[x+3][y+1] == 'X' &&
181 | board[x+2][y+2] == 'X' &&
182 | board[x+1][y+3] == 'X' &&
183 | board[x][y+4] == 'X')
184 | return 0;
185 | } catch (Exception e) {};
186 | try {
187 | if (board[x][y] == 'O' &&
188 | board[x+1][y+1] == 'O' &&
189 | board[x+2][y+2] == 'O' &&
190 | board[x+3][y+3] == 'O' &&
191 | board[x+4][y+4] == 'O')
192 | return 1;
193 | } catch (Exception e) {};
194 | try {
195 | if (board[x][y] == 'O' &&
196 | board[x+1][y] == 'O' &&
197 | board[x+2][y] == 'O' &&
198 | board[x+3][y] == 'O' &&
199 | board[x+4][y] == 'O')
200 | return 1;
201 | } catch (Exception e) {};
202 | try {
203 | if (board[x][y] == 'O' &&
204 | board[x][y+1] == 'O' &&
205 | board[x][y+2] == 'O' &&
206 | board[x][y+3] == 'O' &&
207 | board[x][y+4] == 'O')
208 | return 1;
209 | } catch (Exception e) {};
210 | try {
211 | if (board[x+4][y] == 'O' &&
212 | board[x+3][y+1] == 'O' &&
213 | board[x+2][y+2] == 'O' &&
214 | board[x+1][y+3] == 'O' &&
215 | board[x][y+4] == 'O')
216 | return 1;
217 | } catch (Exception e) {};
218 | }
219 | }
220 | return -1;
221 | }
222 |
223 | public void run() {
224 | sendInitialData();
225 | String move = "";
226 | int winner = -1;
227 | while (existMove()) {
228 | move = readMove();
229 | if (validMove(move) == false) {
230 | if (move.equals("board")) {
231 | sendBoard();
232 | } else {
233 | System.out.println("Illegal Move/Statement");
234 | try {
235 | out[1-turn].write("Illegal Move/Statement");
236 | out[1-turn].flush();
237 | } catch(IOException e) {System.out.println("run() : error");};
238 | System.exit(1);
239 | }
240 | } else {
241 | makeMove(move);
242 | if ((winner = winner()) != -1) {
243 | printBoard();
244 | System.out.println("Player " + (2-winner) + " has won!");
245 | try {
246 | out[turn].write("You lost!");
247 | out[turn].flush();
248 | out[1-turn].write("You win!");
249 | out[1-turn].flush();
250 | } catch (IOException e) {System.out.println("run() : error");}
251 | System.exit(1);
252 | }
253 | printBoard();
254 | try {
255 | out[turn].write(move + "\n");
256 | out[turn].flush();
257 | } catch (IOException e) {System.out.println("makemove() : error");}
258 | move = "";
259 | }
260 | }
261 |
262 | if (move != "") System.out.println("No valid move: " + move);
263 | }
264 |
265 | public static void main (String[] args) {
266 | Server server = new Server(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
267 | server.run();
268 | }
269 | }
270 |
271 |
272 |
273 |
274 |
--------------------------------------------------------------------------------
/src/dbsearch/GBBoard.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Text;
4 | using System.Collections;
5 |
6 |
7 | public class
8 | GoBangBoard
9 | {
10 | public static int boardDim = 15;
11 |
12 | public object Clone ()
13 | {
14 | GoBangBoard gbNew = new GoBangBoard ((int[,]) board.Clone ());
15 |
16 | return (gbNew);
17 | }
18 |
19 | /** Flip all stones in color so the player situations are reversed.
20 | */
21 | public void Flip ()
22 | {
23 | for (int y = 0 ; y < boardDim ; ++y)
24 | for (int x = 0 ; x < boardDim ; ++x)
25 | board[y,x] = -board[y,x];
26 | }
27 |
28 | // [y, x]
29 | internal int[,] board;
30 |
31 | public int CompareTo (object o2)
32 | {
33 | GoBangBoard gb2 = (GoBangBoard) o2;
34 |
35 | for (int y = 0 ; y < boardDim ; ++y) {
36 | for (int x = 0 ; x < boardDim ; ++x) {
37 | if (board[y,x] < gb2.board[y,x])
38 | return (-1);
39 | else if (board[y,x] > gb2.board[y,x])
40 | return (1);
41 | }
42 | }
43 |
44 | return (0);
45 | }
46 |
47 | public void AddExtraStones (GoBangBoard b2)
48 | {
49 | for (int y = 0 ; y < boardDim ; ++y) {
50 | for (int x = 0 ; x < boardDim ; ++x) {
51 | if (board[y,x] == 0 && b2.board[y,x] != 0)
52 | board[y,x] = b2.board[y,x];
53 | }
54 | }
55 | }
56 |
57 | // Check if there are no conflicting stones.
58 | public bool CompatibleWith (GoBangBoard b2)
59 | {
60 | for (int y = 0 ; y < boardDim ; ++y) {
61 | for (int x = 0 ; x < boardDim ; ++x) {
62 | if (board[y,x] == 0 || b2.board[y,x] == 0 ||
63 | board[y,x] == b2.board[y,x])
64 | continue;
65 |
66 | return (false);
67 | }
68 | }
69 |
70 | return (true);
71 | }
72 |
73 | public static void Main (string[] args)
74 | {
75 | GoBangBoard gb = new GoBangBoard ();
76 | Random rnd = new Random ();
77 |
78 | // Initialize board randomly
79 | for (int n = 0 ; n < 130 ; ++n)
80 | gb.board[rnd.Next (0, boardDim), rnd.Next (0, boardDim)] =
81 | rnd.Next (0, 3) - 1;
82 |
83 | int count = 0;
84 | foreach (StoneSet ss in gb.G5) {
85 | Console.Write ("ss at ({0},{1}) to ({2},{3}), len {4}: ",
86 | ss.x, ss.y, ss.ax, ss.ay, ss.stones.Length);
87 | foreach (int stone in ss.stones) {
88 | Console.Write ("{0}", (stone == 0) ? "." :
89 | ((stone == 1) ? "O" : "X"));
90 | }
91 | Console.WriteLine ();
92 |
93 | count += 1;
94 | }
95 | Console.WriteLine ("|G5| = {0}", count);
96 |
97 | count = 0;
98 | foreach (StoneSet ss in gb.G6)
99 | count += 1;
100 | Console.WriteLine ("|G6| = {0}", count);
101 |
102 | count = 0;
103 | foreach (StoneSet ss in gb.G7)
104 | count += 1;
105 | Console.WriteLine ("|G7| = {0}", count);
106 |
107 | // Test operators a little
108 | gb.DumpBoard ();
109 |
110 | GBSpaceState state = new GBSpaceState (gb);
111 | GBOperator[] legalOpers = GBOperator.LegalOperators (state, 2);
112 | foreach (GBOperator gop in legalOpers)
113 | Console.WriteLine ("oper: {0}", gop);
114 | }
115 |
116 | public GoBangBoard ()
117 | : this (new int[boardDim, boardDim])
118 | {
119 | }
120 |
121 | public GoBangBoard (int[,] board)
122 | {
123 | this.board = board;
124 |
125 | g5 = new GCl (this, 5);
126 | g6 = new GCl (this, 6);
127 | g7 = new GCl (this, 7);
128 | }
129 |
130 | public void DumpBoard ()
131 | {
132 | Console.WriteLine (ToString ());
133 | }
134 |
135 | public override string ToString ()
136 | {
137 | StringBuilder sb = new StringBuilder ();
138 |
139 | sb.Append ("\n");
140 | sb.Append (" a b c d e f g h i j k l m n o\n");
141 |
142 | for (int y = 0 ; y < boardDim ; ++y) {
143 | sb.AppendFormat ("{0:D2} ", y);
144 |
145 | for (int x = 0 ; x < boardDim ; ++x) {
146 | sb.AppendFormat ("{0} ", (board[y,x] == 0 ? "." :
147 | (board[y,x] == 1 ? "O" : "X")));
148 | }
149 | sb.Append ("\n");
150 | }
151 | sb.Append ("\n");
152 |
153 | return (sb.ToString ());
154 | }
155 |
156 | private GCl g5;
157 | public GCl G5 {
158 | get {
159 | return (g5);
160 | }
161 | }
162 |
163 | private GCl g6;
164 | public GCl G6 {
165 | get {
166 | return (g6);
167 | }
168 | }
169 |
170 | private GCl g7;
171 | public GCl G7 {
172 | get {
173 | return (g7);
174 | }
175 | }
176 |
177 | public struct StoneSet {
178 | internal int[] stones;
179 |
180 | // The x/y position on the board where the stone has been found
181 | // This is x_0, y_0
182 | internal int x;
183 | internal int y;
184 |
185 | // The x/y addition to deduce the position of the n'th stone:
186 | // x_n = x_0 + n*ax
187 | // y_n = y_0 + n*ay
188 | internal int ax;
189 | internal int ay;
190 |
191 | internal StoneSet (int x, int y, int ax, int ay, int size)
192 | {
193 | stones = new int[size];
194 | this.x = x;
195 | this.y = y;
196 | this.ax = ax;
197 | this.ay = ay;
198 | }
199 | }
200 |
201 | public class GCl : IEnumerable
202 | {
203 | private GCl () {
204 | }
205 |
206 | public GCl (GoBangBoard gb, int gSize)
207 | {
208 | this.gb = gb;
209 | this.gSize = gSize;
210 | }
211 |
212 | private GoBangBoard gb;
213 | private int gSize;
214 |
215 | // Make it foreach'able
216 | public IEnumerator GetEnumerator () {
217 | return (new GClEnumerator (gb, gSize));
218 | }
219 |
220 | public class GClEnumerator : IEnumerator
221 | {
222 | private GClEnumerator ()
223 | {
224 | }
225 |
226 | GoBangBoard gb;
227 | int dir = 0;
228 | int y = 0;
229 | int x = -1;
230 | int didx = 0;
231 | int ax = 1, ay = 0;
232 |
233 | int gSize;
234 |
235 | public GClEnumerator (GoBangBoard gb, int gSize)
236 | {
237 | this.gb = gb;
238 | this.gSize = gSize;
239 | }
240 |
241 | public object Current {
242 | get {
243 | StoneSet ss = new StoneSet (x, y, ax, ay, gSize);
244 |
245 | //Console.WriteLine ("dir {0} from ({1},{2})", dir, x, y);
246 | int px = x, py = y;
247 | for (int n = 0 ; n < gSize ; ++n) {
248 | ss.stones[n] = gb.board[py, px];
249 | py += ay;
250 | px += ax;
251 | }
252 |
253 | return (ss);
254 | }
255 | }
256 |
257 | public void Reset ()
258 | {
259 | x = -1;
260 | y = 0;
261 | dir = 0;
262 | ax = 1;
263 | ay = 0;
264 | didx = 0;
265 | }
266 |
267 | public bool MoveNext ()
268 | {
269 | //x += ax;
270 | //y += ay;
271 |
272 | if (dir == 0) {
273 | x += 1;
274 | if (x <= (GoBangBoard.boardDim - gSize))
275 | return (true);
276 |
277 | y += 1;
278 | x = 0;
279 | if (y < GoBangBoard.boardDim)
280 | return (true);
281 |
282 | dir = 1;
283 | x = y = 0;
284 |
285 | ax = 0;
286 | ay = 1;
287 |
288 | return (true);
289 | } else if (dir == 1) {
290 | y += 1;
291 | if (y <= (GoBangBoard.boardDim - gSize))
292 | return (true);
293 |
294 | x += 1;
295 | y = 0;
296 | if (x < GoBangBoard.boardDim)
297 | return (true);
298 |
299 | dir = 2;
300 | x = 0;
301 | y = GoBangBoard.boardDim - gSize;
302 | ax = ay = 1;
303 | didx = 0;
304 |
305 | return (true);
306 | } else if (dir == 2) {
307 | x += 1;
308 | y += 1;
309 | if (x <= (GoBangBoard.boardDim - gSize) &&
310 | y <= (GoBangBoard.boardDim - gSize))
311 | {
312 | return (true);
313 | }
314 |
315 | // Increase diagonal index and get new start coordinates
316 | didx += 1;
317 | if (didx <= ((GoBangBoard.boardDim - gSize) << 1)) {
318 | x = y = 0;
319 | if (didx < (GoBangBoard.boardDim - gSize)) {
320 | y = (GoBangBoard.boardDim - gSize) - didx;
321 | }
322 | if (didx > (GoBangBoard.boardDim - gSize)) {
323 | x = didx - (GoBangBoard.boardDim - gSize);
324 | }
325 |
326 | return (true);
327 | }
328 |
329 | // All diagonals done.
330 | dir = 3;
331 | didx = 0;
332 | x = 0;
333 | y = gSize - 1;
334 |
335 | ax = 1;
336 | ay = -1;
337 |
338 | return (true);
339 | } else if (dir == 3) {
340 | x += 1;
341 | y -= 1;
342 | if (x <= (GoBangBoard.boardDim - gSize) && y >= (gSize - 1))
343 | return (true);
344 |
345 | didx += 1;
346 | if (didx <= ((GoBangBoard.boardDim - gSize) << 1)) {
347 | if (didx <= (GoBangBoard.boardDim - gSize)) {
348 | x = 0;
349 | y = didx + (gSize - 1);
350 | } else if (didx > (GoBangBoard.boardDim - gSize)) {
351 | x = didx - (GoBangBoard.boardDim - gSize);
352 | y = GoBangBoard.boardDim - 1;
353 | }
354 | return (true);
355 | }
356 |
357 | return (false);
358 | }
359 |
360 | return (false);
361 | }
362 | }
363 | }
364 | }
365 |
366 |
--------------------------------------------------------------------------------
/src/gtksharp-gui/smiley100.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
170 |
--------------------------------------------------------------------------------
/src/gtksharp-gui/smiley111.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
172 |
--------------------------------------------------------------------------------
/src/newaiplayer/StatValEvaluator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | public class StatValEvaluator
4 | {
5 | public const int WIN = 1000000;
6 | public const int WINBORDER = 900000;
7 | public const int UNDEF = -10000000;
8 | public const int OWNTHREATBONUS = 256;
9 | public const int OPPTHREATBONUS = 256;
10 |
11 | public int[] statValues;
12 | InterestingFieldAgent fieldagent;
13 |
14 | public StatValEvaluator(InterestingFieldAgent ifa)
15 | {
16 | statValues = new int[262144];
17 | generateStatValues();
18 | fieldagent = ifa;
19 |
20 | }
21 |
22 | private void generateStatValues()
23 | {
24 | Console.WriteLine("Generating static values: start");
25 |
26 | for (int i = 0; i < 262144; ++i) {
27 |
28 | int digit = i;
29 |
30 | int left = 0;
31 | int right = 0;
32 | int ownposs = 0;
33 | int ownrow = 0;
34 | int opp_cutoff = 0;
35 | int win = 0;
36 | int oppl = 0;
37 | int oppr = 0;
38 | int ownlpos = 4;
39 | int ownrpos = 4;
40 | int opplstop = 0;
41 | int opprstop = 0;
42 | int ownbolpos = -1;
43 | int ownborpos = 9;
44 | int leftpos = 4;
45 | int rightpos = 4;
46 | int opplpos = 4;
47 | int opprpos = 4;
48 |
49 | bool leftinclown = false;
50 | bool rightinclown = false;
51 |
52 | // free Field = 1
53 | // our Field = 2
54 | // opp Field = 0
55 | // nonexist Field = 3
56 | int[] line = new int[9];
57 | for (int y = 0; y < 9; ++y) {
58 | line[y] = digit % 4;
59 | digit >>= 2;
60 | }
61 | for (int ii = 4; ii >= 0; --ii) {
62 | if (left == 0 && oppl == 0 && line[ii] == 2) {
63 | ++ownrow;
64 | ownlpos = ii;
65 | }
66 | if (oppl == 0 && line[ii] == 1) {
67 | ++left;
68 | leftpos = ii;
69 | }
70 | if (left != 0 && oppl == 0 && line[ii] == 2) {
71 | leftinclown = true;
72 | ++left;
73 | leftpos = ii;
74 | }
75 | if (line[ii] == 0 && opplstop == 0)
76 | ++oppl;
77 | if ((line[ii] == 1 || line[ii] == 2) && oppl != 0)
78 | opplstop = 1;
79 | if (line[ii] == 3)
80 | break;
81 | }
82 | for (int ii = 4; ii < 9; ++ii) {
83 | if (right == 0 && oppr == 0 && line[ii] == 2) {
84 | ++ownrow;
85 | ownrpos = ii;
86 | }
87 | if (oppr == 0 && line[ii] == 1) {
88 | ++right;
89 | rightpos = ii;
90 | }
91 | if (right != 0 && oppr == 0 && line[ii] == 2) {
92 | rightinclown = true;
93 | ++right;
94 | rightpos = ii;
95 | }
96 | if (line[ii] == 0 && opprstop == 0) ++oppr;
97 | if ((line[ii] == 1 || line[ii] == 2) && oppr != 0) opprstop = 1;
98 | if (line[ii] == 3) break;
99 | }
100 |
101 | for (int ii = 3; ii >= 0; --ii)
102 | {
103 | if (line[ii] == 1 || line[ii] == 0)
104 | continue;
105 | if (line[ii] == 2 || line[ii] == 3)
106 | {
107 | ownbolpos = ii;
108 | break;
109 | }
110 | }
111 | for (int ii = 5; ii < 9; ++ii)
112 | {
113 | if (line[ii] == 1 || line[ii] == 0)
114 | continue;
115 | if (line[ii] == 2 || line[ii] == 3)
116 | {
117 | ownborpos = ii;
118 | break;
119 | }
120 | }
121 |
122 | if (oppl != 0) opplpos = leftpos - 1;
123 | if (oppr != 0) opprpos = rightpos + 1;
124 | --ownrow;
125 |
126 | //numbers are initialized to 0
127 | //oppl now contains number of opponent stones in a row left of the middle.
128 | //left contains the number of free or our fields left of the ownrow.
129 | //ownrow now contains the length of our row of stones over the middle
130 | //right contains the number of free or our fields right of the ownrow.
131 | //oppr now contains number of opponent stones in a row right of the middle.
132 | //
133 | //positions are initialized to 4
134 | //ownlpos contains the index of our leftmost piece in the ownrow.
135 | //ownrpos contains the index of our rightmost piece in the ownrow.
136 | //ownbolpos contains the index of our[or nonexistent] rightmost piece left of the opponent row.
137 | //ownborpos contains the index of our[or nonexistent leftmost piece right of the opponent row.
138 | //leftpos contains the index of the leftmost piece in left.
139 | //rightpos contains the index of the rightmost piece in right.
140 | //opplpos contains the first opponent piece on the left side.
141 | //opprpos contains the first opponent piece on the right side.
142 | //
143 | //leftinclown is true if left includes an own piece.
144 | //rightinclown is true if right includes an own piece.
145 | if (left + ownrow + right >= 5) ownposs = (left + ownrow + right) - 4;
146 |
147 | int ownrowbonus = 0;
148 | if (ownposs > 0) ownrowbonus = 8 * ownrow;
149 |
150 | if (ownborpos - ownbolpos > 5 &&
151 | (opplpos != 4 || opprpos != 4) &&
152 | (opplpos > ownbolpos || opprpos < ownborpos))
153 | opp_cutoff = 5 - (4 - opplpos) + 5 - (opprpos - 4);
154 | if (ownrow >= 5) win = WIN;
155 |
156 |
157 | statValues[i] = 2 * opp_cutoff + ownrowbonus + win;
158 |
159 | // statValues[i] = /*2 * ownposs + */4 * opp_cutoff + ownrowbonus + win;
160 | /*
161 | if (line[4] == 2) {
162 | Console.Write("Generating static values: key: {0}, string: ", i);
163 | for (int y = 0; y < 9; ++y) {
164 | Console.Write(line[y] == 1?".":line[y]==2?"X":line[y]==3?"#":line[y]==0?"O":"");
165 | }
166 | Console.WriteLine(" , val: {0}, ownposs: {1}, opp_cutoff: {2}", statValues[i], ownposs, opp_cutoff);
167 | }
168 | */
169 |
170 | }
171 | Console.WriteLine("Generating static values: finished");
172 | }
173 |
174 | /** Computes the static value of a MoveNode.
175 | *
176 | * @param board The actual board.
177 | * @param node The node to be rated
178 | * @returns the static value of the Node.
179 | */
180 | public int statVal(int[,] board, Coordinate node, int attacker)
181 | {
182 | //Console.WriteLine("Calculating statVal");
183 | int turn = attacker;
184 | if (turn != board[node.X, node.Y]) throw new Exception();
185 |
186 | // free Field = 1
187 | // our Field = 2
188 | // opp Field = 0
189 | // nonexist Field = 3
190 | int tmpval = 0;
191 | int digit1 = 0;
192 | int digit2 = 0;
193 | int digit3 = 0;
194 | int digit4 = 0;
195 | for (int j = 0; j < 9; ++j)
196 | {
197 | if (j + node.X - 4 >= 0 && j + node.X - 4 < NewAiPlayer.BOARDSIZE)
198 | {
199 | digit1 += board[node.X + j - 4, node.Y] * turn + 1;
200 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x + j - 4, node.move.y);
201 | }
202 | else digit1 += 3;
203 | if (j + node.Y - 4 >= 0 && j + node.Y - 4 < NewAiPlayer.BOARDSIZE)
204 | {
205 | digit2 += board[node.X, node.Y + j - 4] * turn + 1;
206 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x , node.move.y + j -4);
207 | }
208 | else digit2 += 3;
209 | if (node.X + j - 4 >= 0 && node.X + j - 4 < NewAiPlayer.BOARDSIZE &&
210 | node.Y + j - 4 >= 0 && node.Y + j - 4 < NewAiPlayer.BOARDSIZE )
211 | {
212 | digit3 += board[node.X + j - 4, node.Y + j - 4] * turn + 1;
213 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x + j - 4, node.move.y + j - 4);
214 | }
215 | else digit3 += 3;
216 | if (node.X - j + 4 >= 0 && node.X - j + 4 < NewAiPlayer.BOARDSIZE &&
217 | node.Y + j - 4 >= 0 && node.Y + j - 4 < NewAiPlayer.BOARDSIZE )
218 | {
219 | digit4 += board[node.X - j + 4, node.Y + j - 4] * turn + 1;
220 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x - j + 4, node.move.y + j - 4);
221 | }
222 | else digit4 += 3;
223 | if (j != 8)
224 | {
225 | digit1 <<= 2;
226 | digit2 <<= 2;
227 | digit3 <<= 2;
228 | digit4 <<= 2;
229 | }
230 | }
231 |
232 | /*
233 | Console.WriteLine("Value for horizontal row: {0}", statValues[digit1]);
234 | Console.WriteLine("Value for vertical row: {0}", statValues[digit2]);
235 | Console.WriteLine("Value for diag1 row: {0}", statValues[digit3]);
236 | Console.WriteLine("Value for diag2 row: {0}", statValues[digit4]);
237 | */
238 | tmpval += statValues[digit1];
239 | tmpval += statValues[digit2];
240 | tmpval += statValues[digit3];
241 | tmpval += statValues[digit4];
242 |
243 | // look up the threat-situation
244 | int ownthreatbonus = 0;
245 | int oppthreatbonus = 0;
246 |
247 | foreach (Threat t in fieldagent.ownaddedthreatlist)
248 | {
249 | if (t.create == false) ownthreatbonus += 3 - t.category;
250 | }
251 | if (attacker == 1)
252 | {
253 | foreach (Threat t in fieldagent.oppremovedthreatlist)
254 | {
255 | if (t.create == false) ownthreatbonus += 3 - t.category;
256 | }
257 | }
258 |
259 | foreach (Threat t in fieldagent.oppaddedthreatlist)
260 | {
261 | if (t.create == false) oppthreatbonus += 3 - t.category;
262 | }
263 | if (attacker == -1)
264 | {
265 | foreach (Threat t in fieldagent.ownremovedthreatlist)
266 | {
267 | if (t.create == false) oppthreatbonus += 3 - t.category;
268 | }
269 | }
270 |
271 | int bonus = OWNTHREATBONUS * ownthreatbonus - OPPTHREATBONUS * oppthreatbonus;
272 |
273 | return bonus + (tmpval * turn);
274 |
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/src/Gomocup/PisqPipe.cs:
--------------------------------------------------------------------------------
1 | /** functions that communicate with Piskvork manager through pipes */
2 | /** don't modify this file */
3 | using System;
4 | using System.Threading;
5 |
6 | abstract class GomocupInterface
7 | {
8 | /* information about a game - you should use these variables */
9 | public int width, height; /* the board size */
10 | public int info_timeout_turn = 30000; /* time for one turn in milliseconds */
11 | public int info_timeout_match = 1000000000; /* total time for a game */
12 | public int info_time_left = 1000000000; /* left time for a game */
13 | public int info_max_memory = 0; /* maximum memory in bytes, zero if unlimited */
14 | public int info_game_type = 1; /* 0:human opponent, 1:AI opponent, 2:tournament, 3:network tournament */
15 | public bool info_exact5 = false; /* false:five or more stones win, true:exactly five stones win */
16 | public bool info_continuous = false; /* false:single game, true:continuous */
17 | public int terminate; /* return from brain_turn when terminate>0 */
18 | public int start_time; /* tick count at the beginning of turn */
19 | public string dataFolder; /* folder for persistent files, can be null */
20 |
21 | /* you have to implement these functions */
22 | abstract public string brain_about { get; } /* copyright, version, homepage */
23 | abstract public void brain_init(); /* create the board and call Console.WriteLine("OK"); */
24 | abstract public void brain_restart(); /* delete old board, create new board, call Console.WriteLine("OK"); */
25 | abstract public void brain_turn(); /* choose your move and call do_mymove(x,y); 0<=x= 0 && y >= 0)
50 | return true;
51 | x = y = 0;
52 | return false;
53 | }
54 |
55 | /** parse coordinates x,y */
56 | private bool parse_coord(string param, out int x, out int y)
57 | {
58 | return parse_coord2(param, out x, out y) && x < width && y < height;
59 | }
60 |
61 | /** parse coordinates x,y and player number z */
62 | private void parse_3int_chk(string param, out int x, out int y, out int z)
63 | {
64 | string[] p = param.Split(',');
65 | if (!(p.Length == 3 && int.TryParse(p[0], out x) && int.TryParse(p[1], out y) && int.TryParse(p[2], out z)
66 | && x >= 0 && y >= 0 && x < width && y < height))
67 | x = y = z = 0;
68 | }
69 |
70 | /** return pointer to word after command if input starts with command, otherwise return NULL */
71 | private static string get_cmd_param(string command, out string param)
72 | {
73 | param = "";
74 | int pos = command.IndexOf(' ');
75 | if (pos >= 0)
76 | {
77 | param = command.Substring(pos + 1).TrimStart(' ');
78 | command = command.Substring(0, pos);
79 | }
80 | return command.ToLower();
81 | }
82 |
83 | /** send suggest */
84 | protected void suggest(int x, int y)
85 | {
86 | Console.WriteLine("SUGGEST {0},{1}", x, y);
87 | }
88 |
89 | /** write move to the pipe and update internal data structures */
90 | protected void do_mymove(int x, int y)
91 | {
92 | brain_my(x, y);
93 | Console.WriteLine("{0},{1}", x, y);
94 | }
95 |
96 | /** main function for the working thread */
97 | private void threadLoop()
98 | {
99 | for (; ; )
100 | {
101 | event1.WaitOne();
102 | brain_turn();
103 | event2.Set();
104 | }
105 | }
106 |
107 | /** start thinking */
108 | private void turn()
109 | {
110 | terminate = 0;
111 | event2.Reset();
112 | event1.Set();
113 | }
114 |
115 | /** stop thinking */
116 | private void stop()
117 | {
118 | terminate = 1;
119 | event2.WaitOne();
120 | }
121 |
122 | private void start()
123 | {
124 | start_time = Environment.TickCount;
125 | stop();
126 | if (width == 0)
127 | {
128 | width = height = 20;
129 | brain_init();
130 | }
131 | }
132 |
133 | /** do command cmd */
134 | private void do_command()
135 | {
136 | string param, info;
137 | int x, y, who, e;
138 |
139 | switch (get_cmd_param(cmd, out param))
140 | {
141 | case "info":
142 | switch (get_cmd_param(param, out info))
143 | {
144 | case "max_memory":
145 | int.TryParse(info, out info_max_memory); break;
146 | case "timeout_match":
147 | int.TryParse(info, out info_timeout_match); break;
148 | case "timeout_turn":
149 | int.TryParse(info, out info_timeout_turn); break;
150 | case "time_left":
151 | int.TryParse(info, out info_time_left); break;
152 | case "game_type":
153 | int.TryParse(info, out info_game_type); break;
154 | case "rule":
155 | if (int.TryParse(info, out e))
156 | {
157 | info_exact5 = (e & 1) != 0;
158 | info_continuous = (e & 2) != 0;
159 | }
160 | break;
161 | case "folder":
162 | dataFolder = info; break;
163 | case "evaluate":
164 | if (parse_coord(info, out x, out y)) brain_eval(x, y);
165 | break;
166 | /* unknown info is ignored */
167 | }
168 | break;
169 | case "start":
170 | if (!int.TryParse(param, out width) || width < 5)
171 | {
172 | width = 0;
173 | Console.WriteLine("ERROR bad START parameter");
174 | }
175 | else
176 | {
177 | height = width;
178 | start();
179 | brain_init();
180 | }
181 | break;
182 | case "rectstart":
183 | if (!parse_coord2(param, out width, out height) || width < 5 || height < 5)
184 | {
185 | width = height = 0;
186 | Console.WriteLine("ERROR bad RECTSTART parameters");
187 | }
188 | else
189 | {
190 | start();
191 | brain_init();
192 | }
193 | break;
194 | case "restart":
195 | start();
196 | brain_restart();
197 | break;
198 | case "turn":
199 | start();
200 | if (!parse_coord(param, out x, out y))
201 | {
202 | Console.WriteLine("ERROR bad coordinates");
203 | }
204 | else
205 | {
206 | brain_opponents(x, y);
207 | turn();
208 | }
209 | break;
210 | case "play":
211 | start();
212 | if (!parse_coord(param, out x, out y))
213 | {
214 | Console.WriteLine("ERROR bad coordinates");
215 | }
216 | else
217 | {
218 | do_mymove(x, y);
219 | }
220 | break;
221 | case "begin":
222 | start();
223 | turn();
224 | break;
225 | case "about":
226 | Console.WriteLine(brain_about);
227 | break;
228 | case "end":
229 | stop();
230 | brain_end();
231 | Environment.Exit(0);
232 | break;
233 | case "board":
234 | start();
235 | for (; ; ) /* fill the whole board */
236 | {
237 | get_line();
238 | parse_3int_chk(cmd, out x, out y, out who);
239 | if (who == 1) brain_my(x, y);
240 | else if (who == 2) brain_opponents(x, y);
241 | else if (who == 3) brain_block(x, y);
242 | else
243 | {
244 | if (!cmd.Equals("done", StringComparison.InvariantCultureIgnoreCase))
245 | Console.WriteLine("ERROR x,y,who or DONE expected after BOARD");
246 | break;
247 | }
248 | }
249 | turn();
250 | break;
251 | case "takeback":
252 | start();
253 | string t = "ERROR bad coordinates";
254 | if (parse_coord(param, out x, out y))
255 | {
256 | e = brain_takeback(x, y);
257 | if (e == 0) t = "OK";
258 | else if (e == 1) t = "UNKNOWN";
259 | }
260 | Console.WriteLine(t);
261 | break;
262 | default:
263 | Console.WriteLine("UNKNOWN command");
264 | break;
265 | }
266 | }
267 |
268 |
269 | /** main function for AI console application */
270 | public void main()
271 | {
272 | try
273 | {
274 | int dummy = Console.WindowHeight;
275 | //ERROR, process started from the Explorer or command line
276 | Console.WriteLine("MESSAGE Gomoku AI should not be started directly. Please install gomoku manager (http://sourceforge.net/projects/piskvork). Then enter path to this exe file in players settings.");
277 | }
278 | catch (System.IO.IOException)
279 | {
280 | //OK, process started from the Piskvork manager
281 | }
282 |
283 | event1 = new AutoResetEvent(false);
284 | new Thread(threadLoop).Start();
285 | event2 = new ManualResetEvent(true);
286 | for (; ; )
287 | {
288 | get_line();
289 | do_command();
290 | }
291 | }
292 |
293 | static void Main(string[] args)
294 | {
295 | new GomocupEngine().main();
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/src/win-project/win-project.csproj:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
25 |
45 |
65 |
85 |
86 |
87 |
92 |
97 |
102 |
107 |
112 |
113 |
114 |
115 |
116 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
175 |
180 |
185 |
190 |
195 |
200 |
205 |
210 |
215 |
220 |
225 |
230 |
231 |
232 |
233 |
234 |
235 |
--------------------------------------------------------------------------------
/src/gtksharp-gui/FiveGUI.cs:
--------------------------------------------------------------------------------
1 | /* Five-Wins Gtk# GUI
2 | *
3 | * Marco Kunze, Sebastian Nowozin
4 | */
5 |
6 | using System;
7 | using System.IO;
8 | using System.Net;
9 | using System.Net.Sockets;
10 | using GLib;
11 | using Gtk;
12 | using GtkSharp;
13 | using Glade;
14 |
15 | public class FiveGUI
16 | {
17 | // Glade-bound widgets
18 | [Glade.Widget]
19 | Gtk.Window mainWindow;
20 |
21 | [Glade.Widget]
22 | Gtk.Button connectButton;
23 |
24 | [Glade.Widget]
25 | Gtk.Button quitButton;
26 |
27 | [Glade.Widget]
28 | Gtk.Entry connectAddress;
29 |
30 | [Glade.Widget]
31 | Gtk.CheckButton logCheckBox;
32 |
33 | [Glade.Widget]
34 | Gtk.CheckButton clientDBSearchEnabled;
35 |
36 | [Glade.Widget]
37 | Gtk.Entry logFilename;
38 |
39 | // Self defined widgets
40 | [Glade.Widget]
41 | Gtk.Image boardImage; // The board
42 |
43 | [Glade.Widget]
44 | Gtk.Label boardLabel;
45 |
46 | // Global game variables
47 | int xyDim = -1; // The number of rows and columns on the board. Must be symmetric.
48 | int pixStep;
49 | int ownPlayer = -1;
50 | int currentPlayer = -1;
51 | Gdk.Pixbuf pBoard;
52 | Gdk.Pixbuf stone0;
53 | Gdk.Pixbuf stone1;
54 | int[,] board;
55 |
56 | // Network related data
57 | bool connected = false;
58 | System.Net.Sockets.Socket clientSocket;
59 | StreamReader netRead;
60 | StreamWriter netWrite;
61 |
62 | // Initialization code
63 | public static void Main (string[] args)
64 | {
65 | Application.Init ();
66 |
67 | FiveGUI fiveg = new FiveGUI ();
68 | fiveg.Init ();
69 |
70 | fiveg.ownPlayer = 0;
71 | fiveg.currentPlayer = 0;
72 |
73 | Application.Run ();
74 | }
75 |
76 | private void Init ()
77 | {
78 | //Glade.XML gxml = new Glade.XML ("autopanog.glade", "autopanogWin", null);
79 | Glade.XML gxml = new Glade.XML (null, "fivegui.glade", null, null);
80 | gxml.Autoconnect (this);
81 |
82 | InitializeBoard (10);
83 | RedrawBoard (450);
84 | // TODO
85 | }
86 |
87 | // Helper Initialization
88 | public void InitializeBoard (int dim)
89 | {
90 | xyDim = dim;
91 | board = new int[dim, dim];
92 |
93 | for (int y = 0 ; y < dim ; ++y)
94 | for (int x = 0 ; x < dim ; ++x)
95 | board[x, y] = -1;
96 | }
97 |
98 | int boardSizeChanged = 0;
99 | Gdk.Pixbuf blackV, blackH;
100 |
101 | public void RedrawLines ()
102 | {
103 | if (boardSizeChanged > 0) {
104 | blackV = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, 8, 2, boardSizeChanged);
105 | blackH = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, false, 8, boardSizeChanged, 2);
106 | blackV.Fill (0x00000000);
107 | blackH.Fill (0x00000000);
108 | boardSizeChanged = 0;
109 | }
110 |
111 | for (int l = 0 ; l <= xyDim ; ++l) {
112 | blackV.CopyArea (0, 0, blackV.Width, blackV.Height, pBoard, l * pixStep, 0);
113 | blackH.CopyArea (0, 0, blackH.Width, blackH.Height, pBoard, 0, l * pixStep);
114 | }
115 | }
116 |
117 | public void RedrawBoard (int pixelDim)
118 | {
119 | pixStep = pixelDim / xyDim;
120 | int min = xyDim * pixStep + 2;
121 | pBoard = new Gdk.Pixbuf (Gdk.Colorspace.Rgb, true,
122 | 8, min, min);
123 |
124 | // SVG does not work well under windows yet
125 | /*string goodSmiley = "smiley100.svg";
126 | string badSmiley = "smiley111.svg";
127 | stone0 = Rsvg.Pixbuf.FromFileAtSize (ownPlayer == 0 ? goodSmiley : badSmiley,
128 | pixStep, pixStep);
129 | stone1 = Rsvg.Pixbuf.FromFileAtSize (ownPlayer == 1 ? goodSmiley : badSmiley,
130 | pixStep, pixStep);*/
131 | string goodSmiley = "smiley100.png";
132 | string badSmiley = "smiley111.png";
133 |
134 | stone0 = new Gdk.Pixbuf (ownPlayer == 0 ? goodSmiley : badSmiley);
135 | stone0 = stone0.ScaleSimple (pixStep, pixStep, Gdk.InterpType.Bilinear);
136 |
137 | stone1 = new Gdk.Pixbuf (ownPlayer == 1 ? goodSmiley : badSmiley);
138 | stone1 = stone1.ScaleSimple (pixStep, pixStep, Gdk.InterpType.Bilinear);
139 |
140 | // Draw board full with stones (DEBUG)
141 | for (int y = 0 ; y < xyDim ; ++y) {
142 | for (int x = 0 ; x < xyDim ; ++x) {
143 | if (board[x, y] == 0)
144 | DrawStone (0, x, y);
145 | else if (board[x, y] == 1)
146 | DrawStone (1, x, y);
147 | }
148 | }
149 | boardSizeChanged = min;
150 | RedrawLines ();
151 |
152 | boardImage.FromPixbuf = pBoard;
153 | }
154 |
155 | public bool DrawStone (int player, int x, int y)
156 | {
157 | Gdk.Pixbuf stone = null;
158 | if (player == 0) {
159 | stone = stone0;
160 | } else if (player == 1) {
161 | stone = stone1;
162 | }
163 | if (stone == null)
164 | throw (new ArgumentException ("player not 0 or 1"));
165 |
166 | // Check if field is empty
167 | if (board[x, y] != -1)
168 | return (false);
169 | board[x, y] = player;
170 |
171 | stone.CopyArea (0, 0, stone.Width, stone.Height,
172 | pBoard, x * pixStep, y * pixStep);
173 | RedrawLines ();
174 | boardImage.FromPixbuf = pBoard;
175 | return (true);
176 | }
177 |
178 |
179 | // Default events
180 | public void OnFiveGUIDelete (object obj, DeleteEventArgs args)
181 | {
182 | Application.Quit ();
183 |
184 | args.RetVal = true;
185 | }
186 |
187 | // Buttons
188 | public void OnConnectClicked (object obj, EventArgs ev)
189 | {
190 | Console.WriteLine ("connect");
191 | connected = false;
192 |
193 | string[] addrParts = connectAddress.Text.Split (':');
194 | if (addrParts.Length != 2)
195 | throw (new ArgumentException ("invalid address specified"));
196 |
197 | IPAddress ipAddress = Dns.Resolve (addrParts[0]).AddressList[0];
198 | IPEndPoint ipEndpoint = new IPEndPoint (ipAddress, Int32.Parse (addrParts[1]));
199 |
200 | clientSocket = new System.Net.Sockets.Socket (AddressFamily.InterNetwork,
201 | SocketType.Stream, ProtocolType.Tcp);
202 | try {
203 | clientSocket.Connect (ipEndpoint);
204 | } catch (Exception ex) {
205 | Console.WriteLine ("connection failed.");
206 |
207 | return;
208 | }
209 |
210 | if (clientSocket.Connected) {
211 | connected = true;
212 | Console.WriteLine ("connected.");
213 |
214 | NetworkStream ns = new NetworkStream (clientSocket);
215 | netRead = new StreamReader (ns);
216 | netWrite = new StreamWriter (ns);
217 |
218 | BootNetwork ();
219 | SetupGame ();
220 | }
221 | }
222 |
223 | public void SetupGame ()
224 | {
225 | InitializeBoard (xyDim);
226 | boardSizeChanged = 450;
227 | RedrawBoard (boardSizeChanged);
228 | currentPlayer = 0;
229 |
230 | UpdateMoveDisplay ();
231 | GetRemoteMove ();
232 | }
233 |
234 | public bool GetRemoteMove ()
235 | {
236 | if (currentPlayer == ownPlayer)
237 | return (false);
238 |
239 | string netline = ReadNetworkLine ();
240 | string[] win = netline.Split ('\n');
241 | if (String.Compare (win[0], "You win!") == 0) {
242 | MessageDialog md = new MessageDialog (mainWindow,
243 | DialogFlags.DestroyWithParent, MessageType.Info,
244 | ButtonsType.Close,
245 | "You won the game!\n\nCongratulations!");
246 |
247 | md.Run ();
248 | md.Destroy();
249 | Application.Quit ();
250 | return (true);
251 | } else if (String.Compare (win[0], "You lost!") == 0) {
252 | MessageDialog md = new MessageDialog (mainWindow,
253 | DialogFlags.DestroyWithParent, MessageType.Info,
254 | ButtonsType.Close,
255 | "You lost the game...\n\nMaybe you have better luck next time!");
256 |
257 | md.Run ();
258 | md.Destroy();
259 | Application.Quit ();
260 | return (true);
261 | }
262 | string[] pos = netline.Split ('/');
263 | int px, py;
264 | px = Int32.Parse (pos[0]);
265 | py = Int32.Parse (pos[1]);
266 |
267 | DrawStone (currentPlayer, px, py);
268 |
269 | // Test GBThreatSearch
270 | if (clientDBSearchEnabled.Active) {
271 | int[,] boardRev = new int[xyDim, xyDim];
272 | for (int y = 0 ; y < xyDim ; ++y) {
273 | for (int x = 0 ; x < xyDim ; ++x) {
274 | int stoneV = 0;
275 |
276 | if (board[x,y] == currentPlayer) {
277 | stoneV = -1;
278 | } else if (board[x,y] == -1) {
279 | stoneV = 0;
280 | } else
281 | stoneV = 1;
282 | boardRev[y,x] = stoneV;
283 | }
284 | }
285 |
286 | Console.WriteLine ("Searching for board");
287 | Console.WriteLine (new GoBangBoard (boardRev));
288 | GBThreatSearch.FindWinning (boardRev, 5000);
289 | }
290 |
291 | currentPlayer = (currentPlayer == 0) ? 1 : 0;
292 |
293 | return (true);
294 | }
295 |
296 | public void UpdateMoveDisplay ()
297 | {
298 | if (ownPlayer == currentPlayer)
299 | boardLabel.Text = "Board - your move";
300 | else
301 | boardLabel.Text = "Board - opponent's move";
302 |
303 | while (Application.EventsPending ())
304 | Application.RunIteration (false);
305 | }
306 |
307 | public void BootNetwork ()
308 | {
309 | // One line to waste
310 | ReadNetworkLine ();
311 |
312 | // Board dimension
313 | string boardSizeStr = ReadNetworkLine ();
314 | Console.WriteLine ("boardSizeStr: {0}", boardSizeStr);
315 |
316 | // Player color
317 | string playerBootStr = ReadNetworkLine ();
318 | Console.WriteLine ("playerBootStr: {0}", playerBootStr);
319 |
320 | xyDim = Int32.Parse (boardSizeStr);
321 | string[] colorL = playerBootStr.Split ('\n');
322 | if (String.Compare (colorL[0], "white") == 0) {
323 | ownPlayer = 0;
324 | } else if (String.Compare (colorL[0], "black") == 0) {
325 | ownPlayer = 1;
326 | } else {
327 | throw (new ArgumentException ("player color is neither white nor black"));
328 | }
329 | }
330 |
331 | public string ReadNetworkLine ()
332 | {
333 | if (connected == false)
334 | throw (new Exception ("not connected"));
335 |
336 | return (netRead.ReadLine ());
337 | }
338 |
339 | // line already has to be line-terminated
340 | public void WriteNetworkLine (string line)
341 | {
342 | netWrite.Write (line);
343 | netWrite.Flush ();
344 | }
345 |
346 | public void OnQuitClicked (object obj, EventArgs ev)
347 | {
348 | Application.Quit ();
349 | }
350 |
351 | // Board events
352 | public void OnBoardClicked (object obj, ButtonPressEventArgs bev)
353 | {
354 | Gdk.EventButton evb = bev.Event;
355 | Console.WriteLine ("boardclick at: {0}, {1}", evb.X, evb.Y);
356 |
357 | int px = ((int) evb.X) / pixStep;
358 | int py = ((int) evb.Y) / pixStep;
359 | Console.WriteLine (" --> {0}, {1}", px, py);
360 |
361 | /* TODO: uncomment
362 | if (ownPlayer != currentPlayer) {
363 | Console.WriteLine ("move move move, but its not your move...");
364 | return;
365 | }
366 | */
367 |
368 | if (DrawStone (currentPlayer, px, py) == false) {
369 | // Move was not ok
370 | Console.WriteLine (" invalid move");
371 |
372 | return;
373 | }
374 |
375 | // Move was ok
376 | DoPlayerMove (px, py);
377 | WriteNetworkLine (String.Format ("{0}/{1}\n", px, py));
378 |
379 | currentPlayer = (currentPlayer == 0) ? 1 : 0;
380 | UpdateMoveDisplay ();
381 |
382 | GetRemoteMove ();
383 | UpdateMoveDisplay ();
384 | }
385 |
386 | public void DoPlayerMove (int px, int py)
387 | {
388 | // TODO: send to network
389 | }
390 | }
391 |
392 |
--------------------------------------------------------------------------------
/src/newaiplayer/InterestingFieldAgent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 |
4 | public class InterestingFieldAgent : ICloneable
5 | {
6 | bool[,] interestingfields;
7 | int size;
8 |
9 | /** Contains the threats we built.
10 | * ArrayList
11 | */
12 | public ThreatList ownthreatlist;
13 |
14 | /** Contains the threats the opponent built.
15 | * ThreatList
16 | */
17 | public ThreatList oppthreatlist;
18 |
19 | /** Contains the removed threats removed from our list.
20 | * ThreatList
21 | */
22 | public ThreatList ownremovedthreatlist;
23 |
24 | /** Contains the removed threats removed from opponent's list.
25 | * ThreatList
26 | */
27 | public ThreatList oppremovedthreatlist;
28 |
29 | /** Contains the added threats added from our list.
30 | * ThreatList
31 | */
32 | public ThreatList ownaddedthreatlist;
33 |
34 | /** Contains the added threats added from opponent's list..
35 | * ThreatList
36 | */
37 | public ThreatList oppaddedthreatlist;
38 |
39 | ThreatSearcher searcher;
40 |
41 | private InterestingFieldAgent()
42 | {
43 | }
44 |
45 | public InterestingFieldAgent(ThreatSearcher searcher, int size)
46 | {
47 | interestingfields = new bool[size, size];
48 | this.size = size;
49 | ownthreatlist = new ThreatList();
50 | oppthreatlist = new ThreatList();
51 | ownremovedthreatlist = new ThreatList();
52 | oppremovedthreatlist = new ThreatList();
53 | ownaddedthreatlist = new ThreatList();
54 | oppaddedthreatlist = new ThreatList();
55 | this.searcher = searcher;
56 | }
57 |
58 | /** Generates a list of interesting moves from the matrix.
59 | *
60 | * @returns ArrayList
61 | */
62 | public ArrayList InterestingFields()
63 | {
64 | ArrayList res = new ArrayList();
65 |
66 | for (int y = 0; y < interestingfields.GetLength(0); ++y) {
67 | for (int x = 0; x < interestingfields.GetLength(1); ++x) {
68 | if (interestingfields[x, y]) res.Add(new Coordinate(x, y));
69 | }
70 | }
71 | return res;
72 | }
73 |
74 | public void PrintArray()
75 | {
76 | for (int y = 0; y < interestingfields.GetLength(0); ++y) {
77 | for (int x = 0; x < interestingfields.GetLength(1); ++x) {
78 | Console.Write("{0} ", interestingfields[x, y]?"X":".");
79 | }
80 | Console.WriteLine();
81 | }
82 | }
83 |
84 | /** Updates the internal matrix of interestingfields with a new move.
85 | *
86 | * @param board The actual board, needed to make no occupied fields interesting.
87 | */
88 | public void UpdateInterestingFieldArray(int[,] board, Coordinate field)
89 | {
90 | /*
91 | bool[,] opMat = new bool[,]{{true , false, false, false, true , false, false, false, true },
92 | {false, true , false, false, true , false, false, true , false},
93 | {false, false, true , false, true , false, true , false, false},
94 | {false, false, false, true , true , true , false, false, false},
95 | {true , true , true , true , true , true , true , true , true },
96 | {false, false, false, true , true , true , false, false, false},
97 | {false, false, true , false, true , false, true , false, false},
98 | {false, true , false, false, true , false, false, true , false},
99 | {true , false, false, false, true , false, false, false, true }
100 | };
101 | */
102 | bool[,] opMat = new bool[,]{
103 | {true , false, true , false, true },
104 | {false, true , true , true , false},
105 | {true , true , true , true , true },
106 | {false, true , true , true , false},
107 | {true , false, true , false, true }
108 | };
109 | int opMatdelta = opMat.GetLength(0) / 2;
110 |
111 |
112 | int x = field.X;
113 | int y = field.Y;
114 | for (int j = 0; j < opMat.GetLength(0); ++j)
115 | {
116 | for (int k = 0; k < opMat.GetLength(1); ++k)
117 | {
118 | if (x + (j - opMatdelta) < board.GetLength(0) && x + (j - opMatdelta) >= 0 &&
119 | y + (k - opMatdelta) < board.GetLength(0) && y + (k - opMatdelta) >= 0)
120 | {
121 | interestingfields[x + (j - opMatdelta), y +(k - opMatdelta)] =
122 | ((interestingfields[x + (j - opMatdelta), y +(k - opMatdelta)] || opMat[j, k]) &&
123 | board[x + (j - opMatdelta), y +(k - opMatdelta)] == 0);
124 | }
125 | }
126 | }
127 |
128 | }
129 |
130 | /** Add threats from from into into if they are not contained yet.
131 | *
132 | * @returns ArrayList List of the added threats.
133 | */
134 | private ThreatList Merge(ThreatList into, ThreatList from)
135 | {
136 | ThreatList res = new ThreatList ();
137 | foreach (Threat t in from)
138 | {
139 | // check for every t if it is already in into-list
140 | bool alreadyin = false;
141 | foreach (Threat ct in into)
142 | {
143 | alreadyin = true;
144 | if (ct.category == t.category && ct.fields.Count == t.fields.Count)
145 | {
146 | for (int i = 0; i < ct.fields.Count; ++i)
147 | {
148 | alreadyin = alreadyin &&
149 | ( ( ((Coordinate)(ct.fields[i])).X == ((Coordinate)(t.fields[i])).X ) &&
150 | ( ((Coordinate)(ct.fields[i])).Y == ((Coordinate)(t.fields[i])).Y ) );
151 | }
152 | }
153 | else alreadyin = false;
154 | }
155 | if (alreadyin == false) {
156 | into.Add(t);
157 | res.Add(t);
158 | }
159 | }
160 | return res;
161 | }
162 |
163 | public void UpdateThreatLists(int[,] board, Coordinate move, int attacker)
164 | {
165 | // 0. Initialized the removelists
166 | ownremovedthreatlist = new ThreatList();
167 | oppremovedthreatlist = new ThreatList();
168 | ownaddedthreatlist = new ThreatList();
169 | oppaddedthreatlist = new ThreatList();
170 |
171 | // 1. Remove blocked threats from both lists.
172 | ThreatList lookup = new ThreatList();
173 | foreach (Threat t in oppthreatlist) {
174 | foreach (Coordinate c in t.fields) {
175 | if (c.X == move.X && c.Y == move.Y) {
176 | oppremovedthreatlist.Add(t);
177 | lookup.Add(t.cause);
178 | }
179 | }
180 | }
181 | foreach (Threat t in oppremovedthreatlist) oppthreatlist.Remove(t);
182 | foreach (Threat t in ownthreatlist) {
183 | foreach (Coordinate c in t.fields) {
184 | if (c.X == move.X && c.Y == move.Y) {
185 | ownremovedthreatlist.Add(t);
186 | lookup.Add(t.cause);
187 | }
188 | }
189 | }
190 | foreach (Threat t in ownremovedthreatlist) ownthreatlist.Remove(t);
191 |
192 | //Lookup if "causes" still cause threats
193 | foreach (Coordinate c in lookup)
194 | {
195 | if (board[c.X, c.Y] == 1)
196 | {
197 | ThreatList curthreats = searcher.investigate(board, c, 1);
198 |
199 | ownaddedthreatlist = Merge(ownthreatlist, curthreats);
200 |
201 | }
202 | if (board[c.X, c.Y] == -1)
203 | {
204 | ThreatList curthreats = searcher.investigate(board, c, -1);
205 |
206 | oppaddedthreatlist = Merge(oppthreatlist, curthreats);
207 | }
208 | }
209 |
210 | if (attacker == 1)
211 | {
212 | // We are attacking
213 | // 2. Add our threats we build to our list
214 |
215 | ThreatList curthreats = searcher.investigate(board, move, attacker, true);
216 |
217 | Merge(ownaddedthreatlist, Merge(ownthreatlist, curthreats));
218 |
219 | }
220 | else
221 | {
222 | // The opponent is attacking
223 | // 2. Add his threats to his list
224 | ThreatList curthreats = searcher.investigate(board, move, attacker, true);
225 |
226 | Merge(oppaddedthreatlist, Merge(oppthreatlist, curthreats));
227 | }
228 |
229 | }
230 |
231 | /** Generates a list of "really" interesting moves, using the following
232 | * method:
233 | *
234 | * 1. Read all the interestingfields from the array.
235 | * 2. Look if we are forced to move, if yes, add them to "really" interesting
236 | * moves.
237 | * 2a. If we are not forced, add all the interesting fields to the really
238 | * interesting moves.
239 | * 3. If we didn't just add all the moves look if we can force to move, if
240 | * category is less than lowest category in (2), add them to "really"
241 | * interesting moves.
242 | *
243 | */
244 | public ArrayList ReallyInterestingFields(int[,] board, int attacker)
245 | {
246 | int minoppcat = 3;
247 | ArrayList allfields = InterestingFields();
248 | ArrayList fields = new ArrayList();
249 |
250 | // Guaranteed to be set below.
251 | ArrayList opponentlist = null;
252 | ArrayList ownlist = null;
253 |
254 | if (attacker == 1) {
255 | opponentlist = oppthreatlist;
256 | ownlist = ownthreatlist;
257 | }
258 | else
259 | {
260 | opponentlist = ownthreatlist;
261 | ownlist = oppthreatlist;
262 | }
263 | // Step 2
264 | bool fieldadded = false;
265 | foreach(Coordinate field in allfields)
266 | {
267 | fieldadded = false;
268 | foreach (Threat t in opponentlist) {
269 | if (t.create != false)
270 | continue;
271 |
272 | if (t.fields.Contains (field)) {
273 | if (fieldadded == false) {
274 | fields.Add(field);
275 | fieldadded = true;
276 | }
277 |
278 | if (t.category < minoppcat)
279 | minoppcat = t.category;
280 | }
281 | }
282 | }
283 |
284 | // Step 2a
285 | if (fields.Count == 0) {
286 | fields = allfields;
287 | } else {
288 | // Step 3
289 | foreach (Coordinate field in allfields) {
290 | foreach (Threat t in ownlist) {
291 | if (t.category > minoppcat)
292 | continue;
293 |
294 | if (t.fields.Contains (field)) {
295 | fields.Add(field);
296 |
297 | break;
298 | }
299 | }
300 | }
301 | /*
302 | foreach(Coordinate field in allfields)
303 | {
304 | ArrayList forced = searcher.investigate(board, field, attacker, null, false, minoppcat-1);
305 | if (forced != null && forced.Count > 0) {
306 | //Console.WriteLine("We force Enemy: {0}", field);
307 | foreach (Threat t in forced)
308 | {
309 | fields.Add(field);
310 | }
311 | }
312 | }
313 | */
314 | }
315 | return fields;
316 | }
317 |
318 | public object Clone ()
319 | {
320 | InterestingFieldAgent ic = new InterestingFieldAgent ();
321 |
322 | ic.searcher = searcher;
323 | ic.interestingfields = (bool[,]) interestingfields.Clone ();
324 |
325 | ic.ownthreatlist = (ThreatList) ownthreatlist.Clone ();
326 | ic.oppthreatlist = (ThreatList) oppthreatlist.Clone ();
327 |
328 | ic.ownremovedthreatlist = (ThreatList) ownremovedthreatlist.Clone ();
329 | ic.oppremovedthreatlist = (ThreatList) oppremovedthreatlist.Clone ();
330 |
331 | ic.ownaddedthreatlist = (ThreatList) ownaddedthreatlist.Clone ();
332 | ic.oppaddedthreatlist = (ThreatList) oppaddedthreatlist.Clone ();
333 |
334 | return (ic);
335 | }
336 |
337 | public void PrintThreats()
338 | {
339 | Console.WriteLine("Current Threats: ");
340 | Console.WriteLine("Own Threats :");
341 | foreach (Threat t in ownthreatlist) {
342 | Console.WriteLine("Category : {0}\t CreateFlag: {1}", t.category, t.create);
343 | foreach (Coordinate c in t.fields)
344 | {
345 | Console.WriteLine("Coordinate : {0}", c);
346 | }
347 | }
348 | Console.WriteLine("Opp Threats :");
349 | foreach (Threat t in oppthreatlist) {
350 | Console.WriteLine("Category : {0}\t CreateFlag: {1}", t.category, t.create);
351 | foreach (Coordinate c in t.fields)
352 | {
353 | Console.WriteLine("Coordinate : {0}", c);
354 | }
355 | }
356 | }
357 | }
358 |
359 |
360 |
--------------------------------------------------------------------------------
/src/gtksharp-gui/fivegui.glade:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | True
8 | Five-wins human interface
9 | GTK_WINDOW_TOPLEVEL
10 | GTK_WIN_POS_NONE
11 | False
12 | True
13 | False
14 | True
15 | False
16 | False
17 | GDK_WINDOW_TYPE_HINT_NORMAL
18 | GDK_GRAVITY_NORTH_WEST
19 |
20 |
21 |
22 |
23 | True
24 | False
25 | 0
26 |
27 |
28 |
29 | 5
30 | True
31 | 0
32 | 0.5
33 | GTK_SHADOW_NONE
34 |
35 |
36 |
37 | True
38 | 0.5
39 | 0.5
40 | 1
41 | 1
42 | 0
43 | 0
44 | 12
45 | 0
46 |
47 |
48 |
49 | True
50 | False
51 | 0
52 |
53 |
54 |
55 | True
56 | True
57 | True
58 | True
59 | 0
60 | 127.0.0.1:7777
61 | True
62 | *
63 | False
64 |
65 |
66 | 0
67 | True
68 | True
69 |
70 |
71 |
72 |
73 |
74 | 5
75 | True
76 | True
77 | Connect
78 | True
79 | GTK_RELIEF_NORMAL
80 | True
81 |
82 |
83 |
84 | 0
85 | False
86 | False
87 |
88 |
89 |
90 |
91 |
92 | 5
93 | True
94 | True
95 | Quit
96 | True
97 | GTK_RELIEF_NORMAL
98 | True
99 |
100 |
101 |
102 | 0
103 | False
104 | False
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | True
115 | <b>Options</b>
116 | False
117 | True
118 | GTK_JUSTIFY_LEFT
119 | False
120 | False
121 | 0.5
122 | 0.5
123 | 0
124 | 0
125 |
126 |
127 | label_item
128 |
129 |
130 |
131 |
132 | 0
133 | False
134 | True
135 |
136 |
137 |
138 |
139 |
140 | 5
141 | True
142 | 0
143 | 0.5
144 | GTK_SHADOW_NONE
145 |
146 |
147 |
148 | True
149 | 0.5
150 | 0.5
151 | 1
152 | 1
153 | 0
154 | 0
155 | 12
156 | 0
157 |
158 |
159 |
160 | True
161 | False
162 | 0
163 |
164 |
165 |
166 | 5
167 | True
168 | True
169 | Log to/from:
170 | True
171 | GTK_RELIEF_NORMAL
172 | True
173 | False
174 | False
175 | True
176 |
177 |
178 | 0
179 | False
180 | False
181 |
182 |
183 |
184 |
185 |
186 | True
187 | True
188 | True
189 | True
190 | 0
191 |
192 | True
193 | *
194 | False
195 |
196 |
197 | 0
198 | True
199 | True
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | True
210 | <b>Move logging</b>
211 | False
212 | True
213 | GTK_JUSTIFY_LEFT
214 | False
215 | False
216 | 0.5
217 | 0.5
218 | 0
219 | 0
220 |
221 |
222 | label_item
223 |
224 |
225 |
226 |
227 | 0
228 | True
229 | True
230 |
231 |
232 |
233 |
234 |
235 | True
236 | 0
237 | 0.5
238 | GTK_SHADOW_NONE
239 |
240 |
241 |
242 | True
243 | 0.5
244 | 0.5
245 | 1
246 | 1
247 | 0
248 | 0
249 | 12
250 | 0
251 |
252 |
253 |
254 | 5
255 | True
256 | True
257 | GB search on client side (DEBUG)
258 | True
259 | GTK_RELIEF_NORMAL
260 | True
261 | False
262 | False
263 | True
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 | True
272 | <b>GB</b>
273 | False
274 | True
275 | GTK_JUSTIFY_LEFT
276 | False
277 | False
278 | 0.5
279 | 0.5
280 | 5
281 | 5
282 |
283 |
284 | label_item
285 |
286 |
287 |
288 |
289 | 0
290 | False
291 | False
292 |
293 |
294 |
295 |
296 |
297 | 5
298 | True
299 | 0
300 | 0.5
301 | GTK_SHADOW_NONE
302 |
303 |
304 |
305 | True
306 | True
307 | False
308 |
309 |
310 |
311 |
312 | True
313 | 0.5
314 | 0.5
315 | 0
316 | 0
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 | True
326 | <b>Board</b>
327 | False
328 | True
329 | GTK_JUSTIFY_LEFT
330 | False
331 | False
332 | 0.5
333 | 0.5
334 | 0
335 | 0
336 |
337 |
338 | label_item
339 |
340 |
341 |
342 |
343 | 0
344 | True
345 | True
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 |
3 | GNU GENERAL PUBLIC LICENSE
4 | Version 2, June 1991
5 |
6 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.
7 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
8 |
9 | Everyone is permitted to copy and distribute verbatim copies
10 | of this license document, but changing it is not allowed.
11 |
12 | Preamble
13 |
14 | The licenses for most software are designed to take away your
15 | freedom to share and change it. By contrast, the GNU General Public
16 | License is intended to guarantee your freedom to share and change free
17 | software--to make sure the software is free for all its users. This
18 | General Public License applies to most of the Free Software
19 | Foundation's software and to any other program whose authors commit to
20 | using it. (Some other Free Software Foundation software is covered by
21 | the GNU Library General Public License instead.) You can apply it to
22 | your programs, too.
23 |
24 | When we speak of free software, we are referring to freedom, not
25 | price. Our General Public Licenses are designed to make sure that you
26 | have the freedom to distribute copies of free software (and charge for
27 | this service if you wish), that you receive source code or can get it
28 | if you want it, that you can change the software or use pieces of it
29 | in new free programs; and that you know you can do these things.
30 |
31 | To protect your rights, we need to make restrictions that forbid
32 | anyone to deny you these rights or to ask you to surrender the rights.
33 | These restrictions translate to certain responsibilities for you if you
34 | distribute copies of the software, or if you modify it.
35 |
36 | For example, if you distribute copies of such a program, whether
37 | gratis or for a fee, you must give the recipients all the rights that
38 | you have. You must make sure that they, too, receive or can get the
39 | source code. And you must show them these terms so they know their
40 | rights.
41 |
42 | We protect your rights with two steps: (1) copyright the software, and
43 | (2) offer you this license which gives you legal permission to copy,
44 | distribute and/or modify the software.
45 |
46 | Also, for each author's protection and ours, we want to make certain
47 | that everyone understands that there is no warranty for this free
48 | software. If the software is modified by someone else and passed on, we
49 | want its recipients to know that what they have is not the original, so
50 | that any problems introduced by others will not reflect on the original
51 | authors' reputations.
52 |
53 | Finally, any free program is threatened constantly by software
54 | patents. We wish to avoid the danger that redistributors of a free
55 | program will individually obtain patent licenses, in effect making the
56 | program proprietary. To prevent this, we have made it clear that any
57 | patent must be licensed for everyone's free use or not licensed at all.
58 |
59 | The precise terms and conditions for copying, distribution and
60 | modification follow.
61 |
62 | GNU GENERAL PUBLIC LICENSE
63 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
64 |
65 | 0. This License applies to any program or other work which contains
66 | a notice placed by the copyright holder saying it may be distributed
67 | under the terms of this General Public License. The "Program", below,
68 | refers to any such program or work, and a "work based on the Program"
69 | means either the Program or any derivative work under copyright law:
70 | that is to say, a work containing the Program or a portion of it,
71 | either verbatim or with modifications and/or translated into another
72 | language. (Hereinafter, translation is included without limitation in
73 | the term "modification".) Each licensee is addressed as "you".
74 |
75 | Activities other than copying, distribution and modification are not
76 | covered by this License; they are outside its scope. The act of
77 | running the Program is not restricted, and the output from the Program
78 | is covered only if its contents constitute a work based on the
79 | Program (independent of having been made by running the Program).
80 | Whether that is true depends on what the Program does.
81 |
82 | 1. You may copy and distribute verbatim copies of the Program's
83 | source code as you receive it, in any medium, provided that you
84 | conspicuously and appropriately publish on each copy an appropriate
85 | copyright notice and disclaimer of warranty; keep intact all the
86 | notices that refer to this License and to the absence of any warranty;
87 | and give any other recipients of the Program a copy of this License
88 | along with the Program.
89 |
90 | You may charge a fee for the physical act of transferring a copy, and
91 | you may at your option offer warranty protection in exchange for a fee.
92 |
93 | 2. You may modify your copy or copies of the Program or any portion
94 | of it, thus forming a work based on the Program, and copy and
95 | distribute such modifications or work under the terms of Section 1
96 | above, provided that you also meet all of these conditions:
97 |
98 | a) You must cause the modified files to carry prominent notices
99 | stating that you changed the files and the date of any change.
100 |
101 | b) You must cause any work that you distribute or publish, that in
102 | whole or in part contains or is derived from the Program or any
103 | part thereof, to be licensed as a whole at no charge to all third
104 | parties under the terms of this License.
105 |
106 | c) If the modified program normally reads commands interactively
107 | when run, you must cause it, when started running for such
108 | interactive use in the most ordinary way, to print or display an
109 | announcement including an appropriate copyright notice and a
110 | notice that there is no warranty (or else, saying that you provide
111 | a warranty) and that users may redistribute the program under
112 | these conditions, and telling the user how to view a copy of this
113 | License. (Exception: if the Program itself is interactive but
114 | does not normally print such an announcement, your work based on
115 | the Program is not required to print an announcement.)
116 |
117 | These requirements apply to the modified work as a whole. If
118 | identifiable sections of that work are not derived from the Program,
119 | and can be reasonably considered independent and separate works in
120 | themselves, then this License, and its terms, do not apply to those
121 | sections when you distribute them as separate works. But when you
122 | distribute the same sections as part of a whole which is a work based
123 | on the Program, the distribution of the whole must be on the terms of
124 | this License, whose permissions for other licensees extend to the
125 | entire whole, and thus to each and every part regardless of who wrote it.
126 |
127 | Thus, it is not the intent of this section to claim rights or contest
128 | your rights to work written entirely by you; rather, the intent is to
129 | exercise the right to control the distribution of derivative or
130 | collective works based on the Program.
131 |
132 | In addition, mere aggregation of another work not based on the Program
133 | with the Program (or with a work based on the Program) on a volume of
134 | a storage or distribution medium does not bring the other work under
135 | the scope of this License.
136 |
137 | 3. You may copy and distribute the Program (or a work based on it,
138 | under Section 2) in object code or executable form under the terms of
139 | Sections 1 and 2 above provided that you also do one of the following:
140 |
141 | a) Accompany it with the complete corresponding machine-readable
142 | source code, which must be distributed under the terms of Sections
143 | 1 and 2 above on a medium customarily used for software interchange; or,
144 |
145 | b) Accompany it with a written offer, valid for at least three
146 | years, to give any third party, for a charge no more than your
147 | cost of physically performing source distribution, a complete
148 | machine-readable copy of the corresponding source code, to be
149 | distributed under the terms of Sections 1 and 2 above on a medium
150 | customarily used for software interchange; or,
151 |
152 | c) Accompany it with the information you received as to the offer
153 | to distribute corresponding source code. (This alternative is
154 | allowed only for noncommercial distribution and only if you
155 | received the program in object code or executable form with such
156 | an offer, in accord with Subsection b above.)
157 |
158 | The source code for a work means the preferred form of the work for
159 | making modifications to it. For an executable work, complete source
160 | code means all the source code for all modules it contains, plus any
161 | associated interface definition files, plus the scripts used to
162 | control compilation and installation of the executable. However, as a
163 | special exception, the source code distributed need not include
164 | anything that is normally distributed (in either source or binary
165 | form) with the major components (compiler, kernel, and so on) of the
166 | operating system on which the executable runs, unless that component
167 | itself accompanies the executable.
168 |
169 | If distribution of executable or object code is made by offering
170 | access to copy from a designated place, then offering equivalent
171 | access to copy the source code from the same place counts as
172 | distribution of the source code, even though third parties are not
173 | compelled to copy the source along with the object code.
174 |
175 | 4. You may not copy, modify, sublicense, or distribute the Program
176 | except as expressly provided under this License. Any attempt
177 | otherwise to copy, modify, sublicense or distribute the Program is
178 | void, and will automatically terminate your rights under this License.
179 | However, parties who have received copies, or rights, from you under
180 | this License will not have their licenses terminated so long as such
181 | parties remain in full compliance.
182 |
183 | 5. You are not required to accept this License, since you have not
184 | signed it. However, nothing else grants you permission to modify or
185 | distribute the Program or its derivative works. These actions are
186 | prohibited by law if you do not accept this License. Therefore, by
187 | modifying or distributing the Program (or any work based on the
188 | Program), you indicate your acceptance of this License to do so, and
189 | all its terms and conditions for copying, distributing or modifying
190 | the Program or works based on it.
191 |
192 | 6. Each time you redistribute the Program (or any work based on the
193 | Program), the recipient automatically receives a license from the
194 | original licensor to copy, distribute or modify the Program subject to
195 | these terms and conditions. You may not impose any further
196 | restrictions on the recipients' exercise of the rights granted herein.
197 | You are not responsible for enforcing compliance by third parties to
198 | this License.
199 |
200 | 7. If, as a consequence of a court judgment or allegation of patent
201 | infringement or for any other reason (not limited to patent issues),
202 | conditions are imposed on you (whether by court order, agreement or
203 | otherwise) that contradict the conditions of this License, they do not
204 | excuse you from the conditions of this License. If you cannot
205 | distribute so as to satisfy simultaneously your obligations under this
206 | License and any other pertinent obligations, then as a consequence you
207 | may not distribute the Program at all. For example, if a patent
208 | license would not permit royalty-free redistribution of the Program by
209 | all those who receive copies directly or indirectly through you, then
210 | the only way you could satisfy both it and this License would be to
211 | refrain entirely from distribution of the Program.
212 |
213 | If any portion of this section is held invalid or unenforceable under
214 | any particular circumstance, the balance of the section is intended to
215 | apply and the section as a whole is intended to apply in other
216 | circumstances.
217 |
218 | It is not the purpose of this section to induce you to infringe any
219 | patents or other property right claims or to contest validity of any
220 | such claims; this section has the sole purpose of protecting the
221 | integrity of the free software distribution system, which is
222 | implemented by public license practices. Many people have made
223 | generous contributions to the wide range of software distributed
224 | through that system in reliance on consistent application of that
225 | system; it is up to the author/donor to decide if he or she is willing
226 | to distribute software through any other system and a licensee cannot
227 | impose that choice.
228 |
229 | This section is intended to make thoroughly clear what is believed to
230 | be a consequence of the rest of this License.
231 |
232 | 8. If the distribution and/or use of the Program is restricted in
233 | certain countries either by patents or by copyrighted interfaces, the
234 | original copyright holder who places the Program under this License
235 | may add an explicit geographical distribution limitation excluding
236 | those countries, so that distribution is permitted only in or among
237 | countries not thus excluded. In such case, this License incorporates
238 | the limitation as if written in the body of this License.
239 |
240 | 9. The Free Software Foundation may publish revised and/or new versions
241 | of the General Public License from time to time. Such new versions will
242 | be similar in spirit to the present version, but may differ in detail to
243 | address new problems or concerns.
244 |
245 | Each version is given a distinguishing version number. If the Program
246 | specifies a version number of this License which applies to it and "any
247 | later version", you have the option of following the terms and conditions
248 | either of that version or of any later version published by the Free
249 | Software Foundation. If the Program does not specify a version number of
250 | this License, you may choose any version ever published by the Free Software
251 | Foundation.
252 |
253 | 10. If you wish to incorporate parts of the Program into other free
254 | programs whose distribution conditions are different, write to the author
255 | to ask for permission. For software which is copyrighted by the Free
256 | Software Foundation, write to the Free Software Foundation; we sometimes
257 | make exceptions for this. Our decision will be guided by the two goals
258 | of preserving the free status of all derivatives of our free software and
259 | of promoting the sharing and reuse of software generally.
260 |
261 | NO WARRANTY
262 |
263 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
264 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
265 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
266 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
267 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
268 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
269 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
270 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
271 | REPAIR OR CORRECTION.
272 |
273 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
274 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
275 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
276 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
277 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
278 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
279 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
280 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
281 | POSSIBILITY OF SUCH DAMAGES.
282 |
283 | END OF TERMS AND CONDITIONS
284 |
285 |
--------------------------------------------------------------------------------
/src/newaiplayer/ThreatSearcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 |
4 | public class ThreatSearcher
5 | {
6 | private class Item
7 | {
8 | public int category;
9 | public int offset;
10 | public ArrayList fields; // contains integers
11 | public bool create = false;
12 |
13 | public Item(int cat, int off, ArrayList ff)
14 | {
15 | category = cat;
16 | offset = off;
17 | fields = ff;
18 | }
19 | public Item(int cat, int off, ArrayList ff, bool create)
20 | {
21 | category = cat;
22 | offset = off;
23 | fields = ff;
24 | this.create = create;
25 | }
26 |
27 | public static Item Five(int offset)
28 | {
29 | ArrayList list = new ArrayList();
30 | return new Item(0, offset, list);
31 | }
32 |
33 | public static Item Four(int offset)
34 | {
35 | ArrayList list = new ArrayList();
36 | list.Add(4);
37 | return new Item(1, offset, list);
38 | }
39 |
40 | public static Item ShiftOneFour(int offset)
41 | {
42 | ArrayList list = new ArrayList();
43 | list.Add(3);
44 | return new Item(1, offset, list);
45 | }
46 |
47 | public static Item ShiftTwoFour(int offset)
48 | {
49 | ArrayList list = new ArrayList();
50 | list.Add(2);
51 | return new Item(1, offset, list);
52 | }
53 |
54 | public static Item ShiftThreeFour(int offset)
55 | {
56 | ArrayList list = new ArrayList();
57 | list.Add(1);
58 | return new Item(1, offset, list);
59 | }
60 |
61 | public static Item ShiftFourFour(int offset)
62 | {
63 | ArrayList list = new ArrayList();
64 | list.Add(0);
65 | return new Item(1, offset, list);
66 | }
67 |
68 | public static Item Three(int offset)
69 | {
70 | ArrayList list = new ArrayList();
71 | list.Add(1);
72 | list.Add(5);
73 | return new Item(2, offset, list);
74 | }
75 |
76 | public static Item OccupiedThree(int offset)
77 | {
78 | ArrayList list = new ArrayList();
79 | list.Add(1);
80 | list.Add(5);
81 | list.Add(6);
82 | return new Item(2, offset, list);
83 | }
84 | public static Item ReversedOccupiedThree(int offset)
85 | {
86 | ArrayList list = new ArrayList();
87 | list.Add(0);
88 | list.Add(1);
89 | list.Add(5);
90 | return new Item(2, offset, list);
91 | }
92 | public static Item StraightFour(int offset)
93 | {
94 | ArrayList list = new ArrayList();
95 | list.Add(0);
96 | list.Add(5);
97 | return new Item(1, offset, list);
98 | }
99 | public static Item BrokenThree(int offset)
100 | {
101 | ArrayList list = new ArrayList();
102 | list.Add(0);
103 | list.Add(2);
104 | list.Add(5);
105 | return new Item(2, offset, list);
106 | }
107 | public static Item ReversedBrokenThree(int offset)
108 | {
109 | ArrayList list = new ArrayList();
110 | list.Add(0);
111 | list.Add(3);
112 | list.Add(5);
113 | return new Item(2, offset, list);
114 | }
115 | public static Item CBrokenThreeOne(int offset)
116 | {
117 | ArrayList list = new ArrayList();
118 | list.Add(3);
119 | list.Add(4);
120 | return new Item(2, offset, list, true);
121 | }
122 | public static Item CBrokenThreeTwo(int offset)
123 | {
124 | ArrayList list = new ArrayList();
125 | list.Add(2);
126 | list.Add(4);
127 | return new Item(2, offset, list, true);
128 | }
129 | public static Item CBrokenThreeThree(int offset)
130 | {
131 | ArrayList list = new ArrayList();
132 | list.Add(2);
133 | list.Add(3);
134 | return new Item(2, offset, list, true);
135 | }
136 | public static Item CBrokenThreeFour(int offset)
137 | {
138 | ArrayList list = new ArrayList();
139 | list.Add(1);
140 | list.Add(4);
141 | return new Item(2, offset, list, true);
142 | }
143 | public static Item CBrokenThreeFive(int offset)
144 | {
145 | ArrayList list = new ArrayList();
146 | list.Add(1);
147 | list.Add(3);
148 | return new Item(2, offset, list, true);
149 | }
150 | public static Item CBrokenThreeSix(int offset)
151 | {
152 | ArrayList list = new ArrayList();
153 | list.Add(1);
154 | list.Add(2);
155 | return new Item(2, offset, list, true);
156 | }
157 | public static Item CBrokenThreeSeven(int offset)
158 | {
159 | ArrayList list = new ArrayList();
160 | list.Add(0);
161 | list.Add(4);
162 | return new Item(2, offset, list, true);
163 | }
164 | public static Item CBrokenThreeEight(int offset)
165 | {
166 | ArrayList list = new ArrayList();
167 | list.Add(0);
168 | list.Add(3);
169 | return new Item(2, offset, list, true);
170 | }
171 | public static Item CBrokenThreeNine(int offset)
172 | {
173 | ArrayList list = new ArrayList();
174 | list.Add(0);
175 | list.Add(2);
176 | return new Item(2, offset, list, true);
177 | }
178 | public static Item CBrokenThreeTen(int offset)
179 | {
180 | ArrayList list = new ArrayList();
181 | list.Add(0);
182 | list.Add(1);
183 | return new Item(2, offset, list, true);
184 | }
185 |
186 | }
187 |
188 | private Item[] table = new Item[262144];
189 | private int[,] board;
190 | private int attacker;
191 | bool create = false;
192 |
193 |
194 | private void generateTable()
195 | {
196 | Console.WriteLine("Generating searcher table: start");
197 | int digit = 0;
198 | for (int i = 0; i < 262144; ++i)
199 | {
200 |
201 | digit = i;
202 | bool assigned = false;
203 |
204 | int[] line = new int[9];
205 | for (int y = 0; y < 9; ++y)
206 | {
207 | line[y] = digit % 4;
208 | digit >>= 2;
209 | }
210 | // free Field = 1
211 | // our Field = 2
212 | // opp Field = 0
213 | // nonexist Field = 3
214 |
215 | for (int k = 0; k < 5; ++k)
216 | {
217 | if (line[k+0] == 2
218 | && line[k+1] == 2
219 | && line[k+2] == 2
220 | && line[k+3] == 2
221 | && line[k+4] == 2)
222 | {
223 | table[i] = Item.Five(k-4);
224 | assigned = true;
225 | // Console.WriteLine("Found a five");
226 | }
227 | }
228 | if (assigned == false)
229 | {
230 | for (int k = 0; k < 3; ++k)
231 | {
232 | if (line[k+0] == 1
233 | && line[k+1] == 1
234 | && line[k+2] == 2
235 | && line[k+3] == 2
236 | && line[k+4] == 2
237 | && line[k+5] == 1
238 | && line[k+6] == 1)
239 | {
240 | table[i] = Item.Three(k-4);
241 | assigned = true;
242 | // Console.WriteLine("Found a three");
243 | }
244 | }
245 | }
246 | if (assigned == false)
247 | {
248 | for (int k = 0; k < 3; ++k)
249 | {
250 | if ( ((line[k+0] == 0) || (line[k+0] == 3))
251 | && line[k+1] == 1
252 | && line[k+2] == 2
253 | && line[k+3] == 2
254 | && line[k+4] == 2
255 | && line[k+5] == 1
256 | && line[k+6] == 1)
257 | {
258 | table[i] = Item.OccupiedThree(k-4);
259 | assigned = true;
260 | // Console.WriteLine("Found a three");
261 | }
262 | if (line[k+0] == 1
263 | && line[k+1] == 1
264 | && line[k+2] == 2
265 | && line[k+3] == 2
266 | && line[k+4] == 2
267 | && line[k+5] == 1
268 | && ((line[k+6] == 0) || (line[k+6] == 3)) )
269 | {
270 | table[i] = Item.ReversedOccupiedThree(k-4);
271 | assigned = true;
272 | // Console.WriteLine("Found a three");
273 | }
274 |
275 | }
276 | }
277 |
278 | if (assigned == false)
279 | {
280 | for (int k = 0; k < 4; ++k)
281 | {
282 | if (line[k+0] == 1
283 | && line[k+1] == 2
284 | && line[k+2] == 2
285 | && line[k+3] == 2
286 | && line[k+4] == 2
287 | && line[k+5] == 1)
288 | {
289 | table[i] = Item.StraightFour(k-4);
290 | assigned = true;
291 | // Console.WriteLine("Found a straight four");
292 | }
293 | }
294 | }
295 |
296 | if (assigned == false)
297 | {
298 | for (int k = 0; k < 5; ++k)
299 | {
300 | if (line[k+0] == 2
301 | && line[k+1] == 2
302 | && line[k+2] == 2
303 | && line[k+3] == 2
304 | && line[k+4] == 1)
305 | {
306 | table[i] = Item.Four(k-4);
307 | assigned = true;
308 | // Console.WriteLine("Found a four");
309 | }
310 | if (line[k+0] == 2
311 | && line[k+1] == 2
312 | && line[k+2] == 2
313 | && line[k+3] == 1
314 | && line[k+4] == 2)
315 | {
316 | table[i] = Item.ShiftOneFour(k-4);
317 | assigned = true;
318 | // Console.WriteLine("Found a four");
319 | }
320 | if (line[k+0] == 2
321 | && line[k+1] == 2
322 | && line[k+2] == 1
323 | && line[k+3] == 2
324 | && line[k+4] == 2)
325 | {
326 | table[i] = Item.ShiftTwoFour(k-4);
327 | assigned = true;
328 | // Console.WriteLine("Found a four");
329 | }
330 | if (line[k+0] == 2
331 | && line[k+1] == 1
332 | && line[k+2] == 2
333 | && line[k+3] == 2
334 | && line[k+4] == 2)
335 | {
336 | table[i] = Item.ShiftThreeFour(k-4);
337 | assigned = true;
338 | // Console.WriteLine("Found a four");
339 | }
340 | if (line[k+0] == 1
341 | && line[k+1] == 2
342 | && line[k+2] == 2
343 | && line[k+3] == 2
344 | && line[k+4] == 2)
345 | {
346 | table[i] = Item.ShiftFourFour(k-4);
347 | assigned = true;
348 | // Console.WriteLine("Found a four");
349 | }
350 | }
351 | }
352 | if (assigned == false)
353 | {
354 | for (int k = 0; k < 4; ++k)
355 | {
356 | if (line[k+0] == 1
357 | && line[k+1] == 2
358 | && line[k+2] == 1
359 | && line[k+3] == 2
360 | && line[k+4] == 2
361 | && line[k+5] == 1)
362 | {
363 | table[i] = Item.BrokenThree(k-4);
364 | assigned = true;
365 | //Console.WriteLine("Found a broken three");
366 | }
367 | if (line[k+0] == 1
368 | && line[k+1] == 2
369 | && line[k+2] == 2
370 | && line[k+3] == 1
371 | && line[k+4] == 2
372 | && line[k+5] == 1)
373 | {
374 | table[i] = Item.ReversedBrokenThree(k-4);
375 | assigned = true;
376 | //Console.WriteLine("Found a reversed broken three");
377 | }
378 | }
379 | }
380 | if (assigned == false)
381 | {
382 | for (int k = 0; k < 5; ++k)
383 | {
384 | if (line[k+0] == 2
385 | && line[k+1] == 2
386 | && line[k+2] == 2
387 | && line[k+3] == 1
388 | && line[k+4] == 1)
389 | {
390 | table[i] = Item.CBrokenThreeOne(k-4);
391 | assigned = true;
392 | //Console.WriteLine("Found a broken three");
393 | }
394 | }
395 | }
396 | if (assigned == false)
397 | {
398 | for (int k = 0; k < 5; ++k)
399 | {
400 | if (line[k+0] == 2
401 | && line[k+1] == 2
402 | && line[k+2] == 1
403 | && line[k+3] == 2
404 | && line[k+4] == 1)
405 | {
406 | table[i] = Item.CBrokenThreeTwo(k-4);
407 | assigned = true;
408 | //Console.WriteLine("Found a broken three");
409 | }
410 | }
411 | }
412 | if (assigned == false)
413 | {
414 | for (int k = 0; k < 5; ++k)
415 | {
416 | if (line[k+0] == 2
417 | && line[k+1] == 2
418 | && line[k+2] == 1
419 | && line[k+3] == 1
420 | && line[k+4] == 2)
421 | {
422 | table[i] = Item.CBrokenThreeThree(k-4);
423 | assigned = true;
424 | //Console.WriteLine("Found a broken three");
425 | }
426 | }
427 | }
428 | if (assigned == false)
429 | {
430 | for (int k = 0; k < 5; ++k)
431 | {
432 | if (line[k+0] == 2
433 | && line[k+1] == 1
434 | && line[k+2] == 2
435 | && line[k+3] == 2
436 | && line[k+4] == 1)
437 | {
438 | table[i] = Item.CBrokenThreeFour(k-4);
439 | assigned = true;
440 | //Console.WriteLine("Found a broken three");
441 | }
442 | }
443 | }
444 | if (assigned == false)
445 | {
446 | for (int k = 0; k < 5; ++k)
447 | {
448 | if (line[k+0] == 2
449 | && line[k+1] == 1
450 | && line[k+2] == 2
451 | && line[k+3] == 1
452 | && line[k+4] == 2)
453 | {
454 | table[i] = Item.CBrokenThreeFive(k-4);
455 | assigned = true;
456 | //Console.WriteLine("Found a broken three");
457 | }
458 | }
459 | }
460 | if (assigned == false)
461 | {
462 | for (int k = 0; k < 5; ++k)
463 | {
464 | if (line[k+0] == 2
465 | && line[k+1] == 1
466 | && line[k+2] == 1
467 | && line[k+3] == 2
468 | && line[k+4] == 2)
469 | {
470 | table[i] = Item.CBrokenThreeSix(k-4);
471 | assigned = true;
472 | //Console.WriteLine("Found a broken three");
473 | }
474 | }
475 | }
476 | if (assigned == false)
477 | {
478 | for (int k = 0; k < 5; ++k)
479 | {
480 | if (line[k+0] == 1
481 | && line[k+1] == 2
482 | && line[k+2] == 2
483 | && line[k+3] == 2
484 | && line[k+4] == 1)
485 | {
486 | table[i] = Item.CBrokenThreeSeven(k-4);
487 | assigned = true;
488 | //Console.WriteLine("Found a broken three");
489 | }
490 | }
491 | }
492 | if (assigned == false)
493 | {
494 | for (int k = 0; k < 5; ++k)
495 | {
496 | if (line[k+0] == 1
497 | && line[k+1] == 2
498 | && line[k+2] == 2
499 | && line[k+3] == 1
500 | && line[k+4] == 2)
501 | {
502 | table[i] = Item.CBrokenThreeEight(k-4);
503 | assigned = true;
504 | //Console.WriteLine("Found a broken three");
505 | }
506 | }
507 | }
508 | if (assigned == false)
509 | {
510 | for (int k = 0; k < 5; ++k)
511 | {
512 | if (line[k+0] == 1
513 | && line[k+1] == 2
514 | && line[k+2] == 1
515 | && line[k+3] == 2
516 | && line[k+4] == 2)
517 | {
518 | table[i] = Item.CBrokenThreeNine(k-4);
519 | assigned = true;
520 | //Console.WriteLine("Found a broken three");
521 | }
522 | }
523 | }
524 | if (assigned == false)
525 | {
526 | for (int k = 0; k < 5; ++k)
527 | {
528 | if (line[k+0] == 1
529 | && line[k+1] == 1
530 | && line[k+2] == 2
531 | && line[k+3] == 2
532 | && line[k+4] == 2)
533 | {
534 | table[i] = Item.CBrokenThreeTen(k-4);
535 | assigned = true;
536 | //Console.WriteLine("Found a broken three");
537 | }
538 | }
539 | }
540 | /*
541 | if (line[4] == 2 && table[i] != null) {
542 | Console.Write("Generating searcher table: key: {0}, string: ", i);
543 | for (int y = 0; y < 9; ++y) {
544 | Console.Write(line[y] == 1?".":line[y]==2?"X":line[y]==3?"#":line[y]==0?"O":"");
545 | }
546 | Console.WriteLine(" , cat: {0}", table[i].category);
547 | }
548 | */
549 |
550 | }
551 | Console.WriteLine("Generating searcher table: finished");
552 |
553 | }
554 |
555 | private ThreatList SearchBoards(Coordinate node)
556 | {
557 | ThreatList res = new ThreatList ();
558 | int turn = attacker;
559 |
560 | Item tmpval;
561 | int digit1 = 0;
562 | int digit2 = 0;
563 | int digit3 = 0;
564 | int digit4 = 0;
565 | for (int j = 8; j >= 0; --j)
566 | {
567 | if (j + node.X - 4 >= 0 && j + node.X - 4 < board.GetLength(0))
568 | {
569 | digit1 += board[node.X + j - 4, node.Y] * turn + 1;
570 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x + j - 4, node.move.y);
571 | }
572 | else digit1 += 3;
573 | if (j + node.Y - 4 >= 0 && j + node.Y - 4 < board.GetLength(1))
574 | {
575 | digit2 += board[node.X, node.Y + j - 4] * turn + 1;
576 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x , node.move.y + j -4);
577 | }
578 | else digit2 += 3;
579 | if (node.X + j - 4 >= 0 && node.X + j - 4 < board.GetLength(0) &&
580 | node.Y + j - 4 >= 0 && node.Y + j - 4 < board.GetLength(1) )
581 | {
582 | digit3 += board[node.X + j - 4, node.Y + j - 4] * turn + 1;
583 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x + j - 4, node.move.y + j - 4);
584 | }
585 | else digit3 += 3;
586 | if (node.X - j + 4 >= 0 && node.X - j + 4 < board.GetLength(0) &&
587 | node.Y + j - 4 >= 0 && node.Y + j - 4 < board.GetLength(1) )
588 | {
589 | digit4 += board[node.X - j + 4, node.Y + j - 4] * turn + 1;
590 | // Console.WriteLine("Grabbing {0}/{1}", node.move.x - j + 4, node.move.y + j - 4);
591 | }
592 | else digit4 += 3;
593 | if (j != 0)
594 | {
595 | digit1 <<= 2;
596 | digit2 <<= 2;
597 | digit3 <<= 2;
598 | digit4 <<= 2;
599 | }
600 | }
601 |
602 | tmpval = table[digit1];
603 |
604 | if (tmpval != null && (tmpval.create == false || create == true))
605 | {
606 | //Console.WriteLine("horz category: {0}", tmpval.category);
607 | ArrayList moves = new ArrayList();
608 | foreach(int field in tmpval.fields)
609 | {
610 | moves.Add(new Coordinate(node.X + tmpval.offset + field, node.Y));
611 | }
612 | Threat t = new Threat(node, tmpval.category, moves, tmpval.create);
613 | res.Add(t);
614 | }
615 | tmpval = table[digit2];
616 | if (tmpval != null && (tmpval.create == false || create == true))
617 | {
618 | //Console.WriteLine("vert category: {0}", tmpval.category);
619 | //Console.WriteLine("Node: {0}", node);
620 | //Console.WriteLine("offset: {0}", tmpval.offset);
621 | ArrayList moves = new ArrayList();
622 | foreach(int field in tmpval.fields)
623 | {
624 | //Coordinate tmp = new Coordinate(node.X , node.Y + tmpval.offset + field);
625 | //Console.WriteLine("adding move: {0}", tmp);
626 | moves.Add(new Coordinate(node.X, node.Y + tmpval.offset + field));
627 | }
628 | Threat t = new Threat(node, tmpval.category, moves, tmpval.create);
629 | res.Add(t);
630 | }
631 | tmpval = table[digit3];
632 | if (tmpval != null && (tmpval.create == false || create == true))
633 | {
634 | //Console.WriteLine("diag 1 category: {0}", tmpval.category);
635 | ArrayList moves = new ArrayList();
636 | foreach(int field in tmpval.fields)
637 | {
638 | moves.Add(new Coordinate(node.X + tmpval.offset + field, node.Y + tmpval.offset + field));
639 | }
640 | Threat t = new Threat(node, tmpval.category, moves, tmpval.create);
641 | res.Add(t);
642 | }
643 | tmpval = table[digit4];
644 | if (tmpval != null && (tmpval.create == false || create == true))
645 | {
646 | //Console.WriteLine("diag 2 category: {0}", tmpval.category);
647 | //Console.WriteLine("Node: {0}", node);
648 | //Console.WriteLine("offset: {0}", tmpval.offset);
649 | ArrayList moves = new ArrayList();
650 | foreach(int field in tmpval.fields)
651 | {
652 | //Coordinate tmp = new Coordinate(node.X -(tmpval.offset + field), node.Y + tmpval.offset + field);
653 | //Console.WriteLine("adding move: {0}", tmp);
654 | moves.Add(new Coordinate(node.X -(tmpval.offset + field), node.Y + tmpval.offset + field));
655 | }
656 | Threat t = new Threat(node, tmpval.category, moves, tmpval.create);
657 | res.Add(t);
658 | }
659 |
660 | return res;
661 |
662 | }
663 |
664 | public ThreatSearcher()
665 | {
666 | generateTable();
667 | }
668 |
669 | public ThreatList investigate(int[,] board, Coordinate move, int attacker)
670 | {
671 | return investigate(board, move, attacker, false);
672 | }
673 |
674 | public ThreatList investigate(int[,] board,
675 | Coordinate move,
676 | int attacker,
677 | bool create)
678 | {
679 | this.board = board;
680 | this.attacker = attacker;
681 | this.create = create;
682 |
683 | bool reset = false;
684 | if (board[move.X, move.Y] == attacker * -1) throw new Exception();
685 | if (board[move.X, move.Y] != attacker)
686 | {
687 | board[move.X, move.Y] = attacker;
688 | reset = true;
689 | }
690 |
691 | ThreatList res = SearchBoards(move);
692 |
693 | if (reset == true) board[move.X, move.Y] = 0;
694 | return res;
695 | }
696 |
697 | }
698 |
699 |
--------------------------------------------------------------------------------
/doc/solution.tex:
--------------------------------------------------------------------------------
1 | \NeedsTeXFormat{LaTeX2e}
2 | \documentclass[10pt]{article}
3 |
4 | \addtolength{\hoffset}{-0.5cm}
5 | \addtolength{\textwidth}{1cm}
6 |
7 | \usepackage{a4wide,latexsym,amssymb,amsfonts,verbatim}
8 | \usepackage[all]{xy}
9 | \usepackage{pgf,pgfarrows,pgfnodes}
10 |
11 | %\nonfrenchspacing
12 |
13 | \input{ai-inc}
14 |
15 | \begin{document}
16 | \blattheader{An AI for Gomoku/Wuziqi\\
17 | $\alpha-\beta$ and more...}{2004/12/29}
18 | \begin{center}
19 | How to think 20 moves ahead.
20 | \end{center}
21 |
22 | \section{Introduction}
23 |
24 | This document details our solution to the second of two projects of the
25 | ``Artificial Intelligence'' course in fall 2004 at the SJTU.
26 |
27 | Like the first project, the problem given to the students is easy to describe,
28 | but difficult to solve. The students are asked to write a program playing the
29 | game of Wuziqi, which is also known by Gobang, free-stlye Gomoku or ``Five in
30 | a row''.
31 |
32 | \subsection{The game}
33 |
34 | The standard playing rules are the following: On a board of 15x15 rows and
35 | column two players, black and white, in turns place one stone of their color
36 | on a free field until either the field is full or one player has won. A
37 | player has won if he has five or more stones of his color in a
38 | straight row, be it horizontal, vertical or diagonal.\footnote{There are more
39 | advanced rules in variations of this game, for example in ``standard Gomoku''
40 | a player is only granted a win if he has {\em exactly} five stones in a row.
41 | Also, professional ``Renju'' is a more complex variant restricting movements
42 | even further.}
43 |
44 | The game is interesting for two reasons. The first is that the rules are
45 | very easy to understand and everybody can play this game to some degree. The
46 | second reason - and the challenging part of the excercise - is that the game
47 | can develop a deep strategy, with moves having to be planned ahead 10 or more
48 | plys ahead to win successfully or even to just defend successfully against a
49 | strong opponent. World class Gomoku player plan ahead more than 25 plys.
50 |
51 | Gomoku has a seemingly large search space due to many possible moves on a
52 | 15x15 board. Additionally, it has been shown that generalized Gomoku is
53 | PSPACE-complete, which means that Gomoku played on an board with $n$ fields,
54 | the complexity increases without bounds for increasing $n$. The net result
55 | is, that without any clever tricks, Gomoku even on a 15x15 board is
56 | computationally very difficult to solve.
57 |
58 | Albeit this difficulties Gomoku has been solved by L. Victor Allis
59 | in 1992~\cite{Allis92} by using a new method he coined ``dependency-based
60 | search'', which we will describe in detail below.
61 |
62 | We apply $\alpha-\beta$ search with some modifications and the
63 | dependency-based search method to create a strong AI player, with no limit of
64 | planning ahead for some situations of the game.
65 |
66 |
67 | \section{Basic definitions}
68 |
69 | We use a number of definitions in the following sections, which are explained
70 | here.
71 |
72 | \begin{itemize}
73 | \item {\em Threat}. A threat is a move to which you have to react. The most
74 | simple example is a {\em four}, which is a series of four same-coloured
75 | stones, with one bordering field being free. If the the opponent does not
76 | react or does not win himself in the next move, the player owning the threat
77 | has won. We use a limited set of possible threats, which we will discuss in
78 | detail now. We assume the attacker always has the white color. All cases are
79 | shown with one example, but there are more cases than the one shown, because
80 | of symmetry.
81 |
82 | \begin{itemize}
83 | \item {\em Five}. A five is a simple row of five stones and the player has won.
84 |
85 | \begin{center}
86 | \begin{pgfpicture}{0cm}{0cm}{6cm}{2cm}
87 | % (0cm,0cm) is the lower left corner,
88 | % (5cm,2cm) is the upper right corner.
89 | \pgfcircle[stroke]{\pgfxy(1,1)}{0.4cm}
90 | \pgfcircle[stroke]{\pgfxy(2,1)}{0.4cm}
91 | \pgfcircle[stroke]{\pgfxy(3,1)}{0.4cm}
92 | \pgfcircle[stroke]{\pgfxy(4,1)}{0.4cm}
93 | \pgfcircle[stroke]{\pgfxy(5,1)}{0.4cm}
94 | \end{pgfpicture}
95 | \end{center}
96 |
97 | \item {\em Straight four}. A straight four is a row of four stones with both
98 | ends free. If the other player has not won in his next move, this situation
99 | is a sure win.
100 |
101 | \begin{center}
102 | \begin{pgfpicture}{0cm}{0cm}{6cm}{2cm}
103 | \pgfcircle[stroke]{\pgfxy(1,1)}{0.05cm}
104 | \pgfcircle[stroke]{\pgfxy(2,1)}{0.4cm}
105 | \pgfcircle[stroke]{\pgfxy(3,1)}{0.4cm}
106 | \pgfcircle[stroke]{\pgfxy(4,1)}{0.4cm}
107 | \pgfcircle[stroke]{\pgfxy(5,1)}{0.4cm}
108 | \pgfcircle[stroke]{\pgfxy(6,1)}{0.05cm}
109 | \end{pgfpicture}
110 | \end{center}
111 |
112 | \item {\em Four}. If the other player cannot win in his next move, he has to
113 | defend against the four, which is a row of four stones, but with one end
114 | closed by the opponent and the other end being free.
115 |
116 | \begin{center}
117 | \begin{pgfpicture}{0cm}{0cm}{6cm}{2cm}
118 | \pgfcircle[fill]{\pgfxy(1,1)}{0.4cm}
119 | \pgfcircle[stroke]{\pgfxy(2,1)}{0.4cm}
120 | \pgfcircle[stroke]{\pgfxy(3,1)}{0.4cm}
121 | \pgfcircle[stroke]{\pgfxy(4,1)}{0.4cm}
122 | \pgfcircle[stroke]{\pgfxy(5,1)}{0.4cm}
123 | \pgfcircle[stroke]{\pgfxy(6,1)}{0.05cm}
124 | \end{pgfpicture}
125 | \end{center}
126 |
127 | \item {\em Three}. The three is a more long term threat to the opponent and
128 | although he has to react to it, he has more choice in the possible defending
129 | moves to make. If undefended, the three can be extended to a straight four,
130 | which is a sure win.
131 |
132 | \begin{center}
133 | \begin{pgfpicture}{0cm}{0cm}{6cm}{2cm}
134 | \pgfcircle[stroke]{\pgfxy(1,1)}{0.05cm}
135 | \pgfcircle[stroke]{\pgfxy(2,1)}{0.4cm}
136 | \pgfcircle[stroke]{\pgfxy(3,1)}{0.4cm}
137 | \pgfcircle[stroke]{\pgfxy(4,1)}{0.4cm}
138 | \pgfcircle[stroke]{\pgfxy(5,1)}{0.05cm}
139 | \end{pgfpicture}
140 | \end{center}
141 |
142 | \item {\em Broken three}. Using the broken three, the attacker can exert
143 | great control about the opponents stones. It has to be defended against, just
144 | as with the three.
145 |
146 | \begin{center}
147 | \begin{pgfpicture}{0cm}{0cm}{6cm}{2cm}
148 | \pgfcircle[stroke]{\pgfxy(1,1)}{0.05cm}
149 | \pgfcircle[stroke]{\pgfxy(2,1)}{0.4cm}
150 | \pgfcircle[stroke]{\pgfxy(3,1)}{0.05cm}
151 | \pgfcircle[stroke]{\pgfxy(4,1)}{0.4cm}
152 | \pgfcircle[stroke]{\pgfxy(5,1)}{0.4cm}
153 | \pgfcircle[stroke]{\pgfxy(6,1)}{0.05cm}
154 | \end{pgfpicture}
155 | \end{center}
156 | \end{itemize}
157 |
158 | \item {\em Threat category}. To each type of threat we associate a threat
159 | category (or class), which equals the number of undefended moves necessary to
160 | win. The five has a class of 0, the straight four and the four have class 1
161 | and the three and broken three have class 2.
162 | \item {\em Threat sequence}. A threat sequence is a series of single attacker
163 | moves and one or more defending moves. The last attacking move reaches a goal
164 | state and has no defending moves. A goal state is either a five or the
165 | undefendable straight four.
166 | \end{itemize}
167 |
168 |
169 | \section{Alpha-Beta search}
170 |
171 | We use a standard four level (two pair move) alpha-beta search stage as
172 | detailed in~\cite{Nilsson99}. The state space searched is the board itself
173 | and the operations are single moves.
174 |
175 | \subsection{The static evaluation function}
176 |
177 | The basic static evaluation function basically uses three measurements to rate a move:
178 |
179 | \begin{enumerate}
180 | \item The possibilities the move gives us to build rows of five stones in the future.
181 |
182 | The space around the newly set stone is the lowest measurement we apply for
183 | every move. That means, if no other criteria work, we will set the stone that
184 | we, we can build most possible combinations of rows of five stones over it.
185 |
186 | \item Building up long lines (or combinations of long lines) to work towards
187 | threats and to prefer special kinds of threats.
188 |
189 | This is done pretty easily: The row of own stones built up by the new stone is
190 | counted and assigned by a multiplier. This for example has the advantage, that
191 | a ``straight four'' will be preferred to a ``four'' with a hole.
192 |
193 | \item The identification of a winning situation.
194 |
195 | This is obvious.
196 |
197 | \end{enumerate}
198 |
199 | Besides the pure measurements and assigned points, the static evaluation
200 | function has to be fast.
201 |
202 | In order to need not to evaluate the whole board in every move, we just
203 | consider the space around a newly set stone for the rating. This means, we look
204 | at the 4 lines of 9 pieces which lead through the new stone, building up a
205 | star. In order to avoid the calculation useless every time we want to rate a
206 | move, we generate the values before the game starts into a table with something
207 | around 260000 entries, which is much less and faster than doing this during the
208 | execution of the alpha-beta algorithm.
209 |
210 | This fast way of rating single nodes allows us not just to call the static
211 | evaluation function in all the leaf nodes, but in every single node. During the
212 | alpha-beta-search, the nodes are sorted (higher successors for MAX-nodes and
213 | lower successors first for MIN-nodes) allowing a much faster and higher cut-off
214 | rate during the alpha-beta-procedure~\cite{Nilsson99}.
215 |
216 | \subsection{Supporting db-search: statically evaluating threats}
217 |
218 | The alpha-beta method tries to support the db-search. This is done by
219 | favoring the creation of new high category threats. Our assumption is that
220 | other AI players will be unable to track a large number of high category
221 | threats more successful than we are able to do using db-search. Additionally
222 | the large number of threats on the board will create many possible threat
223 | sequences of which we hope there will be a winning one.
224 |
225 | During the static evaluation function, threats therefore are higher rated than
226 | anything else (except win of course), and in threats of the same category,
227 | longer lines of own stones are preferred.
228 |
229 | Since the threat detection for the static evaluation function has to be much
230 | faster, but less generalized than during the db-search, it has it's own methods
231 | for this purpose, allowing a fast pattern matching on the same local lines as
232 | for the normal static evaluation function, also using a cached look-up table.
233 |
234 | \subsection{Keeping the branching factor small: finding interesting fields}
235 |
236 | For the speed of the alpha-beta-procedure, it is crucial to keep the branching
237 | factor small. Since the branching factor is decided by the new number of
238 | interesting moves (which means the number of moves we should consider to take
239 | in the next step) we have to find ways to exclude as many fields as possible.
240 |
241 | This is done in three steps:
242 |
243 | \begin{enumerate}
244 | \item Using ``binary dilation'' on the board.
245 |
246 | Binary dilation, originally coming from the field of digital image processing,
247 | allows us to select just the fields around already set stones, using a fixed
248 | pattern. This way we just consider the 16 fields (starformed) around already
249 | set stones as interesting.
250 |
251 | \item Looking for threats.
252 |
253 | If we are already forced to block threats, we don't need to consider other
254 | fields than the blocking ones, except...
255 |
256 | \item Looking for own possible threats.
257 |
258 | If we are able to build up threats of lower categories than the opponent
259 | threats, we can force him before he turns his threat into a win, so these moves
260 | can be considerer.
261 | \end{enumerate}
262 |
263 | This method allows us crucial branching limitations in threat situation (which
264 | we are looking for) and in end-game situations. The threats and interesting
265 | moves ar also locally evaluated, never have to been recalculated for the whole
266 | board, to let us be fast.
267 |
268 | \section{Dependency-based search}
269 |
270 | Dependency-based search is a single-agent search algorithm to explore a space
271 | state under certain constraints. It was invented by Victor Allis specifically
272 | to solve thinking games such as Connect-four and Gomoku.
273 |
274 | Now, following~\cite{Allis92} we will discuss two points that are crucial for
275 | the understanding of how we use db-search in Gomoku. The first is how
276 | the adversary-agent problem of Gomoku is converted to a single-agent search
277 | space. The second is how db-search works on this single-agent search space.
278 |
279 | \subsection{Converting Gomoku to a single-agent search space}
280 |
281 | The idea of converting the adversary search space to a single agent one, is a
282 | simple and clever one: we allow the defender to make all possible defending
283 | moves at once. Then, every threatening move is combined with all the possible
284 | moves to defend against this threat and the whole pair-move is combined as one
285 | operation in the search space.
286 |
287 | Of course, allowing the opponent to make more than one move at a time will
288 | give him a huge advantage. But the advantage to safely plan ahead a large
289 | number of moves far outweights this disadvantage. That is, we are denied the
290 | ability to recognize a large number of cases where we could have won, but
291 | without this simplification we would not have the computational ability
292 | anyway. What remains is a small portion of the possible cases from which we
293 | can try to find a win.
294 |
295 | \subsection{Applying db-search}
296 |
297 | The theoretic framework for db-search provided in~\cite{Allis92} is quite
298 | large and generic. We implemented this framework completely and besides
299 | solving the Gomoku with it, we solved a simple problem known as ``Double
300 | Letter Puzzle'' for testing purposes. Below we will shortly describe the
301 | operation of db-search, although a larger discussion would take to much time.
302 |
303 | \subsubsection{The basic idea}
304 |
305 | The basic idea of db-search is to use operators to transfer between states in
306 | the state space. The already discovered states are kept in a directed acyclic
307 | graph (DAG). The constraints to apply an operator on a state already
308 | discovered are the following:
309 | \begin{itemize}
310 | \item The operator is valid.
311 |
312 | The operators preconditions are valid on the given state and the operator
313 | can be applied.
314 | \item The application of the operator {\em depends} on the previous one.
315 |
316 | This condition is always assumed true if the state is the root state, as there
317 | was no previous operator.
318 |
319 | Otherwise, if there was a previous operator leading to this state, the
320 | application of the new operator examined has to depend on the result of the
321 | previous operator. That is, without the previous operator applied before, the
322 | operator would not work. This is one important part where useless
323 | explorations of the space state are limited.
324 | \item The application of the operator has to {\em create new dependent
325 | operators}.
326 |
327 | The exploration of the state space is greatly limited by this condition. Only
328 | operators are applied, if they depend on the previous operator {\em and} if
329 | their application creates at least one operator that is dependent of the
330 | result of the operator considered.
331 | \end{itemize}
332 |
333 | \subsection{Implementation}
334 |
335 | The db-search description is quite simple and intuitive, but an efficient
336 | implementation of this semantics is another matter. One pseudo-code
337 | implementation is given in the original paper by Allis, which we used as basic
338 | design guideline for our from-scratch implementation. The really difficult
339 | part in implementing db-search were a number of predicates (such as
340 | \verb|NotInConflict| given in the original paper) and meta-operators
341 | (such as \verb|Combine|). The implementation we use works in two stages, a
342 | {\em dependency following} and a {\em state combination} stage. An
343 | in-depth discussion would take too much time, but suffice to say, the three
344 | conditions listed above - also known as ``meta operator'' used in the
345 | theoretical db-search framework - are ensured by successively executing this
346 | stages in pairs, called levels.
347 |
348 | For Gomoku, we often have seen one single application of the dependency
349 | following stage following more than 10 moves in a row. The largest sequences
350 | we have seen being checked extended more than 25 moves. In no case observed
351 | by us the search has required more than four levels.
352 |
353 | On an algorithmic level our implementation is very space and time efficient,
354 | often exploring less than a thousand states and taking less than a few seconds
355 | to explore all possible threat trees. However, we did not profile and
356 | manually optimize the code so far, which could still result in improvements of
357 | an order of magnitude.
358 |
359 | Note that the winning threat sequence search is not complete. This means it
360 | will not find all winning threat sequences or miss them while searching.
361 | However, if it finds one, it guarantees it is not refutable. As such, it is
362 | used only in conjunction with the $\alpha-\beta$ search, not as replacement.
363 |
364 | One caveat: we did not implement complete three- and four-state combinations
365 | in the db-search combination stage, which can lead to problems, see below.
366 | This is because we ran out of time to devise an efficient implementation and
367 | our implementation already took considerable time to complete.
368 |
369 |
370 | \subsection{Winning threat sequence search}
371 |
372 | The winning threat sequence search is called with a board position. The goal
373 | condition for the search to succeed is a {\em five} or {\em straight four}
374 | pattern on the board. As long as this pattern is not found and the search
375 | space is not exhausted, we continue to search.
376 |
377 | Also, as an extra condition to ensure timely execution, we added a timeout
378 | mechanism. If the timeout given in milliseconds is exceeded, the search is
379 | cancelled and no winning threat sequence is returned.
380 |
381 | The basic operation of the search is the following:
382 |
383 | \begin{enumerate}
384 | \item Search for potential winning threat sequences
385 |
386 | The db-search is started with the board as parameter and proceeds expanding
387 | the search tree using the given constraints. For each new state reached it is
388 | checked if it is a goal state. If it is no goal state, the search proceeds.
389 | If it is a goal state, a potential winning threat sequence is extracted by
390 | converting all the operators on the path from the root state to the goal state
391 | into moves. Then
392 |
393 | \item For each potential winning threat sequence found, we check if it can be
394 | defended against by the opponent. Defending means, that by making a good
395 | choice on his defending moves, he can build threat sequences himself. His
396 | threat sequences are a danger to us for two reasons:
397 | \begin{itemize}
398 | \item They could lead to a win for the opponent.
399 | \item They could lead to occupation of squares we would need ourselves
400 | later in our threat sequence or occupy defending squares ahead of time.
401 | \end{itemize}
402 |
403 | To check for this, we start a second db-search, this time trying to defend
404 | against our own attacks. Note, while the attack sequence search we did for
405 | ourselves is not complete, this defending search has to be complete to be sure
406 | our attack is working every time. See the next subsection for an explanation
407 | why this is so.
408 |
409 | The defending db-search works like this:
410 | \begin{enumerate}
411 | \item Make an attacker move
412 | \item If the attacker has won with this move, we mark the sequence as
413 | unrefutable.
414 | \item Try to find a potential winning threat sequence, but limit the
415 | applicable threats to a class one lower than the attacker threat.
416 |
417 | It would make no sense to search for threats with a class as high as or higher
418 | than the attacker has challenged us with, as a challenge with a class as high
419 | as his would mean he is always a class lower than we are in the next move,
420 | leading to a win for the attacker. On the other hand, for example he
421 | challenges us with a class 2 threat, we can successfully defend ourselves with
422 | both a class 1 (if he does not react, we win in the next move) and class 0 (we
423 | won) threat.
424 | \item If the threat sequences are potential winning sequences or if they
425 | lead to an occupation of any attacker or defender square later in the
426 | attackers threat sequence, we return and mark the threat sequence as refuted.
427 | \item If there are still moves left in the attackers threat sequence,
428 | advance to the next move and start at (a).
429 | \end{enumerate}
430 |
431 | If a winning threat sequence has been found, we abort the search and return
432 | early with the sequence.
433 | \end{enumerate}
434 |
435 | \subsubsection{Completeness of the defending db-search}
436 |
437 | The defending threat sequence search has to be complete in that it never
438 | misses any defending sequence the opponent can make. This requires
439 | completeness in the db-search algorithm. However, as we have seen that the
440 | attacker winning threat sequence search is not complete, how can we make the
441 | defending one complete?
442 |
443 | This is achieved by restricting the number of applicable operators
444 | drastically: only operators with a category below the attackers one are
445 | allowed. As the highest category is 2, for threats that will win in two moves
446 | if not defended against, the maximum possible threat category the defender can
447 | apply is 1. However, to all threats of class 1, there is always a reply using
448 | only one stone, which does not require our ``multiple-defender-stone''
449 | assumption, which makes the attackers search incomplete. Hence the defending
450 | search is complete.
451 |
452 | However, in our implementation a small part of the db-search algorithm is
453 | missing. This leads to some rare cases not being explored in the search tree
454 | although they should be. We have seen no such incident in a real game, but
455 | there are artificial situations in which this happens. As this is a
456 | vulnerability in our AI player, we will not describe it in too much detail
457 | here which situations could trigger this, but we think they are unlikely to
458 | occur in a real game our player plays.
459 |
460 | \subsection{In game operation}
461 |
462 | The winning threat sequence search is called whenever an opponent move has
463 | been made. Then, after the alpha-beta and interesting moves module is ran, we
464 | use the remaining time to run a time-constrained db-search, giving the current
465 | board position.
466 |
467 | If the winning threat sequence finds no winning threat sequence, the
468 | alpha-beta result is used. If it does find a sequence, this guarantees us a
469 | win:
470 |
471 | \begin{itemize}
472 | \item We have won in no more than $n$ moves, where $n$ is the depth given in
473 | the threat sequence plus the number of opponent threats on the board.
474 | \item We have a move sequence which the opponent cannot refute at the last
475 | move.
476 | \item We know all possible defender moves in advance that can delay our win.
477 | If the defender does not make any of this moves, we fall back to
478 | $\alpha-\beta$ search and finding a new winning threat sequence. In almost
479 | all cases the $\alpha-\beta$ search will find a guaranteed win already, as the
480 | latest threat was not defended against. In rare cases it might not find a
481 | good move and a new much shorter threat sequence is returned by the winning
482 | threat sequence search.
483 | \end{itemize}
484 |
485 | The threat sequence is stored and followed as long as the defender makes one
486 | of the known defending moves. We expect almost all opponent AIs to blindly
487 | following the threat sequences expected defending moves until at least near
488 | the end.
489 |
490 | \begin{thebibliography}{99}
491 | \bibitem{Allis92} L. Victor Allis, ``Searching for Solutions in Games and
492 | Artificial Intelligence'', Ph.D. thesis available online at
493 | \verb|http://www.cs.vu.nl/~victor/thesis.html| and available as book (ISBN
494 | 90-9007488-0)
495 | \bibitem{Nilsson99} Nils J. Nilsson, ``Artificial Intelligence - A New
496 | Synthesis'', book (ISBN 7-111-07438-6)
497 | \end{thebibliography}
498 | \end{document}
499 |
--------------------------------------------------------------------------------