Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWalter Bender <walter@walter-laptop.(none)>2010-01-08 23:47:58 (GMT)
committer Walter Bender <walter@walter-laptop.(none)>2010-01-08 23:47:58 (GMT)
commitd287399b5fc873c0bd545eb5ed37cd9158a76350 (patch)
tree0460934ff0275ec5bb19a59008325d567d030e9f
parent77633fe553d5940bb0eaeb4d174d9ac5557ac39a (diff)
added sharing
-rw-r--r--VisualMatchActivity.py266
-rwxr-xr-xgencards.py48
-rw-r--r--grid.py74
-rw-r--r--window.py144
4 files changed, 384 insertions, 148 deletions
diff --git a/VisualMatchActivity.py b/VisualMatchActivity.py
index 707c5ee..87f850b 100644
--- a/VisualMatchActivity.py
+++ b/VisualMatchActivity.py
@@ -40,12 +40,32 @@ from sugar.graphics.menuitem import MenuItem
from sugar.graphics.icon import Icon
from sugar.datastore import datastore
+import telepathy
+from dbus.service import method, signal
+from dbus.gobject_service import ExportedGObject
+from sugar.presence import presenceservice
+from sugar.presence.tubeconn import TubeConnection
+from sugar import profile
+
from gettext import gettext as _
import locale
import os.path
import logging
_logger = logging.getLogger('visualmatch-activity')
-import json
+try:
+ _old_Sugar_system = False
+ import json
+ json.dumps
+ from json import load as jload
+ from json import dump as jdump
+except (ImportError, AttributeError):
+ try:
+ import simplejson as json
+ from simplejson import load as jload
+ from simplejson import dump as jdump
+ except:
+ _old_Sugar_system = True
+
from StringIO import StringIO
from constants import *
@@ -104,7 +124,8 @@ class VisualMatchActivity(activity.Activity):
try:
datapath = os.path.join(activity.get_activity_root(), 'data')
except:
- datapath = os.path.join(os.environ['HOME'], SERVICE, 'data')
+ datapath = os.path.join(os.environ['HOME'], ".sugar", "default",
+ SERVICE, 'data')
gencards.generator(datapath, _numberO, _numberC)
# Create the toolbars
@@ -113,6 +134,8 @@ class VisualMatchActivity(activity.Activity):
# Activity toolbar
activity_button = ActivityToolbarButton(self)
+
+ '''
journal_button = ToolButton( "journal-write" )
journal_button.set_tooltip(_('Write in Journal'))
journal_button.props.accelerator = '<Ctrl>j'
@@ -120,6 +143,8 @@ class VisualMatchActivity(activity.Activity):
activity.get_bundle_path())
activity_button.props.page.insert(journal_button, -1)
journal_button.show()
+ '''
+
toolbar_box.toolbar.insert(activity_button, 0)
activity_button.show()
@@ -356,13 +381,30 @@ class VisualMatchActivity(activity.Activity):
self.vmw.matches = _matches
self.vmw.robot_matches = _robot_matches
self.vmw.total_time = _total_time
+ self.vmw.buddies = []
if not hasattr(self,'_saved_state'):
self._saved_state = None
# Start playing the game
- window.new_game(self.vmw, self.vmw.cardtype,
+ window.new_game(self.vmw, self.vmw.cardtype, False,
self._saved_state, _deck_index)
+ #
+ # A simplistic sharing model: the sharer is the master
+ #
+
+ # Get the Presence Service
+ self.pservice = presenceservice.get_instance()
+ self.initiating = None # sharing (True) or joining (False)
+
+ # Add my buddy object to the list
+ owner = self.pservice.get_owner()
+ self.owner = owner
+ self.vmw.buddies.append(self.owner)
+ self._share = ""
+ self.connect('shared', self._shared_cb)
+ self.connect('joined', self._joined_cb)
+
#
# Write data to the Journal
#
@@ -382,44 +424,59 @@ class VisualMatchActivity(activity.Activity):
self.metadata['deck_index'] = self.vmw.deck.index
self.metadata['mime_type'] = 'application/x-visualmatch'
f = file(file_path, 'w')
- data = []
- for i in self.vmw.grid.grid:
- if i is None:
- data.append(None)
- else:
- data.append(i.index)
- for i in self.vmw.clicked:
- if i is None:
- data.append(None)
- else:
- data.append(self.vmw.deck.spr_to_card(i).index)
- for i in self.vmw.deck.cards:
- data.append(i.index)
- for i in self.vmw.match_list:
- data.append(self.vmw.deck.spr_to_card(i).index)
- io = StringIO()
- json.dump(data,io)
- f.write(io.getvalue())
+ f.write(self._dump())
f.close()
else:
_logger.debug("Deferring saving to %s" % file_path)
+ def _dump(self):
+ data = []
+ for i in self.vmw.grid.grid:
+ if i is None:
+ data.append(None)
+ else:
+ data.append(i.index)
+ for i in self.vmw.clicked:
+ if i is None:
+ data.append(None)
+ else:
+ data.append(self.vmw.deck.spr_to_card(i).index)
+ for i in self.vmw.deck.cards:
+ data.append(i.index)
+ for i in self.vmw.match_list:
+ data.append(self.vmw.deck.spr_to_card(i).index)
+
+ if _old_Sugar_system is True:
+ return json.write(data)
+ else:
+ io = StringIO()
+ jdump(data, io)
+ return io.getvalue()
+
#
# Read data from the Journal
#
def read_file(self, file_path):
_logger.debug("Resuming from: %s" % file_path)
f = open(file_path, 'r')
- io = StringIO(f.read())
- saved_state = json.load(io)
+ self._load(f.read())
+ f.close()
+
+ def _load(self, data):
+ if _old_Sugar_system is True:
+ saved_state = json.read(data)
+ else:
+ io = StringIO(data)
+ saved_state = jload(io)
if len(saved_state) > 0:
self._saved_state = saved_state
- f.close()
#
# Button callbacks
#
def _select_game_cb(self, button, activity, cardtype):
+ if window._joiner(self.vmw): # joiner cannot change level
+ return
window.new_game(activity.vmw, cardtype)
def _robot_cb(self, button, activity):
@@ -434,6 +491,8 @@ class VisualMatchActivity(activity.Activity):
self.robot_button.set_icon('robot-on')
def _level_cb(self, button, activity):
+ if window._joiner(self.vmw): # joiner cannot change level
+ return
activity.vmw.level = 1-activity.vmw.level
self.level_label.set_text(self.calc_level_label(activity.vmw.low_score,
activity.vmw.level))
@@ -452,6 +511,8 @@ class VisualMatchActivity(activity.Activity):
int(low_score[play_level]%60))
def _number_card_O_cb(self, button, activity, numberO):
+ if window._joiner(self.vmw): # joiner cannot change level
+ return
activity.vmw.numberO = numberO
gencards.generate_number_cards(activity.vmw.path,
activity.vmw.numberO,
@@ -460,6 +521,8 @@ class VisualMatchActivity(activity.Activity):
window.new_game(activity.vmw, 'number')
def _number_card_C_cb(self, button, activity, numberC):
+ if window._joiner(self.vmw): # joiner cannot change level
+ return
activity.vmw.numberC = numberC
gencards.generate_number_cards(activity.vmw.path,
activity.vmw.numberO,
@@ -478,6 +541,161 @@ class VisualMatchActivity(activity.Activity):
self.reveal()
return True
+ #
+ # Sharing-related callbacks
+ #
+
+ # Either set up initial share...
+ def _shared_cb(self, activity):
+ if self._shared_activity is None:
+ _logger.error("Failed to share or join activity ... \
+ _shared_activity is null in _shared_cb()")
+ return
+
+ self.initiating = True
+ self.waiting_for_deck = False
+ _logger.debug('I am sharing...')
+
+ self.conn = self._shared_activity.telepathy_conn
+ self.tubes_chan = self._shared_activity.telepathy_tubes_chan
+ self.text_chan = self._shared_activity.telepathy_text_chan
+
+ # call back for "NewTube" signal
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal \
+ ('NewTube', self._new_tube_cb)
+
+ _logger.debug('This is my activity: making a tube...')
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
+ SERVICE, {})
+
+ # ...or join an exisiting share.
+ def _joined_cb(self, activity):
+ if self._shared_activity is None:
+ _logger.error("Failed to share or join activity ... \
+ _shared_activity is null in _shared_cb()")
+ return
+
+ self.initiating = False
+ _logger.debug('I joined a shared activity.')
+
+ self.conn = self._shared_activity.telepathy_conn
+ self.tubes_chan = self._shared_activity.telepathy_tubes_chan
+ self.text_chan = self._shared_activity.telepathy_text_chan
+
+ # call back for "NewTube" signal
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( \
+ 'NewTube', self._new_tube_cb)
+
+ _logger.debug('I am joining an activity: waiting for a tube...')
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+
+ self.waiting_for_deck = True
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _list_tubes_error_cb(self, e):
+ _logger.error('ListTubes() failed: %s', e)
+
+ def _new_tube_cb(self, id, initiator, type, service, params, state):
+ _logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
+ 'params=%r state=%d', id, initiator, type, service,
+ params, state)
+
+ if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[ \
+ telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
+
+ tube_conn = TubeConnection(self.conn,
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \
+ group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])
+
+ # We'll use a chattube to send serialized data back and forth.
+ self.chattube = ChatTube(tube_conn, self.initiating, \
+ self.event_received_cb)
+
+ # Now that we have the tube, we can ask for the deck of cards.
+ if self.waiting_for_deck is True:
+ self._send_event("j")
+
+ # Data is passed as tuples: cmd:text.
+ def event_received_cb(self, text):
+ if text[0] == 'B':
+ e,card_index = text.split(':')
+ _logger.debug("receiving card index: " + card_index)
+ window._process_selection(self.vmw,
+ self.vmw.deck.index_to_card(int(card_index)).spr)
+ elif text[0] == 'S':
+ e,card_index = text.split(':')
+ _logger.debug("receiving selection index: " + card_index)
+ window._process_selection(self.vmw,
+ self.vmw.selected[int(card_index)].spr)
+ elif text[0] == 'j': # request for current state from joiner
+ if self.initiating is True:
+ _logger.debug("serialize the project and send to joiner")
+ self._send_event("P:" + str(self.vmw.level))
+ self._send_event("X:" + str(self.vmw.deck.index))
+ self._send_event("M:" + str(self.vmw.matches))
+ self._send_event("D:" + str(self._dump()))
+ elif text[0] == 'J': # new game, so make a request for current state
+ self._send_event("j")
+ self.waiting_for_deck = True
+ elif text[0] == 'P':
+ e,text = text.split(':')
+ _logger.debug("receiving play level from sharer " + text)
+ self.vmw.level = int(text)
+ elif text[0] == 'X':
+ e,text = text.split(':')
+ _logger.debug("receiving deck index from sharer " + text)
+ self.vmw.deck.index = int(text)
+ elif text[0] == 'M':
+ e,text = text.split(':')
+ _logger.debug("receiving matches from sharer " + text)
+ self.vmw.matches = int(text)
+ elif text[0] == 'D':
+ if self.waiting_for_deck:
+ e,text = text.split(':')
+ _logger.debug("receiving deck data from sharer")
+ self._load(text)
+ self.waiting_for_deck = False
+ window.new_game(self.vmw, self.vmw.cardtype, False,
+ self._saved_state, self.vmw.deck.index)
+
+ # Send event through the tube
+ def _send_event(self, entry):
+ if hasattr(self, 'chattube') and self.chattube is not None:
+ self.chattube.SendText(entry)
+
+#
+# Class for setting up tube for sharing
+#
+class ChatTube(ExportedGObject):
+
+ def __init__(self, tube, is_initiator, stack_received_cb):
+ super(ChatTube, self).__init__(tube, PATH)
+ self.tube = tube
+ self.is_initiator = is_initiator # Are we sharing or joining activity?
+ self.stack_received_cb = stack_received_cb
+ self.stack = ''
+
+ self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, \
+ path=PATH, sender_keyword='sender')
+
+ def send_stack_cb(self, text, sender=None):
+ if sender == self.tube.get_unique_name():
+ return
+ # _logger.debug("This connection has no unique name yet.")
+ self.stack = text
+ self.stack_received_cb(text)
+
+ @signal(dbus_interface=IFACE, signature='s')
+ def SendText(self, text):
+ self.stack = text
+
#
# Toolbars for pre-0.86 Sugar
#
diff --git a/gencards.py b/gencards.py
index c40d075..b59f22a 100755
--- a/gencards.py
+++ b/gencards.py
@@ -30,30 +30,29 @@ BLUE_STROKE = "#0060C8"
BLUE_FILL = "#ACC8E4"
GREEN_STROKE = "#00B418"
GREEN_FILL = "#AFE8A8"
-# PURPLE_STROKE = "#780078"
-# PURPLE_FILL = "#E4AAE4"
color_pairs = ([RED_STROKE,RED_FILL],
[GREEN_STROKE,GREEN_FILL],
[BLUE_STROKE,BLUE_FILL])
-
-fill_styles = ("solid","none","gradient")
-card_types = ("X","O","C")
+fill_styles = ["solid","none","gradient"]
+card_types = ["X","O","C"]
roman_numerals = {5:'V',7:'VII',10:'X',11:'XI',14:'XIV',15:'XV',\
21:'XXI',22:'XXII',33:'XXXIII'}
-
number_names = {5:_('five'),7:_('seven'),11:_('eleven'),10:_('ten'),\
14:_('fourteen'),15:_('fifteen'),22:_('twenty two'),\
21:_('twenty one'),33:_('thirty three')}
-
number_products = {5:'1×5',7:'1×7',11:'1×11',10:'2×5',\
14:'2×7',15:'3×5',22:'2×11',\
21:'3×7',33:'3×11'}
-
chinese_numerals = {5:'五',7:'七',10:'十',11:'十一',14:'十四',15:'十五',\
21:'廿一',22:'廿二',33:'卅三'}
+word_lists = [[_('mouse'),_('cat'),_('dog')],\
+ [_('cheese'),_('apple'),_('bread')],\
+ [_('moon'),_('sun'),_('earth')]]
+word_styles = ["font-weight:bold;","","font-style:italic;"]
+
#
# SVG generators
#
@@ -92,7 +91,6 @@ def svg_text(f,x,y,size,stroke,font,style,string):
f.write(" style=\"font-size:"+str(size)+"px;\">"+string+"</tspan>\n")
f.write(" </text>\n")
-
def svg_check(f, x, style, stroke, fill):
f.write("<path d=\"m 28.4,70.2 -5.9,5.9 -5.9,-5.9 -4.1,-4.1 c -0.7,-0.7" +\
" -1.2,-1.8 -1.2,-2.9 0,-2.3 1.9,-4.1 4.1,-4.1 1.1,0 2.2,0.5" +\
@@ -331,7 +329,6 @@ def number_hash(f, t, n, stroke):
for i in range(n):
hash(f, nn, 5, y, stroke)
y += 20
-
def dice(f, t, n, stroke):
if n == 5:
@@ -470,14 +467,12 @@ def number_card(f, t, n, stroke, methodX, methodO, methodC):
else:
methodC(f, t, n, stroke)
-def word_card(f, t, s, string, stroke):
- if t == 'X':
- svg_text(f,63.5,45.5,30,stroke,"DejaVu","font-weight:bold;",string[s])
- elif t == 'O':
- svg_text(f,63.5,45.5,30,stroke,"DejaVu Serif","font-style:italic;",\
- string[s])
- else:
- svg_text(f,63.5,45.5,30,stroke,"DejaVu","",string[s])
+def word_card(f, t, c, n, s):
+ svg_text(f,63.5,45.5,30,c[0],"DejaVu",s,word_lists[card_types.index(t)][n])
+
+def pattern_card(f, t, c, n, s):
+ pattern_styles = [cross_card, circle_card, check_card]
+ pattern_styles[card_types.index(t)](f,n,s,c[0],c[1])
def open_file(datapath, filename):
return file(os.path.join(datapath, filename), "w")
@@ -499,12 +494,7 @@ def generate_pattern_cards(datapath):
filename = "pattern-%d.svg" % (i)
f = open_file(datapath, filename)
header(f,"#000000",c[1],"0.5")
- if t == "O":
- circle_card(f,n,s,c[0],c[1])
- elif t == "C":
- check_card(f,n,s,c[0],c[1])
- else:
- cross_card(f,n,s,c[0],c[1])
+ pattern_card(f,t,c,n,s)
footer(f)
close_file(f)
i += 1
@@ -533,17 +523,11 @@ def generate_word_cards(datapath):
for t in card_types:
for c in color_pairs:
for n in range(0,3):
- for s in range(0,3):
+ for s in word_styles:
filename = "word-%d.svg" % (i)
f = open_file(datapath, filename)
header(f,"#000000",c[1],"0.5")
- if n == 0:
- word_card(f,t,s,[_("mouse"),_("cat"),_("dog")],c[0])
- elif n == 1:
- word_card(f,t,s,[_("cheese"),_("bread"),_("apple")],
- c[0])
- else:
- word_card(f,t,s,[_("moon"),_("sun"),_("earth")],c[0])
+ word_card(f,t,c,n,s)
footer(f)
close_file(f)
i += 1
diff --git a/grid.py b/grid.py
index b4e5efe..1aadcdc 100644
--- a/grid.py
+++ b/grid.py
@@ -31,48 +31,46 @@ from deck import *
from constants import *
#
-# class for managing 3x5 matrix of cards
+# Class for managing ROWxCOL matrix of cards
#
class Grid:
def __init__(self, width, height, card_width, card_height):
- # the playing surface is a 3x5 grid
+ # The playing surface
self.grid = []
for i in range(ROW*COL):
self.grid.append(None)
- # how many cards are on the playing field
- self.cards = 0
- # card spacing
+ # Card spacing
self.left = int((width-(card_width*2))/2)
self.xinc = int(card_width*1.2)
- # self.top = int((height-(card_height*3.5))/2)
self.top = 10
self.yinc = int(card_height*1.33)
- # deal the initial deck of cards
+ # Deal an initial set of cards.
def deal(self, deck):
- self.cards = 0
for i in range(ROW*COL):
if i < (ROW-1)*COL:
self.grid[i] = deck.deal_next_card()
self.place_a_card(self.grid[i], self.grid_to_xy(i)[0],
self.grid_to_xy(i)[1])
- self.cards += 1
- else: # leave a blank row for extra cards
+ else: # Leave a blank row for extra cards at the bottom.
self.grid[i] = None
- # add cards when there is no match
+ # Add cards to the bottom row when there is no match.
def deal_extra_cards(self, deck):
- # if there are still cards in the deck and only 12 cards in the grid
- if deck.empty() is False and self.cards == DEAL:
- # add three extra cards to the playing field
+ # But only if there are still cards in the deck
+ # and only 12 cards in the grid
+ if deck.empty() is False and self.cards_in_grid() == DEAL:
for c in range(0,COL):
i = self.grid.index(None)
self.grid[i] = deck.deal_next_card()
self.place_a_card(self.grid[i], self.grid_to_xy(i)[0],
self.grid_to_xy(i)[1])
- self.cards += 1
- # restore cards to grid upon resume
+ # How many cards are on the grid?
+ def cards_in_grid(self):
+ return ROW*COL-self.grid.count(None)
+
+ # Restore cards to grid upon resume or share.
def restore(self, deck, saved_card_index):
self.hide()
j = 0
@@ -84,33 +82,32 @@ class Grid:
j += 1
self.show()
- # remove a match from the grid and deal new cards from the deck
+ # Remove a match from the grid and replace it with new cards from the deck.
def remove_and_replace(self, clicked_set, deck):
for a in clicked_set:
- # find the position in the grid of the clicked card
- i = self.xy_to_grid(a.x,a.y)
- # only add new cards if we are down to 12 cards
- if self.cards == DEAL:
+ # Find the index into the grid of the clicked card
+ i = self.spr_to_grid(a)
+ # Don't add new cards if bottom row is occupied
+ if self.cards_in_grid() == DEAL:
if deck.empty():
self.grid[i] = None
else:
- # save card in grid position of card we are replacing
+ # Put new card in grid position of card we are replacing.
self.grid[i] = deck.deal_next_card()
- self.place_a_card(self.grid[i], a.x, a.y)
+ self.place_a_card(self.grid[i],
+ self.grid_to_xy(i)[0],
+ self.grid_to_xy(i)[1])
else:
- self.cards -= 1
- # mark grid positions of cards we are not replacing
+ # Mark as empty the grid positions we are not refilling
self.grid[i] = None
self.display_match(a, clicked_set.index(a))
- # move clicked card to the match area
+ # Move clicked card to the match area
def display_match(self, spr, i):
- spr.x = MATCH_POSITION
- spr.y = self.top + i*self.yinc
- spr.set_layer(2000)
- spr.draw()
+ spr.move((MATCH_POSITION, self.top + i*self.yinc))
+ spr.set_layer(2000)
- # if we have removed cards from an expanded grid, we may need to consolidate
+ # If we have removed cards from an expanded grid, we have to consolidate.
def consolidate(self):
for j in range((ROW-1)*COL,ROW*COL):
i = 0
@@ -118,43 +115,44 @@ class Grid:
if self.grid[i] is None:
self.grid[i] = self.grid[j]
self.grid[i].spr.move(self.grid_to_xy(i))
+ self.grid[i].spr.set_layer(2000)
self.grid[j] = None
else:
i+=1
- # place a card at position x,y and display it
+ # Place a card at position x,y and display it.
def place_a_card(self, c, x, y):
if c is not None:
c.spr.x = x
c.spr.y = y
c.show_card()
- # convert from sprite x,y to grid index
+ # Convert from sprite x,y to grid index.
def xy_to_grid(self, x, y):
return int(COL*(y-self.top)/self.yinc) + int((x-self.left)/self.xinc)
- # convert from grid index to sprite x,y
+ # Convert from grid index to sprite x,y.
def grid_to_xy(self, i):
return ((self.left+i%COL*self.xinc),(self.top+(i/COL)*self.yinc))
- # return sprite in grid element i
+ # Return the sprite in grid-position i.
def grid_to_spr(self, i):
return self.grid[i].spr
- # return index of sprite in grid
+ # Return the index of a sprite in grid.
def spr_to_grid(self, spr):
for i in range(ROW*COL):
if self.grid[i] is not None and self.grid[i].spr == spr:
return(i)
return None
- # hide all of the cards on the gird
+ # Hide all of the cards on the grid.
def hide(self):
for i in range(ROW*COL):
if self.grid[i] is not None:
self.grid[i].hide_card()
- # restore all card on the grid to their x,y positions
+ # Restore all card on the grid to their x,y positions.
def show(self):
for i in range(ROW*COL):
self.place_a_card(self.grid[i],self.grid_to_xy(i)[0],
diff --git a/window.py b/window.py
index 6b089f5..e5f3b2a 100644
--- a/window.py
+++ b/window.py
@@ -45,7 +45,7 @@ difficulty_level = [LOW,HIGH]
class vmWindow: pass
#
-# Handle launch from both within and without of Sugar environment
+# Handle launch from both within and without of Sugar environment.
#
def new_window(canvas, path, parent=None):
vmw = vmWindow()
@@ -82,11 +82,11 @@ def new_window(canvas, path, parent=None):
return vmw
#
-# Initialize for a new game
+# Start a new game.
#
-def new_game(vmw, cardtype, saved_state=None, deck_index=0):
+def new_game(vmw, cardtype, button_push=True, saved_state=None, deck_index=0):
if not hasattr(vmw, 'deck'):
- # first time through, initialize the deck, grid, and overlays
+ # The first time through, initialize the deck, grid, and overlays.
vmw.deck = Deck(vmw.sprites, vmw.path, cardtype, vmw.card_width,
vmw.card_height, difficulty_level[vmw.level])
vmw.grid = Grid(vmw.width, vmw.height, vmw.card_width, vmw.card_height)
@@ -98,19 +98,21 @@ def new_game(vmw, cardtype, saved_state=None, deck_index=0):
vmw.card_height, [MATCHMASK,0,0,0]))
vmw.grid.display_match(vmw.match_display_area[i].spr, i)
- vmw.deck.hide() # start by hiding everything
+ # Joiners in share cannot initiate new games.
+ if button_push and _joiner(vmw):
+ return
+
+ vmw.deck.hide()
+ _unselect(vmw)
+
+ # If cardtype has changed, we need to generate a new deck.
if vmw.cardtype is not cardtype:
vmw.cardtype = cardtype
vmw.deck = Deck(vmw.sprites, vmw.path, vmw.cardtype,
vmw.card_width, vmw.card_height,
difficulty_level[vmw.level])
- vmw.deck.shuffle()
- vmw.grid.deal(vmw.deck)
- if _find_a_match(vmw) is False:
- vmw.grid.deal_extra_cards(vmw.deck)
- _unselect(vmw)
- # restore saved state on resume
+ # Restore saved state on resume or share.
if saved_state is not None:
_logger.debug("Restoring state: %s" % (str(saved_state)))
vmw.deck.index = deck_index
@@ -121,18 +123,44 @@ def new_game(vmw, cardtype, saved_state=None, deck_index=0):
_restore_selected(vmw, saved_state[ROW*COL:ROW*COL+3])
_restore_matches(vmw, saved_state[deck_stop:deck_stop+3*vmw.matches])
else:
+ vmw.deck.shuffle()
+ vmw.grid.deal(vmw.deck)
+ if _find_a_match(vmw) is False:
+ vmw.grid.deal_extra_cards(vmw.deck)
vmw.matches = 0
vmw.robot_matches = 0
vmw.match_list = []
vmw.total_time = 0
+ # If sharer starts a new game, ask joiners to request new deck.
+ if button_push and _sharer(vmw):
+ vmw.activity._send_event("J")
+
_update_labels(vmw)
if _game_over(vmw):
if hasattr(vmw,'timeout_id') and vmw.timeout_id is not None:
gobject.source_remove(vmw.timeout_id)
else:
+ if hasattr(vmw,'match_timeout_id') and vmw.match_timeout_id is not None:
+ gobject.source_remove(vmw.match_timeout_id)
_timer_reset(vmw)
+def _joiner(vmw):
+ if vmw.sugar is True and \
+ hasattr(vmw.activity, 'chattube') and \
+ vmw.activity.chattube is not None and \
+ vmw.activity.initiating is False:
+ return True
+ return False
+
+def _sharer(vmw):
+ if vmw.sugar is True and \
+ hasattr(vmw.activity, 'chattube') and \
+ vmw.activity.chattube is not None and \
+ vmw.activity.initiating is True:
+ return True
+ return False
+
#
# Button press
#
@@ -149,24 +177,36 @@ def _button_release_cb(win, event, vmw):
spr = vmw.sprites.find_sprite((x, y))
if spr is None:
return True
+ if vmw.sugar is True and \
+ hasattr(vmw.activity, 'chattube') and \
+ vmw.activity.chattube is not None:
+ if vmw.deck.spr_to_card(spr) is not None:
+ vmw.activity._send_event("B:"+str(vmw.deck.spr_to_card(spr).index))
+ i = _selected(vmw, spr)
+ if i is not -1:
+ vmw.activity._send_event("S:"+str(i))
return _process_selection(vmw, spr)
+def _selected(vmw, spr):
+ for i in range(3):
+ if vmw.selected[i].spr == spr:
+ return i
+ return -1
+
def _process_selection(vmw, spr):
- # check to make sure a card in the matched pile isn't selected
+ # Make sure a card in the matched pile isn't selected.
if spr.x == MATCH_POSITION:
return True
- # check to make sure that the current card isn't already selected
- for a in vmw.clicked:
- if a is spr:
- # on second click, unselect
- i = vmw.clicked.index(a)
- vmw.clicked[i] = None
- vmw.selected[i].hide_card()
- return True
+ # Make sure that the current card isn't already selected.
+ i = _selected(vmw, spr)
+ if i is not -1:
+ # On a second click, unselect it.
+ vmw.clicked[i] = None
+ vmw.selected[i].hide_card()
+ return True
- # add the selected card to the list
- # and highlight it with the selection mask
+ # Otherwise highlight the card with a selection mask.
for a in vmw.clicked:
if a is None:
i = vmw.clicked.index(a)
@@ -174,17 +214,15 @@ def _process_selection(vmw, spr):
vmw.selected[i].spr.x = spr.x
vmw.selected[i].spr.y = spr.y
vmw.selected[i].show_card()
- break # we only want to add the card to the list once
+ break
- # if we have three cards selected, test for a match
- if None in vmw.clicked:
- pass
- else:
+ # If we have three cards selected, test for a match.
+ if None not in vmw.clicked:
_test_for_a_match(vmw)
return True
#
-# Game is over when the deck is empty and there are no more matches
+# Game is over when the deck is empty and there are no more matches.
#
def _game_over(vmw):
if vmw.deck.empty() and _find_a_match(vmw) is False:
@@ -192,7 +230,7 @@ def _game_over(vmw):
set_label(vmw,"clock","")
set_label(vmw,"status","%s (%d:%02d)" %
(_("Game over"),int(vmw.total_time/60),int(vmw.total_time%60)))
- _show_matches(vmw, 0)
+ vmw.match_timeout_id = gobject.timeout_add(2000,_show_matches,vmw,0)
return True
return False
@@ -205,22 +243,22 @@ def _test_for_a_match(vmw):
vmw.deck.spr_to_card(vmw.clicked[2])],
vmw.cardtype):
- # stop the timer
+ # Stop the timer.
if vmw.timeout_id is not None:
gobject.source_remove(vmw.timeout_id)
vmw.total_time += gobject.get_current_time()-vmw.start_time
- # increment the match counter and add the match to the match list
+ # Increment the match counter and add the match to the match list.
vmw.matches += 1
for i in vmw.clicked:
vmw.match_list.append(i)
- # remove the match and deal 3 new cards
+ # Remove the match and deal three new cards.
vmw.grid.remove_and_replace(vmw.clicked, vmw.deck)
set_label(vmw, "deck", "%d %s" %
(vmw.deck.cards_remaining(), _("cards")))
- # test to see if the game is over
+ # Test to see if the game is over.
if _game_over(vmw):
gobject.source_remove(vmw.timeout_id)
_unselect(vmw)
@@ -235,20 +273,19 @@ def _test_for_a_match(vmw):
vmw.activity.save_score()
return True
- # consolidate the grid
+ # Consolidate the grid.
vmw.grid.consolidate()
- # test to see if we need to deal extra cards
+ # Test to see if we need to deal extra cards.
if _find_a_match(vmw) is False:
vmw.grid.deal_extra_cards(vmw.deck)
- # keep playing
+ # Keep playing.
_update_labels(vmw)
_timer_reset(vmw)
- # whether or not there was a match, unselect all cards and refresh display
+ # Whether or not there was a match, unselect all cards.
_unselect(vmw)
- vmw.sprites.redraw_sprites()
#
# Unselect the cards
@@ -275,7 +312,7 @@ def _destroy_cb(win, event, vmw):
gtk.main_quit()
#
-# Write strings to a label in the toolbar
+# Write strings to a label in the toolbar.
#
def _update_labels(vmw):
set_label(vmw, "deck", "%d %s" %
@@ -309,7 +346,7 @@ def set_label(vmw, label, s):
vmw.win.set_title("%s: %s" % (_("Visual Match"),s))
#
-# Restore selected cards upon resume
+# Restore the selected cards upon resume or share.
#
def _restore_selected(vmw, saved_selected_indices):
j = 0
@@ -325,7 +362,7 @@ def _restore_selected(vmw, saved_selected_indices):
j += 1
#
-# Restore match list upon resume
+# Restore the match list upon resume or share.
#
def _restore_matches(vmw, saved_match_list_indices):
j = 0
@@ -339,7 +376,7 @@ def _restore_matches(vmw, saved_match_list_indices):
vmw.grid.display_match(vmw.match_list[l-3+j], j)
#
-# Display of seconds since start_time or find a match
+# Display of seconds since start_time.
#
def _counter(vmw):
seconds = int(gobject.get_current_time()-vmw.start_time)
@@ -355,7 +392,7 @@ def _timer_reset(vmw):
_counter(vmw)
#
-# Show all the matches
+# Show all the matches as a simple animation.
#
def _show_matches(vmw, i):
if i < vmw.matches:
@@ -364,11 +401,11 @@ def _show_matches(vmw, i):
vmw.match_timeout_id = gobject.timeout_add(2000,_show_matches,vmw,i+1)
#
-# Check to see whether there are any matches on the board
+# Check to see whether there are any matches on the board.
#
def _find_a_match(vmw, robot_match=False):
a = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
- for i in Permutation(a): # really should be Combination
+ for i in Permutation(a): # TODO: really should be combination
cardarray = [vmw.grid.grid[i[0]],\
vmw.grid.grid[i[1]],\
vmw.grid.grid[i[2]]]
@@ -382,28 +419,27 @@ def _find_a_match(vmw, robot_match=False):
return False
#
-# For each attribute, either it is the same or different on every card
+# For each attribute, either it is the same or different on every card.
#
def _match_check(cardarray, cardtype):
for a in cardarray:
if a is None:
return False
- if (cardarray[0].num + cardarray[1].num + cardarray[2].num)%3 != 0:
+ if (cardarray[0].shape + cardarray[1].shape + cardarray[2].shape)%3 != 0:
return False
if (cardarray[0].color + cardarray[1].color + cardarray[2].color)%3 != 0:
return False
- if (cardarray[0].shape + cardarray[1].shape + cardarray[2].shape)%3 != 0:
+ if (cardarray[0].fill + cardarray[1].fill + cardarray[2].fill)%3 != 0:
return False
- # special case for the word game:
- # only check fill when numbers are the same
+ # Special case: only check number when shapes are the same
if cardtype == 'word':
- if cardarray[0].num == cardarray[1].num and \
- cardarray[0].num == cardarray[2].num and \
- (cardarray[0].fill + cardarray[1].fill + cardarray[2].fill)%3 != 0:
+ if cardarray[0].shape == cardarray[1].shape and \
+ cardarray[0].shape == cardarray[2].shape and \
+ (cardarray[0].num + cardarray[1].num + cardarray[2].num)%3 != 0:
return False
else:
- if (cardarray[0].fill + cardarray[1].fill + cardarray[2].fill)%3 != 0:
+ if (cardarray[0].num + cardarray[1].num + cardarray[2].num)%3 != 0:
return False
return True