python Ticket to Ride Game

在这里插入图片描述
在这里插入图片描述
TTRPlayer.py

import collections

from TTRBoard import Board


class Player(object):
    
    def __init__(self, 
                 startingHand, 
                 startingTickets, 
                 playerBoard, 
                 playerPosition, 
                 numTrains
                 ):
        """orderNumber: int
        startingHand: list
        startingTickets: list
        playerBoard: PlayerBoard object from the TTRBoard module
        playerPosition: int
        """
        self.name           = '' #ask for them to enter it on first turn
        self.isComputerPlayer = False
        #implimented as a collection to avoid O(n) hand.remove(x)
        self.hand           = collections.Counter(startingHand)
        
        self.tickets        = {x:False for x in startingTickets}
        self.numTrains      = numTrains
        self.points         = 0
        self.useDijkstra = False
        self.playerPosition = playerPosition
        self.board = Board()
        self.wayAvailable = []
        self.results = []
        #custom board to represent
        self.playerBoard    = playerBoard
                    
    def removeCardsFromHand(self, color, numColor):
        """removes one ore more cards from hand
        assumes all cards are in hand, error if not
        cards: list
        """
        assert self.hand[color] >= numColor
        self.hand[color] -= numColor
        
    #add card to hand
    def addCardToHand(self, card):
        """adds a single card to hand
        assumes card is a valid choice
        card: String
        """
        if card != None:
            self.hand[card] += 1
    
    #add ticket to hand
    def addTicket(self, ticket):
        """adds a single ticket to tickets
        ticket: tuple(city1, city2, value)
        """
        self.tickets[ticket] = False
    
    def completeTicket(self, ticket):
        """updates the value in the tickets dict to True for key: ticket
        ticket: tuple(city1, city2, value)
        """
        assert ticket in self.tickets
        self.tickets[ticket] = True
    
    def getHand(self):
        return self.hand
    
    def addPoints(self, numPoints):
        self.points += numPoints
    
    def subtractPoints(self, numPoints):
        self.points -= numPoints
        
    def getPoints(self):
        return self.points
        
    def getTickets(self):
        return self.tickets
    
    def getNumTrains(self):
        return self.numTrains
    
    def playNumTrains(self, numTrains):
        assert numTrains <= self.numTrains
        self.numTrains -= numTrains
        
    def setPlayerName(self, name):
        """sets playerName to name
        name: string
        """
        self.name = name
    
    def getName(self):
        return self.name

    def getConnectCityList(self):
        cityList = []
        for ticket in self.getTickets():
            if self.useDijkstra:
                shortest_path = self.board.dijkstraShortestPath(ticket[0], ticket[1])
            else:
                shortest_path = self.board.findAnyPath(ticket[0], ticket[1])
            cityList.append(shortest_path)
        return cityList

    def hasTrainsConnectTicket(self, ways):
        for way in ways:
            for citys in self.getConnectCityList():
                if way[0] in citys and way[1] in citys:
                    self.wayAvailable = [way[0], way[1]]
                    return True
        return False

    def getNeedColors(self):
        colors = []
        for cities in self.getConnectCityList():
            self.results = [[cities[i], cities[i + 1]] for i in range(len(cities) - 1)]
            for result in self.results:

                colors += self.board.getEdgeColors(result[0], result[1])

        return colors


TTRGameSim.py

# this version no longer takes input from user.
# Designed for computer OR human player (mainly comp v comp sim)
import random

# All values and methods associated with the Original Ticket to Ride board game
import TTRBoard
import TTRCards
import TTRPlayer
import collections
import pprint


