コード例 #1
0
def test_encoding(tc):
    g1 = sgf.Sgf_game(19)
    tc.assertEqual(g1.get_charset(), "UTF-8")
    root = g1.get_root()
    tc.assertEqual(root.get_encoding(), "UTF-8")
    root.set("C", "£")
    tc.assertEqual(root.get("C"), "£")
    tc.assertEqual(root.get_raw("C"), "£")
    tc.assertEqual(g1.serialise(),
                   dedent("""\
    (;FF[4]C[£]CA[UTF-8]GM[1]SZ[19])
    """))

    g2 = sgf.Sgf_game(19, encoding="iso-8859-1")
    tc.assertEqual(g2.get_charset(), "ISO-8859-1")
    root = g2.get_root()
    tc.assertEqual(root.get_encoding(), "ISO-8859-1")
    root.set("C", "£")
    tc.assertEqual(root.get("C"), "£")
    tc.assertEqual(root.get_raw("C"), "\xa3")
    tc.assertEqual(
        g2.serialise(),
        dedent("""\
    (;FF[4]C[\xa3]CA[ISO-8859-1]GM[1]SZ[19])
    """))

    tc.assertRaisesRegexp(ValueError, "unknown encoding: unknownencoding",
                          sgf.Sgf_game, 19, "unknownencoding")
コード例 #2
0
ファイル: gtp_states.py プロジェクト: BorisNA/goreviewpartner
    def handle_savesgf(self, args):
        try:
            pathname = args[0]
        except IndexError:
            gtp_engine.report_bad_arguments()

        sgf_game = sgf.Sgf_game(self.board_size)
        root = sgf_game.get_root()
        root.set('KM', self.komi)
        root.set('AP', ("gomill", __version__))
        sgf_game.set_date()
        if self.handicap is not None:
            root.set('HA', self.handicap)
        for arg in args[1:]:
            try:
                identifier, value = arg.split("=", 1)
                if not identifier.isalpha():
                    raise ValueError
                identifier = identifier.upper()
                value = value.replace("\\_", " ").replace("\\\\", "\\")
            except Exception:
                gtp_engine.report_bad_arguments()
            root.set_raw(identifier, sgf_grammar.escape_text(value))
        sgf_moves.set_initial_position(sgf_game, self.history_base)
        for history_move in self.move_history:
            node = sgf_game.extend_main_sequence()
            node.set_move(history_move.colour, history_move.move)
            if history_move.comments is not None:
                node.set("C", history_move.comments)
        sgf_moves.indicate_first_player(sgf_game)
        try:
            self._save_file(pathname, sgf_game.serialise())
        except EnvironmentError, e:
            raise GtpError("error writing file: %s" % e)
コード例 #3
0
def test_get_setup_and_moves(tc):
    g1 = sgf.Sgf_game.from_string(SAMPLE_SGF)
    board1, plays1 = sgf_moves.get_setup_and_moves(g1)
    tc.assertBoardEqual(board1, DIAGRAM1)
    tc.assertEqual(plays1,
                   [('b', (2, 3)), ('w', (3, 4)), ('b', None), ('w', None)])

    g2 = sgf.Sgf_game(size=9)
    root = g2.get_root()
    root.set("AB", [(1, 2), (3, 4)])
    node = g2.extend_main_sequence()
    node.set("B", (5, 6))
    node = g2.extend_main_sequence()
    node.set("W", (5, 7))
    board2, plays2 = sgf_moves.get_setup_and_moves(g2)
    tc.assertBoardEqual(board2, DIAGRAM2)
    tc.assertEqual(plays2,
                   [('b', (5, 6)), ('w', (5, 7))])

    g3 = sgf.Sgf_game.from_string("(;AB[ab][ba]AW[aa])")
    tc.assertRaisesRegexp(ValueError, "setup position not legal",
                          sgf_moves.get_setup_and_moves, g3)

    g4 = sgf.Sgf_game.from_string("(;SZ[9];B[ab];AW[bc])")
    tc.assertRaisesRegexp(ValueError, "setup properties after the root node",
                          sgf_moves.get_setup_and_moves, g4)

    g5 = sgf.Sgf_game.from_string("(;SZ[26];B[ab];W[bc])")
    board5, plays5 = sgf_moves.get_setup_and_moves(g5)
    tc.assertEqual(plays5,
                   [('b', (24, 0)), ('w', (23, 1))])
