Python遊戲編程(七)Sonar Treasure Hunt

這裏將介紹一個採用聲納尋找寶藏的遊戲。

首先來了解一下這個遊戲涉及到一些概念,並且介紹這個遊戲是如何玩的。

目錄

 

(一)遊戲說明

(二)導入模塊

(三)getNewBoard():

(四)drawBoard(board):

(五)getRandomChests(numChests):

(六)isOnBoard(x, y):

(七)makeMove(board, chests, x, y):

(八)enterPlayerMove(previousMoves):

(九)showInstructions():

(十)遊戲循環


(一)遊戲說明

先來了解一些簡單的概念

笛卡爾座標系:所謂笛卡爾座標系,就是常見的xoy平面直角座標系。

數據結構:數據結構是存儲表示某些事物的值的排列組合。這個遊戲採用列表的列表這樣的複雜變量作爲數據結構。

聲納:聲納是輪船用於定位海底物體的一種技術。這個遊戲的“聲納”設備將告訴玩家距離最近的寶藏有多遠,但是不會告訴玩家藏寶箱在哪個方向。

遊戲規則:

玩家要收集3個藏寶箱,而玩家只有16個聲納設備可以用於找到它們。

 

 

下面分析這個遊戲的源代碼,將分爲一下幾個部分。

 

(二)導入模塊

在程序的開始處,我們導入random、sys和math模塊:

#Sonar Treasure Hunt

import random
import sys
import math

這裏需要介紹的是sys模塊。sys模塊包含了exit()函數,該函數立即終止了程序的執行。在sys.exit()調用之後,就沒有要運行的代碼行了。

 

下面將自定義一些函數。

(三)getNewBoard():

通過這個函數拆功能鍵一個新的遊戲版,包含列表的列表。通過在循環裏嵌套循環實現,並通過random()函數隨機繪製符號,構成海洋。

#創建一個新的遊戲板,Sonar遊戲的遊戲版是周圍由X軸和Y軸座標包圍這的一個ASCII字符圖“海洋”。
#                       board數據結構是字符串的列表的一個列表。
def getNewBoard():
    #Create a new 60x15 board date structure.
    board = []
    for x in range(60):
        #The main list is a list of 60 lists.
        board.append([])
        for y in range(15):
            #Each list in the main list has 15 single-caracter string.
            #Use different character for the ocean to make it more readable.
            if random.randint(0, 1) == 0:
                board[x].append('~')
            else:
                board[x].append('`')
    return board

 

(四)drawBoard(board):

通過drawBoard()函數打印遊戲版。遊戲版就是一個笛卡爾座標系。

#繪製遊戲板
def drawBoard(board):
    #Draw the board data structure.
    tensDigitsLine = ' '#Initial space for the numbers down the left side of the board.
    for i in range(1, 6):
        tensDigitsLine += (' ' * 9) + str(i)
        
    #Print the numbers across the top of the board.
    print(tensDigitsLine)
    print('  ' + ('0123456789' * 6))
    print()
    
    # Print each of the 15 rows.
    for row in range(15):
        #Single-digit numbers need to be padded with an extra space.
        if row < 10:
            extraSpace = ' '
        else:
            extraSpace = ''
            
        # Creat the string for this row on the board.
        boardRow = ''
        for column in range(60):
            boardRow += board[column][row]
            
        print('%s%s %s %s' % (extraSpace, row, boardRow, row))
        
    # Print the numbers across the bottom of the board.
    print()
    print(' ' + ('0123456789' * 6))
    print(tensDigitsLine)