class Game(object):
    def __init__(self, numPlayers):

        self.sizeDrawPile = 5
        self.numTicketsDealt = 3
        self.sizeStartingHand = 4
        self.maxWilds = 3

        self.endingTrainCount = 3  # ending condition to trigger final round

        self.pointsForLongestRoute = 10
        self.startingNumOfTrains = 10  # 45
        self.deck = TTRCards.Cards(self.sizeDrawPile, self.maxWilds)

        self.board = TTRBoard.Board()
        self.numPlayers = numPlayers
        self.players = []

        self.posToMove = 0

        # point values for tracks of different lengths
        self.routeValues = {1: 1, 2: 2, 3: 4, 4: 7, 5: 10, 6: 15}

        for position in range(numPlayers):
            startingHand = self.deck.dealCards(self.sizeStartingHand)
            startingTickets = []  # self.deck.dealTickets(self.numTicketsDealt)
            # this is now done in initialize method below
            # occurs before first player's first move
            playerBoard = TTRBoard.PlayerBoard()

            player = TTRPlayer.Player(startingHand,
                                      startingTickets,
                                      playerBoard,
                                      position,
                                      self.startingNumOfTrains
                                      )
            self.players.append(player)

    def printSepLine(self, toPrint):
        print(toPrint)

    def advanceOnePlayer(self):
        """Updates self.posToMove"""
        self.posToMove += 1
        self.posToMove %= self.numPlayers

    def getCurrentPlayer(self):
        return self.players[self.posToMove]

    def doesPlayerHaveCardsForEdge(self, player, city1, city2):
        if player.playerBoard.hasEdge(city1, city2):
            return False
        routeDist = self.board.getEdgeWeight(city1, city2)
        routeColors = self.board.getEdgeColors(city1, city2)
        for col in routeColors:
            if col == 'grey':
                if max([x for x in player.hand.values() if x != 'wild']) \
                        + player.hand['wild'] >= routeDist:
                    return True
            else:
                routeDist = self.board.getEdgeWeight(city1, city2)
                if player.hand[col] + player.hand['wild'] >= routeDist:
                    return True
        return False

    def checkEndingCondition(self, player):
        return player.getNumTrains() < self.endingTrainCount

    def initialize(self):
        """Before game turns starts, enter names and pick destination tickets
        """

        for player in self.players:
            # pick desination tickets
            if len(player.getName()) == 0:
                player.setPlayerName(input("Enter your name: "))

            if input("Human(0) or Computer(1) ? ") == '1':
                player.isComputerPlayer = True
                if input("Use Dijkstra(1) or not(0)?") == '1':
                    player.useDijkstra = True

            self.pickTickets(player, 2)

            self.advanceOnePlayer()

    def scorePlayerTickets(self, player):
        """returns None.  
        Scores player's destination tickets and 
        adds/subtracts from player's points
        """
        for ticket in player.tickets:
            city1 = ticket[0]
            city2 = ticket[1]
            value = ticket[2]
            posNodes = player.playerBoard.getNodes()
            if city1 not in posNodes or city2 not in posNodes:
                player.subtractPoints(value)
                continue
            if player.playerBoard.hasPath(city1, city2):
                player.addPoints(value)
                player.completeTicket(ticket)

    def scoreLongestPath(self):
        """determines which player has the longest route and 
        adjusts their score accordingly
        players: list of players
        adds self.pointsForLongestRoute to player with longest route
        """

        scores = {x: (0, ()) for x in self.players}
        longestPath = 0
        for player in scores:

            for city in player.playerBoard.getCities():
                pathInfo = player.playerBoard.longestPath(city)
                if pathInfo[0] > scores[player][0]:
                    scores[player] = pathInfo

            if scores[player][0] > longestPath:
                longestPath = scores[player][0]

        print(scores)

        for player in scores:
            if scores[player][0] == longestPath:
                player.addPoints(self.pointsForLongestRoute)

        # does not return anthing

    def printAllPlayerData(self):
        """prints out all of the non method attributes values for all players
        """
        for player in self.players:
            print(player.name)
            print("------------------------------")
            for x in player.__dict__:
                print(x, player.__dict__[x])
            print(player.playerBoard.iterEdges())


            print("==============================")

    def playTurn(self, player):
        """player chooses 'cards', 'trains', 'tickets'
        player: player object
        """
        choice = ""
        if player.isComputerPlayer:
            print("Please type: cards, trains or tickets: ")
            ways = [x for x in sorted(self.board.iterEdges()) if self.doesPlayerHaveCardsForEdge(player, x[0], x[1])]
            if player.hasTrainsConnectTicket(ways):
                choice = 'trains'
            else:
                choice = 'cards'
            print(choice)
        else:
            choice = input("Please type: cards, trains or tickets: ")

            count = 0  # a way out of the loop if 5 invalid responses
            while choice not in ['cards', 'trains', 'tickets'] and count < 5:
                choice = input("Invalid repsonse. Please select either cards, "
                               + "trains or tickets: ")
                count += 1

            displayMap = input("Display map? y/n: ")
            if displayMap == 'y':
               pauseTime = input("For how many seconds? (between 1 and 30): ")
               if int(pauseTime) not in range(1, 31):
                   pass
               else:
                   self.board.showBoard(self.board.G, int(pauseTime))
                   #Depricated?

            if count >= 5:
                return "Move complete"

        if choice == 'cards':
            self.pickCards(player)
            return "Move complete"

        elif choice == 'trains':
            self.placeTrains(player)
            return "Move complete"
        else:
            self.pickTickets(player)
            return "Move complete"

    def pickCards(self, player):
        count = 0  # a way out of the loop if 5 invalid responses
        print("Your hand consists of: ")
        self.printSepLine(player.getHand())

        print("Draw pile consists of: ")
        self.printSepLine(self.deck.getDrawPile())
        choice1 = ""
        if player.isComputerPlayer:
            print("Please type a card from the above list or "
                  + "type 'drawPile': ")
            if 'wild' in self.deck.getDrawPile():
                choice1 = 'wild'
            else:
                colors = player.getNeedColors()
                for card in self.deck.getDrawPile():
                    if card in colors:
                        choice1 = card
                        break
                if choice1 == '':
                    choice1 = 'drawPile'

        else:
            choice1 = input("Please type a card from the above list or "
                            + "type 'drawPile': ")
            while choice1 not in self.deck.getDrawPile() + ['drawPile'] \
                    and count < 5:
                choice1 = input("Invalid repsonse. Please type either from "
                                + str(self.deck.getDrawPile())
                                + " or type 'drawPile' "
                                )
                count += 1

        # add card to player's hand
        # remove it from drawPile or cards and
        # add new card to drawPile
        if count >= 5:
            pass
        elif choice1 == 'drawPile':
            chosenCard = self.deck.pickFaceDown()
            print("You selected: " + str(chosenCard))
            player.addCardToHand(chosenCard)
        else:
            player.addCardToHand(self.deck.pickFaceUpCard(choice1))

        # start second card selection
        if choice1 == 'wild':
            print("Your hand now consists of: ")
            self.printSepLine(player.getHand())

            return "Move complete"

        count = 0

        self.printSepLine(self.deck.getDrawPile())

        choice2 = ""
        if player.isComputerPlayer:
            print("Please type a card from the above list or "
                  + "type 'drawPile': ")
            colors = player.getNeedColors()
            for card in self.deck.getDrawPile():
                if card in colors:
                    choice2 = card
                    break
            if choice2 == '':
                choice2 = 'drawPile'

        else:
            choice2 = input("Please type another card from the above list or "
                            + "type 'drawPile': ")
            while choice2 == 'wild' \
                    or (choice2 not in self.deck.getDrawPile() + ['drawPile'] \
                        and count < 5):
                choice2 = input("Invalid repsonse. Please type either from "
                                + str(self.deck.getDrawPile())
                                + " or type 'drawPile' \
                                    NOTE: second choice cannot be 'wild' "
                                )
                count += 1

        # add card to player's hand
        # remove it from drawPile or cards and
        # add new card to drawPile
        if count >= 5:
            return "Move complete"
        elif choice2 == 'drawPile':
            chosenCard = self.deck.pickFaceDown()
            print("You selected: " + str(chosenCard))
            player.addCardToHand(chosenCard)
        else:
            player.addCardToHand(self.deck.pickFaceUpCard(choice2))

        print("Your hand now consists of: ")
        self.printSepLine(player.getHand())
        return "Move complete"

    def placeTrains(self, player):
        count = 0
        print("Available cities:")

        # only print routes that are legal given the players cards
        # sort alphabetically
        self.printSepLine([x for x in sorted(self.board.iterEdges())
                           if self.doesPlayerHaveCardsForEdge(player, x[0], x[1])])

        print("Your hand consists of: ")
        self.printSepLine(player.getHand())
        city1 = ''
        city2 = ''
        if player.isComputerPlayer:
            print("Please type the start city of desired route: ", end='')
            city1 = player.wayAvailable[0]
            print(city1)
            print("Please type the destination city to go to from "
                          + str(city1)
                          + " : ", end='')
            city2 = player.wayAvailable[1]
            print(city2)
        else:
            city1 = input("Please type the start city of desired route: ")

            while city1 not in self.board.getCities() and count < 5:
                city1 = input("Invalid response.  "
                              + "Please select from the above city list: "
                              )
                count += 1

            if count >= 5:
                return "Move complete"

            if len([x for x in self.board.G.neighbors(city1)
                    if self.board.hasEdge(city1, x)]) == 0:
                print("You have a selected a city with no legal destination")
                return "Move complete"

            # start city2
            count = 0

            print("Available destination cities: " \
                  + str([x for x in self.board.G.neighbors(city1)
                         if self.doesPlayerHaveCardsForEdge(player, city1, x)]))

            city2 = input("Please type the destination city to go to from "
                          + str(city1)
                          + " : "
                          )

            while not self.board.hasEdge(city1, city2) and count < 5:
                city2 = input("Invalid response.  "
                              + "Please type one of the following cities "
                              + "(without quotes): \n"
                              + str([x for x in self.board.G.neighbors(city1)
                                     if self.board.hasEdge(city1, x)])
                              + " : "
                              )
                count += 1

            if count >= 5:
                return "Move complete"

        # start exchange cards and place trains
        routeDist = self.board.getEdgeWeight(city1, city2)
        spanColors = self.board.getEdgeColors(city1, city2)

        if len(spanColors) == 0:
            # a little harsh but will updated later to players start over
            print("You have a selected two cities with no legal route")
            return "Move complete"

        print("\n This route is of length: ")
        self.printSepLine(routeDist)
        print("Your hand consists of: ")
        self.printSepLine(player.getHand())

        color = ""
        if len(spanColors) == 1:
            color = spanColors[0]  # use first element, getEdgeColors returns list
            print("This route is: " + str(color))
        else:
            color = ""
            if player.isComputerPlayer:
                num = 0
                for i in spanColors:
                    if player.getHand()[i] > num:
                        num = player.getHand()[i]
                        color = i
                if color == 'wild':
                    color = random.choice(spanColors)


            else:
                color = input("which color track would you like to claim? ("
                              + str(spanColors)
                              + " available): "
                              )
                if color not in spanColors:
                    print("Invalid Color")
                    return "Move complete"

        # check to see if player has appropriate cards to play route
        # (edge weight, color/wild)
        if not self.doesPlayerHaveCardsForEdge(player, city1, city2):
            print("You do not have sufficient cards to play this route")
            return "Move complete"
        if color == 'grey':
            availColor = max(x for x in player.hand.values())
        else:
            availColor = player.hand[color]

        availWild = player.hand['wild']
        if color == 'grey':
            if player.isComputerPlayer:
                print(("Which color would you like to play "
                              + "on this grey route? "
                              + "(pick a color, not 'wild'): "
                              ), end='')
                num = 0
                for i in player.getHand().keys():
                    if player.getHand()[i] > num and i != 'wild':
                        num = player.getHand()[i]
                        color = i
                print(color)
            else:
                color = input("Which color would you like to play "
                              + "on this grey route? "
                              + "(pick a color, not 'wild'): "
                              )

                if color not in self.deck.possibleColors:
                    print("Invalid Color")
                    return "Move complete"

            availColor = player.hand[color]

            if player.isComputerPlayer:
                if availColor > routeDist:
                    numColor = routeDist
                else:
                    numColor = availColor
            else:
                numColor = input("How many " + str(color)
                                 + " cards would you like to play? ("
                                 + str(availColor)
                                 + " available): "
                                 )
        else:
            if player.isComputerPlayer:
                if availColor > routeDist:
                    numColor = routeDist
                else:
                    numColor = availColor
            else:
                numColor = input("How many "
                                 + str(color)
                                 + " cards would you like to play? ("
                                 + str(availColor)
                                 + " available) "
                                 )
        if str(numColor) not in [str(x) for x in range(routeDist + 1)]:
            print("Invalid Entry")
            return "Move complete"
        numColor = int(numColor)  # change raw string to int
        if numColor not in range(0, availColor + 1):
            print("You do not have that many")
            return "Move complete"

        if numColor < routeDist:  # span distance

            if player.isComputerPlayer:
                numWild = routeDist - numColor
            else:
                numWild = input("How many wild cards would you like to play? ("
                                + str(availWild)
                                + " available) "
                                )
                numWild = int(numWild)
                if numWild not in range(0, availWild + 1):
                    print("You do not have that many")
                    return "Move complete"
        else:
            numWild = 0

        # verify that this is a legal move
        if numWild + numColor != routeDist:
            print("Selected cards do not properly span that route")
            return "Move complete"

        # claim route for player (see dedicated method within Game class)
        player.playerBoard.addEdge(city1, city2, routeDist, color)

        # remove route from main board
        self.board.removeEdge(city1, city2, color)

        # calculate points
        player.addPoints(self.routeValues[routeDist])

        # remove cards from player's hand
        player.removeCardsFromHand(color, numColor)
        player.removeCardsFromHand('wild', numWild)

        # add cards to discard pile
        self.deck.addToDiscard([color for x in range(numColor)]
                               + ['wild' for x in range(numWild)]
                               )

        # remove trains from players numTrains
        player.playNumTrains(routeDist)

        print("Number of trains left to play: ")
        self.printSepLine(player.getNumTrains())

        return "Move complete"

    def pickTickets(self, player, minNumToSelect=1):
        count = 0
        tickets = self.deck.dealTickets(self.numTicketsDealt)

        # assign a number to each ticket to make it easier to choose
        tickets = {x[0]: x[1] for x in zip(range(len(tickets)), tickets)}
        print("Please select at least " + str(minNumToSelect) + ": ")

        self.printSepLine(tickets)

        choices = set()
        if player.isComputerPlayer:
            choices.add(tickets[0])
            choices.add(tickets[1])
            choices.add(tickets[2])
        else:
            choice = input("Select a number corresponding to the above tickets,"
                           + " type done when finished: ")

            while (choice != 'done' and count < 7) \
                    or len(choices) < minNumToSelect:
                try:
                    choices.add(tickets[int(choice)])
                    choice = input("Select the number corresponding to the "
                                   + "above tickets, type 'done' when finished: "
                                   )
                except:
                    choice = input("Invalid Choice: Select the number "
                                   + "corresponding to the above tickets, "
                                   + "type 'done' when finished: "
                                   + " (must select at least "
                                   + str(minNumToSelect)
                                   + ") "
                                   )
                    count += 1

        print("You selected: ")
        for ticket in choices:
            player.addTicket(ticket)
            print(ticket)

        # add tickets that weren't chosen to the ticketDiscardPile
        notChosen = set(range(len(tickets))).difference(choices)
        for i in notChosen:
            self.deck.addToTicketDiscard(tickets[i])

        print("All of your tickets: ")
        self.printSepLine(player.getTickets())

        return "Move complete"


