Jump to content
  • Advertisement
Sign in to follow this  
Lempface

PyGame, Scrambled Words (Basic Graphics)

This topic is 3813 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Download Here You can download it and try it out there, it requires a couple font files to be in the same directory so just extract the whole thing to a folder and run game.py It doesn't have a whole lot of functionality right now but it shows a scrambled word and you can enter input on the pygame window. Press enter to submit your guess, and it quits the program should you have guessed it correctly. The code is really messy though, I'm not happy with it, so any suggestions on how to tidy it up/organize it would be awesome. Game.py
# SCRAMBLED WORDS
# v0.1b
#
# A game that asks users to guess scrambled words.
# Stephen Lemp - 2008

# IMPORTS #

from random import randrange, shuffle, choice
from sys import exit
from pygame.locals import *
from words import Words, scramble_word
from round import Round
import pygame, os, resource, textbox

# VARIABLES/CONSTANTS #

''' Contains all the strings we may want to show the user for quick access.'''
prompts = { 'wrong_answer': 'Sorry, that is incorrect.\n',
            'right_answer': 'Congratulations, you\'ve guessed the word.\n',
            'welcome': 'Welcome to Scrambled Words!\n',
            'goodbye': 'Thanks for playing Scrambled Words!\n' }

''' Number of words per round. '''
ROUNDLENGTH = 4

''' Pygame Constants'''
SCREEN_SIZE = [640, 480]
BLACK = (0, 0, 0)
WHITE = (255, 255 ,255)
BLUE = (25 ,25 , 255)
 
# METHODS #
    
def init_bg(pysurface):
    bg = pygame.Surface(pysurface.get_size())
    bg = bg.convert()
    bg.fill(BLACK)
    return bg

def init_title():
    scrubble_font = resource.get_font("scrubble.ttf", 36)
    text = scrubble_font.render("SCRAMBLED WORDS!", 1, BLUE)
    return text

def main():
    # Initializations #
    pygame.init()
    resource.set_fonts_path(os.path.dirname(__file__))
    
    # Instantiations #
    WORDS = Words()
    ROUND = Round()
    user_input_string = []
    
    # -------------------------------------------------------------
    screen = pygame.display.set_mode((SCREEN_SIZE))
    clock = pygame.time.Clock()
    background = init_bg(screen)
    title = init_title()
    font = pygame.font.Font(None, 24)
    font = font.render(WORDS.init_word(), 1, WHITE)

    # -------------------------------------------------------------
    textbox.draw_input_box(background, WHITE, BLACK, 160, 70, 200, 40, 2)
    background.blit(font, (160, 46))
    background.blit(title, (160, 5))
    screen.blit(background, (0,0))
    pygame.display.flip()
    
    # -------------------------------------------------------------
    ''' Game Loop Variable '''
    RUNNING = True
    
    while RUNNING:
        if textbox.get_input(screen, background) == WORDS.the_word:
            RUNNING = False
            print prompts['right_answer']
            pygame.quit()
                
        pygame.time.wait(20)


    
if __name__ == '__main__':
    main()


Words.py
# ------------------------------------------------------------
# The Words class
#
# ------------------------------------------------------------

from random import shuffle, choice, randrange

class Words():    
    def __init__(self):
        """ This class selects and scrambles words. """
        self.clues = { 'gasoline': 'Fuel',
                       'airplane': 'Flying Machine',
                       'cardstock': 'Thick Paper',
                       'keyboard': 'Instrument',
                       'monitor': 'A Display',
                       'calculator': 'Number Cruncher',
                       'gigantic': 'Huge'}
        self.the_word = ""       # Chosen word from clues
        self.scrambled_word = "" # the_word once scrambled
        self.remaining_words = self.clues.keys()    # A list of used words so we don't pick one twice.
        
    def init_word(self):
        """ Selects a word, then scrambles it. """
        shuffle(self.remaining_words)
        self.the_word = self.remaining_words.pop()
        self.scrambled_word = scramble_word(self.the_word)
                 
        return self.scrambled_word

    def show_scramble(self):
        """ Display the scrambled word. """
        font = pygame.font.Font(None, 24)
        text = font.render("The Scrambled word is: " + self.scrambled_word, 1, WHITE)
        print "The scramble is:", self.scrambled_word + '\n'
        return text

    def show_clue(self, tries):
        """ Display a clue once the user has tried to guess it 3 times. """
        if tries == 4:
            print "Your clue is: ", self.clues[self.the_word], '\n'
            
def scramble_word(the_word):
        """ Shuffle/Scramble a word up. """
        word = list(the_word)
        shuffle(word)
        shuffled_word = "".join(word)
        return shuffled_word


Round.py
# ------------------------------------------------------------
# The round class
#
# ------------------------------------------------------------

