miércoles, 6 de mayo de 2009

Contando

Ahora vamos a descongelar el tiempo... Vamos a hacer que el contador se mueva y que el juego termine cuando el tiempo se acabe. Para este tipo de labores, es ideal la función timeout_add del módulo gobject. Esta función espera cierto tiempo, y luego ejecuta una segunda función, entonces lo que hacemos es definir una función que cuando sea llamada, reste uno del tiempo restante (remaining_time), y actualice el tiempo que se muestra en la interfaz. Además, vuelve a crear un contador de tiempo que vuelve a llamar esta función cuando haya pasado un segundo (1000 milisegundos). De esta forma, tendremos un contador. Cuando el tiempo llegue a 0, la casilla de entrada de texto es bloqueada para que no se pueda entrar más palabras.

def update_timer(self):
self.remaining_time -= 1
timer_text = str(int(self.remaining_time / 60)) + ":"
timer_text += "%0.2i" % (self.remaining_time % 60)
self.timer_label.set_text(timer_text)
if self.remaining_time == 0:
self.word_entry.set_state(gtk.STATE_INSENSITIVE)
else:
gobject.timeout_add(1000, self.update_timer)
return False

Además de importar el módulo gobject al inicio para poder usar el contador:

import gobject

al final de la función __init__ que ya tenemos (llamada el constructor), llamamos una vez la función update_timer para que el contador empiece a contar:

self.update_timer()

También voy a aprovechar para ir acumulando las palabras correctas en una lista (valid_words), e iré mostrando las palabras que ha entrado el jugador. Esto lo hago cada vez que se entra una palabra válida (dentro de la función word_entered). Es importante revisar que la palabra entrada no esté contenida en las palabras váidas que ya tengo, para no incluirla dos veces en la lista.

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:
words = ""
if not word in self.valid_words:
self.valid_words.append(word)
for valid_word in self.valid_words:
words += valid_word + ", "
self.player_panel.set_text(words)
self.mark_word(word_chain)
else:
self.player_panel.set_text("Inválida")

Ah! Además puedo aprovechar para mostrar dónde está ubicada la palabra con la nueva función mark_word. Esta función marca en azul las letras que forman la palabra. Lo que debo hacer es recorrer las posiciones de la palabra e ir cambiando el color del gtk.Label de esa posición. Además quiero que dure marcada 5 segundos, pero que luego se desmarque automáticamente. Para esto activo un contador que llame la función unmark_word que se encarga de dejar todas las letras negras otra vez. Es importante cancelar el contador de la palabra anterior para garantizar que siempre dura 5 segundos marcada la palabra con la función source_remove de gobject.

def mark_word(self, word_chain):
self.unmark_word()
for position in word_chain:
self.grid.set_color(gtk.gdk.Color(100, 100, 60000),
position[0], position[1])
gobject.source_remove(self.highlight_timer)
self.highlight_timer = gobject.timeout_add(5000, self.unmark_word)
return

La función unmark_word se ve así:

def unmark_word(self):
for row in range(self.size):
for column in range(self.size):
self.grid.set_color(gtk.gdk.Color(0,0,0),
row, column)
return False #to stop timer

Y listo, cada vez se siente más funcional el juego. Como siempre, y por última vez aquí está el código:

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

import gtk
import gobject
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)

def set_color(self, color, row, column):
self.dice_labels[row][column].modify_fg(gtk.STATE_NORMAL, color)

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()
self.valid_words = []
self.highlight_timer = 0
self.remaining_time = 91 #always one second more than needed
mainwindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
mainwindow.set_size_request(640, 480)
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
self.timer_label = gtk.Label("1:00")
self.timer_label.modify_font(pango.FontDescription("mono 24"))
self.timer_label.set_size_request(100, 90)
self.timer_label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(60000, 5000, 5000))
self.timer_label.show()
box3.pack_start(self.timer_label)
#Create Player Panel
self.player_panel = gtk.Label(" ")
self.player_panel.show()
self.player_panel.set_line_wrap(True)
self.player_panel.set_size_request(140, 100)
box3.pack_start(self.player_panel)
#Create dummy Definitions panel
definition_panel = gtk.Label(" ")
definition_panel.show()
box3.pack_start(definition_panel)
mainwindow.show()
self.update_timer()

def new_dice_set(self):
available_dice = range(16)
for row in range(self.size):
for column in range(self.size):
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(self.size):
for column in range(self.size):
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(self.size):
for column in range(self.size):
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:
words = ""
if not word in self.valid_words:
self.valid_words.append(word)
for valid_word in self.valid_words:
words += valid_word + ", "
self.player_panel.set_text(words)
self.mark_word(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

def mark_word(self, word_chain):
self.unmark_word()
for position in word_chain:
self.grid.set_color(gtk.gdk.Color(100, 100, 60000),
position[0], position[1])
gobject.source_remove(self.highlight_timer)
self.highlight_timer = gobject.timeout_add(5000, self.unmark_word)
return

def unmark_word(self):
for row in range(self.size):
for column in range(self.size):
self.grid.set_color(gtk.gdk.Color(0,0,0),
row, column)
return False #to stop timer

def update_timer(self):
self.remaining_time -= 1
timer_text = str(int(self.remaining_time / 60)) + ":"
timer_text += "%0.2i" % (self.remaining_time % 60)
self.timer_label.set_text(timer_text)
if self.remaining_time == 0:
self.word_entry.set_state(gtk.STATE_INSENSITIVE)
else:
gobject.timeout_add(1000, self.update_timer)
return False

wh = wordhunt()
gtk.main()

Imágen:
wwarby: http://www.flickr.com/photos/wwarby/3297205226/

No hay comentarios:

Publicar un comentario