コード例 #4
0
def test_add_comment_text(tc):
    sgf_game = sgf.Sgf_game(9)
    root = sgf_game.get_root()
    root.add_comment_text("hello\nworld")
    tc.assertEqual(root.get('C'), "hello\nworld")
    root.add_comment_text("hello\naga]in")
    tc.assertEqual(root.get('C'), "hello\nworld\n\nhello\naga]in")
コード例 #5
0
def test_set_initial_position(tc):
    board = ascii_boards.interpret_diagram(DIAGRAM1, 9)
    sgf_game = sgf.Sgf_game(9)
    sgf_moves.set_initial_position(sgf_game, board)
    root = sgf_game.get_root()
    tc.assertEqual(root.get("AB"), set([(0, 0), (1, 1), (4, 4)]))
    tc.assertEqual(root.get("AW"), set([(6, 5), (6, 6)]))
    tc.assertRaises(KeyError, root.get, 'AE')
コード例 #6
0
def test_get_setup_and_moves_move_in_root(tc):
    # A move in the root node is allowed (though deprecated) if there are no
    # setup stones.
    g1 = sgf.Sgf_game(size=9)
    root = g1.get_root()
    root.set("B", (1, 2))
    node = g1.extend_main_sequence()
    node.set("W", (3, 4))
    board1, plays1 = sgf_moves.get_setup_and_moves(g1)
    tc.assertTrue(board1.is_empty())
    tc.assertEqual(plays1, [('b', (1, 2)), ('w', (3, 4))])

    g2 = sgf.Sgf_game(size=9)
    root = g2.get_root()
    root.set("B", (1, 2))
    root.set("AW", [(3, 3)])
    node = g2.extend_main_sequence()
    node.set("W", (3, 4))
    tc.assertRaisesRegexp(ValueError, "mixed setup and moves in root node",
                          sgf_moves.get_setup_and_moves, g2)
コード例 #7
0
 def load_sgf_from_file(self, filen):
     print 'asked to load from', filen
     fileh = open(filen, 'r')
     sgfdata = fileh.read()
     fileh.close()
     try:
         self.game = sgf.Sgf_game.from_string(sgfdata)
         self.filepath = filen
     except ValueError:
         self.game = sgf.Sgf_game(19)
     self.reset_position()
コード例 #8
0
def test_extend_main_sequence(tc):
    g1 = sgf.Sgf_game(9)
    for i in xrange(6):
        g1.extend_main_sequence().set("N", "e%d" % i)
    tc.assertEqual(
        g1.serialise(),
        "(;FF[4]CA[UTF-8]GM[1]SZ[9];N[e0];N[e1];N[e2];N[e3];N[e4];N[e5])\n")
    g2 = sgf.Sgf_game.from_string("(;SZ[9](;N[n1];N[n3])(;N[n2]))")
    for i in xrange(6):
        g2.extend_main_sequence().set("N", "e%d" % i)
    tc.assertEqual(
        g2.serialise(),
        "(;SZ[9](;N[n1];N[n3];N[e0];N[e1];N[e2];N[e3];N[e4];N[e5])(;N[n2]))\n")
コード例 #9
0
ファイル: abstractboard.py プロジェクト: pombredanne/noGo
    def __init__(self, game=None, gridsize=19):
        if game is None:
            game = sgf.Sgf_game(gridsize)
        print 'abstractboard initialised with size', game.size, gridsize

        self.game = game
        self.prisoners = [0, 0]
        self.variation_index = 0

        self.boards = {}
        self.curnode = game.get_root()
        board = boards.Board(self.game.size)
        board, instructions = apply_node_to_board(board, self.curnode)
        self.boards[self.curnode] = board
        self.varcache = {}
        self.filepath = ''
コード例 #10
0
def test_new_sgf_game(tc):
    g1 = sgf.Sgf_game(9)
    tc.assertEqual(g1.get_size(), 9)
    root = g1.get_root()
    tc.assertEqual(root.get_raw('FF'), '4')
    tc.assertEqual(root.get_raw('GM'), '1')
    tc.assertEqual(root.get_raw('SZ'), '9')
    tc.assertEqual(root.get_raw_property_map(), {
        'FF': ['4'],
        'GM': ['1'],
        'SZ': ['9'],
        'CA': ['UTF-8'],
    })
    tc.assertEqual(list(root), [])
    tc.assertEqual(root.parent, None)
    tc.assertIs(root.owner, g1)