運行這個函數的話將會打印如下的遊戲版:

          1         2         3         4         5
  012345678901234567890123456789012345678901234567890123456789

 0 ```~~~`~~`~`~~~~```~~~`~~`~``~~~~~~~~``~~``~``~~``~~``~`~``` 0
 1 ~~~``~~````~`~~`~~~~~~`~~~~```~`~~``~~``~~`~``~`~``~``~~~~~` 1
 2 ~`~~~```~~~~```~``~`~~`~~~`~`~``~``~~``~`~~~~`~``~~```~``~~~ 2
 3 ~~`~`~~``~`~~~``~~~~`~``~`~`~~~~`~`~~`~~`~````~`~``~~`~`~`~~ 3
 4 ~~~~~`~`~~``~`~~~`~``~`~~```~`~```~~~~~``~~~`````~`~`~````~~ 4
 5 ~~`~`~~``~~`~`~`~~~`~`~`~~~`~~~`~~`~~`~````~``~````~~~~`~~~` 5
 6 ~~`````~`~````~`~`~```~`~~~~~~~`~~``~````~``~```~`~~~```~~~` 6
 7 ~````~~`~~~`~````~```~`~`~`~~``~`~~~~``~`~```~``~~~~~```~`~~ 7
 8 `~~```~~~~`~~`~`~~`~~```~~`~~~~~```~~~~~~````~~``~`````~~`~~ 8
 9 ``~``~~~``~`~`~~~~`````~~``~~`~`~`~```~`~~~~`~````~``~~``~~~ 9
10 `~~~``~~```~``~`~~````~`````~~~``~``~``~~~~~~`~``~```~`~`~`~ 10
11 `~~`~`~`~~~`~`~~`~~~```~~``~~`~``~~~`~``~````````~~~```~`~`` 11
12 ~```~`~~~`~```~~~~`~``~~``~~`~~~````````~~~~~`~`~`~~~`~``~`` 12
13 `~~~~`~`~`~`~``~~```~````~~~~``~`~``~``~`~~`~~~~~~`~~~`~`~`` 13
14 ~`~``~`~~`~~~``~`~``~``~~`~~~~``~```~`~~~``~`~`~`~`~~~~```~` 14

 

(五)getRandomChests(numChests):

遊戲隨機決定將藏寶箱藏在哪兒,用包含兩個整數列表的一個列表來表示藏寶箱。這兩個整數是藏寶箱的X座標和Y座標。

#創建隨機的藏寶箱
def getRandomChests(numChests):
    # Create a list of chest data structures (two-item lists of x, y int coordinates ).
    chests = []
    while len(chests) < numChests:
        newChest = [random.randint(0, 59), random.randint(0, 14)]
        # Make sure a chest is not already here
        if newChest not in chests:
            chests.append(newChest)
    return chests

 

(六)isOnBoard(x, y):

遊戲獲取用戶輸入的座標時,要確保用戶輸入的數字在遊戲版座標系範圍之內。

#判斷一次移動是否有效
def isOnBoard(x, y):
    # Return True if the coordinates are on the board; otherwise, return False.
    return x >= 0 and x <= 59 and y >= 0 and y <= 14

 

(七)makeMove(board, chests, x, y):

在Sonar遊戲中,通過更新遊戲版,針對投下的每個聲納設備顯示一個數字,表示它距離最近的藏寶箱有多遠。所以,當玩家通過給程序一個X座標和Y座標來進行一次移動的時候,遊戲版會根據藏寶箱的位置做出修改。

makeMove()函數接受4個參數:遊戲版數據結構、藏寶箱數據結構、X座標和Y座標。

makeMove()函數將返回一個字符串,描述如何響應移動;

#在遊戲板上進行一次移動
def makeMove(board, chests, x, y):
    # Change the board data structure with a sonar device character.
    # Remove treasure chests from the chests list as they are found.
    # Return False if this is an invalid move.
    # Otherwise, return the string of the result of this move.
    smallestDistance = 100    # Any chest will be closer than 100.
    for cx, cy in chests:
        distance = math.sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y))
        
        # We want the closest treasure chest.
        if distance < smallestDistance:
            
            smallestDistance = distance
    
    smallestDistance = round(smallestDistance)
    
    if smallestDistance == 0:
        # xy is directly on a treasure chest!
        chests.remove([x, y])
        return 'You have found a sunken treasure chest!'
    else:
        if smallestDistance < 10:
            board[x][y] = str(smallestDistance)
            return 'Treasure detected at a distance of %s from the sonar device.' % (smallestDistance)
        else:
            board[x][y] = 'X'
            return 'Sonar did not detect anything. All treasure chests out of range.'

 

(八)enterPlayerMove(previousMoves):

previousMoves參數是兩個整數的一個列表,它表示玩家之前放置聲納設備的位置。該函數將會用到這一信息,以便玩家不能夠將聲納設備放到已經放置了一個聲納設備的位置。

