def play(startingPlayer):
    # csvGameRecords = []
    csvTurnRecords = []
    scores = [0 for i in range(2)]
    hands = []
    hands.append([])
    hands.append([])

    while scores[0] < GinRummyUtil.GOAL_SCORE and scores[
            1] < GinRummyUtil.GOAL_SCORE:
        currentPlayer = startingPlayer
        opponent = 1 if currentPlayer == 0 else 0
        prevScores = copy.copy(scores)

        deck = Card.getShuffle(random.randrange(sys.maxsize))
        # deck = Card.getShuffle(1)
        # print(deck)
        hands[0].clear()
        hands[1].clear()
        # deal cards to players
        for i in range(2 * HAND_SIZE):
            hands[i % 2].append(deck.pop())
        for i in range(2):
            handArr = hands[i]
            players[i].startGame(i, startingPlayer, handArr)
            if playVerbose:
                print("Player {} is dealt {}.\n".format(i, hands[i]))
        if playVerbose:
            print("Player {} starts.\n".format(startingPlayer))
        discards = []  # new  Stack<Card>()
        discards.append(deck.pop())
        if playVerbose:
            print("The initial face up card is {}.\n".format(discards[-1]))
        firstFaceUpCard = discards[
            -1]  #https://stackoverflow.com/questions/4085417/how-to-check-the-last-element-of-a-python-list discards.peek()
        turnsTaken = 0
        knockMelds = None

        revealedCards = []  # ArrayList<Card>()
        revealedCards.append(firstFaceUpCard)
        if currentPlayer == 0:
            currentCards = copy.copy(hands[currentPlayer])
            revealedCards.extend(currentCards)

        # while there still card in the deck of card, play
        while len(deck) > 2:
            # print('len deck {}'.format(len(deck)))
            if toCSV1:
                turn = []  # new ArrayList<Object>()
                turn.append(currentPlayer)
                turn.append(turnsTaken + 1)
                unmeldedCards = copy.copy(hands[currentPlayer])

                turn.append(unmeldedCards)

                bestMelds = GinRummyUtil.cardsToBestMeldSets(unmeldedCards)
                if bestMelds:  #!bestMelds.isEmpty()
                    melds = bestMelds[0]
                    # turn[3]
                    turn.append(copy.copy(melds))
                    for meld in melds:
                        for card in meld:
                            unmeldedCards.remove(card)
                    melds.append(unmeldedCards)
                    # turn[4] - deadwoods
                    turn.append(copy.copy(unmeldedCards))
                else:
                    turn.append([])
                    turn.append(copy.copy(unmeldedCards))

                drawFaceUp = False
                faceUpCard = discards[-1]
                # turn[5]
                turn.append(faceUpCard)
                if not (turnsTaken == 3 and faceUpCard == firstFaceUpCard
                        ):  # only for the beginning of the hand
                    drawFaceUp = players[currentPlayer].willDrawFaceUpCard(
                        faceUpCard)
                    # print("drawFaceUp {}".format(drawFaceUp))
                    if not drawFaceUp and faceUpCard == firstFaceUpCard and turnsTaken < 2:
                        if playVerbose:
                            print("Player {} declines {}.\n".format(
                                currentPlayer, firstFaceUpCard))
                        turn.append(False)
                        turn.append(False)
                        turn.append(None)
                        if (currentPlayer == 0):
                            turn.append(copy.deepcopy(revealedCards))

                if not ((not drawFaceUp) and turnsTaken < 2
                        and faceUpCard == firstFaceUpCard):
                    drawCard = discards.pop() if drawFaceUp else deck.pop()
                    for i in range(2):
                        players[i].reportDraw(
                            currentPlayer, drawCard
                            if i == currentPlayer or drawFaceUp else None)
                    if playVerbose:
                        print("Player {} draws {}.\n".format(
                            currentPlayer, drawCard))
                    hands[currentPlayer].append(drawCard)

                    if (drawFaceUp):
                        turn.append(True)
                        turn.append(False)
                    else:
                        turn.append(False)
                        turn.append(True)
                    if (currentPlayer == 0):
                        revealedCards.append(drawCard)
                    discardCard = players[currentPlayer].getDiscard()
                    if (not (discardCard in hands[currentPlayer])
                        ) or discardCard == faceUpCard:
                        if playVerbose:
                            print(
                                "Player {} discards {} illegally and forfeits.\n"
                                .format(currentPlayer, discardCard))
                        return opponent
                    hands[currentPlayer].remove(discardCard)
                    for i in range(2):
                        players[i].reportDiscard(currentPlayer, discardCard)
                    if playVerbose:
                        print("Player {} discards {}.\n".format(
                            currentPlayer, discardCard))
                    discards.append(discardCard)

                    # turn[8]
                    turn.append(discardCard)
                    if (currentPlayer == 1):
                        revealedCards.append(discardCard)

                    if (playVerbose):
                        if (not bestMelds):  #if bestMelds.isEmpty()
                            print(
                                "Player {} has {} with {} deadwood.\n".format(
                                    currentPlayer, unmeldedCards,
                                    GinRummyUtil.getDeadwoodPoints(
                                        None, None, None, unmeldedCards)))
                        else:
                            melds = bestMelds[0]
                            for meld in melds:
                                for card in meld:
                                    unmeldedCards.remove(card)
                            melds.append(unmeldedCards)
                            print(
                                "Player {} has {} with {} deadwood.\n".format(
                                    currentPlayer, melds,
                                    GinRummyUtil.getDeadwoodPoints(
                                        None, None, None, unmeldedCards)))
                    # only player, not opponent receive revealed cards info
                    if (currentPlayer == 0):
                        # turn[9]
                        turn.append(copy.deepcopy(revealedCards))

                    csvTurnRecords.append(turn)

                    knockMelds = players[currentPlayer].getFinalMelds()
                    # print("knockMelds {}".format(knockMelds))
                    if (knockMelds != None):
                        break
                turnsTaken = turnsTaken + 1
                currentPlayer = 1 if currentPlayer == 0 else 0
                opponent = 1 if currentPlayer == 0 else 0

        if (knockMelds != None):
            handBitstring = GinRummyUtil.cardsToBitstring(hands[currentPlayer])
            # print("handBitstring {}".format(handBitstring))
            unmelded = handBitstring
            for meld in knockMelds:
                # print("meld {}".format(meld))
                meldBitstring = GinRummyUtil.cardsToBitstring(meld)
                # print("meldBitstring {}".format(meldBitstring))
                if (not (meldBitstring in GinRummyUtil.getAllMeldBitstrings())
                    ) or ((meldBitstring & unmelded) != meldBitstring):  # TODO
                    if playVerbose:
                        print("Player {} melds {} illegally and forfeits.\n".
                              format(currentPlayer, knockMelds))
                    print("not here 1")
                    return opponent
                unmelded = unmelded & ~meldBitstring
            #print("current hand {}".format(hands[currentPlayer]))
            knockingDeadwood = GinRummyUtil.getDeadwoodPoints(
                knockMelds, hands[currentPlayer], None, None)
            # print("knowckingDeadwood {}".format(knockingDeadwood))
            if (knockingDeadwood > GinRummyUtil.MAX_DEADWOOD):
                if (playVerbose):
                    print(
                        "Player {} melds {} with greater than {} deadwood and forfeits.\n"
                        .format(currentPlayer, knockMelds, knockingDeadwood))
                print("not here 2")
                return opponent

            meldsCopy = []
            for i in range(2):
                # meldsCopy =   < <Card>>()
                for meld in knockMelds:
                    meldsCopy.append(copy.copy(meld))
                meldsCopy.append(GinRummyUtil.bitstringToCards(unmelded))
                # print("meldsCopy {}".format(meldsCopy))
                players[i].reportFinalMelds(currentPlayer, meldsCopy)

            if playVerbose:
                if (knockingDeadwood > 0):
                    print("Player {} melds {} with {} deadwood from {}.\n".
                          format(currentPlayer, knockMelds, knockingDeadwood,
                                 GinRummyUtil.bitstringToCards(unmelded)))
                else:
                    print("Player {} goes gin with melds {}.\n".format(
                        currentPlayer, knockMelds))

            opponentMelds = players[opponent].getFinalMelds()
            # print("opponentMelds {}".format(opponentMelds))
            opponentHandBitstring = GinRummyUtil.cardsToBitstring(
                hands[opponent])
            # print("opponentHandBitstring {}".format(opponentHandBitstring))
            opponentUnmelded = opponentHandBitstring
            for meld in opponentMelds:
                meldBitstring = GinRummyUtil.cardsToBitstring(meld)
                if not (meldBitstring in GinRummyUtil.getAllMeldBitstrings(
                )) or ((meldBitstring & opponentUnmelded) != meldBitstring):
                    if (playVerbose):
                        print("Player {} melds {} illegally and forfeits.\n".
                              format(opponent, opponentMelds))
                    print("not here 3")
                    return currentPlayer
                opponentUnmelded = opponentUnmelded & ~meldBitstring
            unmeldedCards = GinRummyUtil.bitstringToCards(opponentUnmelded)
            # print("unmeldedCards {}".format(unmeldedCards))
            if (knockingDeadwood > 0):
                cardWasLaidOff = True
                while True:
                    cardWasLaidOff = False
                    layOffCard = None
                    layOffMeld = None
                    for card in unmeldedCards:
                        for meld in knockMelds:
                            newMeld = copy.deepcopy(meld)
                            newMeld.append(card)
                            newMeldBitstring = GinRummyUtil.cardsToBitstring(
                                newMeld)
                            if (newMeldBitstring
                                    in GinRummyUtil.getAllMeldBitstrings()):
                                layOffCard = card
                                layOffMeld = meld
                                break
                        if (layOffCard != None):
                            if (playVerbose):
                                print("Player {} lays off {} on {}.\n".format(
                                    opponent, layOffCard, layOffMeld))
                            for player in range(2):
                                players[player].reportLayoff(
                                    opponent, layOffCard, layOffMeld)
                            unmeldedCards.remove(layOffCard)
                            layOffMeld.append(layOffCard)
                            cardWasLaidOff = True
                            break
                    if (not cardWasLaidOff):
                        break

            opponentDeadwood = 0
            for card in unmeldedCards:
                opponentDeadwood += GinRummyUtil.getDeadwoodPoints(
                    None, None, card, None)
            # print("opponentDeadwood {}".format(opponentDeadwood))
            if (playVerbose):
                print("Player {} has {} deadwood with {}\n".format(
                    opponent, opponentDeadwood, unmeldedCards))
            if (playVerbose):
                print("Player {} melds {}.\n".format(opponent, opponentMelds))

            for i in range(2):
                meldsCopy = []
                for meld in opponentMelds:
                    meldsCopy.append(copy.copy(meld))
                meldsCopy.append(copy.copy(unmeldedCards))
                players[i].reportFinalMelds(opponent, meldsCopy)

            if (knockingDeadwood == 0):
                scores[
                    currentPlayer] += GinRummyUtil.GIN_BONUS + opponentDeadwood
                if (playVerbose):
                    print(
                        "Player {} scores the gin bonus of {} plus opponent deadwood {} for {} total points.\n"
                        .format(currentPlayer, GinRummyUtil.GIN_BONUS,
                                opponentDeadwood,
                                GinRummyUtil.GIN_BONUS + opponentDeadwood))
            elif (knockingDeadwood < opponentDeadwood):
                scores[currentPlayer] += opponentDeadwood - knockingDeadwood
                if (playVerbose):
                    print("Player {} scores the deadwood difference of {}.\n".
                          format(currentPlayer,
                                 opponentDeadwood - knockingDeadwood))
            else:
                scores[
                    opponent] += GinRummyUtil.UNDERCUT_BONUS + knockingDeadwood - opponentDeadwood
                if (playVerbose):
                    print(
                        "Player {} undercuts and scores the undercut bonus of {} plus deadwood difference of {} for {} total points.\n"
                        .format(
                            opponent, GinRummyUtil.UNDERCUT_BONUS,
                            knockingDeadwood - opponentDeadwood,
                            GinRummyUtil.UNDERCUT_BONUS + knockingDeadwood -
                            opponentDeadwood))
            startingPlayer = 1 if startingPlayer == 0 else 0
        else:  # If the round ends due to a two card draw pile with no knocking, the round is cancelled.
            if (playVerbose):
                print(
                    "The draw pile was reduced to two cards without knocking, so the hand is cancelled."
                )

        for i in range(2):
            for j in range(2):
                players[i].reportFinalHand(j, hands[j])

        if (playVerbose):
            print("Player\tScore\n0\t%d\n1\t%d\n", scores[0], scores[1])
        for i in range(2):
            players[i].reportScores(copy.deepcopy(scores))

        if (toCSV1):
            # fix: for some reason an odd number of turn records produce error. Thus if odd, remove the last turn record
            if (len(csvTurnRecords) % 2 != 0):  # &
                del csvTurnRecords[len(csvTurnRecords) - 1]
            i = 0
            while i < len(csvTurnRecords) and (i + 1) < len(csvTurnRecords):
                # Get turn of player and opponent
                playerTurn = []
                oppTurn = []
                firstPlayer = (csvTurnRecords[i])[0]
                if (firstPlayer == 0):
                    playerTurn = csvTurnRecords[i]
                    oppTurn = csvTurnRecords[i + 1]
                else:
                    playerTurn = csvTurnRecords[i + 1]
                    oppTurn = csvTurnRecords[i]

                # add opponent draw face up card (oppTurn[5]) to playerTurn
                if oppTurn[6]:
                    playerTurn.append(oppTurn[5])
                else:
                    playerTurn.append(None)
                # add opponent discard card (oppTurn[8]) to playerTurn
                playerTurn.append(oppTurn[8])
                i = i + 2

            # add hand outcome
            for i in range(len(csvTurnRecords)):
                turn = csvTurnRecords[i]
                turnPlayer = turn[0]
                turnOpponent = 1 - turnPlayer
                handOutcome = scores[turnPlayer] - prevScores[
                    turnPlayer] - scores[turnOpponent] + prevScores[
                        turnOpponent]
                turn.append(handOutcome)

            # write to csv file
            for turn in csvTurnRecords:
                csv = turn  # redundancy just so that don't have to modify code below
                #matrix = [[0 for c in range(65)] for r in range(4)] #All info of the turn
                matrix = [[0 for c in range(91)]
                          for r in range(4)]  #All info of the turn
                handOutcome = 0
                if (csv[0] == 0):
                    for i in range(len(csv)):
                        if i > 1 and i != 9 and i != 10:  # disregard [0] (player), [1] (turn), revealed cards, and opp draw face up
                            if (i == 12):
                                handOutcome = csv[i]
                            #elif (i != 6): # { // if not numeric values: draw card, discard card, revealed cards, opp draw, opp discard
                            else:  # { // if not numeric values: draw card, discard card, revealed cards, opp draw, opp discard
                                rankOffset = 0  # // indicate to which set of the 13 rank the info belong
                                #// suit order: club, heart, space, diamond
                                # if (i == 2): # if player's hand
                                if (i == 3):  # if melds
                                    rankOffset = 0
                                    cards = []
                                    for meld in csv[i]:
                                        cards.extend(meld)
                                    for c in range(len(cards)):
                                        card = cards[c]
                                        suit = card.getSuit()
                                        rank = card.getRank()
                                        matrix[suit][rank + rankOffset] = 1
                                elif (i == 4):  # if deadwoods
                                    rankOffset = 1 * 13
                                    cards = []
                                    cards.extend(csv[i])
                                    for c in range(len(cards)):
                                        card = cards[c]
                                        suit = card.getSuit()
                                        rank = card.getRank()
                                        matrix[suit][rank + rankOffset] = 1
                                elif i > 4 and i <= 11:
                                    if i == 11:
                                        rankOffset = 2 * 13
                                    elif i == 5:  # face up card
                                        rankOffset = 3 * 13
                                    elif (i == 6):  # draw face up?
                                        rankOffset = 4 * 13
                                    elif (i == 7):  # draw face down?
                                        rankOffset = 5 * 13
                                    elif (i == 8):  # discard card
                                        rankOffset = 6 * 13
                                    if i == 6 or i == 7:
                                        if csv[i]:
                                            for r in range(4):
                                                for c in range(
                                                        rankOffset,
                                                        rankOffset + 13):
                                                    matrix[r][c] = 1
                                    elif (csv[i] != None):
                                        card = csv[i]
                                        suit = card.getSuit()
                                        rank = card.getRank()
                                        matrix[suit][rank + rankOffset] = 1

                    for r in range(len(matrix)):
                        out.writerow(matrix[r])
                    outY.writerow([handOutcome])

            #csvGameRecords.extend(copy.deepcopy(csvTurnRecords))
            #DataCollector.csvRecords.extend(csvGameRecords)
            csvTurnRecords.clear()
    #if playVerbose:
    #print("Player {} wins.\n".format(0 if scores[0] > scores[1] else 1))
    # winOutcome = [1 if scores[0] > scores[1] else 0, 0 if scores[0] > scores[1] else 1]
    # for turn in csvGameRecords:
    #     turnPlayer = turn[0]
    #     turn.append(winOutcome[turnPlayer])
    # DataCollector.csvRecords.extend(copy.deepcopy(csvGameRecords))
    # csvGameRecords.clear()
    return 0 if scores[0] >= GinRummyUtil.GOAL_SCORE else 1