コード例 #11
0
ファイル: abstractboard.py プロジェクト: pombredanne/noGo
 def load_sgf_from_file(self, filen):
     print 'abstractboard asked to load from', filen
     print 'opening file'
     fileh = open(filen, 'r')
     print 'opened'
     sgfdata = fileh.read()
     print 'read from file'
     print 'sgf is', sgfdata
     fileh.close()
     print 'file closed'
     try:
         self.game = sgf.Sgf_game.from_string(sgfdata)
         self.filepath = filen
         print 'Successfully parsed string'
     except ValueError:
         print 'Failed to parse string'
         self.game = sgf.Sgf_game(19)
     print 'loaded from file'
     self.reset_position()
     print 'reset position'
コード例 #12
0
ファイル: gameplay.py プロジェクト: BenisonSam/goprime
    def make_sgf(self):
        """Return an SGF description of the game.

        Returns an Sgf_game object with the following root node properties set:
          FF GM CA
          DT AP SZ KM
          HA (if there was a handicap)
          RE (if the result is known)

        Doesn't set a root node comment. Doesn't put result.detail anywhere.

        The moves described are the same as those from get_moves().

        Anything returned by backend.get_last_move_comment() is used as a
        comment on the corresponding move (in the final node for comments on
        resignation, forfeits and so on).

        """
        sgf_game = sgf.Sgf_game(self.board_size)
        root = sgf_game.get_root()
        root.set('KM', self.komi)
        root.set('AP', ("gomill", __version__))
        if self.result is not None:
            root.set('RE', self.result.sgf_result)
        for prop, value in self.additional_sgf_props:
            root.set(prop, value)
        sgf_game.set_date()
        if self.handicap_stones:
            root.set_setup_stones(black=self.handicap_stones, white=[])
        for colour, move, comment in self.moves:
            node = sgf_game.extend_main_sequence()
            node.set_move(colour, move)
            if comment is not None:
                node.set("C", comment)
        final = self.get_final_diagnostics()
        if final is not None:
            sgf_game.get_last_node().add_comment_text(
                "final message from %s: <<<\n%s\n>>>" %
                (final.colour, final.message))
        return sgf_game
コード例 #13
0
    def make_sgf(self, game_end_message=None):
        """Return an SGF description of the game.

        Returns an Sgf_game object.

        game_end_message -- optional string to put in the final comment.

        If game_end_message is specified, it appears before the text describing
        'late errors'.

        """
        sgf_game = sgf.Sgf_game(self.board_size)
        root = sgf_game.get_root()
        root.set('KM', self.komi)
        root.set('AP', ("gomill", __version__))
        for prop, value in self.additional_sgf_props:
            root.set(prop, value)
        sgf_game.set_date()
        if self.engine_names:
            root.set('PB', self.engine_names[self.players['b']])
            root.set('PW', self.engine_names[self.players['w']])
        if self.game_id:
            root.set('GN', self.game_id)
        if self.handicap_stones:
            root.set_setup_stones(black=self.handicap_stones, white=[])
        for colour, move, comment in self.moves:
            node = sgf_game.extend_main_sequence()
            node.set_move(colour, move)
            if comment is not None:
                node.set("C", comment)
        last_node = sgf_game.get_last_node()
        if self.result is not None:
            root.set('RE', self.result.sgf_result)
            last_node.add_comment_text(self.describe_scoring())
        if game_end_message is not None:
            last_node.add_comment_text(game_end_message)
        late_error_messages = self.describe_late_errors()
        if late_error_messages is not None:
            last_node.add_comment_text(late_error_messages)
        return sgf_game
