X Tutup
"""The Royal Game of Ur, by Al Sweigart al@inventwithpython.com A 5,000 year old board game from Mesopotamia. Two players knock each other back as they race for the goal. More info https://en.wikipedia.org/wiki/Royal_Game_of_Ur This code is available at https://nostarch.com/big-book-small-python-programming Tags: large, board game, game, two-player """ import random, sys X_PLAYER = 'X' O_PLAYER = 'O' EMPTY = ' ' # Set up constants for the space labels: X_HOME = 'x_home' O_HOME = 'o_home' X_GOAL = 'x_goal' O_GOAL = 'o_goal' # The spaces in left to right, top to bottom order: ALL_SPACES = 'hgfetsijklmnopdcbarq' X_TRACK = 'HefghijklmnopstG' # (H stands for Home, G stands for Goal.) O_TRACK = 'HabcdijklmnopqrG' FLOWER_SPACES = ('h', 't', 'l', 'd', 'r') BOARD_TEMPLATE = """ {} {} Home Goal v ^ +-----+-----+-----+--v--+ +--^--+-----+ |*****| | | | |*****| | |* {} *< {} < {} < {} | |* {} *< {} | |****h| g| f| e| |****t| s| +--v--+-----+-----+-----+-----+-----+-----+--^--+ | | | |*****| | | | | | {} > {} > {} >* {} *> {} > {} > {} > {} | | i| j| k|****l| m| n| o| p| +--^--+-----+-----+-----+-----+-----+-----+--v--+ |*****| | | | |*****| | |* {} *< {} < {} < {} | |* {} *< {} | |****d| c| b| a| |****r| q| +-----+-----+-----+--^--+ +--v--+-----+ ^ v Home Goal {} {} """ def main(): print('''The Royal Game of Ur, by Al Sweigart This is a 5,000 year old game. Two players must move their tokens from their home to their goal. On your turn you flip four coins and can move one token a number of spaces equal to the heads you got. Ur is a racing game; the first player to move all seven of their tokens to their goal wins. To do this, tokens must travel from their home to their goal: X Home X Goal v ^ +---+---+---+-v-+ +-^-+---+ |v<<<<<<<<<<<<< | | ^<|<< | |v | | | | | | ^ | +v--+---+---+---+---+---+---+-^-+ |>>>>>>>>>>>>>>>>>>>>>>>>>>>>>^ | |>>>>>>>>>>>>>>>>>>>>>>>>>>>>>v | +^--+---+---+---+---+---+---+-v-+ |^ | | | | | | v | |^<<<<<<<<<<<<< | | v<<<< | +---+---+---+-^-+ +-v-+---+ ^ v O Home O Goal If you land on an opponent's token in the middle track, it gets sent back home. The **flower** spaces let you take another turn. Tokens in the middle flower space are safe and cannot be landed on.''') input('Press Enter to begin...') gameBoard = getNewBoard() turn = O_PLAYER while True: # Main game loop. # Set up some variables for this turn: if turn == X_PLAYER: opponent = O_PLAYER home = X_HOME track = X_TRACK goal = X_GOAL opponentHome = O_HOME elif turn == O_PLAYER: opponent = X_PLAYER home = O_HOME track = O_TRACK goal = O_GOAL opponentHome = X_HOME displayBoard(gameBoard) input('It is ' + turn + '\'s turn. Press Enter to flip...') flipTally = 0 print('Flips: ', end='') for i in range(4): # Flip 4 coins. result = random.randint(0, 1) if result == 0: print('T', end='') # Tails. else: print('H', end='') # Heads. if i != 3: print('-', end='') # Print separator. flipTally += result print(' ', end='') if flipTally == 0: input('You lose a turn. Press Enter to continue...') turn = opponent # Swap turns to the other player. continue # Ask the player for their move: validMoves = getValidMoves(gameBoard, turn, flipTally) if validMoves == []: print('There are no possible moves, so you lose a turn.') input('Press Enter to continue...') turn = opponent # Swap turns to the other player. continue while True: print('Select move', flipTally, 'spaces: ', end='') print(' '.join(validMoves) + ' quit') move = input('> ').lower() if move == 'quit': print('Thanks for playing!') sys.exit() if move in validMoves: break # Exit the loop when a valid move is selected. print('That is not a valid move.') # Perform the selected move on the board: if move == 'home': # Subtract tokens at home if moving from home: gameBoard[home] -= 1 nextTrackSpaceIndex = flipTally else: gameBoard[move] = EMPTY # Set the "from" space to empty. nextTrackSpaceIndex = track.index(move) + flipTally movingOntoGoal = nextTrackSpaceIndex == len(track) - 1 if movingOntoGoal: gameBoard[goal] += 1 # Check if the player has won: if gameBoard[goal] == 7: displayBoard(gameBoard) print(turn, 'has won the game!') print('Thanks for playing!') sys.exit() else: nextBoardSpace = track[nextTrackSpaceIndex] # Check if the opponent has a tile there: if gameBoard[nextBoardSpace] == opponent: gameBoard[opponentHome] += 1 # Set the "to" space to the player's token: gameBoard[nextBoardSpace] = turn # Check if the player landed on a flower space and can go again: if nextBoardSpace in FLOWER_SPACES: print(turn, 'landed on a flower space and goes again.') input('Press Enter to continue...') else: turn = opponent # Swap turns to the other player. def getNewBoard(): """ Returns a dictionary that represents the state of the board. The keys are strings of the space labels, the values are X_PLAYER, O_PLAYER, or EMPTY. There are also counters for how many tokens are at the home and goal of both players. """ board = {X_HOME: 7, X_GOAL: 0, O_HOME: 7, O_GOAL: 0} # Set each space as empty to start: for spaceLabel in ALL_SPACES: board[spaceLabel] = EMPTY return board def displayBoard(board): """Display the board on the screen.""" # "Clear" the screen by printing many newlines, so the old # board isn't visible anymore. print('\n' * 60) xHomeTokens = ('X' * board[X_HOME]).ljust(7, '.') xGoalTokens = ('X' * board[X_GOAL]).ljust(7, '.') oHomeTokens = ('O' * board[O_HOME]).ljust(7, '.') oGoalTokens = ('O' * board[O_GOAL]).ljust(7, '.') # Add the strings that should populate BOARD_TEMPLATE in order, # going from left to right, top to bottom. spaces = [] spaces.append(xHomeTokens) spaces.append(xGoalTokens) for spaceLabel in ALL_SPACES: spaces.append(board[spaceLabel]) spaces.append(oHomeTokens) spaces.append(oGoalTokens) print(BOARD_TEMPLATE.format(*spaces)) def getValidMoves(board, player, flipTally): validMoves = [] # Contains the spaces with tokens that can move. if player == X_PLAYER: opponent = O_PLAYER track = X_TRACK home = X_HOME elif player == O_PLAYER: opponent = X_PLAYER track = O_TRACK home = O_HOME # Check if the player can move a token from home: if board[home] > 0 and board[track[flipTally]] == EMPTY: validMoves.append('home') # Check which spaces have a token the player can move: for trackSpaceIndex, space in enumerate(track): if space == 'H' or space == 'G' or board[space] != player: continue nextTrackSpaceIndex = trackSpaceIndex + flipTally if nextTrackSpaceIndex >= len(track): # You must flip an exact number of moves onto the goal, # otherwise you can't move on the goal. continue else: nextBoardSpaceKey = track[nextTrackSpaceIndex] if nextBoardSpaceKey == 'G': # This token can move off the board: validMoves.append(space) continue if board[nextBoardSpaceKey] in (EMPTY, opponent): # If the next space is the protected middle space, you # can only move there if it is empty: if nextBoardSpaceKey == 'l' and board['l'] == opponent: continue # Skip this move, the space is protected. validMoves.append(space) return validMoves if __name__ == '__main__': main()
X Tutup