class Round():
    """ This class keeps track of scores and game variables. """
    def __init__(self):
        self.roundnumber = 1
        self.score = []
        self.wordcount = 1
        self.possiblescore = 0
        
    def start_round(self, word):
        """ Sets the required variables for the beinning of a round. """
        self.tries = 1
        self.the_word = word
        self.possiblescore += len(word)
        
    def get_guess(self):
        """ Get the user's guess. """
        guess = raw_input("Enter your guess: ")
        guess = guess.lower()
        print ""
        return guess
    
    def guess_again(self):
        """ See if the user would like to guess again."""
        print "Would you like to guess again?"
        usr_input = raw_input("y or n: ")
        usr_input = usr_input.lower()
        print ""
        self.tries += 1
        if usr_input == "y":
            return True
        else:
            return False
        
    def calc_score(self):
        """ Calculates the users score, on a per word basis
            then it adds it to a list so we can see how they did
            on each word as well as their final score. """
        word_length = float(len(self.the_word))
        tries = float(self.tries)
        score = int(word_length / tries * 100)
        self.score.append(score)
        print "Your score for that word was:", score, "out of a possible", int(word_length * 100), '\n'
        
    def show_score(self):
        """ Displays the sum of each words score. """
        total_score = 0
        for item in self.score:
            total_score += item
        print "Your score was:", total_score, 'out of a possible:', self.possiblescore * 100, '\n'


textbox.py
import pygame, pygame.font, pygame.event, pygame.draw, resource, os
from pygame.locals import *

# COLORS #
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

# Letter List #
letter_list = [K_a, K_b, K_c, K_d, K_e, K_f, K_g, K_h, K_i, K_j, K_k,
               K_l, K_m, K_n, K_o, K_p, K_q, K_r, K_s, K_t, K_u, K_v,
               K_w, K_x, K_y, K_z]

def get_key_stroke():
    """ Get user interaction from PyGame """
    for event in pygame.event.get():
        if event.type == KEYDOWN:
            return event.key

def draw_input_box(surface, border_color, background_color, x, y, width, height, outline_width):
    """ Draw two boxes to create an empty text box. """
    pygame.draw.rect(surface, border_color, (x, y, width, height), 0)
    pygame.draw.rect(surface, background_color, (x + outline_width, y + outline_width, width - (outline_width * 2), height - (outline_width * 2)), 0)
    
def draw_font(surface, message, color, x, y):
    """ Draw a text message on a surface. """
    font = resource.get_font("lucon.ttf", 18)
    renderedfont = font.render(message, 1, color)
    surface.blit(renderedfont, (x + 12, y + 12))
    
def get_input(screen, surface):
    input_string = []
    
    while 1:
        key = get_key_stroke()
        if key == K_ESCAPE:
            return "".join(input_string)
        elif key == K_RETURN:
            return "".join(input_string)
        elif key == K_BACKSPACE:
            if len(input_string) > 0:
                input_string.pop()
        elif key in letter_list:
            if len(input_string) > 15:
                input_string.pop()
            input_string.append(chr(key))
        draw_input_box(surface, WHITE, BLACK, 160, 70, 200, 40, 2)
        draw_font(surface, "".join(input_string), WHITE, 160, 70)
        screen.blit(surface, (0,0))
        pygame.display.flip()

def main():
    pygame.init()
    resource.set_fonts_path(os.path.dirname(__file__))
    screen = pygame.display.set_mode((640, 480))
    background = pygame.Surface((640, 480))
    background.convert()
    screen.blit(background, (0,0))
    pygame.display.flip()
    
    print get_input(screen, background)
    
if __name__ == '__main__' :
    main()
    


Share this post


Link to post
Share on other sites
Advertisement
Hm, I have Python 2.5 and it says it can load the module 'resource'. What version are you using?

You probably want to use py2exe to wrap the whole thing into an .exe so that people can run it without having to install python or pygame.

Share this post


Link to post
Share on other sites
Quote:
Original post by mikeman
Hm, I have Python 2.5 and it says it can load the module 'resource'. What version are you using?

You probably want to use py2exe to wrap the whole thing into an .exe so that people can run it without having to install python or pygame.


I'll do that. one sec. It failed on MSCVR80.DLL and I have to head to work.

but here is resource.py

import pygame, os
# import pygame.image, pygame.font, pygame.mixer # remove for pygame >=1.4

"""
resource.py
A caching resource loader
License: Public Domain
Author: David Clark (silenus at telus.net)

This module wraps pygame's resource loading functions. It ensures that only
one copy of any given resource will be in memory at any time - further requests
for that same resource will get a reference to the loaded copy. This behavoir
can be overridden by specifying that the resource loading be forced - all
clients of that resource will then refer to the new version. The resource can
be manually removed from memory with the clear_* set of functions.

This code might be useful if a game is going to be loading many copies of a
given disk resource. Most of the time, you'd write your own cache; now you
can just use this one.

Remember to call the 'set path' functions before trying to load anything;
otherwise, you'll get a ValueError.


"""