while循環將一直詢問玩家下一步移動,知道玩家輸入一個有效的移動,也可以輸入quit來退出遊戲,這是將帶哦用sys.exit()退出遊戲。

#獲取玩家的移動
def enterPlayerMove(previousMoves):
    # Let the player enter their move. Return a two-item of int xy coordinates.
    print('Where do you want to drop the next sonar device? (0 - 59 0 - 14) (or type quit)')
    while True:
        move = input()
        if move.lower() == 'quit':
            print('Thanks for playing!')
            sys.exit()
            
        move = move.split()
        if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isOnBoard(int(move[0]), int(move[1])):
            if [int(move[0]), int(move[1])] in previousMoves:
                print('You alresdy moved there.')
                continue
            return [int(move[0]), int(move[1])]
        
        print('Enter a number from 0 to 59, a space, then a number from 0 to 14.')

 

(九)showInstructions():

打印出遊戲說明

# 爲玩家打印出遊戲說明
def showInstructions():
    
    print('''Instructions:
 You are the captain of the Simon, a treasure-hunting ship. Your current
   mission
 is to use sonar devices to find three sunken treasure chests at the
   bottom of
 the ocean. But you only have cheap sonar that finds distance, not
   direction.

 Enter the coordinates to drop a sonar device. The ocean map will be
   marked with
 how far away the nearest chest is, or an X if it is beyond the sonar
   device's
 range. For example, the C marks are where chests are. The sonar device
   shows a
 3 because the closest chest is 3 spaces away.

                     1         2         3
           012345678901234567890123456789012

         0 ~~~~`~```~`~``~~~``~`~~``~~~``~`~ 0
         1 ~`~`~``~~`~```~~~```~~`~`~~~`~~~~ 1
         2 `~`C``3`~~~~`C`~~~~`````~~``~~~`` 2
         3 ````````~~~`````~~~`~`````~`~``~` 3
         4 ~`~~~~`~~`~~`C`~``~~`~~~`~```~``~ 4

           012345678901234567890123456789012
                     1         2         3
(In the real game, the chests are not visible in the ocean.)

Press enter to continue...
    ''')
    input()
    
    

    print('''When you drop a sonar device directly on a chest, you
       retrieve it and the other
 sonar devices update to show how far away the next nearest chest is. The
   chests
 are beyond the range of the sonar device on the left, so it shows an X.

                     1         2         3
           012345678901234567890123456789012

         0 ~~~~`~```~`~``~~~``~`~~``~~~``~`~ 0
         1 ~`~`~``~~`~```~~~```~~`~`~~~`~~~~ 1
         2 `~`X``7`~~~~`C`~~~~`````~~``~~~`` 2
         3 ````````~~~`````~~~`~`````~`~``~` 3
         4 ~`~~~~`~~`~~`C`~``~~`~~~`~```~``~ 4

           012345678901234567890123456789012
                     1         2         3

 The treasure chests don't move around. Sonar devices can detect treasure
   chests
 up to a distance of 9 spaces. Try to collect all 3 chests before running
   out of
 sonar devices. Good luck!

 Press enter to continue...''')
    input()

 

(十)遊戲循環

 

print('S O N A R !')
print()
print('Would you like to view the instructions? (yes/no)')
if input().lower().startswith('y'):
    showInstructions()
    
# 遊戲循環
while True:
    # Game setup
    sonarDevices = 20
    theBoard = getNewBoard()
    theChests = getRandomChests(3)
    drawBoard(theBoard)
    previousMoves = []
    
    while sonarDevices > 0:
        
        # Show sonar device and chest statuses.
        print('You have %s sonar device(s) left. %s treasure chest(s)remaining.' % (sonarDevices, len(theChests)))

        x, y = enterPlayerMove(previousMoves)
        previousMoves.append([x, y]) # We must track all moves so that sonar devices can be updated.

        moveResult = makeMove(theBoard, theChests, x, y)
        if moveResult == False:
            continue
        else:
            if moveResult == 'You have found a sunken treasure chest!':
                
                # Update all the sonar devices currently on the map.
                for x, y in previousMoves:
                    
                    makeMove(theBoard, theChests, x, y)
            drawBoard(theBoard)
            print(moveResult)

        if len(theChests) == 0:
            print('You have found all the sunken treasure chests! Congratulations and good game!')
            break

        sonarDevices -= 1

    if sonarDevices == 0:
        
        print('We\'ve run out of sonar devices! Now we have to turn the ship around and head')
        print('for home with treasure chests still out there! Gameover.')
        print('    The remaining chests were here:')
        
        for x, y in theChests:
            print('    %s, %s' % (x, y))

    print('Do you want to play again? (yes or no)')
    if not input().lower().startswith('y'):
        
        sys.exit() # 使用該函數使程序終止

 

 