def playTTR():
    # before first turn, select 1, 2 or 3 destination tickets

    print("\n Welcome to Ticket to Ride! \n")

    numPlayers = input("How many players will be playing today? "
                       + "1,2,3,4,5 or 6? ")

    count = 0
    while int(numPlayers) not in range(1, 7) and count < 5:
        if numPlayers == 'exit': return "Thanks for playing!"
        numPlayers = input("Please enter either 1,2,3,4,5 or 6: ")
        count += 1
    if count >= 5:
        print("Default player count has been set to 2")
        numPlayers = 2

    game = Game(int(numPlayers))

    game.initialize()

    player = game.players[game.posToMove]

    # main game loop
    while True:
        print("\n_________________NEW PLAYER'S TURN_________________ \n")

        print("It's your turn " + str(player.getName()) + "! ")
        game.playTurn(player)

        # condition to break out of loop
        if game.checkEndingCondition(player):
            game.advanceOnePlayer()
            player = game.getCurrentPlayer()
            break
        game.advanceOnePlayer()
        player = game.getCurrentPlayer()

    print("\n This is the last round!  Everyone has one more turn! \n")

    for i in range(len(game.players)):
        print("\n_________________NEW PLAYER'S TURN_________________ \n")
        print("This is your LAST TURN " + str(player.getName()) + "! ")
        game.playTurn(player)
        game.advanceOnePlayer()
        player = game.getCurrentPlayer()

    for player in game.players:
        game.scorePlayerTickets(player)

    game.scoreLongestPath()

    scores = []
    for player in game.players:
        print(str(player.getName())
              + " had "
              + str(player.getPoints())
              + " points!"
              )
        score = player.getPoints()
        scores.append(score)

    winners = [x.getName() for x in game.players
               if x.getPoints() == max(scores)]

    if len(winners) == 1:
        print("The winner is " + str(winners[0]))
    else:
        print("The winners are " + ' and '.join(winners))

    print("\n =========== Data =========== \n")

    game.printAllPlayerData()

    print("\n =========== fin =========== \n")


if __name__ == "__main__":
    playTTR()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值