你就是举世闻名的大侦探玛蒂尔德·加缪。猫佐菲不见了,你必须筛选线索。嫌疑人要么总是说谎,要么总是说真话。你会及时找到猫佐菲并指控有罪的一方吗?
在这个游戏中,你乘出租车到城市的不同地点。每个地方都有一个嫌疑犯和一件物品。可以向嫌疑人询问其他嫌疑人和物品的情况,将他们的回答与自己的探索笔记进行对比,确定他们是在说谎还是在说真话。有些人会知道谁绑架了佐菲(或者她在哪里,或者在绑架者的位置发现了什么物品),但是你必须确定你是否能相信他们。你有五分钟时间找到罪犯,但是如果你三次指控错误,你就输了。这款游戏的灵感来源于 Homestar Runner 的“鸡蛋在哪里?”游戏。
当您运行jaccuse.py时,输出将如下所示:
J'ACCUSE! (a mystery game)
`--snip--`
Time left: 5 min, 0 sec
You are in your TAXI. Where do you want to go?
(A)LBINO ALLIGATOR PIT
(B)OWLING ALLEY
(C)ITY HALL
(D)UCK POND
(H)IPSTER CAFE
(O)LD BARN
(U)NIVERSITY LIBRARY
(V)IDEO GAME MUSEUM
(Z)OO
> a
Time left: 4 min, 48 sec
You are at the ALBINO ALLIGATOR PIT.
ESPRESSA TOFFEEPOT with the ONE COWBOY BOOT is here.
(J) "J'ACCUSE!" (3 accusations left)
(Z) Ask if they know where ZOPHIE THE CAT is.
(T) Go back to the TAXI.
(1) Ask about ESPRESSA TOFFEEPOT
(2) Ask about ONE COWBOY BOOT
> z
They give you this clue: "DUKE HAUTDOG"
Press Enter to continue...
`--snip--`要完全理解这个程序,您应该密切关注clues字典,它位于第 51 行到第 109 行。您可以取消对第 151 到 154 行的注释,以便在屏幕上显示它。这个字典有来自SUSPECTS列表的字符串作为键,有“线索字典”作为值。每个线索字典都包含来自SUSPECTS和ITEMS的字符串。当被问及另一个嫌疑人或物品时,最初的嫌疑人会用这些字符串回答。例如,如果clues['DUKE HAUTDOG']['CANDLESTICK']设定为'DUCK POND',那么当玩家向杜克·豪特多格询问烛台时,他们会说它在鸭塘。每次玩游戏时,嫌疑人、物品、地点和罪犯都会被洗牌。
这个程序的代码围绕着这个数据结构,所以理解它对于理解程序的其余部分是必要的。
"""J'ACCUSE!, by Al Sweigart email@protected
A mystery game of intrigue and a missing cat.
This code is available at https://nostarch.com/big-book-small-python-programming
Tags: extra-large, game, humor, puzzle"""
# Play the original Flash game at:
# https://homestarrunner.com/videlectrix/wheresanegg.html
# More info at: http://www.hrwiki.org/wiki/Where's_an_Egg%3F
import time, random, sys
# Set up the constants:
SUSPECTS = ['DUKE HAUTDOG', 'MAXIMUM POWERS', 'BILL MONOPOLIS', 'SENATOR SCHMEAR', 'MRS. FEATHERTOSS', 'DR. JEAN SPLICER', 'RAFFLES THE CLOWN', 'ESPRESSA TOFFEEPOT', 'CECIL EDGAR VANDERTON']
ITEMS = ['FLASHLIGHT', 'CANDLESTICK', 'RAINBOW FLAG', 'HAMSTER WHEEL', 'ANIME VHS TAPE', 'JAR OF PICKLES', 'ONE COWBOY BOOT', 'CLEAN UNDERPANTS', '5 DOLLAR GIFT CARD']
PLACES = ['ZOO', 'OLD BARN', 'DUCK POND', 'CITY HALL', 'HIPSTER CAFE', 'BOWLING ALLEY', 'VIDEO GAME MUSEUM', 'UNIVERSITY LIBRARY', 'ALBINO ALLIGATOR PIT']
TIME_TO_SOLVE = 300 # 300 seconds (5 minutes) to solve the game.
# First letters and longest length of places are needed for menu display:
PLACE_FIRST_LETTERS = {}
LONGEST_PLACE_NAME_LENGTH = 0
for place in PLACES:
PLACE_FIRST_LETTERS[place[0]] = place
if len(place) > LONGEST_PLACE_NAME_LENGTH:
LONGEST_PLACE_NAME_LENGTH = len(place)
# Basic sanity checks of the constants:
assert len(SUSPECTS) == 9
assert len(ITEMS) == 9
assert len(PLACES) == 9
# First letters must be unique:
assert len(PLACE_FIRST_LETTERS.keys()) == len(PLACES)
knownSuspectsAndItems = []
# visitedPlaces: Keys=places, values=strings of the suspect & item there.
visitedPlaces = {}
currentLocation = 'TAXI' # Start the game at the taxi.
accusedSuspects = [] # Accused suspects won't offer clues.
liars = random.sample(SUSPECTS, random.randint(3, 4))
accusationsLeft = 3 # You can accuse up to 3 people.
culprit = random.choice(SUSPECTS)
# Common indexes link these; e.g. SUSPECTS[0] and ITEMS[0] are at PLACES[0].
random.shuffle(SUSPECTS)
random.shuffle(ITEMS)
random.shuffle(PLACES)
# Create data structures for clues the truth-tellers give about each
# item and suspect.
# clues: Keys=suspects being asked for a clue, value="clue dictionary".
clues = {}
for i, interviewee in enumerate(SUSPECTS):
if interviewee in liars:
continue # Skip the liars for now.
# This "clue dictionary" has keys=items & suspects,
# value=the clue given.
clues[interviewee] = {}
clues[interviewee]['debug_liar'] = False # Useful for debugging.
for item in ITEMS: # Select clue about each item.
if random.randint(0, 1) == 0: # Tells where the item is:
clues[interviewee][item] = PLACES[ITEMS.index(item)]
else: # Tells who has the item:
clues[interviewee][item] = SUSPECTS[ITEMS.index(item)]
for suspect in SUSPECTS: # Select clue about each suspect.
if random.randint(0, 1) == 0: # Tells where the suspect is:
clues[interviewee][suspect] = PLACES[SUSPECTS.index(suspect)]
else: # Tells what item the suspect has:
clues[interviewee][suspect] = ITEMS[SUSPECTS.index(suspect)]
# Create data structures for clues the liars give about each item
# and suspect:
for i, interviewee in enumerate(SUSPECTS):
if interviewee not in liars:
continue # We've already handled the truth-tellers.
# This "clue dictionary" has keys=items & suspects,
# value=the clue given:
clues[interviewee] = {}
clues[interviewee]['debug_liar'] = True # Useful for debugging.
# This interviewee is a liar and gives wrong clues:
for item in ITEMS:
if random.randint(0, 1) == 0:
while True: # Select a random (wrong) place clue.
# Lies about where the item is.
clues[interviewee][item] = random.choice(PLACES)
if clues[interviewee][item] != PLACES[ITEMS.index(item)]:
# Break out of the loop when wrong clue is selected.
break
else:
while True: # Select a random (wrong) suspect clue.
clues[interviewee][item] = random.choice(SUSPECTS)
if clues[interviewee][item] != SUSPECTS[ITEMS.index(item)]:
# Break out of the loop when wrong clue is selected.
break
for suspect in SUSPECTS:
if random.randint(0, 1) == 0:
while True: # Select a random (wrong) place clue.
clues[interviewee][suspect] = random.choice(PLACES)
if clues[interviewee][suspect] != PLACES[ITEMS.index(item)]:
# Break out of the loop when wrong clue is selected.
break
else:
while True: # Select a random (wrong) item clue.
clues[interviewee][suspect] = random.choice(ITEMS)
if clues[interviewee][suspect] != ITEMS[SUSPECTS.index(suspect)]:
# Break out of the loop when wrong clue is selected.
break
# Create the data structures for clues given when asked about Zophie:
zophieClues = {}
for interviewee in random.sample(SUSPECTS, random.randint(3, 4)):
kindOfClue = random.randint(1, 3)
if kindOfClue == 1:
if interviewee not in liars:
# They tell you who has Zophie.
zophieClues[interviewee] = culprit
elif interviewee in liars:
while True:
# Select a (wrong) suspect clue.
zophieClues[interviewee] = random.choice(SUSPECTS)
if zophieClues[interviewee] != culprit:
# Break out of the loop when wrong clue is selected.
break
elif kindOfClue == 2:
if interviewee not in liars:
# They tell you where Zophie is.
zophieClues[interviewee] = PLACES[SUSPECTS.index(culprit)]
elif interviewee in liars:
while True:
# Select a (wrong) place clue.
zophieClues[interviewee] = random.choice(PLACES)
if zophieClues[interviewee] != PLACES[SUSPECTS.index(culprit)]:
# Break out of the loop when wrong clue is selected.
break
elif kindOfClue == 3:
if interviewee not in liars:
# They tell you what item Zophie is near.
zophieClues[interviewee] = ITEMS[SUSPECTS.index(culprit)]
elif interviewee in liars:
while True:
# Select a (wrong) item clue.
zophieClues[interviewee] = random.choice(ITEMS)
if zophieClues[interviewee] != ITEMS[SUSPECTS.index(culprit)]:
# Break out of the loop when wrong clue is selected.
break
# EXPERIMENT: Uncomment this code to view the clue data structures:
#import pprint
#pprint.pprint(clues)
#pprint.pprint(zophieClues)
#print('culprit =', culprit)
# START OF THE GAME
print("""J'ACCUSE! (a mystery game)")
By Al Sweigart email@protected
Inspired by Homestar Runner\'s "Where\'s an Egg?" game
You are the world-famous detective, Mathilde Camus.
ZOPHIE THE CAT has gone missing, and you must sift through the clues.
Suspects either always tell lies, or always tell the truth. Ask them
about other people, places, and items to see if the details they give are
truthful and consistent with your observations. Then you will know if
their clue about ZOPHIE THE CAT is true or not. Will you find ZOPHIE THE
CAT in time and accuse the guilty party?
""")
input('Press Enter to begin...')
startTime = time.time()
endTime = startTime + TIME_TO_SOLVE
while True: # Main game loop.
if time.time() > endTime or accusationsLeft == 0:
# Handle "game over" condition:
if time.time() > endTime:
print('You have run out of time!')
elif accusationsLeft == 0:
print('You have accused too many innocent people!')
culpritIndex = SUSPECTS.index(culprit)
print('It was {} at the {} with the {} who catnapped her!'.format(culprit, PLACES[culpritIndex], ITEMS[culpritIndex]))
print('Better luck next time, Detective.')
sys.exit()
print()
minutesLeft = int(endTime - time.time()) // 60
secondsLeft = int(endTime - time.time()) % 60
print('Time left: {} min, {} sec'.format(minutesLeft, secondsLeft))
if currentLocation == 'TAXI':
print(' You are in your TAXI. Where do you want to go?')
for place in sorted(PLACES):
placeInfo = ''
if place in visitedPlaces:
placeInfo = visitedPlaces[place]
nameLabel = '(' + place[0] + ')' + place[1:]
spacing = " " * (LONGEST_PLACE_NAME_LENGTH - len(place))
print('{} {}{}'.format(nameLabel, spacing, placeInfo))
print('(Q)UIT GAME')
while True: # Keep asking until a valid response is given.
response = input('> ').upper()
if response == '':
continue # Ask again.
if response == 'Q':
print('Thanks for playing!')
sys.exit()
if response in PLACE_FIRST_LETTERS.keys():
break
currentLocation = PLACE_FIRST_LETTERS[response]
continue # Go back to the start of the main game loop.
# At a place; player can ask for clues.
print(' You are at the {}.'.format(currentLocation))
currentLocationIndex = PLACES.index(currentLocation)
thePersonHere = SUSPECTS[currentLocationIndex]
theItemHere = ITEMS[currentLocationIndex]
print(' {} with the {} is here.'.format(thePersonHere, theItemHere))
# Add the suspect and item at this place to our list of known
# suspects and items:
if thePersonHere not in knownSuspectsAndItems:
knownSuspectsAndItems.append(thePersonHere)
if ITEMS[currentLocationIndex] not in knownSuspectsAndItems:
knownSuspectsAndItems.append(ITEMS[currentLocationIndex])
if currentLocation not in visitedPlaces.keys():
visitedPlaces[currentLocation] = '({}, {})'.format(thePersonHere.lower(), theItemHere.lower())
# If the player has accused this person wrongly before, they
# won't give clues:
if thePersonHere in accusedSuspects:
print('They are offended that you accused them,')
print('and will not help with your investigation.')
print('You go back to your TAXI.')
print()
input('Press Enter to continue...')
currentLocation = 'TAXI'
continue # Go back to the start of the main game loop.
# Display menu of known suspects & items to ask about:
print()
print('(J) "J\'ACCUSE!" ({} accusations left)'.format(accusationsLeft))
print('(Z) Ask if they know where ZOPHIE THE CAT is.')
print('(T) Go back to the TAXI.')
for i, suspectOrItem in enumerate(knownSuspectsAndItems):
print('({}) Ask about {}'.format(i + 1, suspectOrItem))
while True: # Keep asking until a valid response is given.
response = input('> ').upper()
if response in 'JZT' or (response.isdecimal() and 0 < int(response) <= len(knownSuspectsAndItems)):
break
if response == 'J': # Player accuses this suspect.
accusationsLeft -= 1 # Use up an accusation.
if thePersonHere == culprit:
# You've accused the correct suspect.
print('You\'ve cracked the case, Detective!')
print('It was {} who had catnapped ZOPHIE THE CAT.'.format(culprit))
minutesTaken = int(time.time() - startTime) // 60
secondsTaken = int(time.time() - startTime) % 60
print('Good job! You solved it in {} min, {} sec.'.format(minutesTaken, secondsTaken))
sys.exit()
else:
# You've accused the wrong suspect.
accusedSuspects.append(thePersonHere)
print('You have accused the wrong person, Detective!')
print('They will not help you with anymore clues.')
print('You go back to your TAXI.')
currentLocation = 'TAXI'
elif response == 'Z': # Player asks about Zophie.
if thePersonHere not in zophieClues:
print('"I don\'t know anything about ZOPHIE THE CAT."')
elif thePersonHere in zophieClues:
print(' They give you this clue: "{}"'.format(zophieClues[thePersonHere]))
# Add non-place clues to the list of known things:
if zophieClues[thePersonHere] not in knownSuspectsAndItems and zophieClues[thePersonHere] not in PLACES:
knownSuspectsAndItems.append(zophieClues[thePersonHere])
elif response == 'T': # Player goes back to the taxi.
currentLocation = 'TAXI'
continue # Go back to the start of the main game loop.
else: # Player asks about a suspect or item.
thingBeingAskedAbout = knownSuspectsAndItems[int(response) - 1]
if thingBeingAskedAbout in (thePersonHere, theItemHere):
print(' They give you this clue: "No comment."')
else:
print(' They give you this clue: "{}"'.format(clues[thePersonHere][thingBeingAskedAbout]))
# Add non-place clues to the list of known things:
if clues[thePersonHere][thingBeingAskedAbout] not in knownSuspectsAndItems and clues[thePersonHere][thingBeingAskedAbout] not in PLACES:
knownSuspectsAndItems.append(clues[thePersonHere][thingBeingAskedAbout])
input('Press Enter to continue...') 试着找出下列问题的答案。尝试对代码进行一些修改,然后重新运行程序,看看这些修改有什么影响。
- 如果把第 16 行的
TIME_TO_SOLVE = 300改成TIME_TO_SOLVE = 0会怎么样? - 如果把 176 行的
time.time() > endTime or accusationsLeft == 0改成time.time() > endTime and accusationsLeft == 0会怎么样? - 如果把 198 行的
place[1:]改成place会怎么样? - 如果把 173 行的
startTime + TIME_TO_SOLVE改成startTime * TIME_TO_SOLVE会怎么样?
