├── src ├── server │ ├── start.sh │ ├── Makefile │ ├── Server.class │ └── Server.java ├── win-project │ ├── App.ico │ ├── opp.bmp │ ├── own.bmp │ ├── empty.bmp │ ├── faust.bmp │ ├── WinGui.resources │ ├── win-project.suo │ ├── bin │ │ └── win-project.exe │ ├── MyPicBox.cs │ ├── win-project.sln │ ├── History.cs │ ├── Makefile │ ├── AssemblyInfo.cs │ ├── win-project.csproj.user │ └── win-project.csproj ├── gtksharp-gui │ ├── fivegui.exe │ ├── smiley100.png │ ├── smiley111.png │ ├── fivegui.gladep │ ├── circle.svg │ ├── cross.svg │ ├── Makefile │ ├── MoveRecording.cs │ ├── smiley100.svg │ ├── smiley111.svg │ ├── FiveGUI.cs │ └── fivegui.glade ├── newaiplayer │ ├── GoBang.exe │ ├── Player.cs │ ├── Evaluator.cs │ ├── Coordinate.cs │ ├── Makefile │ ├── SortedList.cs │ ├── Threat.cs │ ├── ComparerTest.cs │ ├── Communicator.cs │ ├── TestFirstSearcher.cs │ ├── PrettyPrint.cs │ ├── GoBang.cs │ ├── StatValEvaluator.cs │ ├── InterestingFieldAgent.cs │ └── ThreatSearcher.cs ├── Gomocup │ ├── app.config │ ├── AIwrapper.cs │ ├── Gomocup.csproj │ └── PisqPipe.cs └── dbsearch │ ├── Makefile │ └── GBBoard.cs ├── stahlfaust.png ├── doc ├── solution.pdf ├── Makefile ├── ai-inc.tex └── solution.tex ├── README.txt ├── Readme.md └── LICENSE /src/server/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | while (true) 4 | do 5 | java Server 15 7777; 6 | done; 7 | -------------------------------------------------------------------------------- /stahlfaust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/stahlfaust.png -------------------------------------------------------------------------------- /doc/solution.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/doc/solution.pdf -------------------------------------------------------------------------------- /src/server/Makefile: -------------------------------------------------------------------------------- 1 | Server.class: Server.java 2 | javac Server.java 3 | 4 | clean: 5 | rm -rf Server.class 6 | -------------------------------------------------------------------------------- /src/server/Server.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/server/Server.class -------------------------------------------------------------------------------- /src/win-project/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/App.ico -------------------------------------------------------------------------------- /src/win-project/opp.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/opp.bmp -------------------------------------------------------------------------------- /src/win-project/own.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/own.bmp -------------------------------------------------------------------------------- /src/win-project/empty.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/empty.bmp -------------------------------------------------------------------------------- /src/win-project/faust.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/faust.bmp -------------------------------------------------------------------------------- /src/gtksharp-gui/fivegui.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/gtksharp-gui/fivegui.exe -------------------------------------------------------------------------------- /src/newaiplayer/GoBang.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/newaiplayer/GoBang.exe -------------------------------------------------------------------------------- /src/gtksharp-gui/smiley100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/gtksharp-gui/smiley100.png -------------------------------------------------------------------------------- /src/gtksharp-gui/smiley111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/gtksharp-gui/smiley111.png -------------------------------------------------------------------------------- /src/win-project/WinGui.resources: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/WinGui.resources -------------------------------------------------------------------------------- /src/win-project/win-project.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/win-project.suo -------------------------------------------------------------------------------- /src/win-project/bin/win-project.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gomoku/Stahlfaust---Gomoku-AI-player/HEAD/src/win-project/bin/win-project.exe -------------------------------------------------------------------------------- /src/Gomocup/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/newaiplayer/Player.cs: -------------------------------------------------------------------------------- 1 | public interface Player 2 | { 3 | void RegOppMove(Coordinate move); 4 | Coordinate GetMove(); 5 | int AskSize(); 6 | string AskColor(); 7 | void SetSize(int size); 8 | void SetColor(string color); 9 | } 10 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | all: solution.tex 4 | latex solution.tex 5 | latex solution.tex 6 | dvips -o solution.ps solution.dvi 7 | gzip -f -9 solution.ps 8 | pdflatex solution.tex 9 | pdflatex solution.tex 10 | 11 | clean: 12 | rm -f *.log *.aux *.dvi 13 | 14 | -------------------------------------------------------------------------------- /src/win-project/MyPicBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Windows.Forms; 4 | 5 | namespace win_project 6 | { 7 | public class MyPicBox : PictureBox 8 | { 9 | public int x; 10 | public int y; 11 | 12 | public MyPicBox() : base() 13 | { 14 | } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/gtksharp-gui/fivegui.gladep: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FiveGUI 6 | fivegui 7 | FALSE 8 | 9 | -------------------------------------------------------------------------------- /src/newaiplayer/Evaluator.cs: -------------------------------------------------------------------------------- 1 | /** Interface for Evaluators. Evaluators can rate boards and moves. 2 | */ 3 | public interface Evaluator 4 | { 5 | /** Calculates the static value of a move. 6 | * 7 | * @param board The actual board. 8 | * @param move The node containing the last move. 9 | */ 10 | int statVal(int[,] board, Coordinate move, int attacker); 11 | } 12 | -------------------------------------------------------------------------------- /src/gtksharp-gui/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/gtksharp-gui/cross.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/gtksharp-gui/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CSC=mcs 3 | 4 | SOURCES=../dbsearch/DBSearch.cs \ 5 | ../dbsearch/GBSearch.cs ../dbsearch/GBOperators.cs \ 6 | ../dbsearch/GBBoard.cs ../dbsearch/GBThreat.cs \ 7 | ../newaiplayer/PrettyPrint.cs 8 | 9 | LIBS=-pkg:gtk-sharp -pkg:glade-sharp #-pkg:rsvg-sharp 10 | 11 | 12 | all: fivegui.exe 13 | 14 | fivegui.exe: FiveGUI.cs fivegui.glade $(SOURCES) 15 | $(CSC) $(CSCFLAGS) -out:fivegui.exe -main:FiveGUI FiveGUI.cs \ 16 | -resource:fivegui.glade \ 17 | $(SOURCES) $(LIBS) 18 | 19 | clean: 20 | rm -f *.exe 21 | 22 | -------------------------------------------------------------------------------- /src/dbsearch/Makefile: -------------------------------------------------------------------------------- 1 | 2 | CSC=mcs 3 | 4 | SOURCES=DBSearch.cs \ 5 | GBSearch.cs GBOperators.cs GBBoard.cs GBThreat.cs \ 6 | ../newaiplayer/PrettyPrint.cs 7 | 8 | LIBS= 9 | 10 | 11 | all: GBSearchTest.exe GBThreatTest.exe 12 | 13 | DLPSearchTest.exe: DLPSearch.cs $(SOURCES) 14 | $(CSC) $(CSCFLAGS) -out:DLPSearchTest.exe -main:DLPSearchTest DLPSearch.cs \ 15 | DBSearch.cs $(LIBS) 16 | 17 | GBSearchTest.exe: GBSearch.cs $(SOURCES) 18 | $(CSC) $(CSCFLAGS) -out:GBSearchTest.exe -main:GBSearchModule \ 19 | $(SOURCES) $(LIBS) 20 | 21 | GBThreatTest.exe: $(SOURCES) 22 | $(CSC) $(CSCFLAGS) -out:GBThreatTest.exe -main:GBThreatSearch \ 23 | $(SOURCES) $(LIBS) 24 | 25 | clean: 26 | rm -f *.exe 27 | 28 | -------------------------------------------------------------------------------- /src/newaiplayer/Coordinate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | public struct Coordinate : IComparable 3 | { 4 | public readonly int X; 5 | public readonly int Y; 6 | public int Val; 7 | 8 | public Coordinate(int x, int y) 9 | { 10 | X = x; 11 | Y = y; 12 | Val = StatValEvaluator.UNDEF; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return "abcdefghijklmnopqrstuvwxyz"[X] + "/" + Y; 18 | } 19 | 20 | // Only coordinate based 21 | public override bool Equals (object o2) 22 | { 23 | if (o2 == null) 24 | return (false); 25 | 26 | Coordinate c2 = (Coordinate) o2; 27 | 28 | if (X == c2.X && Y == c2.Y) 29 | return (true); 30 | 31 | return (false); 32 | } 33 | 34 | public int CompareTo(object obj) 35 | { 36 | Coordinate c = (Coordinate)obj; 37 | return this.Val < c.Val?-1: this.Val==c.Val?0:1; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/newaiplayer/Makefile: -------------------------------------------------------------------------------- 1 | SOURCES=Communicator.cs Evaluator.cs \ 2 | Player.cs GoBang.cs Coordinate.cs NewAiPlayer.cs \ 3 | StatValEvaluator.cs Threat.cs ThreatSearcher.cs \ 4 | InterestingFieldAgent.cs PrettyPrint.cs \ 5 | ../dbsearch/DBSearch.cs \ 6 | ../dbsearch/GBSearch.cs ../dbsearch/GBOperators.cs \ 7 | ../dbsearch/GBBoard.cs ../dbsearch/GBThreat.cs 8 | 9 | FLAGS=-define:STATS 10 | 11 | all: bin 12 | 13 | bin: GoBang.exe 14 | 15 | GoBang.exe: ${SOURCES} 16 | mcs ${FLAGS} ${SOURCES} -out:GoBang.exe -main:GoBang 17 | 18 | TestPrettyPrint.exe: PrettyPrint.cs 19 | mcs /out:TestPrettyPrint.exe PrettyPrint.cs 20 | 21 | TestFirstSearcher.exe: ${SOURCES} TestFirstSearcher.cs 22 | mcs ${FLAGS} ${SOURCES} TestFirstSearcher.cs -out:TestFirstSearcher.exe -main:TestFirstSearcher 23 | 24 | doc: ${SOURCES} 25 | doxygen doxyfile 26 | 27 | clean: 28 | rm -rf *exe doc 29 | -------------------------------------------------------------------------------- /doc/ai-inc.tex: -------------------------------------------------------------------------------- 1 | 2 | \usepackage[latin1]{inputenc} 3 | 4 | \pagestyle{empty} 5 | 6 | \newcommand{\N}{{\mathbb N}} 7 | \newcommand{\Z}{{\mathbb Z}} 8 | \newcommand{\R}{{\mathbb R}} 9 | \newcommand{\Q}{{\mathbb Q}} 10 | \newcommand{\C}{{\mathbb C}} 11 | 12 | \renewcommand{\theequation}{\arabic{equation}} 13 | %\renewcommand{\labelenumi}{(\alph{enumi})} 14 | 15 | \newenvironment{solution}[1]{% 16 | \vspace{0.5cm} 17 | \begin{sloppypar}\noindent {\bf #1:}% 18 | {\nopagebreak\hspace*{\fill}} 19 | \end{sloppypar}\medskip} 20 | 21 | \newcommand{\blattheader}[2]{{\bf \noindent 22 | Marco Kunze ({\tt makunze@cs.tu-berlin.de})\hfill #2\\ 23 | Sebastian Nowozin ({\tt nowozin@cs.tu-berlin.de})\\[0.4cm]} 24 | 25 | \begin{center}{{\Large \bf #1}} 26 | \end{center} 27 | 28 | \vspace*{0.3cm} 29 | \noindent} 30 | 31 | %\parindent=0cm 32 | 33 | \usepackage{fancyhdr,lastpage} 34 | \usepackage{a4wide,latexsym,amssymb,amsfonts,verbatim} 35 | %\usepackage[dvips]{graphicx} 36 | \usepackage{color} 37 | \pagestyle{fancy} 38 | \cfoot{ \thepage ~/ \pageref{LastPage} } 39 | \renewcommand{\headrulewidth}{0pt} 40 | \renewcommand{\footrulewidth}{0pt} 41 | 42 | -------------------------------------------------------------------------------- /src/win-project/win-project.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 8.00 2 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "win-project", "win-project.csproj", "{D7B027C9-D4EB-46AE-8CCC-86F9ED928E0F}" 3 | ProjectSection(ProjectDependencies) = postProject 4 | EndProjectSection 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfiguration) = preSolution 8 | Debug = Debug 9 | Release = Release 10 | test = test 11 | EndGlobalSection 12 | GlobalSection(ProjectConfiguration) = postSolution 13 | {D7B027C9-D4EB-46AE-8CCC-86F9ED928E0F}.Debug.ActiveCfg = Debug|.NET 14 | {D7B027C9-D4EB-46AE-8CCC-86F9ED928E0F}.Debug.Build.0 = Debug|.NET 15 | {D7B027C9-D4EB-46AE-8CCC-86F9ED928E0F}.Release.ActiveCfg = Release|.NET 16 | {D7B027C9-D4EB-46AE-8CCC-86F9ED928E0F}.Release.Build.0 = Release|.NET 17 | {D7B027C9-D4EB-46AE-8CCC-86F9ED928E0F}.test.ActiveCfg = test|.NET 18 | {D7B027C9-D4EB-46AE-8CCC-86F9ED928E0F}.test.Build.0 = test|.NET 19 | EndGlobalSection 20 | GlobalSection(ExtensibilityGlobals) = postSolution 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityAddIns) = postSolution 23 | EndGlobalSection 24 | EndGlobal 25 | -------------------------------------------------------------------------------- /src/win-project/History.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | namespace win_project 5 | { 6 | 7 | public class History : ArrayList 8 | { 9 | private int active = -1; 10 | public int Active 11 | { 12 | get 13 | { 14 | return active; 15 | } 16 | set 17 | { 18 | if (0 <= value && value < this.Count) active = value; 19 | } 20 | } 21 | public History() 22 | { 23 | } 24 | 25 | public void Reset() 26 | { 27 | this.Clear(); 28 | active = -1; 29 | } 30 | 31 | public void AddEntry(HistoryEntry e) 32 | { 33 | this.Add(e); 34 | active++; 35 | } 36 | } 37 | 38 | public class HistoryEntry 39 | { 40 | /** Field where the move has been made 41 | */ 42 | public Coordinate field; 43 | 44 | /** 1 for human or -1 for pc 45 | */ 46 | public int owner; 47 | 48 | /** Time needed for the move. 49 | */ 50 | public TimeSpan time; 51 | 52 | public HistoryEntry(Coordinate ff, int oo, TimeSpan tt) 53 | { 54 | field = ff; 55 | owner = oo; 56 | time = tt; 57 | } 58 | 59 | public override string ToString() 60 | { 61 | return (owner == 1?"You: ":"Ai: ") + field + ", " 62 | + time.Seconds + "." + time.Milliseconds + "s"; 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/win-project/Makefile: -------------------------------------------------------------------------------- 1 | 2 | MCS=mcs 3 | 4 | MONO-PROJECT_EXE=mono-project.exe 5 | MONO-PROJECT_SRC=AssemblyInfo.cs \ 6 | MyPicBox.cs \ 7 | WinGui.cs \ 8 | History.cs \ 9 | ../dbsearch/DBSearch.cs \ 10 | ../dbsearch/GBBoard.cs \ 11 | ../dbsearch/GBOperators.cs \ 12 | ../dbsearch/GBSearch.cs \ 13 | ../dbsearch/GBThreat.cs \ 14 | ../newaiplayer/Communicator.cs \ 15 | ../newaiplayer/Coordinate.cs \ 16 | ../newaiplayer/Evaluator.cs \ 17 | ../newaiplayer/InterestingFieldAgent.cs \ 18 | ../newaiplayer/NewAiPlayer.cs \ 19 | ../newaiplayer/Player.cs \ 20 | ../newaiplayer/PrettyPrint.cs \ 21 | ../newaiplayer/Threat.cs \ 22 | ../newaiplayer/ThreatSearcher.cs \ 23 | ../newaiplayer/StatValEvaluator.cs 24 | 25 | #MONO-PROJECT_RES=-resource:WinGui.resx,win_project.WinGui.resx 26 | 27 | #FLAGS=-define:STATS,PRETTY,MONO 28 | 29 | $(MONO-PROJECT_EXE): $(MONO-PROJECT_SRC) 30 | $(MCS) ${FLAGS} $(MCSFLAGS) $(LIBS) -main:win_project.Form1 \ 31 | -r:System.dll -r:System.Data.dll -r:System.Drawing.dll \ 32 | -r:System.Windows.Forms.dll -r:System.Xml.dll \ 33 | -target:winexe -out:$(MONO-PROJECT_EXE) \ 34 | $(MONO-PROJECT_RES) $(MONO-PROJECT_SRC) 35 | 36 | 37 | # common targets 38 | 39 | all: $(MONO-PROJECT_EXE) 40 | 41 | # project names as targets 42 | 43 | mono-project: $(MONO-PROJECT_EXE) 44 | 45 | clean: 46 | rm -f mono-project.exe 47 | -------------------------------------------------------------------------------- /src/newaiplayer/SortedList.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | public class SortedLimitedList : ArrayList 4 | { 5 | private SortedLimitedList () 6 | { 7 | } 8 | 9 | int max; 10 | 11 | public SortedLimitedList (int maxElements) 12 | : base (maxElements) 13 | { 14 | max = maxElements; 15 | } 16 | 17 | /** Add a new object to the list. 18 | * 19 | * The object to be added must implement the IComparable element and must 20 | * be of the same type as all the other elements in the list. 21 | * 22 | * @param obj The object to be added. 23 | * 24 | * @returns The position the object was inserted. 25 | */ 26 | public override int Add (object obj) 27 | { 28 | for (int pos = Count ; pos > 0 && 29 | ((IComparable) base[pos-1]).CompareTo (obj) >= 0 ; --pos) 30 | { 31 | if (pos < max) 32 | Set (pos, base[pos-1]); 33 | 34 | pos -= 1; 35 | } 36 | 37 | if (pos < max) { 38 | Set (pos, obj); 39 | } else { 40 | pos = -1; 41 | } 42 | 43 | return pos; 44 | } 45 | 46 | /** Set an element position to an object reference. 47 | * 48 | * @param idx The index of the element to be set. 49 | * @param obj The object reference to be stored at the element. 50 | */ 51 | private void Set (int idx, object obj) 52 | { 53 | if (idx < Count) { 54 | base[idx] = obj; 55 | } else if (idx == Count) { 56 | base.Add (obj); 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/newaiplayer/Threat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Text; 4 | 5 | public class ThreatList : ArrayList, ICloneable 6 | { 7 | public override object Clone () 8 | { 9 | ThreatList tl = new ThreatList (); 10 | 11 | for (int n = 0 ; n < Count ; ++n) 12 | tl.Add (((Threat) this[n]).Clone ()); 13 | 14 | return (tl); 15 | } 16 | } 17 | 18 | public class Threat : ICloneable 19 | { 20 | /** Field that caused the threat 21 | */ 22 | public readonly Coordinate cause; 23 | 24 | /** List of the fields which can block the threat. 25 | * ArrayList 26 | */ 27 | public readonly ArrayList fields; 28 | 29 | /** Category of the threat 30 | */ 31 | public readonly int category; 32 | 33 | /** Shows special creator situation, where a cat1-threat can be built out of 34 | * nothing. 35 | */ 36 | public readonly bool create; 37 | 38 | public Threat(Coordinate cause, int cc, ArrayList ff, bool create) 39 | { 40 | this.cause = cause; 41 | category = cc; 42 | fields = ff; 43 | this.create = create; 44 | } 45 | public Threat(Coordinate cause, int cc, ArrayList ff) : 46 | this(cause, cc, ff, false) {} 47 | 48 | public object Clone() 49 | { 50 | Threat res = new Threat(cause, category, (ArrayList)fields.Clone(), create); 51 | return res; 52 | } 53 | 54 | public override string ToString() 55 | { 56 | StringBuilder sb = new StringBuilder(); 57 | sb.AppendFormat("cause: {0}\n", cause); 58 | sb.AppendFormat("cat: {0}\n", category); 59 | foreach (Coordinate c in fields) 60 | sb.AppendFormat("Defense Field: {0}\n", c); 61 | return sb.ToString(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/newaiplayer/ComparerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | public class ComparerTest 5 | { 6 | public static void Main() 7 | { 8 | int[,] board = new int[,]{ 9 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 11 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 12 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 13 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 14 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 15 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 16 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, 17 | {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, 18 | {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, 19 | {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 20 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0}, 21 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 22 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 23 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 24 | }; 25 | TunnelSearcher searcher = new FirstSearcher(); 26 | InterestingFieldAgent ifa = new InterestingFieldAgent(searcher, 15); 27 | FirstEvaluator eval = new FirstEvaluator(ifa); 28 | StatValComparer comp = new StatValComparer(eval); 29 | 30 | Coordinate c1 = new Coordinate(7, 7); 31 | Coordinate c2 = new Coordinate(10, 10); 32 | comp.Board = board; 33 | comp.Attacker = 1; 34 | 35 | ArrayList a1 = new ArrayList(); 36 | a1.Add(c1); 37 | a1.Add(c2); 38 | a1.Sort(comp); 39 | 40 | foreach (Coordinate c in a1) 41 | Console.WriteLine("Coordinate: {0}, Val: {1}", c, c.Val); 42 | 43 | 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/newaiplayer/Communicator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | 4 | class Communicator : Player { 5 | 6 | int port; 7 | string color; 8 | int size; 9 | 10 | System.IO.StreamReader inStream; 11 | System.IO.StreamWriter outStream; 12 | 13 | public int AskSize() 14 | { 15 | return size; 16 | } 17 | public void SetSize(int size) 18 | { 19 | } 20 | public string AskColor() 21 | { 22 | return color; 23 | } 24 | public void SetColor(string color) 25 | { 26 | } 27 | 28 | 29 | public Communicator(string host, int port) { 30 | Console.WriteLine("Creating Communicator"); 31 | this.port = port; 32 | 33 | TcpClient socket = null; 34 | 35 | try { 36 | socket = new TcpClient(host, port); 37 | } catch { 38 | Console.WriteLine("Failed to connect"); 39 | } 40 | 41 | inStream = new System.IO.StreamReader(socket.GetStream()); 42 | outStream = new System.IO.StreamWriter(socket.GetStream()); 43 | 44 | Console.WriteLine("Hello: {0}", inStream.ReadLine()); 45 | size = Int32.Parse(inStream.ReadLine()); 46 | Console.WriteLine("Board size: {0}", size); 47 | color = inStream.ReadLine(); 48 | Console.WriteLine("Our color: {0}", color); 49 | } 50 | 51 | public void RegOppMove(Coordinate move) { 52 | Console.WriteLine("Sending move: " + move.X + "/" + move.Y); 53 | outStream.WriteLine("" + move.X + "/" + move.Y); 54 | outStream.Flush(); 55 | } 56 | 57 | public Coordinate GetMove() { 58 | try 59 | { 60 | string input = inStream.ReadLine(); 61 | if (input == "You lost!") 62 | { 63 | Console.WriteLine(input); 64 | return new Coordinate(-1, -1); 65 | } 66 | if (input == "You win!") 67 | { 68 | Console.WriteLine(input); 69 | return new Coordinate(-1, -1); 70 | } 71 | int x = int.Parse(input.Substring(0, input.IndexOf("/"))); 72 | int y = int.Parse(input.Substring(input.IndexOf("/")+1)); 73 | 74 | Coordinate returnMove = new Coordinate(x, y); 75 | return returnMove; 76 | } 77 | catch (Exception) {return new Coordinate(-1, -1);} 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/Gomocup/AIwrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | class GomocupEngine : GomocupInterface 5 | { 6 | NewAiPlayer ai; 7 | TextWriter output; 8 | 9 | public override string brain_about 10 | { 11 | get 12 | { 13 | return "name=\"Stahlfaust\", author=\"Marco Kunze, Sebastian Nowozin\", version=\"1.0\", country=\"Germany\""; 14 | } 15 | } 16 | 17 | public override void brain_init() 18 | { 19 | if (width < 5 || width > 30 || height < 5 || width != height) 20 | { 21 | Console.WriteLine("ERROR size of the board"); 22 | return; 23 | } 24 | brain_restart(); 25 | } 26 | 27 | public override void brain_restart() 28 | { 29 | output = Console.Out; 30 | Console.SetOut(TextWriter.Null); 31 | ai = new NewAiPlayer(); 32 | ai.SetSize(width); 33 | Console.SetOut(output); 34 | Console.WriteLine("OK"); 35 | } 36 | 37 | public override void brain_my(int x, int y) 38 | { 39 | Console.SetOut(TextWriter.Null); 40 | ai.RegOwnMove(new Coordinate(x, y)); 41 | Console.SetOut(output); 42 | } 43 | 44 | public override void brain_opponents(int x, int y) 45 | { 46 | Console.SetOut(TextWriter.Null); 47 | ai.RegOppMove(new Coordinate(x, y)); 48 | Console.SetOut(output); 49 | } 50 | 51 | public override void brain_block(int x, int y) 52 | { 53 | Console.WriteLine("ERROR brain_block not implemented"); 54 | } 55 | 56 | public override int brain_takeback(int x, int y) 57 | { 58 | return 1; 59 | } 60 | 61 | public override void brain_turn() 62 | { 63 | int limit = Math.Min(info_timeout_turn, info_time_left / 10) / 1000; 64 | ai.dbtimelimit = ai.ddbtimelimit = limit / 4; 65 | ai.abtimelimit = Math.Max(1, limit - ai.dbtimelimit - ai.ddbtimelimit - 1); 66 | 67 | Console.SetOut(TextWriter.Null); 68 | Coordinate coord = ai.GetMove(); 69 | Console.SetOut(output); 70 | do_mymove(coord.X, coord.Y); 71 | } 72 | 73 | public override void brain_end() 74 | { 75 | } 76 | 77 | public override void brain_eval(int x, int y) 78 | { 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/gtksharp-gui/MoveRecording.cs: -------------------------------------------------------------------------------- 1 | 2 | /* MoveRecording.cs - Record and replay moves functionality 3 | * 4 | * Marco Kunze, Sebastian Nowozin 5 | */ 6 | 7 | using System; 8 | using System.IO; 9 | using System.Xml; 10 | using System.Xml.Serialization; 11 | using System.Collections; 12 | 13 | public class MoveRecording 14 | { 15 | public static void Main (string[] args) 16 | { 17 | MoveList ml = new MoveList (); 18 | 19 | ml.Add (1, 2); 20 | ml.Add (3, 4); 21 | WriteMoveList ("test.moves", ml); 22 | 23 | MoveList ml2 = ReadMoveList ("test.moves"); 24 | foreach (MoveList.Move move in ml2.moves) 25 | Console.WriteLine ("move: {0}, {1}", move.x, move.y); 26 | } 27 | 28 | public static MoveList ReadMoveList (string filename) 29 | { 30 | XmlSerializer xs = new XmlSerializer (typeof (MoveList)); 31 | TextReader reader = new StreamReader (filename); 32 | 33 | MoveList ml = null; 34 | try { 35 | ml = (MoveList) xs.Deserialize (reader); 36 | } catch (Exception ex) { 37 | Console.Error.WriteLine ("Error deserializing \"{0}\"", filename); 38 | } 39 | reader.Close (); 40 | 41 | return (ml); 42 | } 43 | 44 | public static void WriteMoveList (string filename, MoveList ml) 45 | { 46 | XmlSerializer xs = new XmlSerializer (typeof (MoveList)); 47 | TextWriter writer = new StreamWriter (filename); 48 | xs.Serialize (writer, ml); 49 | writer.Close (); 50 | } 51 | } 52 | 53 | [Serializable] 54 | public class MoveList 55 | { 56 | public Move[] moves = null; 57 | 58 | public Move this[int idx] { 59 | get { 60 | return (moves[idx]); 61 | } 62 | } 63 | 64 | public int Count { 65 | get { 66 | if (moves == null) 67 | return (0); 68 | 69 | return (moves.Length); 70 | } 71 | } 72 | 73 | public void Add (int x, int y) 74 | { 75 | Move m = new Move (); 76 | m.x = x; 77 | m.y = y; 78 | 79 | ArrayList mla; 80 | if (moves != null) 81 | mla = new ArrayList (moves); 82 | else 83 | mla = new ArrayList (); 84 | mla.Add (m); 85 | moves = (Move[]) mla.ToArray (typeof (Move)); 86 | } 87 | 88 | [Serializable] 89 | public class Move { 90 | public int x, y; 91 | } 92 | } 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/newaiplayer/TestFirstSearcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | 4 | public class TestFirstSearcher 5 | { 6 | private static void printBoard(int[,] board) 7 | { 8 | Console.WriteLine("Board:"); 9 | for (int y = 0; y < board.GetLength(0); ++y) { 10 | for (int x = 0; x < board.GetLength(1); ++x) { 11 | Console.Write("{0} ", board[x, y] == -1?"O":board[x, y] == 0?".":"X"); 12 | } 13 | Console.WriteLine(); 14 | } 15 | } 16 | 17 | private static void printBoard(int[,] board, Coordinate move) 18 | { 19 | Console.WriteLine("Board:"); 20 | for (int y = 0; y < board.GetLength(0); ++y) { 21 | for (int x = 0; x < board.GetLength(1); ++x) { 22 | if (move.X == x && move.Y == y) Console.Write("x "); 23 | else Console.Write("{0} ", board[x, y] == -1?"O":board[x, y] == 0?".":"X"); 24 | } 25 | Console.WriteLine(); 26 | } 27 | } 28 | 29 | public static int[,] Flip(int[,] board) 30 | { 31 | int[,] res = new int[board.GetLength(1), board.GetLength(0)]; 32 | for (int y = 0; y < board.GetLength(0); ++y) { 33 | for (int x = 0; x < board.GetLength(1); ++x) { 34 | res[y, x] = board[x, y]; 35 | } 36 | } 37 | return res; 38 | } 39 | public static void Main() 40 | { 41 | int[,] board = new int[,]{ 42 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 43 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 44 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 45 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 46 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 47 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 48 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 49 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 50 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 51 | {0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0}, 52 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 53 | {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 54 | {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 55 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 56 | {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 57 | }; 58 | 59 | board = Flip(board); 60 | 61 | ThreatSearcher searcher = new ThreatSearcher(); 62 | 63 | ThreatList threats = searcher.investigate(board, new Coordinate(0, 13), 1); 64 | 65 | foreach (Threat t in threats) 66 | Console.WriteLine(t.ToString()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/win-project/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | // 9 | [assembly: AssemblyTitle("")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("")] 14 | [assembly: AssemblyCopyright("")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // 19 | // Version information for an assembly consists of the following four values: 20 | // 21 | // Major Version 22 | // Minor Version 23 | // Build Number 24 | // Revision 25 | // 26 | // You can specify all the values or you can default the Revision and Build Numbers 27 | // by using the '*' as shown below: 28 | 29 | [assembly: AssemblyVersion("1.0.*")] 30 | 31 | // 32 | // In order to sign your assembly you must specify a key to use. Refer to the 33 | // Microsoft .NET Framework documentation for more information on assembly signing. 34 | // 35 | // Use the attributes below to control which key is used for signing. 36 | // 37 | // Notes: 38 | // (*) If no key is specified, the assembly is not signed. 39 | // (*) KeyName refers to a key that has been installed in the Crypto Service 40 | // Provider (CSP) on your machine. KeyFile refers to a file which contains 41 | // a key. 42 | // (*) If the KeyFile and the KeyName values are both specified, the 43 | // following processing occurs: 44 | // (1) If the KeyName can be found in the CSP, that key is used. 45 | // (2) If the KeyName does not exist and the KeyFile does exist, the key 46 | // in the KeyFile is installed into the CSP and used. 47 | // (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. 48 | // When specifying the KeyFile, the location of the KeyFile should be 49 | // relative to the project output directory which is 50 | // %Project Directory%\obj\. For example, if your KeyFile is 51 | // located in the project directory, you would specify the AssemblyKeyFile 52 | // attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] 53 | // (*) Delay Signing is an advanced option - see the Microsoft .NET Framework 54 | // documentation for more information on this. 55 | // 56 | [assembly: AssemblyDelaySign(false)] 57 | [assembly: AssemblyKeyFile("")] 58 | [assembly: AssemblyKeyName("")] 59 | -------------------------------------------------------------------------------- /src/win-project/win-project.csproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 37 | 53 | 54 | 55 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/newaiplayer/PrettyPrint.cs: -------------------------------------------------------------------------------- 1 | 2 | // You need graphviz 1.11 or higher (Debian is at 1.13 and ok) 3 | // 4 | // dot -Tps -o test.ps test.dot 5 | 6 | using System; 7 | using System.IO; 8 | using System.Text; 9 | 10 | 11 | public class 12 | PrettyPrint 13 | { 14 | // TODO: maybe you have to flip this colors 15 | public static string OneColor = "#ffffff"; 16 | public static string MOneColor = "#000000"; 17 | public static string FreeColor = "#909090"; 18 | 19 | // TODO: maybe print the moves with a number so you can see the order they 20 | // were selected. 21 | public static void PrintBoard (TextWriter wr, string id, int[,] board, string infostring) 22 | { 23 | wr.WriteLine ("{0} [shape=plaintext", id); 24 | wr.WriteLine (" label = <
{1}
{0}
>];", PrintBoardContent (board), infostring); 25 | } 26 | 27 | public static void PrintBoard (TextWriter wr, string id, int[,] board) 28 | { 29 | wr.WriteLine ("{0} [shape=plaintext", id); 30 | wr.WriteLine (" label = <{0}>];", PrintBoardContent (board)); 31 | } 32 | 33 | public static string PrintBoardContent (int[,] board) 34 | { 35 | return (PrintBoardContent (board, null)); 36 | } 37 | 38 | public static string PrintBoardContent (int[,] board, string bgcolor) 39 | { 40 | return (PrintBoardContent (board, bgcolor, true)); 41 | } 42 | 43 | public static string PrintBoardContent (int[,] board, string bgcolor, 44 | bool printTableEnd) 45 | { 46 | StringBuilder wr = new StringBuilder (); 47 | 48 | if (bgcolor == null) { 49 | wr.Append (""); 50 | } else { 51 | wr.AppendFormat ("
", bgcolor); 52 | } 53 | 54 | wr.Append (" "); 55 | for (int x = 0 ; x < board.GetLength(1) ; ++x) 56 | wr.AppendFormat ("", "abcdefghijklmnopqrstuvwxyz"[x]); 57 | wr.Append ("\n"); 58 | 59 | for (int y = 0 ; y < board.GetLength(0) ; ++y) { 60 | wr.Append (" "); 61 | 62 | wr.AppendFormat ("", y); 63 | for (int x = 0 ; x < board.GetLength(1) ; ++x) { 64 | wr.AppendFormat ("", 65 | board[y,x] == 1 ? OneColor : 66 | (board[y,x] == -1 ? MOneColor : FreeColor)); 67 | } 68 | wr.Append ("\n"); 69 | wr.Append (" \n"); 70 | } 71 | if (printTableEnd) 72 | wr.Append ("
{0}
{0}+
\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 | ![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.](stahlfaust.png "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.") 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 | 20 | 22 | 24 | 28 | 32 | 33 | 35 | 39 | 43 | 44 | 52 | 59 | 60 | 74 | 79 | 82 | 87 | 92 | 96 | 100 | 101 | 104 | 109 | 114 | 118 | 122 | 123 | 128 | 130 | 132 | 135 | Clipart by Nicu Buculei - smiley111 137 | 139 | 141 | Nicu Buculei 143 | 144 | 145 | 148 | 151 | image/svg+xml 153 | 154 | 157 | 160 | 163 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/gtksharp-gui/smiley111.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 20 | 22 | 24 | 28 | 32 | 33 | 35 | 39 | 43 | 44 | 52 | 59 | 60 | 74 | 79 | 82 | 88 | 93 | 97 | 101 | 102 | 105 | 111 | 116 | 120 | 124 | 125 | 130 | 132 | 134 | 137 | Clipart by Nicu Buculei - smiley111 139 | 141 | 143 | Nicu Buculei 145 | 146 | 147 | 150 | 153 | image/svg+xml 155 | 156 | 159 | 162 | 165 | 168 | 169 | 170 | 171 | 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 | --------------------------------------------------------------------------------