コード例 #14
0
def test_reparent(tc):
    g1 = sgf.Sgf_game.from_string("(;SZ[9](;N[n1];N[n3])(;N[n2]))")
    root = g1.get_root()
    # Test with unexpanded root
    tc.assertRaisesRegexp(ValueError, "would create a loop", root.reparent,
                          root)
    n1 = root[0]
    n2 = root[1]
    n3 = root[0][0]
    tc.assertEqual(n1.get("N"), "n1")
    tc.assertEqual(n2.get("N"), "n2")
    tc.assertEqual(n3.get("N"), "n3")
    n3.reparent(n2)
    tc.assertEqual(g1.serialise(), "(;SZ[9](;N[n1])(;N[n2];N[n3]))\n")
    n3.reparent(n2)
    tc.assertEqual(g1.serialise(), "(;SZ[9](;N[n1])(;N[n2];N[n3]))\n")
    tc.assertRaisesRegexp(ValueError, "would create a loop", root.reparent, n3)
    tc.assertRaisesRegexp(ValueError, "would create a loop", n3.reparent, n3)
    g2 = sgf.Sgf_game(9)
    tc.assertRaisesRegexp(ValueError,
                          "new parent doesn't belong to the same game",
                          n3.reparent, g2.get_root())
コード例 #15
0
def test_tree_mutation(tc):
    sgf_game = sgf.Sgf_game(9)
    root = sgf_game.get_root()
    n1 = root.new_child()
    n1.set("N", "n1")
    n2 = root.new_child()
    n2.set("N", "n2")
    n3 = n1.new_child()
    n3.set("N", "n3")
    n4 = root.new_child(1)
    n4.set("N", "n4")
    tc.assertEqual(
        sgf_game.serialise(),
        "(;FF[4]CA[UTF-8]GM[1]SZ[9](;N[n1];N[n3])(;N[n4])(;N[n2]))\n")
    tc.assertEqual([
        node.get_raw_property_map() for node in sgf_game.main_sequence_iter()
    ], [node.get_raw_property_map() for node in root, root[0], n3])
    tc.assertIs(sgf_game.get_last_node(), n3)

    n1.delete()
    tc.assertEqual(sgf_game.serialise(),
                   "(;FF[4]CA[UTF-8]GM[1]SZ[9](;N[n4])(;N[n2]))\n")
    tc.assertRaises(ValueError, root.delete)