def play(startingPlayer):
    # csvGameRecords = []
    csvTurnRecords = []
    scores = [0 for i in range(2)]
    hands = []
    hands.append([])
    hands.append([])

    while scores[0] < GinRummyUtil.GOAL_SCORE and scores[
            1] < GinRummyUtil.GOAL_SCORE:
        turnRecords = []
        currentPlayer = startingPlayer
        opponent = 1 if currentPlayer == 0 else 0
        prevScores = copy.copy(scores)

        deck = Card.getShuffle(random.randrange(sys.maxsize))
        # deck = Card.getShuffle(1)
        # print(deck)
        hands[0].clear()
        hands[1].clear()
        # deal cards to players
        for i in range(2 * HAND_SIZE):
            hands[i % 2].append(deck.pop())
        for i in range(2):
            handArr = hands[i]
            players[i].startGame(i, startingPlayer, handArr)
            if playVerbose:
                print("Player {} is dealt {}.".format(i, hands[i]))
        if playVerbose:
            print("Player {} starts.".format(startingPlayer))
        discards = []  # new  Stack<Card>()
        discards.append(deck.pop())
        if playVerbose:
            print("The initial face up card is {}.".format(discards[-1]))
        firstFaceUpCard = discards[
            -1]  #https://stackoverflow.com/questions/4085417/how-to-check-the-last-element-of-a-python-list discards.peek()
        turnsTaken = 0
        knockMelds = None

        # turn data
        revealedCards = []
        opponentDrawnCards = []
        opponentDiscardedCards = []

        revealedCards.append(firstFaceUpCard)
        if currentPlayer == 0:
            currentCards = copy.copy(hands[currentPlayer])
            revealedCards.extend(currentCards)

        # while there still card in the deck of card
        while len(deck) > 2:
            for i in range(2):
                if playVerbose:
                    print("Player {}'s hand {}.".format(i, hands[i]))
            turn = []
            unmeldedCards = copy.copy(hands[currentPlayer])
            turn.append(copy.copy(hands[currentPlayer]))

            bestMelds = GinRummyUtil.cardsToBestMeldSets(unmeldedCards)
            playerMelds = []
            if bestMelds:  #!bestMelds.isEmpty()
                melds = bestMelds[0]
                for meld in melds:
                    playerMelds.extend(meld)
                    for card in meld:
                        unmeldedCards.remove(card)
                melds.append(unmeldedCards)

            drawFaceUp = False
            faceUpCard = discards[-1]

            #########################################################################################################################
            # prepare info for player draw decision
            # include, in order: current hand, opponent drawn cards, player discards, opponent discards
            if (currentPlayer == 0):
                decisionData = [
                ]  # temporary holder before write out to matrix. Will be cleared immediately after written to matrix
                decisionData.append(hands[currentPlayer])
                decisionData.append(opponentDrawnCards)
                decisionData.append(opponentDiscardedCards)
                decisionData.append(revealedCards)
                # decisionData.append(playerMelds)
                # decisionData.append(unmeldedCards)
                # decisionData.append(opponentDiscardedCards)
                # decisionData.append(faceUpCard)
                ##########################################################################################################################

                #<####################################################################################################
                # TODO implement the decline option for the first 2 turns
                # Continue with turn if not initial declined option
                #<
                #if not ((not drawFaceUp) and turnsTaken < 2 and faceUpCard == firstFaceUpCard):
                # deciding whether draw face up (discards.pop()) or draw face down (deck.pop())
                # draw whichever gives higher predicted hand result
                # 1 if draw face up
                # 1.1 append the face up card to the player hand for testing
                drawFaceUpPoint = 0
                drawFaceDownPoint = 0

                decisionData[0].append(
                    discards[-1])  # assume that the player draw face up
                discardIfDrawFaceUp = None
                maxHandOutcomeIfDrawFaceUp = -1000000
                # 1.2 try different possible discards and get the highest expected score
                cards = copy.copy(
                    decisionData[0]
                )  # so that can freely remove and append on decisionData[0]
                for card in cards:
                    decisionData[0].remove(card)
                    hand = arrayToData(decisionData)
                    # if playVerboseNetwork:
                    #     print("removed card {}".format(card))
                    predictedHandOutcome = network.predict(hand)[0][0]
                    # if playVerboseNetwork:
                    #     print("estimated handoutcome of draw face up card {} with {} discard: {}".format(discards[-1], card, predictedHandOutcome))
                    if predictedHandOutcome > maxHandOutcomeIfDrawFaceUp:
                        maxHandOutcomeIfDrawFaceUp = predictedHandOutcome
                        discardIfDrawFaceUp = card
                    decisionData[0].append(card)
                # print("drawFaceUpPoint {}".format(drawFaceUpPoint))
                # if playVerboseNetwork:
                #     print("max draw face up card {}: {} with {} discard".format(discards[-1], maxHandOutcomeIfDrawFaceUp, discardIfDrawFaceUp))
                # 1.3 remove the face up card. Don't make decision yet till check draw face down
                decisionData[0].remove(discards[-1])
                # hand should still have just 10 cards
                if len(decisionData[0]) > 10 or len(hands[currentPlayer]) > 10:
                    print("more cards in hand than allowed")
                    exit(1)

                # 2: get average point of drawing a face down card
                drawFaceDownPoint = 0
                # 2.1 for each face down card
                for card in deck:
                    # 2.1.1 get the max score if drawing this card by checking point of each possible discard
                    decisionData[0].append(card)
                    cards = copy.copy(decisionData[0])
                    maxHandOutcomeThisFaceDown = -100000000
                    for c in cards:
                        decisionData[0].remove(c)
                        handData = arrayToData(decisionData)
                        predictedHandOutcome = network.predict(handData)[0][0]
                        if maxHandOutcomeThisFaceDown < predictedHandOutcome:
                            maxHandOutcomeThisFaceDown = predictedHandOutcome
                        decisionData[0].append(c)
                    # 2.1.2 add the max score for drawing this card to drawFaceDownPoint
                    drawFaceDownPoint += maxHandOutcomeThisFaceDown
                    # print("pointOfCurrentCard {}".format(pointOfCurrentCard))
                    # 2.1.3 remove this card out of hand
                    decisionData[0].remove(card)
                # hand should still have just 10 cards
                if len(decisionData[0]) > 10 or len(hands[currentPlayer]) > 10:
                    print("more cards in hand than allowed")
                    exit(1)
                drawFaceDownPoint /= len(deck)
                # if playVerboseNetwork:
                #     print("predicted outcome draw face down: {}".format(drawFaceDownPoint))

                drawCard = None
                drawFaceUp = False
                if maxHandOutcomeIfDrawFaceUp >= drawFaceDownPoint:
                    drawCard = discards.pop()
                    drawFaceUp = True
                else:
                    drawCard = deck.pop()
                    revealedCards.append(drawCard)

                if playVerbose:
                    print("Player {} draws {}.\n".format(
                        currentPlayer, drawCard))

                if playVerboseNetwork:
                    if drawFaceUp:
                        print("Conv net draws face-up {}.\n".format(drawCard))
                    else:
                        print(
                            "Conv net draws face-down {}.\n".format(drawCard))

                for i in range(2):
                    players[i].reportDraw(
                        currentPlayer,
                        drawCard if i == currentPlayer or drawFaceUp else None)

                hands[currentPlayer].append(drawCard)

                # deciding which card to discard
                discardCard = None
                if drawFaceUp:
                    discardCard = discardIfDrawFaceUp
                else:
                    bestCardToRemove = None
                    maxPoint = -10000000
                    for card in hands[currentPlayer]:
                        decisionData[0].remove(card)
                        handIfDiscardThisCard = arrayToData(decisionData)
                        predictedPoint = network.predict(handIfDiscardThisCard)
                        predictedPoint = predictedPoint[0][0]
                        # print("predictedPoint {}".format(predictedPoint))
                        if predictedPoint > maxPoint:
                            maxPoint = predictedPoint
                            bestCardToRemove = card
                        decisionData[0].append(card)
                    discardCard = bestCardToRemove
                    # if (not (bestCardToRemove in hands[currentPlayer])) or bestCardToRemove == faceUpCard:
                    #     if playVerbose:
                    #         print("Player {} discards {} illegally and forfeits.\n".format(currentPlayer, discardCard))
                    #     return opponent
                    # if playVerboseNetwork:
                    #     print("predicted maxPoint of discard after a draw-face-down: {}".format(maxPoint))
                hands[currentPlayer].remove(discardCard)

                # report the discarded card to both players
                for i in range(2):
                    players[i].reportDiscard(currentPlayer, discardCard)

                # add to discard pile of possible drawing
                discards.append(discardCard)

                if playVerbose:
                    print("Player {} discards {}.\n".format(
                        currentPlayer, discardCard))

                if playVerboseNetwork:
                    print("Conv net discards {}.\n".format(discardCard))

                # CHECK FOR KNOCK
                knockMelds = players[currentPlayer].getFinalMelds()
                # print("knockMelds {}".format(knockMelds))
                if (knockMelds != None):
                    break  # player knocked, end of round
                #>

                # RECORD TURN DATA
                turn.append(copy.deepcopy(hands[currentPlayer]))
                turn.append(copy.deepcopy(opponentDrawnCards))
                turn.append(copy.deepcopy(opponentDiscardedCards))
                turn.append(copy.deepcopy(revealedCards))
                turnRecords.append(copy.deepcopy(turn))
                turn.clear()
            else:  # Use old implementation for opponent
                # ####< Old implementation ####
                # offer draw face - up iff not 3rd turn with first face up card (decline automatically in that case)
                if not (turnsTaken == 3 and faceUpCard == firstFaceUpCard):
                    # Moment of decision: draw a face up card?
                    drawFaceUp = players[currentPlayer].willDrawFaceUpCard(
                        faceUpCard)
                    # print("drawFaceUp {}".format(drawFaceUp))
                if not drawFaceUp and faceUpCard == firstFaceUpCard and turnsTaken < 2:
                    if playVerbose:
                        print("Player {} declines {}.\n".format(
                            currentPlayer, firstFaceUpCard))
                drawCard = discards.pop() if drawFaceUp else deck.pop()
                for i in range(2):
                    players[i].reportDraw(
                        currentPlayer,
                        drawCard if i == currentPlayer or drawFaceUp else None)
                if playVerbose:
                    print("Player {} draws {}.\n".format(
                        currentPlayer, drawCard))
                hands[currentPlayer].append(drawCard)

                if drawFaceUp:
                    opponentDrawnCards.append(drawCard)

                discardCard = players[currentPlayer].getDiscard()
                if (not (discardCard in hands[currentPlayer])
                    ) or discardCard == faceUpCard:
                    if playVerbose:
                        print(
                            "Player {} discards {} illegally and forfeits.\n".
                            format(currentPlayer, discardCard))
                    return opponent
                hands[currentPlayer].remove(discardCard)
                opponentDiscardedCards.append(discardCard)
                revealedCards.append(discardCard)

                for i in range(2):
                    players[i].reportDiscard(currentPlayer, discardCard)
                if playVerbose:
                    print("Player {} discards {}.\n".format(
                        currentPlayer, discardCard))
                discards.append(discardCard)

                # if (playVerbose):
                #     if (not bestMelds): #if bestMelds.isEmpty()
                #         print("Player {} has {} with {} deadwood.\n".format(currentPlayer, unmeldedCards,
                #               GinRummyUtil.getDeadwoodPoints(None, None, None, unmeldedCards)))
                #     else:
                #         melds = bestMelds[0]
                #         for meld in melds:
                #             for card in meld:
                #                 unmeldedCards.remove(card)
                #         melds.append(unmeldedCards)
                #         print("Player {} has {} with {} deadwood.\n".format(currentPlayer, melds,
                #               GinRummyUtil.getDeadwoodPoints(None, None, None, unmeldedCards)))
                # only player, not opponent receive revealed cards info
                # if (currentPlayer == 0):
                #     turn.append(copy.deepcopy(revealedCards))
                # csvTurnRecords.append(turn)

                knockMelds = players[currentPlayer].getFinalMelds()
                if (knockMelds != None):
                    break
            #>#########################################################################################

            turnsTaken = turnsTaken + 1
            currentPlayer = 1 if currentPlayer == 0 else 0
            opponent = 1 if currentPlayer == 0 else 0

        if (knockMelds != None):
            handBitstring = GinRummyUtil.cardsToBitstring(hands[currentPlayer])
            # print("handBitstring {}".format(handBitstring))
            unmelded = handBitstring
            for meld in knockMelds:
                # print("meld {}".format(meld))
                meldBitstring = GinRummyUtil.cardsToBitstring(meld)
                # print("meldBitstring {}".format(meldBitstring))
                if (not (meldBitstring in GinRummyUtil.getAllMeldBitstrings())
                    ) or ((meldBitstring & unmelded) != meldBitstring):  # TODO
                    if playVerbose:
                        print("Player {} melds {} illegally and forfeits.\n".
                              format(currentPlayer, knockMelds))
                    print("not here 1")
                    return opponent
                unmelded = unmelded & ~meldBitstring
            #print("current hand {}".format(hands[currentPlayer]))
            knockingDeadwood = GinRummyUtil.getDeadwoodPoints(
                knockMelds, hands[currentPlayer], None, None)
            # print("knowckingDeadwood {}".format(knockingDeadwood))
            if (knockingDeadwood > GinRummyUtil.MAX_DEADWOOD):
                if (playVerbose):
                    print(
                        "Player {} melds {} with greater than {} deadwood and forfeits.\n"
                        .format(currentPlayer, knockMelds, knockingDeadwood))
                print("not here 2")
                return opponent

            meldsCopy = []
            for i in range(2):
                # meldsCopy =   < <Card>>()
                for meld in knockMelds:
                    meldsCopy.append(copy.copy(meld))
                meldsCopy.append(GinRummyUtil.bitstringToCards(unmelded))
                # print("meldsCopy {}".format(meldsCopy))
                players[i].reportFinalMelds(currentPlayer, meldsCopy)

            if playVerbose:
                if (knockingDeadwood > 0):
                    print("Player {} melds {} with {} deadwood from {}.\n".
                          format(currentPlayer, knockMelds, knockingDeadwood,
                                 GinRummyUtil.bitstringToCards(unmelded)))
                else:
                    print("Player {} goes gin with melds {}.\n".format(
                        currentPlayer, knockMelds))

            opponentMelds = players[opponent].getFinalMelds()
            # print("opponentMelds {}".format(opponentMelds))
            opponentHandBitstring = GinRummyUtil.cardsToBitstring(
                hands[opponent])
            # print("opponentHandBitstring {}".format(opponentHandBitstring))
            opponentUnmelded = opponentHandBitstring
            for meld in opponentMelds:
                meldBitstring = GinRummyUtil.cardsToBitstring(meld)
                if not (meldBitstring in GinRummyUtil.getAllMeldBitstrings(
                )) or ((meldBitstring & opponentUnmelded) != meldBitstring):
                    if (playVerbose):
                        print("Player {} melds {} illegally and forfeits.\n".
                              format(opponent, opponentMelds))
                    print("not here 3")
                    return currentPlayer
                opponentUnmelded = opponentUnmelded & ~meldBitstring
            unmeldedCards = GinRummyUtil.bitstringToCards(opponentUnmelded)
            # print("unmeldedCards {}".format(unmeldedCards))
            if (knockingDeadwood > 0):
                cardWasLaidOff = True
                while True:
                    cardWasLaidOff = False
                    layOffCard = None
                    layOffMeld = None
                    for card in unmeldedCards:
                        for meld in knockMelds:
                            newMeld = copy.deepcopy(meld)
                            newMeld.append(card)
                            newMeldBitstring = GinRummyUtil.cardsToBitstring(
                                newMeld)
                            if (newMeldBitstring
                                    in GinRummyUtil.getAllMeldBitstrings()):
                                layOffCard = card
                                layOffMeld = meld
                                break
                        if (layOffCard != None):
                            if (playVerbose):
                                print("Player {} lays off {} on {}.\n".format(
                                    opponent, layOffCard, layOffMeld))
                            for player in range(2):
                                players[player].reportLayoff(
                                    opponent, layOffCard, layOffMeld)
                            unmeldedCards.remove(layOffCard)
                            layOffMeld.append(layOffCard)
                            cardWasLaidOff = True
                            break
                    if (not cardWasLaidOff):
                        break

            opponentDeadwood = 0
            for card in unmeldedCards:
                opponentDeadwood += GinRummyUtil.getDeadwoodPoints(
                    None, None, card, None)
            # print("opponentDeadwood {}".format(opponentDeadwood))
            if (playVerbose):
                print("Player {} has {} deadwood with {}\n".format(
                    opponent, opponentDeadwood, unmeldedCards))
            if (playVerbose):
                print("Player {} melds {}.\n".format(opponent, opponentMelds))

            for i in range(2):
                meldsCopy = []
                for meld in opponentMelds:
                    meldsCopy.append(copy.copy(meld))
                meldsCopy.append(copy.copy(unmeldedCards))
                players[i].reportFinalMelds(opponent, meldsCopy)

            if (knockingDeadwood == 0):
                scores[
                    currentPlayer] += GinRummyUtil.GIN_BONUS + opponentDeadwood
                if (playVerbose):
                    print(
                        "Player {} scores the gin bonus of {} plus opponent deadwood {} for {} total points.\n"
                        .format(currentPlayer, GinRummyUtil.GIN_BONUS,
                                opponentDeadwood,
                                GinRummyUtil.GIN_BONUS + opponentDeadwood))
            elif (knockingDeadwood < opponentDeadwood):
                scores[currentPlayer] += opponentDeadwood - knockingDeadwood
                if (playVerbose):
                    print("Player {} scores the deadwood difference of {}.\n".
                          format(currentPlayer,
                                 opponentDeadwood - knockingDeadwood))
            else:
                scores[
                    opponent] += GinRummyUtil.UNDERCUT_BONUS + knockingDeadwood - opponentDeadwood
                if (playVerbose):
                    print(
                        "Player {} undercuts and scores the undercut bonus of {} plus deadwood difference of {} for {} total points.\n"
                        .format(
                            opponent, GinRummyUtil.UNDERCUT_BONUS,
                            knockingDeadwood - opponentDeadwood,
                            GinRummyUtil.UNDERCUT_BONUS + knockingDeadwood -
                            opponentDeadwood))
            startingPlayer = 1 if startingPlayer == 0 else 0
        else:  # If the round ends due to a two card draw pile with no knocking, the round is cancelled.
            if (playVerbose):
                print(
                    "The draw pile was reduced to two cards without knocking, so the hand is cancelled."
                )

        # print("scores1 {}, scores2 {}".format(scores[0],scores[1]))

        # FINAL REPORT
        for i in range(2):
            for j in range(2):
                players[i].reportFinalHand(j, hands[j])

        if (playVerbose):
            print("Player\tScore\n0\t{}\n1\t{}\n".format(scores[0], scores[1]))
        for i in range(2):
            players[i].reportScores(copy.deepcopy(scores))

        if toCSV:
            #WRITE TURN DATA TO FILE
            # write turn data and their according hand outcome to csv file, for training
            for turn in turnRecords:
                matrix = arrayToMatrix(turn)
                for r in range(len(matrix)):
                    out.writerow(matrix[r])
                handOutcome = scores[0] - prevScores[0] - scores[
                    1] + prevScores[1]
                outY.writerow([handOutcome])
        turnRecords.clear()

    #if playVerbose:
    #print("Player {} wins.\n".format(0 if scores[0] > scores[1] else 1))
    # winOutcome = [1 if scores[0] > scores[1] else 0, 0 if scores[0] > scores[1] else 1]
    # for turn in csvGameRecords:
    #     turnPlayer = turn[0]
    #     turn.append(winOutcome[turnPlayer])
    # DataCollector.csvRecords.extend(copy.deepcopy(csvGameRecords))
    # csvGameRecords.clear()
    return 0 if scores[0] >= GinRummyUtil.GOAL_SCORE else 1