__images = {}
__fonts = {}
__sounds = {}

__images_path = None
__fonts_path = None
__sounds_path = None

def get_image(filename, force_reload = 0):
"""
get_image(filename, force_reload = 1) --> surface
Call this function instead of pygame.image.load - it will load the image
from the disk the first time, then just return a reference to the copy each
subsequent time. This function does no colorkey setting or pixel format
conversion; you'll have to do that manually, if you wish.
"""


if not __images_path:
raise ValueError, "resources.set_images_path() not called yet."
if (force_reload == 1 or filename not in __images.keys()):
try:
surface = pygame.image.load(os.path.join(__images_path, filename))
except pygame.error:
raise IOError, "File " + filename + " not found."
__images[filename] = surface
return surface
else:
return __images[filename]

def has_image(filename):
"""
has_image(filename) --> Boolean
Returns true if the image is in memory, false if it has to be loaded from
disk.
"""

return __images.has_key(filename)

def clear_image(filename):
"""
clear_image(filename) --> Boolean
Eliminates the image from memory. Subsequent calls will load it from the
disk. Returns True if the resource was found in memory, False if it
Wasn't. Use this to reduce the memory footprint, if you're sure you won't
be needing the resource again.
"""


try:
del __images[filename]
return 1
except KeyError:
return 0


def get_font(filename, size, force_reload = 0):
"""
get_font(filename, size, force_reload = 1) --> surface
Call this function instead of pygame.font.Font - it will load the font
from the disk the first time, then just return a reference to the copy each
subsequent time.
"""

if not __fonts_path:
raise ValueError, "resources.set_fonts_path() not called yet."
if (force_reload == 1 or filename not in __fonts.keys()):
try:
font = pygame.font.Font(os.path.join(__fonts_path, filename), size)
except pygame.error:
raise IOError, "File " + filename + " not found."
__fonts[filename] = font
return font
else:
return __fonts[filename]

def has_font(filename):
"""
has_font(filename) --> Boolean
Returns true if the font is in memory, false if it has to be loaded from
disk.
"""

return __fonts.has_key(filename)

def clear_font(filename):
"""
clear_font(filename) --> Boolean
Eliminates the font from memory. Subsequent calls will load it from the
disk. Returns True if the resource was found in memory, False if it
Wasn't. Use this to reduce the memory footprint, if you're sure you won't
be needing the resource again.
"""

try:
del __fonts[filename]
return 1
except KeyError:
return 0

def get_sound(filename, force_reload = 0):
"""
get_sound(filename, force_reload = 1) --> sound
Call this function instead of pygame.mixer.Sound - it will load the sound
from the disk the first time, then just return a reference to the copy each
subsequent time.
"""

if not __sounds_path:
raise ValueError, "resources.set_sounds_path() not called yet."
if (force_reload == 1 or filename not in __fonts.keys()):
try:
sound = pygame.mixer.Sound(os.path.join(__sounds_path, filename))
except pygame.error:
raise IOError, "File " + filename + " not found."
__sounds[filename] = sound
return sound
else:
return __sounds[filename]

def has_sound(filename):
"""
has_sound(filename) --> Boolean
Returns true if the sound is in memory, false if it has to be loaded from
disk.
"""

return __sounds.has_key(filename)

def clear_sound(filename):
"""
clear_sound(filename) --> Boolean
Eliminates the sound from memory. Subsequent calls will load it from the
disk. Returns True if the resource was found in memory, False if it
Wasn't. Use this to reduce the memory footprint, if you're sure you won't
be needing the resource again.
"""

try:
del __sounds[filename]
return 1
except KeyError:
return 0

def set_images_path(path):
"""
set_images_path(path) --> Boolean
Set the path you'll be loading the images off of. Pass the string
representation of the new path. Raises an exception if the path doesn't
exist, otherwise it returns True.
"""

if not os.access(path, os.F_OK):
raise IOError, path + " not found."
if path.endswith(os.sep):
path = path[:-1]
global __images_path
__images_path = path
return 1

def set_fonts_path(path):
"""
set_fonts_path(path) --> Boolean
Set the path you'll be loading the fonts off of. Pass the string
representation of the new path. Raises an exception if the path doesn't
exist, otherwise it returns True.
"""

if not os.access(path, os.F_OK):
raise IOError, path + " not found."
if path.endswith(os.sep):
path = path[:-1]
global __fonts_path
__fonts_path = path
return 1

def set_sounds_path(path):
"""
set_sounds_path(path) --> Boolean
Set the path you'll be loading the sounds off of. Pass the string
representation of the new path. Raises an exception if the path doesn't
exist, otherwise it returns True.
"""