コード例 #16
0
def startPK(num, weightb, weightw, spendTime):
    g = sgf.Sgf_game(size=19)
    g.root.set("KM", '7.5')
    g.root.set("PB", weightb)
    g.root.set("PW", weightw)

    #pbscmd = 'D:\\Go\\leela-zero-0.16-win64\\leelaz.exe -g --noponder -t 2 -wD:\\Go\\weights\\' \
    pbscmd = 'E:\\Go\\20190219GCPNext\\leelaz.exe -g --noponder --precision half -wE:\\Go\\Ana_1.26\\weight\\' \
             +weightb+' --gpu 0 -p 100000'
    pbcwdstr = 'E:\\Go\\20190219GCPNext'
    #print pbscmd
    pbcommand = pbscmd.split(' ')
    try:
        pb = goEngin(pbcommand, pbcwdstr)
    except (Exception) as e:
        print("Error found:", e)
        return None

    processTest = True
    while processTest:
        #print 'test pb alive',pb.process.poll()
        if pb.process.poll() is None:
            pass
        else:
            gotStdStr = pb.readAns_nowait()
            while gotStdStr != None:
                print gotStdStr
                gotStdStr = pb.readAns_nowait()
            print "process start failed"
            return None
        sleep(0.001)
        gotErrStr = pb.readErr_nowait()
        if gotErrStr != None:
            while gotErrStr != None:
                if gotErrStr[:24] == 'Setting max tree size to':
                    print 'pb.1', gotErrStr[:-2]
                    print 'Black is ready.'
                    processTest = False
                else:
                    #print 'pb.1',gotErrStr[:-2]
                    pass
                if pb.process.poll() is None:
                    pass
                else:
                    print "process start failed"
                    return None
                gotErrStr = pb.readErr_nowait()

    #pwscmd = 'D:\\Go\\leela-zero-0.16-win64\\leelaz.exe -g --noponder -t 2 -wD:\\Go\\weights\\' \
    pwscmd = 'E:\\Go\\1130fastexit-tensor-accum\\leelaz.exe -g --noponder -t 1 --precision half --batchsize 4 -wE:\\Go\\Ana_1.26\\weight\\' \
             +weightw+' --gpu 0 -p 100000'
    pwcwdstr = 'E:\\Go\\1130fastexit-tensor-accum'
    #print pwscmd
    pwcommand = pwscmd.split(' ')
    try:
        pw = goEngin(pwcommand, pwcwdstr)
    except (Exception) as e:
        print("Error found:", e)
        return None

    processTest = True
    while processTest:
        #print 'test pw alive',pw.process.poll()
        if pw.process.poll() is None:
            pass
        else:
            gotStdStr = pw.readAns_nowait()
            while gotStdStr != None:
                print gotStdStr
                gotStdStr = pw.readAns_nowait()
            print "process start failed"
            return None
        sleep(0.001)
        gotErrStr = pw.readErr_nowait()
        if gotErrStr != None:
            while gotErrStr != None:
                if gotErrStr[:24] == 'Setting max tree size to':
                    print 'pw.1', gotErrStr[:-2]
                    print 'White is ready.'
                    processTest = False
                else:
                    #print 'pw.1',gotErrStr[:-2]
                    pass
                if pw.process.poll() is None:
                    pass
                else:
                    print "process start failed"
                    return None
                gotErrStr = pw.readErr_nowait()

    initCmds = [
        'version', 'boardsize 19', 'komi 7.5',
        'time_settings 0 ' + str(spendTime + 1) + ' 1'
    ]

    for cmd in initCmds:
        print 'Sending pb[', cmd, ']',
        try:
            pb.write(cmd)
        except (Exception) as e:
            print("Error found:", e)
            return None
        gotAns = pb.readAns()
        print 'pb Answer is :', gotAns[:-2]
        pb.clearErrQ()

    for cmd in initCmds:
        print 'Sending pw[', cmd, ']',
        try:
            pw.write(cmd)
        except (Exception) as e:
            print("Error found:", e)
            return None
        gotAns = pw.readAns()
        print 'pw Answer is :', gotAns[:-2]
        pw.clearErrQ()

    #开始对战
    stepTime1 = datetime.datetime.now()
    startTime = stepTime1
    resigned = False
    black_po = 0  #黑棋po总值
    white_po = 0  #白棋po总值
    cmdStr = 'genmove b'
    pb.write(cmdStr)
    gotAns = pb.readAns()
    #print 'Black First Move is :', gotAns
    sleep(0.01)
    #取得对局下一手相关信息:落子点、胜率、预测后几步走法、playouts
    errQTime1 = datetime.datetime.now()  #记录读取errQ的时长
    strInfo = pb.clearErrQ()
    errQTime2 = datetime.datetime.now()  #记录读取errQ的时长
    firstStep, stepWinrate, lcbrate, mightMoves, povalue = getStepInfo(
        strInfo, gotAns[2:-2])
    if (stepWinrate == None or povalue == None) and gotAns[2:-2] != 'resign':
        print repr(strInfo)

    cmdStr = 'play b ' + gotAns[2:-2]
    sgfStr = gotAns[2:-2]  #去除末尾的\r\n
    node = g.extend_main_sequence()
    node.set_move('b', a2num(sgfStr))
    if stepWinrate != None and povalue != None:
        if lcbrate != None:
            node.set("C", stepWinrate + '% lcb:' + lcbrate + '% po:' + povalue)
        else:
            node.set("C", stepWinrate + '% po:' + povalue)
        black_po += int(povalue)

    steps = 1
    whowins = ''

    while not resigned:
        pw.write(cmdStr)
        stepTime2 = datetime.datetime.now()
        print steps,cmdStr,'WinRate:(',stepWinrate,'%)(lcb:',lcbrate,'%)(po:',povalue,')', \
              (stepTime2-stepTime1).total_seconds(),'s',(errQTime2-errQTime1).total_seconds(),'s'
        steps += 1
        gotAns = pw.readAns()
        #print 'White answer is :', gotAns
        cmdStr = 'genmove w'
        pw.write(cmdStr)
        gotAns = pw.readAns()
        #print 'White answer is :', gotAns
        sleep(0.01)
        #取得对局下一手相关信息:落子点、胜率、预测后几步走法
        errQTime1 = datetime.datetime.now()  #记录读取errQ的时长
        strInfo = pw.clearErrQ()
        errQTime2 = datetime.datetime.now()  #记录读取errQ的时长
        firstStep, stepWinrate, lcbrate, mightMoves, povalue = getStepInfo(
            strInfo, gotAns[2:-2])
        if (stepWinrate == None
                or povalue == None) and gotAns[2:-2] != 'resign':
            print repr(strInfo)

        if gotAns[2:] == u'pass\r\n':
            sgfStr += ',' + gotAns[2:-2]
            node = g.extend_main_sequence()
            node.set_move('w', None)
            cmdStr = 'play w ' + gotAns[2:-2]
        elif gotAns[2:] != u'resign\r\n':
            sgfStr += ',' + gotAns[2:-2]
            node = g.extend_main_sequence()
            node.set_move('w', a2num(gotAns[2:-2]))
            if stepWinrate != None and povalue != None:
                if lcbrate != None:
                    node.set(
                        "C",
                        stepWinrate + '% lcb:' + lcbrate + '% po:' + povalue)
                else:
                    node.set("C", stepWinrate + '% po:' + povalue)
                white_po += int(povalue)
            cmdStr = 'play w ' + gotAns[2:-2]
        else:
            print 'White resigned!'
            node = g.extend_main_sequence()
            node.set_move('w', None)
            node.set("C", "White resigned!")
            g.root.set('RE', 'B+')
            resigned = True
            whowins = 'b'
            endTime = datetime.datetime.now()
            continue

        pb.write(cmdStr)
        stepTime1 = datetime.datetime.now()
        print steps,cmdStr,'WinRate:(',stepWinrate,'%)(lcb:',lcbrate,'%)(po:',povalue,')', \
              (stepTime1-stepTime2).total_seconds(),'s',(errQTime2-errQTime1).total_seconds(),'s'
        steps += 1
        gotAns = pb.readAns()
        #print 'Black answer is :', gotAns
        cmdStr = 'genmove b'
        pb.write(cmdStr)
        gotAns = pb.readAns()
        #print 'Black answer is :', gotAns
        sleep(0.01)
        #取得对局下一手相关信息:落子点、胜率、预测后几步走法
        errQTime1 = datetime.datetime.now()  #记录读取errQ的时长
        strInfo = pb.clearErrQ()
        errQTime2 = datetime.datetime.now()  #记录读取errQ的时长
        firstStep, stepWinrate, lcbrate, mightMoves, povalue = getStepInfo(
            strInfo, gotAns[2:-2])
        if (stepWinrate == None
                or povalue == None) and gotAns[2:-2] != 'resign':
            print repr(strInfo)

        if gotAns[2:] == u'pass\r\n':
            sgfStr += ',' + gotAns[2:-2]
            node = g.extend_main_sequence()
            node.set_move('b', None)
            cmdStr = 'play b ' + gotAns[2:-2]
        elif gotAns[2:] != u'resign\r\n':
            sgfStr += ',' + gotAns[2:-2]
            node = g.extend_main_sequence()
            node.set_move('b', a2num(gotAns[2:-2]))
            if stepWinrate != None and povalue != None:
                if lcbrate != None:
                    node.set(
                        "C",
                        stepWinrate + '% lcb:' + lcbrate + '% po:' + povalue)
                else:
                    node.set("C", stepWinrate + '% po:' + povalue)
                black_po += int(povalue)
            cmdStr = 'play b ' + gotAns[2:-2]
        else:
            print 'Black resigned!'
            node = g.extend_main_sequence()
            node.set_move('b', None)
            node.set("C", "Black resigned!")
            g.root.set('RE', 'W+')
            resigned = True
            whowins = 'w'
            endTime = datetime.datetime.now()
            continue

        if steps >= 500:
            print 'Too many Moves!'
            node.set("C", "Too many Moves!")
            resigned = True
            whowins = 'x'
            endTime = datetime.datetime.now()
            continue

    #计算黑白平均po值
    if steps % 2 == 0:  #偶数说明是白投降了
        avgBlackPo = black_po / (steps / 2)
        avgWhitePo = white_po / (steps / 2 - 1)
    else:
        avgBlackPo = black_po / ((steps - 1) / 2)
        avgWhitePo = white_po / ((steps - 1) / 2)

    print '本局共耗时:', (endTime - startTime).total_seconds(
    ), 's', 'avgBpo:', avgBlackPo, 'avgWpo:', avgWhitePo
    #print sgfStr
    sgffile = open(
        weightb + ' B' + str(avgBlackPo) + 'po vs ' + weightw + ' W' +
        str(avgWhitePo) + 'po-' + str(spendTime) + 's-' + str(num) + '-' +
        whowins + '+.sgf', 'w')
    sgffile.write(g.serialise())
    sgffile.close()
    pb.close()
    pw.close()
    return whowins