源代碼:

#Sonar Treasure Hunt

import random
import sys
import math

#創建一個新的遊戲板,Sonar遊戲的遊戲版是周圍由X軸和Y軸座標包圍這的一個ASCII字符圖“海洋”。
#                       board數據結構是字符串的列表的一個列表。
def getNewBoard():
    #Create a new 60x15 board date structure.
    board = []
    for x in range(60):
        #The main list is a list of 60 lists.
        board.append([])
        for y in range(15):
            #Each list in the main list has 15 single-caracter string.
            #Use different character for the ocean to make it more readable.
            if random.randint(0, 1) == 0:
                board[x].append('~')
            else:
                board[x].append('`')
    return board

#繪製遊戲板
def drawBoard(board):
    #Draw the board data structure.
    tensDigitsLine = ' '#Initial space for the numbers down the left side of the board.
    for i in range(1, 6):
        tensDigitsLine += (' ' * 9) + str(i)
        
    #Print the numbers across the top of the board.
    print(tensDigitsLine)
    print('  ' + ('0123456789' * 6))
    print()
    
    # Print each of the 15 rows.
    for row in range(15):
        #Single-digit numbers need to be padded with an extra space.
        if row < 10:
            extraSpace = ' '
        else:
            extraSpace = ''
            
        # Creat the string for this row on the board.
        boardRow = ''
        for column in range(60):
            boardRow += board[column][row]
            
        print('%s%s %s %s' % (extraSpace, row, boardRow, row))
        
    # Print the numbers across the bottom of the board.
    print()
    print(' ' + ('0123456789' * 6))
    print(tensDigitsLine)
    
#創建隨機的藏寶箱
def getRandomChests(numChests):
    # Create a list of chest data structures (two-item lists of x, y int coordinates ).
    chests = []
    while len(chests) < numChests:
        newChest = [random.randint(0, 59), random.randint(0, 14)]
        # Make sure a chest is not already here
        if newChest not in chests:
            chests.append(newChest)
    return chests

#判斷一次移動是否有效
def isOnBoard(x, y):
    # Return True if the coordinates are on the board; otherwise, return False.
    return x >= 0 and x <= 59 and y >= 0 and y <= 14

#在遊戲板上進行一次移動
def makeMove(board, chests, x, y):
    # Change the board data structure with a sonar device character.
    # Remove treasure chests from the chests list as they are found.
    # Return False if this is an invalid move.
    # Otherwise, return the string of the result of this move.
    smallestDistance = 100    # Any chest will be closer than 100.
    for cx, cy in chests:
        distance = math.sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y))
        
        # We want the closest treasure chest.
        if distance < smallestDistance:
            
            smallestDistance = distance
    
    smallestDistance = round(smallestDistance)
    
    if smallestDistance == 0:
        # xy is directly on a treasure chest!
        chests.remove([x, y])
        return 'You have found a sunken treasure chest!'
    else:
        if smallestDistance < 10:
            board[x][y] = str(smallestDistance)
            return 'Treasure detected at a distance of %s from the sonar device.' % (smallestDistance)
        else:
            board[x][y] = 'X'
            return 'Sonar did not detect anything. All treasure chests out of range.'
        
#獲取玩家的移動
def enterPlayerMove(previousMoves):
    # Let the player enter their move. Return a two-item of int xy coordinates.
    print('Where do you want to drop the next sonar device? (0 - 59 0 - 14) (or type quit)')
    while True:
        move = input()
        if move.lower() == 'quit':
            print('Thanks for playing!')
            sys.exit()
            
        move = move.split()
        if len(move) == 2 and move[0].isdigit() and move[1].isdigit() and isOnBoard(int(move[0]), int(move[1])):
            if [int(move[0]), int(move[1])] in previousMoves:
                print('You alresdy moved there.')
                continue
            return [int(move[0]), int(move[1])]
        
        print('Enter a number from 0 to 59, a space, then a number from 0 to 14.')
   
