check tic-tac-toe win conditions, slimmed down _messageCB some more

This commit is contained in:
Brendan Taylor 2008-04-15 05:32:45 +00:00
parent b170e77cdb
commit 76808901eb
3 changed files with 208 additions and 130 deletions

View File

@ -1593,7 +1593,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('FAILED_DECRYPT', (frm, tim))
msgtxt = msg.getBody()
msghtml = msg.getXHTML()
subject = msg.getSubject() # if not there, it's None
tim = msg.getTimestamp()
@ -1612,19 +1611,15 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
frm = address.getAttr('jid')
jid = gajim.get_jid_without_resource(frm)
encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
# invitations
invite = None
encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED)
if not encTag:
invite = msg.getTag('x', namespace = common.xmpp.NS_MUC_USER)
if invite and not invite.getTag('invite'):
invite = None
delayed = msg.getTag('x', namespace = common.xmpp.NS_DELAY) != None
msg_id = None
composing_xep = None
# FIXME: Msn transport (CMSN1.2.1 and PyMSN0.10) do NOT RECOMMENDED
# invitation
# stanza (MUC XEP) remove in 2007, as we do not do NOT RECOMMENDED
@ -1639,37 +1634,6 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
is_continued))
return
form_node = None
for xtag in xtags:
if xtag.getNamespace() == common.xmpp.NS_DATA:
form_node = xtag
break
chatstate = None
# chatstates - look for chatstate tags in a message if not delayed
if not delayed:
composing_xep = False
children = msg.getChildren()
for child in children:
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
chatstate = child.getName()
composing_xep = 'XEP-0085'
break
# No XEP-0085 support, fallback to XEP-0022
if not chatstate:
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
if chatstate_child:
chatstate = 'active'
composing_xep = 'XEP-0022'
if not msgtxt and chatstate_child.getTag('composing'):
chatstate = 'composing'
# XEP-0172 User Nickname
user_nick = msg.getTagData('nick')
if not user_nick:
user_nick = ''
if encTag and self.USE_GPG:
encmsg = encTag.getData()
@ -1683,44 +1647,16 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if mtype == 'error':
self.dispatch_error_message(msg, msgtxt, session, frm, tim, subject)
return
elif mtype == 'groupchat':
self.dispatch_gc_message(msg, subject, frm, msgtxt, jid, tim, msghtml)
return
self.dispatch_gc_message(msg, subject, frm, msgtxt, jid, tim)
elif invite is not None:
self.dispatch_invite_message(invite, frm)
return
elif mtype == 'chat':
if not msg.getTag('body') and chatstate is None: # no <body>
return
log_type = 'chat_msg_recv'
else: # it's a single message
log_type = 'single_msg_recv'
mtype = 'normal'
if session.is_loggable() and msgtxt:
try:
msg_id = gajim.logger.write(log_type, frm, msgtxt,
tim = tim, subject = subject)
except exceptions.PysqliteOperationalError, e:
self.dispatch('ERROR', (_('Disk Write Error'), str(e)))
treat_as = gajim.config.get('treat_incoming_messages')
if treat_as:
mtype = treat_as
# XXX horrible hack
if isinstance(session, ChatControlSession):
session.received(frm, msgtxt, tim, encrypted, mtype, subject, chatstate,
msg_id, composing_xep, user_nick, msghtml, form_node)
else:
session.received(msg)
# XXX horrible hack
if isinstance(session, ChatControlSession):
session.received(frm, msgtxt, tim, encrypted, subject, msg)
else:
session.received(msg)
# END messageCB
# process and dispatch an error message
@ -1741,7 +1677,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
self.dispatch('MSGERROR', (frm, msg.getErrorCode(), error_msg, msgtxt, tim))
# process and dispatch a groupchat message
def dispatch_gc_message(self, msg, subject, frm, msgtxt, jid, tim, msghtml):
def dispatch_gc_message(self, msg, subject, frm, msgtxt, jid, tim):
has_timestamp = bool(msg.timestamp)
if subject != None:
@ -1761,7 +1697,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
if not self.last_history_line.has_key(jid):
return
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msghtml,
self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msg.getXHTML(),
statusCode))
no_log_for = gajim.config.get_per('accounts', self.name,

View File

@ -1,9 +1,12 @@
from common import helpers
from common import exceptions
from common import gajim
from common import stanza_session
from common import contacts
import common.xmpp
import dialogs
import message_control
@ -16,8 +19,70 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
self.control = None
# extracts chatstate from a <message/> stanza
def get_chatstate(self, msg, msgtxt):
composing_xep = None
chatstate = None
# chatstates - look for chatstate tags in a message if not delayed
delayed = msg.getTag('x', namespace=common.xmpp.NS_DELAY) != None
if not delayed:
composing_xep = False
children = msg.getChildren()
for child in children:
if child.getNamespace() == 'http://jabber.org/protocol/chatstates':
chatstate = child.getName()
composing_xep = 'XEP-0085'
break
# No XEP-0085 support, fallback to XEP-0022
if not chatstate:
chatstate_child = msg.getTag('x', namespace = common.xmpp.NS_EVENT)
if chatstate_child:
chatstate = 'active'
composing_xep = 'XEP-0022'
if not msgtxt and chatstate_child.getTag('composing'):
chatstate = 'composing'
return (composing_xep, chatstate)
# dispatch a received <message> stanza
def received(self, full_jid_with_resource, message, tim, encrypted, msg_type, subject, chatstate, msg_id, composing_xep, user_nick, xhtml, form_node):
def received(self, full_jid_with_resource, msgtxt, tim, encrypted, subject, msg):
msg_type = msg.getType()
msg_id = None
# XEP-0172 User Nickname
user_nick = msg.getTagData('nick')
if not user_nick:
user_nick =''
form_node = None
for xtag in msg.getTags('x'):
if xtag.getNamespace() == common.xmpp.NS_DATA:
form_node = xtag
break
composing_xep, chatstate = self.get_chatstate(msg, msgtxt)
xhtml = msg.getXHTML()
if msg_type == 'chat':
if not msg.getTag('body') and chatstate is None:
return
log_type = 'chat_msg_recv'
else:
log_type = 'single_msg_recv'
if self.is_loggable() and msgtxt:
try:
msg_id = gajim.logger.write(log_type, full_jid_with_resource, msgtxt,
tim=tim, subject=subject)
except exceptions.PysqliteOperationalError, e:
gajim.dispatch('ERROR', (_('Disk WriteError'), str(e)))
treat_as = gajim.config.get('treat_incoming_messages')
if treat_as:
msg_type = treat_as
jid = gajim.get_jid_without_resource(full_jid_with_resource)
resource = gajim.get_resource_from_jid(full_jid_with_resource)
@ -65,7 +130,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
# THIS MUST BE AFTER chatstates handling
# AND BEFORE playsound (else we ear sounding on chatstates!)
if not message: # empty message text
if not msgtxt: # empty message text
return
if gajim.config.get('ignore_unknown_contacts') and \
@ -86,16 +151,16 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
if pm:
nickname = resource
groupchat_control.on_private_message(nickname, message, array[2],
groupchat_control.on_private_message(nickname, msgtxt, array[2],
xhtml, session, msg_id)
else:
self.roster_message(jid, message, tim, encrypted, msg_type,
self.roster_message(jid, msgtxt, tim, encrypted, msg_type,
subject, resource, msg_id, user_nick, advanced_notif_num,
xhtml=xhtml, form_node=form_node)
nickname = gajim.get_name_from_jid(self.conn.name, jid)
# Check and do wanted notifications
msg = message
msg = msgtxt
if subject:
msg = _('Subject: %s') % subject + '\n' + msg
focused = False
@ -111,7 +176,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession):
if gajim.interface.remote_ctrl:
gajim.interface.remote_ctrl.raise_signal('NewMessage',
(self.conn.name, [full_jid_with_resource, message, tim,
(self.conn.name, [full_jid_with_resource, msgtxt, tim,
encrypted, msg_type, subject, chatstate, msg_id,
composing_xep, user_nick, xhtml, form_node]))

View File

@ -9,6 +9,8 @@ import cairo
# implements <http://pidgin-games.sourceforge.net/xep/tictactoe.html#invite>
games_ns = 'http://jabber.org/protocol/games'
class InvalidMove(Exception):
pass
@ -24,13 +26,19 @@ class TicTacToeSession(stanza_session.StanzaSession):
else:
self.role_o = 'x'
self.send_invitation()
self.next_move_id = 1
self.received = self.wait_for_invite_response
def send_invitation(self):
msg = xmpp.Message()
invite = msg.NT.invite
invite.setNamespace('http://jabber.org/protocol/games')
invite.setNamespace(games_ns)
game = invite.NT.game
game.setAttr('var', 'http://jabber.org/protocol/games/tictactoe')
game.setAttr('var', games_ns + '/tictactoe')
x = xmpp.DataForm(typ='submit')
@ -38,12 +46,8 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.send(msg)
self.next_move_id = 1
self.state = 'sent_invite'
# received an invitation
def invited(self, msg):
invite = msg.getTag('invite', namespace='http://jabber.org/protocol/games')
def read_invitation(self, msg):
invite = msg.getTag('invite', namespace=games_ns)
game = invite.getTag('game')
x = game.getTag('x', namespace='jabber:x:data')
@ -51,37 +55,47 @@ class TicTacToeSession(stanza_session.StanzaSession):
if form.getField('role'):
self.role_o = form.getField('role').getValues()[0]
else:
self.role_o = 'x'
if form.getField('rows'):
self.rows = int(form.getField('rows').getValues()[0])
else:
self.rows = 3
if form.getField('cols'):
self.cols = int(form.getField('cols').getValues()[0])
# XXX 'strike'
if not hasattr(self, 'rows'):
self.rows = 3
if not hasattr(self, 'cols'):
else:
self.cols = 3
if form.getField('strike'):
self.strike = int(form.getField('strike').getValues()[0])
else:
self.strike = 3
# received an invitation
def invited(self, msg):
self.read_invitation(msg)
# XXX prompt user
# "accept, reject, ignore"
# the number of the move about to be made
self.next_move_id = 1
# display the board
self.board = TicTacToeBoard(self, self.rows, self.cols)
# accept the invitation, join the game
response = xmpp.Message()
join = response.NT.join
join.setNamespace('http://jabber.org/protocol/games')
join.setNamespace(games_ns)
self.send(response)
if not hasattr(self, 'role_o') or self.role_o == 'x':
if self.role_o == 'x':
self.role_s = 'o'
self.role_o = 'x'
self.their_turn()
else:
@ -91,27 +105,28 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.our_turn()
def is_my_turn(self):
return self.state == 'get_input'
# XXX not great semantics
return self.received == self.ignore
def received(self, msg):
# just sent an invitation, expecting a reply
if self.state == 'sent_invite':
if msg.getTag('join', namespace='http://jabber.org/protocol/games'):
self.board = TicTacToeBoard(self, self.rows, self.cols)
# just sent an invitation, expecting a reply
def wait_for_invite_response(self, msg):
if msg.getTag('join', namespace=games_ns):
self.board = TicTacToeBoard(self, self.rows, self.cols)
if self.role_s == 'x':
self.our_turn()
else:
self.their_turn()
if self.role_s == 'x':
self.our_turn()
else:
self.their_turn()
return
elif msg.getTag('decline', namespace=games_ns):
self.XXX()
# ignore messages unless we're expecting a move
if self.state != 'waiting':
return
turn = msg.getTag('turn', namespace='http://jabber.org/protocol/games')
# silently ignores any received messages
def ignore(self, msg):
pass
def wait_for_move(self, msg):
turn = msg.getTag('turn', namespace=games_ns)
move = turn.getTag('move', namespace='http://jabber.org/protocol/games/tictactoe')
row = int(move.getAttr('row'))
@ -128,40 +143,54 @@ class TicTacToeSession(stanza_session.StanzaSession):
print 'received invalid move'
return
# XXX check win conditions
# check win conditions
if self.board.check_for_strike(self.role_o, row, col, self.strike):
self.lost()
elif self.board.full():
self.drawn()
else:
self.next_move_id += 1
self.next_move_id += 1
self.our_turn()
self.our_turn()
def our_turn(self):
self.state = 'get_input'
# ignore messages until we've made our move
self.received = self.ignore
self.board.win.set_title(self.board.title + ': your turn')
def their_turn(self):
self.state = 'waiting'
self.received = self.wait_for_move
self.board.win.set_title(self.board.title + ': their turn')
# called when the board receives input
def move(self, row, column):
def move(self, row, col):
try:
self.board.mark(row, column, self.role_s)
self.board.mark(row, col, self.role_s)
except InvalidMove, e:
print 'invalid move'
print 'you made an invalid move'
return
self.send_move(row, column)
self.send_move(row, col)
# XXX check win conditions
# check win conditions
if self.board.check_for_strike(self.role_s, row, col,self.strike):
self.won()
elif self.board.full():
self.drawn()
else:
self.next_move_id += 1
self.their_turn()
def send_move(self, row, column):
msg = xmpp.Message()
msg.setType('chat')
turn = msg.NT.turn
turn.setNamespace('http://jabber.org/protocol/games')
turn.setNamespace(games_ns)
move = turn.NT.move
move.setNamespace('http://jabber.org/protocol/games/tictactoe')
move.setNamespace(games_ns+'/tictactoe')
move.setAttr('row', str(row))
move.setAttr('col', str(column))
@ -169,11 +198,49 @@ class TicTacToeSession(stanza_session.StanzaSession):
self.send(msg)
self.next_move_id += 1
self.their_turn()
class TicTacToeBoard:
def check_for_strike(self, p, r, c, strike):
# up and down, left and right
tallyI = 0
tally_ = 0
# right triangles: L\ , F/
tallyL = 0
tallyF = 0
# convert real columns to internal columns
r -= 1
c -= 1
for d in xrange(-strike, strike):
# vertical check
try:
tallyI = tallyI + 1 if self.board[r+d][c] == p else 0
except IndexError:
pass
# horizontal check
try:
tally_ = tally_ + 1 if self.board[r][c+d] == p else 0
except IndexError:
pass
# diagonal checks
try:
tallyL = tallyL + 1 if self.board[r+d][c+d] == p else 0
except IndexError:
pass
try:
tallyF = tallyF + 1 if self.board[r+d][c-d] == p else 0
except IndexError:
pass
if any([t == strike for t in (tallyL, tallyF, tallyI, tally_)]):
return True
return False
def __init__(self, session, rows, cols):
self.session = session
@ -184,6 +251,15 @@ class TicTacToeBoard:
self.setup_window()
# is the board full?
def full(self):
for r in xrange(self.rows):
for c in xrange(self.cols):
if self.board[r][c] == None:
return False
return True
def setup_window(self):
self.win = gtk.Window()
@ -214,6 +290,7 @@ class TicTacToeBoard:
self.session.move(row, column)
# this actually draws the board
def expose(self, widget, event):
win = widget.window