domingo, 3 de mayo de 2009

Mezclando


Vamos ahora a mezclar los ingredientes que tenemos. Por un lado habíamos construido el motor del juego, que puede generar la matriz de dados aleatoriamente, y que puede recibir las palabras escritas por el jugador para verificar si se pueden construir con los dados disponibles. De este ingrediente, ya no necesitaremos la función que imprime en la terminal la cuadrícula, ni la entrada de texto desde la terminal, porque esto será ahora el trabajo de nuestro segundo ingrediente. Nuestro segundo ingrediente fue la interfaz gráfica, que nos puede mostrar la cuadrícula, además de tener un espacio para entrar texto. Vamos a agregar un par de cosas a la clase worhuntgrid:

class wordhuntgrid(gtk.Table):
def __init__(self, size):
self.size = size
gtk.Table.__init__(self, self.size, self.size)
self.dice_labels = []
for row in range(self.size):
dice_row = []
for column in range(self.size):
new_label = gtk.Label("A")
new_label.modify_font(pango.FontDescription("sans 32"))
new_label.show()
self.attach(new_label, \
row, row + 1, column, column + 1,
gtk.EXPAND, gtk.EXPAND)
dice_row += [new_label]
self.dice_labels += [dice_row]
def set_dice(self, text, row, column):
self.dice_labels[row][column].set_text(text)


En primer lugar, agrego la opción de determinar el tamaño de la cuadrícula cuando se cree el objeto, por eso ahora el constructor (__init__) toma un valor de entrada. Este valor se guarda en la variable self.size. El objeto self representa al objeto mismo en el que estamos, así que self.size es la variable size que pertence al objeto. No es la misma size que entró al objeto. Además he agregado una función set_dice, que permite dar una letra (text) y su ubicación para colocarla en la cuadrícula.

Y ahora si a mezclar...

Para combinar el motor del programa con su interfaz gráfica, voy a crear una clase llamada wordhunt. Esta clase contendrá elementos de ambos, muchas veces colocando self como prefijo a algunas variables, para que éstas estén disponibles para el resto de funciones de la clase. Naturalmente cuando las use, debo usar también el prefijo self.
Para pasar las letras de los dados que se generaron aleatoriamente, debo usar la función set_dice que mostré arriba de esta forma:

for row in range(self.size):
for column in range(self.size):
self.grid.set_dice(
self.dice[self.facedie[row][column]][self.face[row][column]],
row,column)

Para recibir la entrada de texto, debo monitorear el momento en que se ha hecho "Enter" en la entrada de texto. Muchos objetos en GTK generan "señales" cuando ocurre algo con ellos. Un objeto gtk.Entry genera una señal "activate" cuando se presiona "Enter" estando el cursor en él. Lo que debemos hacer es conectar esta señal a una función (entry_activate_callback), que será llamada cada vez que la señal se genere, así:

self.word_entry.connect("activate", self.entry_activate_callback)

La función que se llama con activate la defino así:

def entry_activate_callback(self,widget):
self.word_entered(self.word_entry.get_text())
self.word_entry.set_text("")
return

Esta función llama a una segunda función (word_entered), pasándole el contenido de la entrada de texto, y luego limpia la entrada de texto. Además, en vez de usar print como antes para reportar si una palabra es válida, pongo el texto en el gtk.Label player_panel. De esta forma el texto se muestra justo debajo del contador de tiempo. La función word_entered queda así:

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import gtk
import pango

import random

class wordhuntgrid(gtk.Table):
def __init__(self, size):
self.size = size
gtk.Table.__init__(self, self.size, self.size)
self.dice_labels = []
for row in range(self.size):
dice_row = []
for column in range(self.size):
new_label = gtk.Label("A")
new_label.modify_font(pango.FontDescription("sans 32"))
new_label.show()
self.attach(new_label, \
row, row + 1, column, column + 1,
gtk.EXPAND, gtk.EXPAND)
dice_row += [new_label]
self.dice_labels += [dice_row]
def set_dice(self, text, row, column):
self.dice_labels[row][column].set_text(text)

class wordhunt():
def __init__(self):
self.dice = [ ["T", "O", "E", "S", "S", "I"],
["A", "S", "P", "F", "F", "K"],
["N", "U", "I", "H", "M", "Qu"],
["O", "B", "J", "O", "A", "B"],
["L", "N", "H", "N", "R", "Z"],
["A", "H", "S", "P", "C", "O"],
["R", "Y", "V", "D", "E", "L"],
["I", "O", "T", "M", "U", "C"],
["L", "R", "E", "I", "X", "D"],
["T", "E", "R", "W", "H", "V"],
["T", "S", "T", "I", "Y", "D"],
["W", "N", "G", "E", "E", "H"],
["E", "R", "T", "T", "Y", "L"],
["O", "W", "T", "O", "A", "T"],
["A", "E", "A", "N", "E", "G"],
["E", "I", "U", "N", "E", "S"] ]

