from pyscrabble.constants import *
from pyscrabble.lookup import *
from pyscrabble.exceptions import *
from pyscrabble import manager
from pyscrabble import util
from random import shuffle
class Letter(object):
'''
Letter class. Represents a letter on the gameboard
'''
def __init__(self, letter="", score=0 ):
'''
Initialize the Letter
@param letter: String representing the Letter
'''
self.score = score
self.__isBlank = False
self.setLetter( letter )
def clone(self):
'''
Clone the letter
@return: Clone of this Letter
'''
l = Letter(self.letter)
l.__isBlank = self.isBlank()
l.score = self.score
return l
def setLetter(self, letter):
'''
Set the Letter string
@param letter:
'''
if (letter == ""):
self.__isBlank = True
#else:
# self.__isBlank = False
self.letter = letter
def setIsBlank(self, isBlank):
'''
Set isBlank property
@param isBlank: True / False
'''
self.__isBlank = isBlank
def getLetter(self):
'''
Get the Letter string
@return: String representation of this Letter
'''
return self.letter
def getCharacter(self):
'''
Get the Letter string
@return: String representation of this Letter
@see: L{Letter.getLetter}
'''
return self.getLetter()
def getScore(self):
'''
Get the score of this letter
@return: Score of this letter
'''
if self.isBlank():
return 0
else:
return int(self.score)
def setScore(self, score):
'''
Set score for letter
@param score: Score
'''
self.score = score
def __repr__(self):
'''
Return a String formatted as follows::
LETTER_STRING: SCORE
@return String representation of this Letter
'''
return self.getLetter() + ": "+str(self.getScore())
def __eq__(self, other):
'''
Check if this Letter equals another Letter.
@param other: Other letter
@return: True if the Letter strings are the same
'''
if isinstance(other, Letter):
a = util.getUnicode(self.getLetter())
b = util.getUnicode(other.getLetter())
if a == b:
return True
elif ((a != b) and (self.isBlank() == True and other.isBlank() == True)):
return True
return False
def __neq__(self, other):
'''
Check if this Letter does not equal another Letter.
@param other: Other letter
@return: True if the LEtter strings are not the same.
'''
if isinstance(other, Letter):
if self.getLetter() == other.getLetter():
return False
return True
def isBlank(self):
'''
Check if the Letter is a Blank.
@return: True if the Letter is a blank letter.
'''
return self.__isBlank
# Represents a tile on the gameboard
class Tile:
'''
Tile class.
Represents a Tile on the gameboard.
'''
def __init__( self, style=TILE_NORMAL):
'''
Initialize a Tile.
@param style: Tile Style
@see: L{constants}
'''
self.__style = style
self.letter = None
def getStyle(self):
'''
Return Tile Style
@return: Tile style
@see: L{constants}
'''
return self.__style
def setStyle(self, style):
'''
Set Tile Style
@param style: Tile Stype
@see: L{constants}
'''
self.__style = style
def setLetter(self, letter):
'''
Set the Letter on this tile
@param letter: Letter to put on the Tile
@see: L{Letter}
'''
self.letter = letter
def getLetter(self):
'''
Return Letter on this Tile or None
@return: Letter on this Tile or None
'''
return self.letter
def getTileScore(self):
'''
Get the score of this Tile.
The score is the score of the Letter on the Tile * TILE_STYLE if TILE_STYLE is a Letter Modifier
@return: Tile score
@see: L{constants}
'''
# If this Tile is a letter modifier, use the stle
# If its a word modifier, return the letter score
if (self.__style in LETTER_MODIFIERS):
return self.letter.getScore() * self.__style
else:
return self.letter.getScore() * TILE_NORMAL
def getWordModifier(self):
'''
Return the word modifier on this tile if it has one. If it doesn't, return TILE_NORMAL
@return Word Modifier on this tile
@see: L{constants}
'''
if (self.style in WORD_MODIFIERS):
# Word modifiers are stored as twice the necessary amount so as not to conflict with letter modifiers
return self.style / 2
else:
return TILE_NORMAL
def __repr__(self):
'''
Format Tile as a string::
Letter: LETTER_ON_TILE Modifier: TILE_MODIFIER
@return: Formatted Tile string
'''
return "Letter: "+self.letter+" Modifier: "+str(self.style)
# Bag is the "bag" of letters
class Bag:
'''
Bag represents a "Bag" of Letters
'''
def __init__(self, rules):
'''
Initialize the Letter bag
@see: L{Letter}
'''
self.letters = []
l = manager.LettersManager()
for letter,count,score in l.getLetters(rules):
for x in range(count):
self.letters.append( Letter(letter, score) )
# Shuffle the letters in the bag
shuffle(self.letters)
def getDistribution(self):
'''
Get Letter distribution
@return: dict(Letter,Count)
'''
result = {}
for letter in self.letters:
key = letter.getCharacter()
if result.has_key(key):
result[key] = result[key] + 1
else:
result[key] = 1
return result
def getLetters(self, count = 7):
'''
Get C{count} number of letters from thebag.IfCcountgreaterthanthenumberofLetters import
left in the Bag, the remaining number of Letters are returned
@param count: Number of letters to get
@raise BagEmptyException: If the Bag is empty.
@see: L{Letter}
'''
if self.isEmpty():
raise BagEmptyException()
# Take "count" number letters from bag, or remaining number of letters
if (self.getCount() < count):
count = self.getCount()
return [ self.letters.pop() for x in range(count) ]
def isEmpty(self):
'''
Check to see if the Bag is empty
@return: True if the Bag is empty
'''
return (self.getCount() == 0)
def getCount(self):
'''
Get the number of Letters in the Bag
@return: Number of letters in the Bag
'''
return len( self.letters )
def returnLetters(self, letters):
'''
Return a list of Letters to the Bag.
@param letters: List of Letters to return to the Bag
@see: L{Letter}
'''
for letter in letters:
self.letters.append( letter )
shuffle(self.letters)
class Move(object):
'''
Move represents a move that is made on the board.
The Move is a list of tuples containing (letter, x-position, y-position) for all Letters in the Move.
'''
def __init__(self, move = None):
'''
Initialize the Move.
@param move: Optional Move to clone
'''
self.score = 0
self.__hasBlank = False
self.move = []
if (move != None): # The move is a list of (letter, x-position, y-position) tuples
for letter,x,y in move:
self.addMove(letter, x, y)
def setScore(self, score):
'''
Set the score
@param score: Score
'''
self.score = score
def getScore(self):
'''
Get the score for this move
@return: Score for the move
'''
return self.score
def addMove(self, letter, x, y):
'''
Add a Letter to the Move
@param letter: Letter
@param x: X position
@param y: Y Position
@see: L{Letter}
'''
if not self.hasBlank():
self.__hasBlank = letter.isBlank()
self.move.append( (letter, x, y) )
def removeMove(self, letter, x, y):
'''
Remove move
@param letter:
@param x:
@param y:
'''
self.move.remove( (letter,x,y) )
def prependMove(self, letter, x, y):
'''
Insert a Letter at the beginning of the Move
@param letter: Letter
@param x: X position
@param y: Y position
@see: L{Letter}
'''
if not self.hasBlank():
self.__hasBlank = letter.isBlank()
self.move.insert(0, (letter,x,y) )
def isEmpty(self):
'''
Check whether the Move is empty
@return: True if the Move has no Letters in it.
'''
return len(self.move) == 0
def isContinuous(self):
'''
Check to see if the move is continous, meaning all the tiles are connected
@return: True if all the tiles are connected
'''
self.sort()
if self.isVertical():
prevy = -1
for letter, x, y in self.move:
if prevy == -1:
prevy = y
continue
if y != (prevy +1):
return False
prevy = y
return True
if self.isHorizontal():
prevx = -1
for letter, x, y in self.move:
if prevx == -1:
prevx = x
continue
if x != (prevx +1):
return False
prevx = x
return True
def isVertical(self):
'''
Check whether the Move is vertical.
@return: True if all Letters in the move are arranged vertically.
'''
if (self.isEmpty()):
return False
letter,x,y = self.move[0]
for _letter,_x,_y in self.move:
if x != _x:
return False
return True
def isHorizontal(self):
'''
Check whether the Move is horizontal.
@return: True if all Letters in the move are arranged horizontally.
'''
if (self.isEmpty()):
return False
letter,x,y = self.move[0]
for _letter, _x, _y in self.move:
if y != _y:
return False
return True
def isValid(self):
'''
Check whether the move is valid.
A Move is valid if it is::
- Not empty
- Horitzontal or
- Veritcal
@return: True if the Move is valid.
'''
return (not self.isEmpty()) and (self.isHorizontal() or self.isVertical())
def clear(self):
'''
Clear the move
'''
self.move = []
def getTiles(self):
'''
Get the list of tuples in the move.
@return: List of (letter, x-position, y-position) tuples.
'''
return self.move
def getFirstMove(self):
'''
Return the first tuple in the Move
@return: The first tuple in the Move or None if the Move is empty
'''
if (self.isEmpty()):
return None
else:
return self.move[0]
def hasCommonTile(self, move):
'''
Check to see if this move has a common tile with C{move}
@param move: Move to check
@return: True if this Move has a letter,x,y tile in common with C{move}
'''
for letter, x, y in move.getTiles():
if (self.contains(letter,x,y)):
return True
return False
def containsMove(self, move):
'''
Check to see if this Move contains the tiles in C{move}
@param move: Move
'''
for letter, x, y in move.getTiles():
if not self.contains(letter, x, y):
return False
return True
def contains(self,letter,x,y):
'''
Check to see if the Move contains C{letter} at C{x,y}
@param letter:
@param x:
@param y:
@return: True if the Move contains C{letter} at C{x,y}
@see: L{Letter}
'''
return (letter,x,y) in self.move
def hasLetterAt(self, x, y):
'''
Checks to see if the move has a letter at C{x,y}
@param x: X position
@param y: Y position
@return: True if the Move has a Letter at C{x,y}
'''
for letter, _x, _y in self.move:
if _x == x and _y == y:
return True
return False
def clone(self):
'''
Clone this move
@return: Cloned Move.
'''
return Move(self.move[:])
def appendMove(self, move):
'''
Append a Move
@param move: Move to append
'''
for letter, x, y in move.getTiles():
self.addMove(letter,x,y)
def length(self):
'''
Get number of tuples in the Move.
@return: Number of tuples in the move.
'''
return len(self.move)
def __repr__(self):
'''
String format.
@return: String format of the Move. Concatenate each letter in the Move.
'''
buf = ''
for letter,x,y in self.move:
buf += str(letter)
buf += ' | '
return buf
def getWord(self):
'''
Get the word that this Move spells
@return: Word that this Move spells
'''
self.sort()
word = unicode('', 'utf-8')
for letter, x, y in self.move:
word += letter.getLetter()
return word
def sort(self):
'''
Sort the move.
If Horizontal, arrange the Letters by the x-position
If Vertical, arrange the Letters by their y-position
'''
if (self.isHorizontal()):
self.move.sort( lambda (t1, x1, y1), (t2, x2, y2): x1 - x2 )
elif (self.isVertical()):
self.move.sort( lambda (t1, x1, y1), (t2, x2, y2): y1 - y2 )
def hasBlank(self):
'''
Check whether this Move contains a blank letter
@return: True if this Move contains a blank Letter
@see: L{Letter}
'''
return self.__hasBlank
|