diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_enums.py | 2 | ||||
-rw-r--r-- | tests/test_gameBoard.py | 28 | ||||
-rw-r--r-- | tests/test_gameMove.py | 6 | ||||
-rw-r--r-- | tests/test_gameState.py | 89 | ||||
-rw-r--r-- | tests/test_imagoIO.py | 201 | ||||
-rw-r--r-- | tests/test_monteCarlo.py | 50 | ||||
-rw-r--r-- | tests/test_neuralNetwork.py | 83 | ||||
-rw-r--r-- | tests/test_parseHelpers.py | 36 | ||||
-rw-r--r-- | tests/test_sgf.py | 27 |
9 files changed, 501 insertions, 21 deletions
diff --git a/tests/test_enums.py b/tests/test_enums.py index 73f4009..21b2057 100644 --- a/tests/test_enums.py +++ b/tests/test_enums.py @@ -5,7 +5,7 @@ import unittest from imago.data.enums import Player class TestEnums(unittest.TestCase): - """Test parseHelpers module.""" + """Test enums module.""" def testOtherPlayer(self): """Test method to get the other player""" diff --git a/tests/test_gameBoard.py b/tests/test_gameBoard.py index 8a7b127..c7808ac 100644 --- a/tests/test_gameBoard.py +++ b/tests/test_gameBoard.py @@ -114,5 +114,33 @@ class TestGameBoard(unittest.TestCase): board.board[9][0] = Player.WHITE self.assertEqual((9, 21), board.score()) + def testToString(self): + """Test formatting of the board as a string.""" + + board = GameBoard(9, 9) + self.assertEqual(' A B C D E F G H J \n\ +9 · · · · · · · · · \n\ +8 · · · · · · · · · \n\ +7 · · · · · · · · · \n\ +6 · · · · · · · · · \n\ +5 · · · · · · · · · \n\ +4 · · · · · · · · · \n\ +3 · · · · · · · · · \n\ +2 · · · · · · · · · \n\ +1 · · · · · · · · · ', board.toString()) + + board.moveAndCapture(2, 6, Player.BLACK) + board.moveAndCapture(5, 4, Player.WHITE) + self.assertEqual(' A B C D E F G H J \n\ +9 · · · · · · · · · \n\ +8 · · · · · · · · · \n\ +7 · · · · · · B · · \n\ +6 · · · · · · · · · \n\ +5 · · · · · · · · · \n\ +4 · · · · W · · · · \n\ +3 · · · · · · · · · \n\ +2 · · · · · · · · · \n\ +1 · · · · · · · · · ', board.toString()) + if __name__ == '__main__': unittest.main() diff --git a/tests/test_gameMove.py b/tests/test_gameMove.py index 6569c5b..a7edfab 100644 --- a/tests/test_gameMove.py +++ b/tests/test_gameMove.py @@ -18,13 +18,13 @@ class TestGameMove(unittest.TestCase): self.assertIsNone(firstMove.coords) - secondMove = firstMove.addMove(1, 2) + secondMove = firstMove.addMoveByCoords(1, 2) self.assertIsNone(firstMove.coords) self.assertEqual(secondMove.coords[0], 1) self.assertEqual(secondMove.coords[1], 2) - thirdMove = secondMove.addMove(5, 7) + thirdMove = secondMove.addMoveByCoords(5, 7) self.assertIsNone(firstMove.coords) self.assertIsNone(thirdMove.previousMove.previousMove.coords) @@ -66,7 +66,7 @@ class TestGameMove(unittest.TestCase): (2,0), (2,1), (2,2))) ) - secondMove = firstMove.addMove(1, 2) + secondMove = firstMove.addMoveByCoords(1, 2) self.assertSetEqual( secondMove.getPlayableVertices(), set(((0,0), (0,1), (0,2), diff --git a/tests/test_gameState.py b/tests/test_gameState.py new file mode 100644 index 0000000..1c6b997 --- /dev/null +++ b/tests/test_gameState.py @@ -0,0 +1,89 @@ +"""Tests for the input/output component.""" + +import unittest + +from imago.data.enums import Player +from imago.gameLogic.gameState import GameState + +class TestGameState(unittest.TestCase): + """Test GameState component.""" + + def testCurrentPlayer(self): + """Test simple commands.""" + size = 9 + state = GameState(size) + + self.assertEqual(state.getCurrentPlayer(), Player.BLACK) + self.assertEqual(state.getPlayerCode(), 'B') + + state.playMove(0, 0) + self.assertEqual(state.getCurrentPlayer(), Player.WHITE) + self.assertEqual(state.getPlayerCode(), 'W') + + def testPlays(self): + """Test simple commands.""" + size = 3 + state = GameState(size) + + self.assertEqual(state.getBoard().getBoard(), + [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ]) + + state.playMove(1, 1) + self.assertEqual(state.getBoard().getBoard(), + [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.BLACK, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ]) + + state.playPass() + self.assertEqual(state.getBoard().getBoard(), + [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.BLACK, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ]) + + state.undo() + self.assertEqual(state.getBoard().getBoard(), + [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.BLACK, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ]) + + state.undo() + self.assertEqual(state.getBoard().getBoard(), + [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ]) + + state.passForPlayer(Player.WHITE) + self.assertEqual(state.getBoard().getBoard(), + [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ]) + + + self.assertRaises(Exception, state.playMove, -1, -1) + self.assertEqual(state.getBoard().getBoard(), + [ + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY], + [Player.EMPTY, Player.EMPTY, Player.EMPTY] + ]) + + state.lastMove = None + self.assertEqual(state.getCurrentPlayer(), Player.BLACK) + self.assertRaises(RuntimeError, state.playMove, 0, 0) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_imagoIO.py b/tests/test_imagoIO.py new file mode 100644 index 0000000..499fdb2 --- /dev/null +++ b/tests/test_imagoIO.py @@ -0,0 +1,201 @@ +"""Tests for the input/output component.""" + +import unittest + +import io +import sys + +from imago.data.enums import DecisionAlgorithms +from imago.engine.imagoIO import ImagoIO +from imago.engine.parseHelpers import parseVertex + +class TestImagoIO(unittest.TestCase): + """Test ImagoIO component.""" + + @unittest.mock.patch('imago.engine.imagoIO.input', create=True) + def testSimpleCommands(self, mocked_input): + """Test simple commands.""" + + self.maxDiff = None + + mocked_input.side_effect = [ + '\n', + 'name\n', + 'version\n', + 'protocol_version\n', + 'clear_board\n', + '\n', + 'known_command\n', + 'known_command name\n', + 'known_command version\n', + 'known_command wrongcommand\n', + '\n', + 'boardsize\n', + 'boardsize 10\n', + '\n', + 'komi\n', + 'komi 5.5\n', + '\n', + 'play\n', + 'play 1\n', + 'play 1 2\n', + 'play b a1\n', + '\n', + 'undo\n', + 'undo\n', + '\n', + 'wrongcommand\n', + '\n', + 'quit\n' + ] + + testout = io.StringIO() + imagoIO = ImagoIO( + decisionAlgorithmId=DecisionAlgorithms.MONTECARLO, + outputStream=testout + ) + + imagoIO.start() + value = testout.getvalue() + self.assertEqual( + '= Imago\n\n' + + '= 0.0.0\n\n' + + '= 2\n\n' + + '= \n\n' + + '? Wrong number of arguments\n' + + '? Usage: known_command COMMAND_NAME\n\n' + + '= true\n\n' + + '= true\n\n' + + '= false\n\n' + + '? Wrong number of arguments\n' + + '? Usage: boardsize <newSize>\n\n' + + '= \n\n' + + '? Wrong number of arguments\n' + + '? Usage: komi <newKomi>\n\n' + + '= \n\n' + + '? Wrong number of arguments\n' + + '? Usage: play <color> <vertex>\n\n' + + '? Wrong number of arguments\n' + + '? Usage: play <color> <vertex>\n\n' + + '? Invalid move: Unknown color [1].\n\n' + + '= \n\n' + + '= \n\n' + + '= \n\n' + + '? unknown command\n\n' + + '= \n\n', + value + ) + + testout.close() + + + @unittest.mock.patch('imago.engine.imagoIO.input', create=True) + def testListsCommands(self, mocked_input): + """Test command for listing all commands.""" + + mocked_input.side_effect = [ + 'list_commands\n', + 'quit\n' + ] + + testout = io.StringIO() + imagoIO = ImagoIO( + decisionAlgorithmId=DecisionAlgorithms.MONTECARLO, + outputStream=testout + ) + + commandsString = "\n".join(list(map( + lambda cmd: "%s - %s" % (cmd.__name__, cmd.__doc__), + imagoIO.commands_set))) + + imagoIO.start() + value = testout.getvalue() + self.assertEqual( + '= ' + + commandsString + + '\n\n' + + '= \n\n', + value + ) + + testout.close() + + + @unittest.mock.patch('imago.engine.imagoIO.input', create=True) + def testFixedHandicap(self, mocked_input): + """Test command for setting fixed handicap stones.""" + + mocked_input.side_effect = [ + 'fixed_handicap\n', + 'fixed_handicap 2\n', + 'quit\n' + ] + + testout = io.StringIO() + imagoIO = ImagoIO( + decisionAlgorithmId=DecisionAlgorithms.MONTECARLO, + outputStream=testout + ) + + imagoIO.start() + value = testout.getvalue() + self.assertEqual( + '? Wrong number of arguments\n' + + '? Usage: fixed_handicap <count>\n\n' + + '= A1 A2\n\n' + + '= \n\n', + value + ) + + testout.close() + + +# @unittest.mock.patch('imago.engine.imagoIO.input', create=True) +# def testGenmove(self, mocked_input): +# """Test command for generating a move.""" +# +# mocked_input.side_effect = [ +# 'genmove\n', +# 'genmove w w\n', +# 'genmove 2\n', +# 'quit\n' +# ] +# +# testout = io.StringIO() +# imagoIO = ImagoIO( +# decisionAlgorithmId=DecisionAlgorithms.MONTECARLO, +# outputStream=testout +# ) +# +# imagoIO.start() +# value = testout.getvalue() +# self.assertEqual( +# '? Wrong number of arguments\n' + +# '? Usage: genmove <color>\n\n' + +# '? Wrong number of arguments\n' + +# '? Usage: genmove <color>\n\n' + +# '? Error: Unknown color [2].\n\n', +# value +# ) +# +# mocked_input.side_effect = [ +# 'genmove w\n', +# 'quit\n'] +# +# testout = io.StringIO() +# imagoIO = ImagoIO( +# decisionAlgorithmId=DecisionAlgorithms.MONTECARLO, +# outputStream=testout +# ) +# imagoIO.start() +# value = testout.getvalue() +# vertexValue = value.split(' ')[1].split('\n')[0] +# +# # Test parsing vertex does not raise an error +# parseVertex(vertexValue, imagoIO.gameEngine.gameState.size) +# +# testout.close() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_monteCarlo.py b/tests/test_monteCarlo.py index b217cf9..9d1fcfc 100644 --- a/tests/test_monteCarlo.py +++ b/tests/test_monteCarlo.py @@ -2,6 +2,7 @@ import unittest +from imago.engine.decisionAlgorithm import DecisionAlgorithm from imago.gameLogic.gameState import GameState from imago.engine.monteCarlo import MCTS from imago.engine.monteCarlo import MCTSNode @@ -17,8 +18,55 @@ class TestMonteCarlo(unittest.TestCase): tree = MCTS(state.lastMove) #print(tree.pickMove().toString()) + def testForceNextMove(self): + """Test forcing next move.""" + + # Next move before expansion (no children nodes) + state = GameState(TEST_BOARD_SIZE) + tree = MCTS(state.lastMove) + self.assertEqual(set(), tree.root.children) + tree.forceNextMove((0, 1)) + self.assertEqual(set(), tree.root.children) + + # Next move after expansion (with children nodes) + tree.expansions = 2 + tree.simulationsPerExpansion = 2 + tree.pickMove() + self.assertEqual(tree.expansions, len(tree.root.children)) + nextMoveCoords = list(tree.root.children)[0].move.coords + tree.forceNextMove(nextMoveCoords) + + def testPass(self): + """Test passing as next move.""" + state = GameState(TEST_BOARD_SIZE) + tree = MCTS(state.lastMove) + self.assertFalse(tree.root.move.isPass) + tree.forceNextMove("pass") + self.assertTrue(tree.root.move.isPass) + + def testClearBoard(self): + """Test clearing board returns root to original and retains information.""" + state = GameState(TEST_BOARD_SIZE) + tree = MCTS(state.lastMove) + + firstMoveCoords = (0,0) + secondMoveCoords = (1,0) + thirdMoveCoords = (0,1) + + tree.forceNextMove(firstMoveCoords) + tree.forceNextMove(secondMoveCoords) + tree.forceNextMove(thirdMoveCoords) + tree.clearBoard() + + nextNode = list(tree.root.children)[0] + self.assertEqual(firstMoveCoords, nextNode.move.coords) + nextNode = list(nextNode.children)[0] + self.assertEqual(secondMoveCoords, nextNode.move.coords) + nextNode = list(nextNode.children)[0] + self.assertEqual(thirdMoveCoords, nextNode.move.coords) + #def testSimulation(self): - # """Test calculation of group liberties.""" + # """Test Monte Carlo simulation.""" # board = GameBoard(TEST_BOARD_SIZE, TEST_BOARD_SIZE) # move = GameMove(board) # node = MCTSNode(move, None) diff --git a/tests/test_neuralNetwork.py b/tests/test_neuralNetwork.py new file mode 100644 index 0000000..42ba4a1 --- /dev/null +++ b/tests/test_neuralNetwork.py @@ -0,0 +1,83 @@ +"""Tests for neural network module.""" + +import os +import shutil +import unittest + +from imago.data.enums import DecisionAlgorithms +from imago.sgfParser.sgf import loadGameTree +from imago.gameLogic.gameState import GameState +from imago.engine.keras.neuralNetwork import NeuralNetwork +from imago.engine.keras.denseNeuralNetwork import DenseNeuralNetwork +from imago.engine.keras.convNeuralNetwork import ConvNeuralNetwork +from imago.engine.keras.keras import Keras + +class TestNeuralNetwork(unittest.TestCase): + """Test neural network module.""" + + def testLoadBaseClass(self): + """Test error when creating model with the base NeuralNetwork class""" + + self.assertRaises(NotImplementedError, + NeuralNetwork, + "non/existing/file") + + def testNetworks(self): + """Test creation of initial model for dense neural network""" + + testModel = 'testModel' + testModelPlot = 'testModelPlot' + + games = [] + for file in [ + '../collections/minigo/matches/1.sgf', + '../collections/minigo/matches/2.sgf', + '../collections/minigo/matches/3.sgf' + ]: + games.append(loadGameTree(file)) + matches = [game.getMainLineOfPlay() for game in games] + + nn = DenseNeuralNetwork(modelPath=testModel, boardSize=9) + nn.trainModel(matches, epochs=1, verbose=0) + + game = GameState(9) + nn.pickMove(game.lastMove, game.getCurrentPlayer()) + + nn.saveModel(testModel) + self.assertTrue(os.path.isdir(testModel)) + shutil.rmtree(testModel, ignore_errors=True) + + nn.saveModel() + self.assertTrue(os.path.isdir(testModel)) + nn = DenseNeuralNetwork(modelPath=testModel, boardSize=9) + + nn.saveModelPlot(testModelPlot) + self.assertTrue(os.path.isfile(testModelPlot)) + + shutil.rmtree(testModel, ignore_errors=True) + os.remove(testModelPlot) + + nn = ConvNeuralNetwork(testModel, boardSize=9) + + def testKeras(self): + """Test keras model loading.""" + + gameState = GameState(9) + move = gameState.lastMove + + keras = Keras(move) + keras.forceNextMove("pass") + + keras = Keras(move, DecisionAlgorithms.DENSE) + keras.forceNextMove((3,3)) + + keras = Keras(move, DecisionAlgorithms.CONV) + self.assertRaises(RuntimeError, keras.forceNextMove, "wrongmove") + pickedCoords = keras.pickMove() + self.assertTrue(len(pickedCoords) == 2 or pickedCoords == "pass") + + self.assertRaises(RuntimeError, Keras, move, DecisionAlgorithms.MONTECARLO) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_parseHelpers.py b/tests/test_parseHelpers.py index 7bbf152..c1405fb 100644 --- a/tests/test_parseHelpers.py +++ b/tests/test_parseHelpers.py @@ -26,7 +26,7 @@ class TestParseHelpers(unittest.TestCase): ) parsedMove = parseHelpers.parseMove(["B", "t1"], TEST_BOARD_SIZE) - targetMove = parseHelpers.GtpMove(Player.BLACK, [18, 18]) + targetMove = parseHelpers.GtpMove(Player.BLACK, (18, 18)) self.assertEqual(parsedMove.color, targetMove.color) self.assertEqual(parsedMove.vertex, targetMove.vertex) @@ -54,26 +54,26 @@ class TestParseHelpers(unittest.TestCase): """Test correct inputs and their resulting coordinates for parseVertex.""" self.assertEqual(parseHelpers.parseVertex( "a1", TEST_BOARD_SIZE), - [18,0]) + (18,0)) self.assertEqual(parseHelpers.parseVertex( "b1", TEST_BOARD_SIZE), - [18,1]) + (18,1)) self.assertEqual(parseHelpers.parseVertex( "a2", TEST_BOARD_SIZE), - [17,0]) + (17,0)) self.assertEqual(parseHelpers.parseVertex( "b2", TEST_BOARD_SIZE), - [17,1]) + (17,1)) self.assertEqual(parseHelpers.parseVertex( "T1", TEST_BOARD_SIZE), - [18,18]) + (18,18)) self.assertEqual(parseHelpers.parseVertex( "T19", TEST_BOARD_SIZE), - [0,18]) + (0,18)) self.assertEqual(parseHelpers.parseVertex( "A19", TEST_BOARD_SIZE), - [0,0]) + (0,0)) self.assertEqual(parseHelpers.parseVertex( "pass", TEST_BOARD_SIZE), @@ -81,10 +81,14 @@ class TestParseHelpers(unittest.TestCase): def testVertexToString(self): """Test converting vertices to strings.""" - self.assertEqual(parseHelpers.vertexToString([0,0], TEST_BOARD_SIZE), "A19") - self.assertEqual(parseHelpers.vertexToString([1,0], TEST_BOARD_SIZE), "A18") - self.assertEqual(parseHelpers.vertexToString([2,0], TEST_BOARD_SIZE), "A17") - self.assertEqual(parseHelpers.vertexToString([0,1], TEST_BOARD_SIZE), "B19") + + # Try with vertices as tuples + self.assertEqual(parseHelpers.vertexToString((0,0), TEST_BOARD_SIZE), "A19") + self.assertEqual(parseHelpers.vertexToString((1,0), TEST_BOARD_SIZE), "A18") + self.assertEqual(parseHelpers.vertexToString((2,0), TEST_BOARD_SIZE), "A17") + self.assertEqual(parseHelpers.vertexToString((0,1), TEST_BOARD_SIZE), "B19") + + # Try with vertices as arrays self.assertEqual(parseHelpers.vertexToString([0,2], TEST_BOARD_SIZE), "C19") self.assertEqual(parseHelpers.vertexToString([0,18], TEST_BOARD_SIZE), "T19") self.assertEqual(parseHelpers.vertexToString([18,0], TEST_BOARD_SIZE), "A1") @@ -93,10 +97,10 @@ class TestParseHelpers(unittest.TestCase): self.assertEqual(parseHelpers.vertexToString("pass", TEST_BOARD_SIZE), "pass") wrongVertices = [ - [-1,0], - [0,-1], - [-1,-1], - [19,0], + (-1,0), + (0,-1), + (-1,-1), + (19,0), [0,19], [19,19], [0], diff --git a/tests/test_sgf.py b/tests/test_sgf.py new file mode 100644 index 0000000..1266429 --- /dev/null +++ b/tests/test_sgf.py @@ -0,0 +1,27 @@ +"""Tests for processing of SGF files.""" + +import unittest + +from imago.sgfParser.sgfyacc import parser + +TESTING_SGF = 'tests/testingSGF.sgf' + +class TestSGF(unittest.TestCase): + """Test processing SGF files.""" + + def testToGameTree(self): + """Test converting file to GameTree""" + + file = open(TESTING_SGF, "r") + text = file.read() + file.close() + + astNode = parser.parse(text) + + astNode.toGameMoveTree() + + astNode.toString() + + +if __name__ == '__main__': + unittest.main() |