self.size = 4
self.face = [ [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0] ]
self.facedie = [ [0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0] ]
self.new_shuffled_group()
self.new_dice_set()
mainwindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
mainwindow.connect("delete_event", gtk.main_quit)
box1 = gtk.HBox()
box1.show()
mainwindow.add(box1)
box2 = gtk.VBox() # Left side panels
box2.show()
box1.pack_start(box2)
box3 = gtk.VBox() # Right side panels
box3.show()
box1.pack_start(box3)
# Create dice grid
self.grid = wordhuntgrid(self.size)
self.grid.set_size_request(250, 250)
for row in range(self.size):
for column in range(self.size):
self.grid.set_dice(
self.dice[self.facedie[row][column]][self.face[row][column]],
row,column)
self.grid.show()
box2.pack_start(self.grid)
# Create text entry line
self.word_entry = gtk.Entry()
self.word_entry.connect("activate", self.entry_activate_callback)
self.word_entry.show()
box2.pack_start(self.word_entry)
# Create timer display
timer_label = gtk.Label("1:00")
timer_label.modify_font(pango.FontDescription("mono 24"))
timer_label.set_size_request(100, 90)
timer_label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(60000, 5000, 5000))
timer_label.show()
box3.pack_start(timer_label)
#Create Player Panel
self.player_panel = gtk.Label(" ")
self.player_panel.show()
box3.pack_start(self.player_panel)
#Create dummy Definitions panel
definition_panel = gtk.Label(" ")
definition_panel.show()
box3.pack_start(definition_panel)
mainwindow.show()

def new_dice_set(self):
available_dice = range(16)
for row in range(4):
for column in range(4):
self.facedie[row][column] = random.randint(0,len(available_dice) - 1)
available_dice.pop(self.facedie[row][column])

def new_shuffled_group(self):
for row in range(4):
for column in range(4):
self.face[row][column] = random.randint(0,5)

def get_letter(self,word, index):
internal_index = 0
real_index = 0
if word == "":
return ""
while internal_index < index:
letter = word[real_index]
if letter == "Q":
real_index = real_index + 1
if real_index < len(word):
letter = word[real_index]
elif letter != "U":
return ""
real_index = real_index + 1
internal_index = internal_index + 1
result = word[real_index]
if result == "Q":
real_index = real_index + 1
if real_index < len(word):
result = result + "u"
return result

def start_positions(self,letter):
positions = []
for row in range(4):
for column in range(4):
if self.dice[self.facedie[row][column]][self.face[row][column]] == letter:
positions.append([row, column])
return positions

def find_word(self,word, row, column, used):
matrix = [[-1, -1], [-1,0], [-1, 1],
[0, -1], [0,1],
[1, -1], [1, 0], [1,1] ]
if word == "":
return [] #fin de la palabra-cerrar recursion
letter = self.get_letter(word,0)
if letter == "Qu":
new_word = word[2:]
else:
new_word = word[1:]
for transform in matrix:
new_row = row + transform[0]
new_column = column + transform[1]
if new_row < 0 or new_column < 0 or \
new_row > 3 or new_column > 3 or \
used[new_row][new_column] == 1:
pass
elif self.dice[self.facedie[new_row][new_column]][self.face[new_row][new_column]] == letter:
used_copy = used
used_copy[new_row][new_column] = 1
return [[new_row, new_column]] + \
self.find_word(new_word, new_row, new_column, used_copy)
return [False]

def word_is_valid(self,word):
letter = self.get_letter(word,0)
if letter == "":
return [False]
elif letter == "Qu":
new_word = word[2:]
else:
new_word = word[1:]
for position in self.start_positions(letter):
used = [[0,0,0,0], [0,0,0,0], [0,0,0,0], [0,0,0,0]]
used[position[0]][position[1]] = 1
used_copy = used
word_chain = [[position[0],position[1]]] + \
self.find_word(new_word, position[0], position[1], used_copy)
if word_chain[-1] != False:
return word_chain
return [False]

def word_entered(self,word):
word = word.upper() #todo en mayusculas
if word == "X":
print "Adiós"
gtk.main_quit()
elif len(word) < 3:
self.player_panel.set_text("Demasiado Corta")
else:
word_chain = self.word_is_valid(word)
if word_chain[-1] != False:
self.player_panel.set_text("Válida")
print word_chain
else:
self.player_panel.set_text("Inválida")

def entry_activate_callback(self,widget):
self.word_entered(self.word_entry.get_text())
self.word_entry.set_text("")
return

wh = wordhunt()
gtk.main()

Imágenes:

vivere libero: http://www.flickr.com/photos/viverelibero/3004553146/
sylvar: http://www.flickr.com/photos/sylvar/348629926/
ginnerobot: http://www.flickr.com/photos/ginnerobot/3156850244/
_e.t: http://www.flickr.com/photos
/45688285@N00/82172576/

mknobil: http://www.flickr.com/photos/knobil/163202136/

1 comentario: