tags:

views:

72

answers:

1

I am writing a game where there are two losing conditions:

  1. Forming a word longer than 3 letters. Bee is okay, Beer is not.
  2. Forming a word that can't be made into a longer word. Zebra is okay, Zebras is not.

Wordlist is a list of words, frag is the previous fragment and a is the new letter a player enters. so frag may look like 'app' and a maybe 'l' with the idea of forming the word apple.

def getLoser(frag, a, wordlist):
word = frag + a

if len(word) > 3:

    if word in wordlist:
        print 'word in wordlist'
        return True

    else:
        for words in wordlist:
            if words[:len(word)] == word:
                print words,':', word
                print 'valid word left'
                return False

            else: 
                print words[:len(word)]
                print words,':', word
                print 'false found'
                return True
else:
    return False

For some reason when I enter my 4th letter, it automatically goes to the else in the for loop, even when the if statement functions in the for loop works correctly when I test it alone on dummy data in the interactive trail.

Here is output for the frag alg and the letter e with the word algebra in the wordlist.

e

aa

aa : alge

false found

True

Any ideas?

+6  A: 

You are overcomplicating things. If the new fragment is less than 3 letters, it is automatically OK. If not, it must be the start of some word and not be a word itself to be OK.

>>> words = { "apple" }
>>> def isOK( fragment, letter ):
...     word = fragment + letter
...     if len( word ) <= 3: return True
...     return word not in words and any( w.startswith( word ) for w in words )
...
>>> isOK( "a", "p" )
True
>>> isOK( "ap", "p" )
True
>>> isOK( "app", "l" )
True
>>> isOK( "appl", "l" )
False
>>> isOK( "appl", "e" )
False

(It would be possible to combine the two tests above into one conditional statement:

return len( word ) <= 3 or word not in words and any( w.startswith( word ) for w in words )

but I think that is overly obscure.)

I can't follow the logic of your code above; it is rather confusingly written. (Why is words a string, for instance?) Try writing the logic of the game in pseudocode before trying to implement it -- that may help you sort out your thoughts.


Here's a clearer version:

def isOK( word ):
    condition_one = len( word ) > 3 and word in words
    condition_two = not any( w.startswith( word ) for word in words )

    return not( condition_one or condition_two )
katrielalex
This works wonderfully. How do I learn to uncomplicated my code?
NoahClark
I write it in pseudo code on a whiteboard. words is a string so I can test each word of a certain length against the current word. If they match then we know that at least one more letter can be added on.
NoahClark
@NoahClark: The use of Python idioms such as `startswith` and `any` comes with practice -- I remember someone explaining the best way to come up with them as to "start by assuming that Python already has some magic function that does what you want." I think your pseudocode may be a bit low-level, too; in this case you just needed to work out what logic you wanted to implement before thinking about how to implement the `startswith` test.
katrielalex
@NoahClark: If you write `Return True if string is shorter than three characters. Return True if it is not a word but is the start of some other word. Return False otherwise.` you're 90% of the way there.
katrielalex
Basically what I did I wrote out "Check to see if longer than 3" "Check to see if part of a bigger word" "Check to see if it is a word" and then erased each one one by one and wrote the piece to do it.
NoahClark
@NoahClark: it's a bit overly-nested, then! Would it be clearer to define three boolean variables, one for each test, and then combine them at the end? That is, `return too_short or not_a_word and is_a_prefix`?
katrielalex
Yeah, that makes sense. It's something I can understand.
NoahClark
Shouldn't those all be or? If you test and any are set to true then you can return true using all or!
NoahClark
No: you should take `c1 or c2`, but condition 1 is a conjunction -- it requires that a fragment be both longer than 3 characters __and__ a real word.
katrielalex