if not os.access(path, os.F_OK):
raise IOError, path + " not found."
if path.endswith(os.sep):
path = path[:-1]
global __sounds_path
__sounds_path = path
return 1


def get_images_path():
"""
get_images_path() --> String
Returns the current value of the images path, or None if it hasn't been
set yet.
"""

return __images_path

def get_fonts_path():
"""
get_fonts_path() --> String
Returns the current value of the fonts path, or None if it hasn't been
set yet.
"""

return __fonts_path

def get_sounds_path():
"""
get_sounds_path() --> String
Returns the current value of the sounds path, or None if it hasn't been
set yet.
"""

return __sounds_path



Share this post


Link to post
Share on other sites
BTW, 'class X():' with nothing inside the ()'s is invalid syntax before Python 2.5. :)

A few suggestions:


# Don't need to document that Words is the words class. But anyway, don't make
# big block comments in Python, when you can use docstrings. :)

from random import shuffle, choice, randrange

# I like to put helper functions up top. Just personal preference.
def scramble_word(the_word):
""" Shuffle/Scramble a word up. """
word = list(the_word)
shuffle(word)
return "".join(word) # Don't bother naming things that are used immediately.


class Words:
def __init__(self):
""" This class selects and scrambles words. """
self.clues = { 'gasoline': 'Fuel',
'airplane': 'Flying Machine',
'cardstock': 'Thick Paper',
'keyboard': 'Instrument',
'monitor': 'A Display',
'calculator': 'Number Cruncher',
'gigantic': 'Huge'}
# Instead of keeping a separate list of remaining words, I'm going to
# remove things from the dictionary as we go. This will require
# remembering the clue for the current word, of course...

def init_word(self):
""" Selects a word, then scrambles it. """
# Instead of shuffling something and taking the first value from the
# result, why not just make a random selection? :)
self.the_word = choice(self.clues.keys())
self.clue = self.clues[self.the_word]
del self.clues[self.the_word] # this is how we remove it from the dict.

self.scrambled_word = scramble_word(self.the_word)
return self.scrambled_word

def show_scramble(self):
""" Display the scrambled word. """
font = pygame.font.Font(None, 24)
text = font.render("The Scrambled word is: " + self.scrambled_word, 1, WHITE)
# I assume the print statement was for debugging?
return text

def show_clue(self, tries):
""" Display a clue once the user has tried to guess it 3 times. """
if tries == 4:
print "Your clue is: ", self.clue, '\n'


# Let's make the "y or n prompt" function more generic, because I'm sure you'll
# find it useful elsewhere.
def ask_yes_no(self, prompt):
""" See if the user would like to guess again."""
print prompt
# Don't store temporary values when the logic is straightforward.
# Also, there's no need to check a boolean value in order to decide whether
# to return True or False. Just return the boolean value (or its negation,
# if appropriate).
return raw_input("y or n: ").lower() == 'y'


# ------------------------------------------------------------
# The round class
#
# ------------------------------------------------------------

class Round:
""" This class keeps track of scores and game variables. """
def __init__(self):
self.roundnumber = 1
self.score = []
self.wordcount = 1
self.possiblescore = 0

def start_round(self, word):
""" Sets the required variables for the beinning of a round. """
self.tries = 1
# We don't need to remember 'the_word' for this class.
# Let's remember the maximum score for the current word, since it's
# used a few places, and factor the '* 100' into the "possible score".
self.wordscore = len(word) * 100
self.possiblescore += self.wordscore

def get_guess(self):
""" Get the user's guess. """
# As before...
guess = raw_input("Enter your guess: ").lower()
print ""
return guess

def guess_again(self):
""" See if the user would like to guess again."""
self.tries += 1
return ask_yes_no("Would you like to guess again?\n")

def calc_score(self):
""" Calculates the users score, on a per word basis
then it adds it to a list so we can see how they did
on each word as well as their final score. """
# A trick for dealing with the "integer division problem" without
# doing annoying stuff with float/int conversion: simply multiply
# before dividing, when you seek an integer percentage.
# Also, let's calculate the max score first, since it's used twice.
score = self.wordscore / self.tries
self.score.append(score)
print "Your score for that word was:", score, "out of a possible", self.wordscore, '\n'

def show_score(self):
""" Displays the sum of each words score. """
# Use built-in functions to add up scores.
total_score = sum(self.score)
print "Your score was:", total_score, 'out of a possible:', self.possiblescore, '\n'



You can, of course, keep going along these lines. I think a lot of the unnecessary complexity here stems from trying to divide labour in unusual ways, and trying to use classes for things where they aren't necessary (hint: a class is supposed to represent "a kind of thing", such that each object is "a thing of that kind". What's a Words? Why does a Round actually remember every round of the game?).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!