check tic-tac-toe win conditions, slimmed down _messageCB some more
This commit is contained in:
parent
b170e77cdb
commit
76808901eb
|
@ -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,42 +1647,14 @@ 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
|
||||
|
||||
else:
|
||||
# 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)
|
||||
session.received(frm, msgtxt, tim, encrypted, subject, msg)
|
||||
else:
|
||||
session.received(msg)
|
||||
# END messageCB
|
||||
|
@ -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,
|
||||
|
|
|
@ -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]))
|
||||
|
||||
|
|
163
src/tictactoe.py
163
src/tictactoe.py
|
@ -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,12 +105,12 @@ 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'):
|
||||
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':
|
||||
|
@ -104,14 +118,15 @@ class TicTacToeSession(stanza_session.StanzaSession):
|
|||
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.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
|
||||
|
||||
|
|
Loading…
Reference in New Issue