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() # 使用该函数使程序终止
     
        
 

 

 

 

 

 

 

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