# 爲玩家打印出遊戲說明
def showInstructions():
    
    print('''Instructions:
 You are the captain of the Simon, a treasure-hunting ship. Your current
   mission
 is to use sonar devices to find three sunken treasure chests at the
   bottom of
 the ocean. But you only have cheap sonar that finds distance, not
   direction.

 Enter the coordinates to drop a sonar device. The ocean map will be
   marked with
 how far away the nearest chest is, or an X if it is beyond the sonar
   device's
 range. For example, the C marks are where chests are. The sonar device
   shows a
 3 because the closest chest is 3 spaces away.

                     1         2         3
           012345678901234567890123456789012

         0 ~~~~`~```~`~``~~~``~`~~``~~~``~`~ 0
         1 ~`~`~``~~`~```~~~```~~`~`~~~`~~~~ 1
         2 `~`C``3`~~~~`C`~~~~`````~~``~~~`` 2
         3 ````````~~~`````~~~`~`````~`~``~` 3
         4 ~`~~~~`~~`~~`C`~``~~`~~~`~```~``~ 4

           012345678901234567890123456789012
                     1         2         3
(In the real game, the chests are not visible in the ocean.)

Press enter to continue...
    ''')
    input()
    
    

    print('''When you drop a sonar device directly on a chest, you
       retrieve it and the other
 sonar devices update to show how far away the next nearest chest is. The
   chests
 are beyond the range of the sonar device on the left, so it shows an X.

                     1         2         3
           012345678901234567890123456789012

         0 ~~~~`~```~`~``~~~``~`~~``~~~``~`~ 0
         1 ~`~`~``~~`~```~~~```~~`~`~~~`~~~~ 1
         2 `~`X``7`~~~~`C`~~~~`````~~``~~~`` 2
         3 ````````~~~`````~~~`~`````~`~``~` 3
         4 ~`~~~~`~~`~~`C`~``~~`~~~`~```~``~ 4

           012345678901234567890123456789012
                     1         2         3

 The treasure chests don't move around. Sonar devices can detect treasure
   chests
 up to a distance of 9 spaces. Try to collect all 3 chests before running
   out of
 sonar devices. Good luck!

 Press enter to continue...''')
    input()



print('S O N A R !')
print()
print('Would you like to view the instructions? (yes/no)')
if input().lower().startswith('y'):
    showInstructions()
    
# 遊戲循環
while True:
    # Game setup
    sonarDevices = 20
    theBoard = getNewBoard()
    theChests = getRandomChests(3)
    drawBoard(theBoard)
    previousMoves = []
    
    while sonarDevices > 0:
        
        # Show sonar device and chest statuses.
        print('You have %s sonar device(s) left. %s treasure chest(s)remaining.' % (sonarDevices, len(theChests)))

        x, y = enterPlayerMove(previousMoves)
        previousMoves.append([x, y]) # We must track all moves so that sonar devices can be updated.

        moveResult = makeMove(theBoard, theChests, x, y)
        if moveResult == False:
            continue
        else:
            if moveResult == 'You have found a sunken treasure chest!':
                
                # Update all the sonar devices currently on the map.
                for x, y in previousMoves:
                    
                    makeMove(theBoard, theChests, x, y)
            drawBoard(theBoard)
            print(moveResult)

        if len(theChests) == 0:
            print('You have found all the sunken treasure chests! Congratulations and good game!')
            break

        sonarDevices -= 1

    if sonarDevices == 0:
        
        print('We\'ve run out of sonar devices! Now we have to turn the ship around and head')
        print('for home with treasure chests still out there! Gameover.')
        print('    The remaining chests were here:')
        
        for x, y in theChests:
            print('    %s, %s' % (x, y))

    print('Do you want to play again? (yes or no)')
    if not input().lower().startswith('y'):
        
        sys.exit() # 使用該函數使程序終止
     
        
 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章