From 79a2fa6eba1648bf2beaaf534d1fa3247c0d6a01 Mon Sep 17 00:00:00 2001 From: InigoGutierrez Date: Wed, 1 Jun 2022 20:14:15 +0200 Subject: Some redesigning. --- doc/Makefile | 4 +- doc/diagrams/analysisClasses.puml | 17 +-- doc/diagrams/planificationWorkPlanGame.puml | 6 + doc/diagrams/useCase_useAsBackend.puml | 23 ++++ doc/tex/biber.bib | 60 ++++++-- doc/tex/planification.tex | 51 ++++--- doc/tex/systemAnalysis.tex | 204 ++++++++++++++++++++++++++-- doc/tex/systemDesign.tex | 2 + doc/tex/tfg.tex | 22 ++- imago/engine/monteCarlo.py | 28 +++- imago/gameLogic/gameBoard.py | 14 +- imago/gameLogic/gameState.py | 6 +- imago/gameLogic/gameTree.py | 8 +- tests/test_monteCarlo.py | 4 +- tests/test_parseHelpers.py | 7 + 15 files changed, 368 insertions(+), 88 deletions(-) create mode 100644 doc/diagrams/useCase_useAsBackend.puml create mode 100644 doc/tex/systemDesign.tex diff --git a/doc/Makefile b/doc/Makefile index a631921..4153756 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -3,8 +3,8 @@ docName = tfg outputFolder = out -texFiles = tex/tfg.tex tex/introduction.tex tex/planification.tex tex/interface.tex tex/implementation.tex tex/systemAnalysis.tex tex/biber.bib -diagramImgs = diagrams/gameRepresentation.png diagrams/gtpEngine.png diagrams/modules.png diagrams/planificationWorkPlanEngine.png diagrams/planificationWorkPlanGame.png diagrams/sgfModule.png diagrams/useCases.png diagrams/analysisClasses.png +texFiles = tex/tfg.tex tex/introduction.tex tex/planification.tex tex/interface.tex tex/implementation.tex tex/systemAnalysis.tex tex/systemDesign.tex tex/biber.bib +diagramImgs = diagrams/gameRepresentation.png diagrams/gtpEngine.png diagrams/modules.png diagrams/planificationWorkPlanEngine.png diagrams/planificationWorkPlanGame.png diagrams/sgfModule.png diagrams/useCases.png diagrams/analysisClasses.png diagrams/useCase_useAsBackend.png all: $(docName).pdf diff --git a/doc/diagrams/analysisClasses.puml b/doc/diagrams/analysisClasses.puml index 6adc849..4a286f7 100644 --- a/doc/diagrams/analysisClasses.puml +++ b/doc/diagrams/analysisClasses.puml @@ -5,27 +5,28 @@ package GameModule { class GameIO class GameState - class GameTree + class GameBoard class GameMove } GameIO -> GameState -GameState -> GameTree -GameTree -> GameMove +GameState -> GameMove +GameMove -> GameBoard package EngineModule { class EngineIO class EngineLogic interface DecisionAlgorithm - class DecisionAlgorithm2 - class DecisionAlgorithm1 + class MonteCarloTreeSearch + class OtherDecisionAlgorithm } EngineIO --> EngineLogic EngineLogic -> DecisionAlgorithm -DecisionAlgorithm <|.. DecisionAlgorithm1 -DecisionAlgorithm <|.. DecisionAlgorithm2 +DecisionAlgorithm <|.. MonteCarloTreeSearch +DecisionAlgorithm <|.. OtherDecisionAlgorithm -EngineLogic --> GameTree +MonteCarloTreeSearch --> GameMove +OtherDecisionAlgorithm --> GameMove @enduml diff --git a/doc/diagrams/planificationWorkPlanGame.puml b/doc/diagrams/planificationWorkPlanGame.puml index 427564a..42b0821 100644 --- a/doc/diagrams/planificationWorkPlanGame.puml +++ b/doc/diagrams/planificationWorkPlanGame.puml @@ -8,10 +8,16 @@ Sunday are closed Project starts 2020-11-02 +-- Preliminary investigation -- +[Previous works investigation] as [PWI] lasts 7 days +[Engines investigation] as [EI] lasts 7 days + -- Game Implementation -- [Domain modelling] as [DM] lasts 6 days [Domain implementation] as [DI] lasts 30 days [Domain testing] as [DT] lasts 30 days + +[PWI] -> [DM] [DM] -> [DI] [DM] -> [DT] diff --git a/doc/diagrams/useCase_useAsBackend.puml b/doc/diagrams/useCase_useAsBackend.puml new file mode 100644 index 0000000..3f1cece --- /dev/null +++ b/doc/diagrams/useCase_useAsBackend.puml @@ -0,0 +1,23 @@ +@startuml + +!include skinparams.puml + +actor "GUI Program" as program + +boundary "Set board state" as setState +control "State set to represent the needed board" as setStateEngine +entity "Board state" as state +boundary "Move is asked for" as ask +control "Engine thinks next move" as think +boundary "Move is suggested" as suggestion + +program -> setState +setState -> setStateEngine +setStateEngine -> state +state -> ask +program -> ask +ask -> think +think -> suggestion +program -> suggestion + +@enduml diff --git a/doc/tex/biber.bib b/doc/tex/biber.bib index ddebe23..ef09d98 100644 --- a/doc/tex/biber.bib +++ b/doc/tex/biber.bib @@ -1,11 +1,3 @@ -@online{sl_go, - title = {Go}, - organization = {Sensei's Library}, - date = {2019}, - urldate = {2021}, - url = {https://senseis.xmp.net/?go} -} - @article{natureAlphaGo2016, author = {David Silver and Aja Huang and Chris J. Maddison and Arthur Guez and Laurent Sifre and George van den Driessche and Julian Schrittwieser and Ioannis Antonoglou and Veda Panneershelvam and Marc Lanctot and Sander Dieleman and Dominik Grewe and John Nham and Nal Kalchbrenner and Ilya Sutskever and Timothy Lillicrap and Madeleine Leach and Koray Kavukcuoglu and Thore Graepel and Demis Hassabis}, title = {Mastering the game of Go with deep neural networks and tree search}, @@ -14,9 +6,61 @@ url = {https://www.nature.com/articles/nature16961} } +@online{gtp, + author = {Gunnar Farnebäck}, + title = {GTP --- Go Text Protocol}, + date = {2002}, + urldate = {2021}, + url = {https://www.lysator.liu.se/~gunnar/gtp} +} + +@online{katago, + author = {David J Wu ("lightvector")}, + title = {KataGo}, + date = {2021}, + urldate = {2021}, + url = {https://github.com/lightvector/KataGo} +} + +@online{gnugo, + title = {GNU Go}, + organization = {Free Software Foundation}, + date = {2019}, + urldate = {2021}, + url = {https://www.gnu.org/software/gnugo} +} + +@online{sabaki, + author = {Yichuan Shen}, + title = {Sabaki --- An elegant Go board and SGF editor for a more civilized age.}, + date = {2019}, + urldate = {2021}, + url = {https://sabaki.yichuanshen.de} +} + +@online{sl_go, + title = {Go}, + organization = {Sensei's Library}, + date = {2019}, + urldate = {2021}, + url = {https://senseis.xmp.net/?go} +} + @online{plantillaRedondo, author = {J. M. Redondo}, title = {Documentos-modelo para Trabajos de Fin de Grado/Master de la Escuela de Informática de Oviedo}, date = {2019-06-17}, url = {https://www.researchgate.net/publication/327882831_Plantilla_de_Proyectos_de_Fin_de_Carrera_de_la_Escuela_de_Informatica_de_Oviedo} } + +@online{python_unittest, + title = {unittest --- Unit testing framework}, + urldate = {2021}, + url = {https://docs.python.org/3/library/unittest.html} +} + +@online{python_coverage, + title = {Coverage.py}, + urldate = {2021}, + url = {https://coverage.readthedocs.io} +} diff --git a/doc/tex/planification.tex b/doc/tex/planification.tex index 9a95e8b..5c9b253 100644 --- a/doc/tex/planification.tex +++ b/doc/tex/planification.tex @@ -57,17 +57,17 @@ the final version of the project. \subsection{Logistics} The project will be developed by Íñigo Gutiérrez Fernández, student of the -Computer Software Engineering at the University of Oviedo, with supervision from -Vicente García Díaz, Associate Professor in the Department of Computer Science -at the University of Oviedo. +Computer Software Engineering Degree at the University of Oviedo, with +supervision from Vicente García Díaz, Associate Professor in the Department of +Computer Science at the University of Oviedo. The used material consists of a development and testing machine owned by the -student with specifications stated later on the project duration. +student with specifications stated later on the project plan. \subsection{Work plan} The sole developer will be the student, who is currently working as a Junior -Software Engineer on a 35 hour per week schedule and no university +Software Engineer on a 35 hour per week schedule and with no university responsibilities other than this project. Taking this into account, a sensible initial assumption is that he will be able to work 3 hours a day, Monday to Saturday. Gantt diagrams for the planned working schedule are shown as @@ -96,9 +96,9 @@ Fig.~\ref{fig:planificationWorkPlanEngine}. \paragraph{AlphaGo} -AlphaGo is a Go play and analysis engine developed by DeepMind Technologies, a -company owned by Google. It revolutionized the world of Go in 2015 and 2016 when -it respectively became the first AI to win against a professional Go player and +A Go play and analysis engine developed by DeepMind Technologies, a company +owned by Google. It revolutionized the world of Go in 2015 and 2016 when it +respectively became the first AI to win against a professional Go player and then won against Lee Sedol, a Korean player of the highest professional rank and one of the strongest players in the world at the time. Its source code is closed, but a paper \parencite{natureAlphaGo2016} written by the team and @@ -108,28 +108,27 @@ https://storage.googleapis.com/deepmind-media/alphago/AlphaGoNaturePaper.pdf. The unprecedented success of AlphaGo served as inspiration for many AI projects, including this one. -\paragraph{KataGo} +\paragraph{KataGo~\cite{katago}} -KataGo is an open source project based on the AlphaGo paper that also achieved -superhuman strength of play. The availability of its implementation and -documentation presents a great resource for this project. +An open source project based on the AlphaGo paper that also achieved superhuman +strength of play. The availability of its implementation and documentation +presents a great resource for this project. -\paragraph{GnuGo} +\paragraph{GnuGo~\cite{gnugo}} -GnuGo is a software capable of playing Go part of the GNU project. Although not -a strong engine anymore, it is interesting for historic reasons as the free -software engine for which the GTP protocol was first defined. +A software capable of playing Go part of the GNU project. Although not a strong +engine anymore, it is interesting for historic reasons as the free software +engine for which the GTP protocol was first defined. -\subsubsection{GTP} +\subsubsection{GTP~\cite{gtp}} -GTP (\textit{Go Text Protocol}) is a text based protocol for communication with -computer go programs. [ref https://www.lysator.liu.se/~gunnar/gtp/] It is the -protocol used by GNU Go and the more modern and powerful KataGo. By supporting -GTP the engine developed for this project can be used with existing GUIs and -other programs, making it easier to use it with the tools users are already -familiar with. +GTP (\textit{Go Text Protocol}) is a text based protocol for +communication with computer go programs. It is the protocol used by GNU Go and +the more modern and powerful KataGo. By supporting GTP the engine developed for +this project can be used with existing GUIs and other programs, making it easier +to use it with the tools users are already familiar with. -\subsubsection{Sabaki} +\subsubsection{Sabaki~\cite{sabaki}} Sabaki is a go board software compatible with GTP engines. It can serve as a GUI for the engine developed in this project and as an example of the advantages of @@ -145,7 +144,7 @@ choice is Python, for various reasons: \begin{itemize} - \item Has established a reputation on scientific fields and more + \item It has established a reputation on scientific fields and more specifically on AI research and development. \item Interpreters are available for many platforms, which allows the most people possible to access the product. @@ -157,6 +156,6 @@ choice is Python, for various reasons: \subsubsection{Interface} Both the game and the engine will offer a text interface. For the game this -allows for quick human testing. For the engine it is mandated by the engine, +allows for quick human testing. For the engine it is mandated by the protocol, since GTP is a text based protocol for programs using text interfaces. Independent programs compatible with this interface can be used as a GUI. diff --git a/doc/tex/systemAnalysis.tex b/doc/tex/systemAnalysis.tex index 7753018..5868422 100644 --- a/doc/tex/systemAnalysis.tex +++ b/doc/tex/systemAnalysis.tex @@ -5,9 +5,9 @@ \subsection{System Requirements} The requirements for the system are expressed here in a nested list way, each of -them with a textual and numeric reference so they are traceable. The functional -requirements are exposed first, followed by the other kinds of requisites needed -for the system. +them with a textual and numeric reference for them to be traceable. The +functional requirements are exposed first, followed by the other kinds of +requisites needed for the system. \setlist[enumerate,2]{label*=\arabic*.} \setlist[enumerate,3]{label*=\arabic*.} @@ -58,7 +58,47 @@ for the system. \begin{enumerate} - \item Coordinates of the board representing valid moves must be printed. + \item The engine implements the GTP (\textit{Go Text Protocol}) for its + interface. + \begin{enumerate} + \item Commands are read from standard input. + \item Responses are provided via standard output. + \item There exist commands to set up the conditions of the match. + \begin{enumerate} + \item The size of the board can be set. + \item The komi can be set. + \end{enumerate} + \item There exist commands to manipulate the internal representation + of the match. + \begin{enumerate} + \item It is possible to indicate a move being played. + \item It is possible to clear the board. + \end{enumerate} + \item There exists a command to generate a move. + \begin{enumerate} + \item The generated move must be a playable move. + \item Generating a move does not change the internal + representation of the match. + \end{enumerate} + \item There exist commands to ask for information about the engine. + \begin{enumerate} + \item It is possible to ask for the protocol version + implemented. + \item It is possible to ask for the name of the engine. + \item It is possible to ask for the version of the engine. + \item It is possible to ask whether a specific command is + known to the engine. + \item It is possible to ask for a list of the known commands. + \end{enumerate} + \item There exists a command to stop the engine. + \end{enumerate} + + \item The engine can be executed from the command line. + \begin{enumerate} + \item The engine can be executed directly from an interactive shell. + \item The engine can be executed by another program to be used as + backend. + \end{enumerate} \end{enumerate} @@ -121,7 +161,7 @@ for the system. \item It will be possible to pass the maximum time as a launch argument. \item It will be possible to store the maximum time as a setting - in a configuration file + in a configuration file. \end{enumerate} \end{enumerate} @@ -170,22 +210,36 @@ The engine is used as the backend for generating moves for a machine player. \subsection{Subsystems} -\subsubsection{Subsystems description} - There will be two main subsystems. % TODO: Are there really two different subsystems? They look very coupled, since % the engine will use some classes of the game. This section is more suited for % independently run systems which communicate through some common interface. +% ...Or maybe not. From the template: "Subsystems are groupings of packages and +% classes with a common objective. Examples of subsystems are the classes which +% handle the database, classes joining a group of related services..." + +\subsubsection{Game System} The first, called the Game System, will be in charge of storing all the state information regarding a Go match, such as the history of moves, the possible variations, the state of the board at any given time or the current number of captured stones. +This system will include a command-line interface with which to play Go matches +between human players to show and test its capabilities. + +\subsubsection{Engine System} + The second, called the Engine System, will implement the GTP interface and use the Game System to analyze positions and generate moves via decision algorithms. +This system can be directly called to manually set up game states and ask for +moves or can be called by other programs to be used as backend for playing +matches against a computer player. + +%\subsubsection{Interface between subsystems} + \subsection{Class analysis} \subsubsection{Class diagram} @@ -262,7 +316,7 @@ The classes resulting from the analysis phase are shown in \tabitem{Analyzing game states and generating moves.} \\ \midrule \textbf{Proposed attributes} \\ - \textit{Depends on the algorithm.} \\ + \textit{(Depends on the algorithm.)} \\ \midrule \textbf{Proposed methods} \\ \tabitem{\textbf{genmove()}: Gives the coordinates of a move to play.} \\ @@ -321,20 +375,27 @@ The classes resulting from the analysis phase are shown in \begin{tabular}{p{\linewidth}} \toprule - \textbf{GameTree} \\ + \textbf{GameBoard} \\ \midrule \textbf{Description} \\ - Stores the moves and variations in a match. \\ + Stores the state of a board position and handles its logic. \\ \midrule \textbf{Responsibilities} \\ - \tabitem{Store the base node of the tree of moves of a match.} \\ + \tabitem{Store the vertices of a board position.} \\ + \tabitem{Logic related to a board position.} \\ \midrule \textbf{Proposed attributes} \\ - \tabitem{\textbf{GameMove root}: First move of the match (normally a - symbolic move representing the empty board before the actual first move of a - player).} \\ + \tabitem{\textbf{Player[][] board}: An array of the stones on the board.} \\ \midrule \textbf{Proposed methods} \\ + \tabitem{\textbf{getGroupLiberties()}: Returns a set with the empty vertices + adjacent to the group occupying a vertex.} \\ + \tabitem{\textbf{getGroupLibertiesCount()}: Returns the number of liberties + of the group occupying a vertex.} \\ + \tabitem{\textbf{getGroupVertices()}: Returns a set with the vertices of the + group occupying a vertex.} \\ + \tabitem{\textbf{getGroupVerticesCount()}: Returns the number of stones of + the group occupying a vertex.} \\ \bottomrule \end{tabular} @@ -379,3 +440,118 @@ The classes resulting from the analysis phase are shown in \end{tabular} \vspace{\interclassSpace} + +\subsection{Use case analysis and scenarios} + +\indent + +\begin{tabular}{lp{0.7\linewidth}} + \toprule + \multicolumn{2}{c}{\textbf{Play a match (Make a move?)}} \\ + \midrule + \textbf{Preconditions} & The game interface has been started. \\ + \midrule + \textbf{Postconditions} & Description of postconditions \\ + \midrule + \textbf{Actors} & Actors \\ + \midrule + \textbf{Description} & Description \\ + \midrule + \textbf{Secondary scenarios} & Secondary scenarios \\ + \midrule + \textbf{Exceptions} & Exceptions \\ + \midrule + \textbf{Notes} & + ---\\ + \bottomrule +\end{tabular} + +\vspace{\interclassSpace} + +\begin{tabular}{lp{0.7\linewidth}} + \toprule + \multicolumn{2}{c}{\textbf{Generate a move}} \\ + \midrule + \textbf{Preconditions} & The game engine has been started. \newline + Optionally, some moves have already been played. \\ + \midrule + \textbf{Postconditions} & A move is suggested via the engine output. \\ + \midrule + \textbf{Actors} & Human user and GUI program. \\ + \midrule + \textbf{Description} & + 1. The user or program enters the player to generate the move + for.\newline + 2. The suggested move is outputted by the engine, either as + coordinates or as an indication to pass. + \\ + \midrule + \textbf{Secondary scenarios} & + \textbf{The move is illegal}: An error message is shown. Go back to step 1 of + main scenario. \\ + \midrule + \textbf{Exceptions} & + \textbf{The input is wrong}: An error message is shown. Go back to step 1 of + main scenario.\\ + \midrule + \textbf{Notes} & + ---\\ + \bottomrule +\end{tabular} + +\vspace{\interclassSpace} + +\subsubsection{Use as backend for machine player} + +\begin{figure}[h] + \begin{center} + \includegraphics[width=\textwidth]{diagrams/useCase_useAsBackend.png} + \caption{Use as backend for machine player} + \end{center} +\end{figure} + +\begin{tabular}{lp{0.7\linewidth}} + \toprule + \multicolumn{2}{c}{\textbf{Use as backend for machine player}} \\ + \midrule + \textbf{Preconditions} & The game engine has been configured as engine for + the software. \\ + \midrule + \textbf{Postconditions} & A match has been played against the engine. \\ + \midrule + \textbf{Actors} & GUI program. \\ + \midrule + \textbf{Description} & + 1. The program gives commands to the engine. The specific commands will + vary from program to program.\newline + 2. The engine suggest moves to the program.\newline + 3. The moves are shown by the program as if made by another player.\\ + \midrule + \textbf{Secondary scenarios} & + ---\\ + \midrule + \textbf{Exceptions} & + ---\\ + \midrule + \textbf{Notes} & + ---\\ + \bottomrule +\end{tabular} + +\subsection{Testing Plan Specification} + +\subsubsection{Unitary Testing} + +Tests for the python code are developed using the unittest\cite{python_unittest} +testing framework. It has been chosen by virtue of being thoroughly documented +and widely used. + +The coverage of unit testing is checked with Coverage.py\cite{python_coverage}, +which can by itself run the unittest tests and generate coverage reports based +on the results. + +% Maybe put an example report here? + +\subsubsection{Integration Testing} + +\subsubsection{System Testing} diff --git a/doc/tex/systemDesign.tex b/doc/tex/systemDesign.tex new file mode 100644 index 0000000..5988ae0 --- /dev/null +++ b/doc/tex/systemDesign.tex @@ -0,0 +1,2 @@ +\section{System Design} + diff --git a/doc/tex/tfg.tex b/doc/tex/tfg.tex index 7296cb9..6e2c472 100644 --- a/doc/tex/tfg.tex +++ b/doc/tex/tfg.tex @@ -10,7 +10,7 @@ \usepackage{chngcntr} \counterwithin{figure}{section} -\usepackage[backend=biber, style=numeric-comp]{biblatex} +\usepackage[backend=biber, style=numeric, sorting=none]{biblatex} \addbibresource{tex/biber.bib} \geometry{left=4.5cm,top=2cm,bottom=2cm,right=4.5cm} @@ -54,9 +54,8 @@ TODO: Acknowledgements To Vicente García Díaz, for directing me in this project. -To José Manuel Redondo López\cite{plantillaRedondo}, for making a very -complete template on which the structure of this documentation is extensively -based. +To José Manuel Redondo López\cite{plantillaRedondo}, for making an extensive +template on which the structure of this documentation is extensively based. \clearpage @@ -69,7 +68,7 @@ based. \textit{Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with the - Copyright notice as an Invariant Section, no Front- Cover Texts, and no + Copyright notice as an Invariant Section, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.} @@ -91,7 +90,7 @@ inclusion to use this template is included here. \textit{Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no - Invariant Sections, no Front- Cover Texts, and no Back-Cover Texts. A copy + Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.} @@ -107,10 +106,19 @@ inclusion to use this template is included here. \input{tex/systemAnalysis.tex} +\input{tex/systemDesign.tex} + %\input{tex/interface.tex} %\input{tex/implementation.tex} -\printbibliography{} +\clearpage + +% References (bibliography) + +\setcounter{secnumdepth}{0} +\section{References} +\printbibliography[type=article,title={Cited articles},heading=subbibintoc]{} +\printbibliography[type=online,title={Online resources},heading=subbibintoc]{} \end{document} diff --git a/imago/engine/monteCarlo.py b/imago/engine/monteCarlo.py index 4540cb8..5c916b0 100644 --- a/imago/engine/monteCarlo.py +++ b/imago/engine/monteCarlo.py @@ -74,9 +74,13 @@ class MCTSNode: """ Returns Upper Confidence Bound of node changing the symbol if the move is for the wite player.""" + return self.ucbForSpecificPlayer(self.move.getPlayer()) - # Account for white player score being negative - if self.move.getPlayer() == Player.WHITE: + def ucbForSpecificPlayer(self, player): + """ + Returns Upper Confidence Bound of node from the perspective of the given + player.""" + if player == Player.WHITE: return self.ucb() * -1 return self.ucb() @@ -86,17 +90,31 @@ class MCTSNode: return bestNode def selectionRec(self, bestNode): - """Searches this node and its children for the node with the best UCB value.""" + """Searches this node and its children for the node with the best UCB value for + the current player.""" + + #TODO: En cada nivel debe escoger como mejor el que sea mejor para quien le toque + # jugar, ya que esa es la partida esperada. Esto es lo que hacía con + # ucbForPlayer() + + # ¿Cada nuevo nodo expandido tiene una puntuación inicial? ¿Se le hacen + # exploraciones? + + # Backpropagation: ¿Es correcto acumular la puntuación de cada nodo hacia + # arriba? ¿Sería mejor acumular hacia arriba el valor del mejor nodo? ¿O del + # juego perfecto? Estos dos últimos valores no son el mismo. + + player = self.move.getPlayer() # Check if node has unexplored children and better UCB than previously explored if len(self.unexploredVertices) > 0: - if self.ucbForPlayer() > bestNode.ucbForPlayer(): + if self.ucbForSpecificPlayer(player) > bestNode.ucbForSpecificPlayer(player): bestNode = self # Recursively search children for better UCB for child in self.children: bestChildNode = child.selectionRec(bestNode) - if bestChildNode.ucbForPlayer() > bestNode.ucbForPlayer(): + if bestChildNode.ucbForSpecificPlayer(player) > bestNode.ucbForSpecificPlayer(player): bestNode = bestChildNode return bestNode diff --git a/imago/gameLogic/gameBoard.py b/imago/gameLogic/gameBoard.py index b22dfd7..d6114b9 100644 --- a/imago/gameLogic/gameBoard.py +++ b/imago/gameLogic/gameBoard.py @@ -81,7 +81,7 @@ class GameBoard: self.__exploreLiberties(rowToExplore, colToExplore, groupColor, emptyCells, exploredCells) - def getGroupCells(self, row, col): + def getGroupVertices(self, row, col): """ Returns a set containing the cells occupied by the group in the given cell. This is also valid if the cell is empty.""" @@ -90,9 +90,9 @@ class GameBoard: self.__exploreGroup(row, col, groupColor, cells) return cells - def getGroupCellsCount(self, row, col): + def getGroupVerticesCount(self, row, col): """Returns the number of cells of a group.""" - return len(self.getGroupCells(row, col)) + return len(self.getGroupVertices(row, col)) def __exploreGroup(self, row, col, groupColor, cells): if self.board[row][col] != groupColor or (row, col) in cells: @@ -133,7 +133,7 @@ class GameBoard: """Removes all the stones from the group occupying the given cell and returns a set containing them. """ - cellsToCapture = self.getGroupCells(row, col) + cellsToCapture = self.getGroupVertices(row, col) for cell in cellsToCapture: self.board[cell[0]][cell[1]] = Player.EMPTY return cellsToCapture @@ -153,7 +153,7 @@ class GameBoard: # if isCellEmpty && all adjacent to group are same color if not self.isCellEmpty(row, col): return Player.EMPTY - groupCells = self.getGroupCells(row, col) + groupCells = self.getGroupVertices(row, col) surroundingColor = Player.EMPTY # Check surrounding cells of each cell in the group for cell in groupCells: @@ -235,7 +235,7 @@ class GameBoard: playable, playableText = self.isPlayable(row, col, player, prevBoards) if not playable: return playable, playableText - if ( self.getGroupCellsCount(row, col) == 1 + if ( self.getGroupVerticesCount(row, col) == 1 and self.isCellEye(row, col) == player ): return False, "Move fills own eye.""" return True, "" @@ -252,7 +252,7 @@ class GameBoard: for row in range(0, self.getBoardHeight()): for col in range(0, self.getBoardWidth()): if not (row, col) in checkedVertices: - group = self.getGroupCells(row, col) + group = self.getGroupVertices(row, col) for cell in group: checkedVertices.add(cell) surroundingColor = self.isCellEye(row, col) diff --git a/imago/gameLogic/gameState.py b/imago/gameLogic/gameState.py index 9aafd11..5232a43 100644 --- a/imago/gameLogic/gameState.py +++ b/imago/gameLogic/gameState.py @@ -1,7 +1,6 @@ """Storing state of the game.""" from imago.data.enums import Player -from imago.gameLogic.gameTree import GameTree from imago.gameLogic.gameMove import GameMove from imago.gameLogic.gameBoard import GameBoard, cellToString @@ -10,6 +9,7 @@ class GameState: def __init__(self, size): self.size = size + self.lastMove = None self.clearBoard() def getCurrentPlayer(self): @@ -26,16 +26,12 @@ class GameState: def getBoard(self): """Returns the board as of the last move.""" - if self.lastMove is None: - return GameBoard(self.size, self.size) return self.lastMove.board def clearBoard(self): """Empties the board and moves tree.""" - self.gameTree = GameTree() newBoard = GameBoard(self.size, self.size) self.lastMove = GameMove(newBoard) - self.gameTree.firstMoves.append(self.lastMove) def playMove(self, row, col): """Execute a move on the board for the current player and switches players.""" diff --git a/imago/gameLogic/gameTree.py b/imago/gameLogic/gameTree.py index 4b88c4d..5955252 100644 --- a/imago/gameLogic/gameTree.py +++ b/imago/gameLogic/gameTree.py @@ -2,7 +2,7 @@ # This class will not be necessary if it just keeps being a list of moves -class GameTree: - - def __init__(self): - self.firstMoves = [] +#class GameTree: +# +# def __init__(self): +# self.firstMoves = [] diff --git a/tests/test_monteCarlo.py b/tests/test_monteCarlo.py index 283c657..b217cf9 100644 --- a/tests/test_monteCarlo.py +++ b/tests/test_monteCarlo.py @@ -14,8 +14,8 @@ class TestMonteCarlo(unittest.TestCase): def testPickMove(self): """Test picking a move.""" state = GameState(TEST_BOARD_SIZE) - tree = MCTS(state) - print(tree.pickMove().toString()) + tree = MCTS(state.lastMove) + #print(tree.pickMove().toString()) #def testSimulation(self): # """Test calculation of group liberties.""" diff --git a/tests/test_parseHelpers.py b/tests/test_parseHelpers.py index cf1fa9f..d03f253 100644 --- a/tests/test_parseHelpers.py +++ b/tests/test_parseHelpers.py @@ -20,6 +20,7 @@ class TestParseHelpers(unittest.TestCase): self.assertEqual(parseHelpers.parseMove(["B", "A1", "W"], TEST_BOARD_SIZE), parseHelpers.ParseCodes.ERROR) + parsedMove = parseHelpers.parseMove(["B", "t1"], TEST_BOARD_SIZE) targetMove = parseHelpers.GtpMove(Player.BLACK, [18, 18]) @@ -67,6 +68,10 @@ class TestParseHelpers(unittest.TestCase): "A19", TEST_BOARD_SIZE), [0,0]) + self.assertEqual(parseHelpers.parseVertex( + "pass", TEST_BOARD_SIZE), + "pass") + def testVertexToString(self): """Test converting vertices to strings.""" self.assertEqual(parseHelpers.vertexToString([0,0], TEST_BOARD_SIZE), "A19") @@ -78,6 +83,8 @@ class TestParseHelpers(unittest.TestCase): self.assertEqual(parseHelpers.vertexToString([18,0], TEST_BOARD_SIZE), "A1") self.assertEqual(parseHelpers.vertexToString([18,18], TEST_BOARD_SIZE), "T1") + self.assertEqual(parseHelpers.vertexToString("pass", TEST_BOARD_SIZE), "pass") + self.assertEqual(parseHelpers.vertexToString([-1,0], TEST_BOARD_SIZE), parseHelpers.ParseCodes.ERROR) self.assertEqual(parseHelpers.vertexToString([0,-1], TEST_BOARD_SIZE), -- cgit v1.2.1