lunes, 2 de junio de 2014

Guess the number, part 5

Finally, the high score system works, and the program now runs from start to finish without trouble, and has all the features that we set as goals in the beginning. Let's take a look at the different files:


main.py

 

from functions import *
from mainGame import *

gamestate = 'menu'  

def GameManager(state):

    while True:
        if state == 'menu':

            state = ShowMenu()

        elif state == 'game':

            MainGame()

            state = ShowMenu()

        elif state == 'score':

            ViewHighScores(LoadScoreFileData())

            state = ShowMenu()

        elif state  == 'quit':

            print("\nSee you soon!\n")

            break

GameManager(gamestate)

This file is the one from which we are going to access the program. It imports all the functions we're going to need, and serves as a sort of 'hub' where the program returns to receive new instructions. This while loop is the main game loop.


mainGame.py 

 

from random import *
from functions import GiveHint,CheckHighScores

def MainGame():

    seed()
    secretNumber = randint(1,10)
    score = 1000

    print("\nI'm thinking of a number between 1 and 10. Can you guess what it is?")

    while True: #Loop until the player wins or quits

        while True: #Loop until a valid answer is given

            answer = input("Your guess: ")

            if answer.upper() == 'Q':
                break
            else:
                try:
                    answerInNumerical = int(answer)
                    break
                except:
                    print("That's not a number.")

        if answer.upper() == 'Q':

            break

        elif answerInNumerical == secretNumber:

            print("\n * * * You are correct! * * *\n"
            CheckHighScores(score)
            break

        else:
            score -= 100
            GiveHint(answerInNumerical,secretNumber)

This file describes the actual game function. It contains a nested loop, because we must check first if the player's input is valid (i.e. a number) and then we must check if that number is actually the right answer. If the player is wrong, his score decreases by 100 points and we show him a hint. If the player's right, we check if his score makes the high score list or not.

classes.py

 

class player:
    def __init__(self,pname,pscore):
        self.name = pname
        self.score = pscore

    def __lt__(self,other):
        return self.score < other.score

This file contains the only class we added to the game. We are only concerned with two attributes of the player: name and score. That's because we only need the player's data for the high score system. __init__ is the function that gets called when the object is created. It's known as a constructor. With it, we can (and must) specify the value of the object's attributes when creating it.

__lt__ is a comparison function ('lt' stands for 'lower than'). Since this is a complex piece of data, we must specify the computer just how you compare 'players'. For the purpose of our program, players get compared by their score. I actually had a little problem with this function, because I forgot to start the statement with return. Don't forget that!

And last, but certainly not least,

functions.py


import pickle
from classes import *

def MenuInput():

    while True:

        string = input("Your choice: ")

        if string.upper() == 'P':
            return 'game'
        elif string.upper() == 'S':
            return 'score'
        elif string.upper() == 'Q':
            return 'quit'
        else:
            print("\nThat is not a valid choice.")



def ShowMenu():
    print(""" * * * * * * * * * *
Welcome to \"Guess the number\"
* * * * * * * * * *

- Type \'p\' to play the game
- Type \'s\' to view the high scores
- Type \'q\' at any time to quit
""")
    return MenuInput()



def GiveHint(number,secretNumber):

    if number > secretNumber:
        print("\nThat's not it. It's too high.\n")
    elif number < secretNumber:
        print("\nThat's not it. It's too low.\n")



def ViewHighScores(scoresList):

    for i in scoresList:
        print(i.name + ' - ' + str(i.score))
    print("")



def EnterHighScore(score,scoresList):

    name = input("Enter your name: ")
    print("")

    newPlayer = player(name,score)

    #If the high score list already
    #has 3 names on it, one of them
    #has to go:

    if len(scoresList) == 3:
        scoresList.pop()

    #Now we add the new name to the
    #list, and sort it in descending
    #order.

    scoresList.append(newPlayer)
    scoresList.sort(reverse=True)

    #Finally, we dump the updated list
    #back to the 'scores' file.

    scoresFile = open('scores','wb')
    pickle.dump(scoresList,scoresFile)
    scoresFile.close()

    ViewHighScores(scoresList)



def LoadScoreFileData():

    #Here we attempt to open the 'scores'
    #file. If it doesn't exist, we catch
    #the error and create it.

    try:
        scoresFile = open('scores','rb')
    except:
        scoresFile = open('scores','wb+')

    #Since we don't know if the file is empty
    #or not, we try to read from it. Remember
    #to return the read pointer back to the
    #beginning of the file when doing this!!!

    if not scoresFile.read(1):
        scoresList = []
    else:
        scoresFile.seek(0)
        scoresList = pickle.load(scoresFile)

    scoresFile.close()

    return scoresList



def CheckHighScores(score):

    scoresList = LoadScoreFileData()

    #If there's nothing in the high score
    #list, we go directly to enter new data.
    #No need to check for anything else.

    if not scoresList:
        EnterHighScore(score,scoresList)
    else:

        #Here we count the each time the iterations
        #of the for loop. If we reach 3, that means
        #that our high score list is full, and no
        #score was beaten.

        for counter,i in enumerate(scoresList):

            if counter == 3:
                break
            if score >= i.score or len(scoresList) < 3:
                EnterHighScore(score,scoresList)
                break

Whew! Who figured that the high score system would be the hardest part? I've added comments in the code to explain the parts I felt wouldn't be easily understood. I tried to make the names of the functions very descriptive, so that skimming the code gives you a good idea of what these functions do without reading the actual code.

 That's it. We have completed one of the most basic games out there. We have a primitive but functional menu system, and added a high score system. I believe that this basic outline will give us a good foundation for making visual games next.

If you have any comments or questions, use the section below. Thanks for reading!

No hay comentarios:

Publicar un comentario