From 86915957fe8ec6b001b45c667ec0fe44274520be Mon Sep 17 00:00:00 2001 From: Pablo Moleri Date: Mon, 24 Aug 2009 04:57:26 +0000 Subject: - Sugarizado. - Juego colaborativo (red) más o menos funcionando, la respuesta de "agua" o "tocado" no está funcionando correctamente. --- diff --git a/BatallaNaval.Activity/BatallaNaval.py b/BatallaNaval.Activity/BatallaNaval.py deleted file mode 100644 index 104c10b..0000000 --- a/BatallaNaval.Activity/BatallaNaval.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/env python -# -*- coding: cp1252 -*- - -import pygtk -pygtk.require('2.0') -import gtk - -import random - -try: - from sugar.activity.activity import Activity, ActivityToolbox -except: - pass - -class PanelPrincipal(gtk.HBox): - - def __init__(self): - gtk.HBox.__init__(self, True) - - self.tablero1 = Tablero(None) # tablero propio - self.tablero2 = Tablero(self.jugada_hecha) # tablero enemigo - - self.barcos_propios = crear_barcos() - for barco in self.barcos_propios: - self.tablero1.agregar_barco(barco, True) - - print "Barcos Enemigos" - self.barcos_enemigos = crear_barcos() - for barco in self.barcos_enemigos: - self.tablero2.agregar_barco(barco, False) - print "%s: %s" % (barco.nombre, barco.pos) - - self.add(self.tablero1) - self.add(self.tablero2) - - self.show_all() - - self.jugadas_enemigas= [] # Lleva un registro de las jugadas hechas por la computadora - - def jugada_hecha(self): - # Cuando se hace una jugada, la computadora hace una jugada al azar sobre el tablero propio - if len(self.jugadas_enemigas) == 100: - return - - ok = False - while not ok: - x, y = random.randint(1, 10), random.randint(1, 10) - if not (x, y) in self.jugadas_enemigas: - ok = True - - self.tablero1.filas[x-1][y-1].clicked() - -def crear_barcos(): - - barcos = [ - Barco("Portaaviones", 5, None), - Barco("Acorazado", 4, None), - Barco("Crucero", 3, None), - Barco("Submarino", 3, None), - Barco("Destructor", 2, None)] - - celdas_ocupadas = [] - - for barco in barcos: - ok = False - while not ok: - # Calculo coordenadas random, asumo orientacion horizontal - posx = random.randint(1, 10) - posy = random.randint(1, 10-barco.largo+1) - - barco.pos = (posx, posy) - ok = True - for celda in barco.get_celdas(): - if celda in celdas_ocupadas: - ok = False - if ok: - celdas_ocupadas.extend(barco.get_celdas()) - return barcos - -class Celda(gtk.Button): - - def __init__(self, pos, clicked_cb): - gtk.Button.__init__(self) - self.pos = pos - - self.connect("clicked", clicked_cb) - - def ocultar(self): - self.set_no_show_all(True) - self.hide() - - def tocado(self): - color = gtk.gdk.Color(65535,65535/2,0) - self.modify_bg(gtk.STATE_NORMAL, color) - self.modify_bg(gtk.STATE_PRELIGHT, color) - self.show() # Por si está oculta atrás de un barco - - def agua(self): - color = gtk.gdk.Color(0,0,65535/2) - self.modify_bg(gtk.STATE_NORMAL, color) - self.modify_bg(gtk.STATE_PRELIGHT, color) - self.show() # Por si está oculta atrás de un barco - -class Tablero(gtk.Frame): - - def __init__(self, llamada_jugada_hecha): - gtk.Frame.__init__(self) - - # Números - tabla_numeros = gtk.Table(1, 10, True) - for i in range(1, 11): - label = gtk.Label(str(i)) - tabla_numeros.attach(label, i-1, i, 0, 1) - - # Letras - tabla_letras = gtk.Table(10, 1, True) - for i in range(1, 11): - char = chr( ord('A') + i - 1 ) - label = gtk.Label(char) - tabla_letras.attach(label, 0, 1, i-1, i) - - # Se hace una tabla para ubicar las letras, los números y el tablero - self.tabla = gtk.Table(2, 2, False) - self.add(self.tabla) - - opciones = gtk.SHRINK|gtk.FILL - - label = gtk.Label(" ") - self.tabla.attach(label, 0, 1, 0, 1, xoptions=opciones, yoptions=opciones) - - self.tabla.attach(tabla_numeros, 1, 2, 0, 1, xoptions=opciones, yoptions=opciones) - self.tabla.attach(tabla_letras, 0, 1, 1, 2, xoptions=opciones, yoptions=opciones) - - self.filas = [] - - # El tablero es otra tabla - self.tabla_celdas = gtk.Table(10, 10, True) - self.tabla.attach(self.tabla_celdas, 1, 2, 1, 2, xoptions=gtk.FILL|gtk.EXPAND, yoptions=gtk.FILL|gtk.EXPAND) - - for i in range(1, 11): - row = [] - for j in range(1, 11): - left = j - 1 - top = i - 1 - celda = Celda((i, j), self.celda_clickeada) - row.append(celda) - self.tabla_celdas.attach(celda, left, j, top, i) - #print label.get_text() - self.filas.append(row) - - self.barcos = [] # Los barcos que hay en el tablero - - self.llamada_jugada_hecha = llamada_jugada_hecha - - def agregar_barco(self, barco, show): - self.barcos.append(barco) - if show: - for i in barco.get_filas(): - for j in barco.get_cols(): - self.ocultar_celda(i, j) - izq = barco.get_inicio()[1]-1 - der = barco.get_fin()[1] - arr = barco.get_inicio()[0]-1 - aba = barco.get_fin()[0] - self.tabla_celdas.attach(barco, izq, der, arr, aba) - - def ocultar_celda(self, i, j): - self.filas[i-1][j-1].ocultar() - - def celda_clickeada(self, celda): - tocado = False - for barco in self.barcos: - if celda.pos in barco.get_celdas(): - tocado = True - celda.tocado() - if not tocado: - celda.agua() - - if self.llamada_jugada_hecha: - self.llamada_jugada_hecha() - -class Barco(gtk.Frame): - - horizontal = 'H' - vertical = 'V' - - def __init__(self, nombre, largo, pos, orientacion = horizontal): - #gtk.Label.__init__(self, nombre) - gtk.Frame.__init__(self) - self.nombre = nombre - self.largo = largo - self.orientacion = orientacion - self.pos = pos - - self.add(gtk.Label(nombre)) - - self.show() - - def get_inicio(self): - return self.pos - - def get_fin(self): - if self.orientacion == Barco.horizontal: - return self.pos[0], self.pos[1] + self.largo - 1 - else: - return self.pos[0] + self.largo - 1, self.pos[1] - - def get_filas(self): - return range(self.get_inicio()[0], self.get_fin()[0]+1) - - def get_cols(self): - return range(self.get_inicio()[1], self.get_fin()[1]+1) - - def get_celdas(self): - return [(f, c) for f in self.get_filas() for c in self.get_cols()] - -# Esta función es el punto de entrada común para sugar y modo standalone -# standalone es un boolean que indica si es Standalone o se ejecuta desde Sugar -def init(standalone, ventana_principal): - - panel_principal = PanelPrincipal() - - if not standalone: - toolbox = ActivityToolbox(toplevel_window) - ventana_principal.set_toolbox(toolbox) - toolbox.show() - ventana_principal.set_canvas(panel_principal) - else: - ventana_principal.add(panel_principal) - - ventana_principal.set_title("Batalla Naval - ceibalJAM") - ventana_principal.connect("destroy", lambda wid: gtk.main_quit()) - ventana_principal.connect("delete_event", lambda a1, a2: gtk.main_quit()) - - ventana_principal.show() - -# Este es el procedimiento principal en modo standalone -def main(): - ventana_principal = gtk.Window(gtk.WINDOW_TOPLEVEL) - init(True, ventana_principal) - gtk.main() - return 0 - -# Este código se ejecuta sólo cuando se ejecuta directo ese módulo (no cuando se importa desde sugar) -if __name__ == "__main__": - main() diff --git a/BatallaNaval.activity/BatallaNavalActivity.py b/BatallaNaval.activity/BatallaNavalActivity.py new file mode 100644 index 0000000..1c77416 --- /dev/null +++ b/BatallaNaval.activity/BatallaNavalActivity.py @@ -0,0 +1,38 @@ +from sugar.activity.activity import Activity, ActivityToolbox +import BatallaNaval +from Collaboration import CollaborationWrapper + +class BatallaNavalActivity(Activity): + def __init__(self, handle): + Activity.__init__(self, handle) + self.connect('destroy', self._cleanup_cb) + + self.gamename = 'BatallaNaval' + self.set_title("Batalla Naval") + + # connect to the in/out events + self.connect('focus_in_event', self._focus_in) + self.connect('focus_out_event', self._focus_out) + + self.colaboracion = CollaborationWrapper(self, None, None, None) + self.connect('shared', self.colaboracion._shared_cb) + if self._shared_activity: + # We are joining the activity + self.connect('joined', self.colaboracion._joined_cb) + if self.get_shared(): + # We've already joined + self.colaboracion._joined_cb() + + # The activity is a subclass of Window, so it passses itself to the init function + BatallaNaval.init(False, self) + # It never returns, gtk.main() + + def _cleanup_cb(self, data=None): + return + + # We could use these methods to conserve power by having the activity stop processing when it is in the background. + def _focus_in(self, event, data=None): + return + + def _focus_out(self, event, data=None): + return diff --git a/BatallaNaval.activity/Collaboration.py b/BatallaNaval.activity/Collaboration.py new file mode 100644 index 0000000..2e7d4e9 --- /dev/null +++ b/BatallaNaval.activity/Collaboration.py @@ -0,0 +1,231 @@ +# -*- coding: UTF-8 -*- +# Copyright 2007-2008 One Laptop Per Child +# Copyright 2007 Gerard J. Cerchio +# Copyright 2008 Andrés Ambrois +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import logging +import sugar.logger + +from sugar.presence import presenceservice +import telepathy +from dbus.service import method, signal +# In build 656 Sugar lacks sugartubeconn +try: + from sugar.presence.sugartubeconn import SugarTubeConnection +except: + from sugar.presence.tubeconn import TubeConnection as SugarTubeConnection +from dbus.gobject_service import ExportedGObject + + +# Todas las actividades colaborativas de sugar deben tener implentadas las señales "Hello" y "World" +# Cuando alguien entra a la colaboración sugar automáticamente emite la señal Hello +# Este wrapper automáticamente responde con la señal World, que sirve para informarle al nuevo +# participante el estado actual. + +SERVICE = "org.ceibaljam.BatallaNaval" +IFACE = SERVICE +PATH = "/org/ceibaljam/BatallaNaval" + +logger = logging.getLogger('BatallaNaval') + +class CollaborationWrapper(ExportedGObject): + ''' A wrapper for the collaboration bureaucracy + Recibe la actividad y los callbacks necesarios ''' + + def __init__(self, activity, buddy_joined_cb, buddy_left_cb, play_cb): + self.activity = activity + self.buddy_joined = buddy_joined_cb + self.buddy_left = buddy_left_cb + self.Play_cb = play_cb + self.world = False + self.entered = False + self.presence_service = presenceservice.get_instance() + self.owner = self.presence_service.get_owner() + + def _shared_cb(self, activity): + #self.activity.gameToolbar.grey_out_size_change() + #self.activity.gameToolbar.grey_out_restart() + #self.activity.gameToolbar.grey_out_ai() + self._sharing_setup() + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + SERVICE, {}) + self.is_initiator = True + #self.activity.undo_button.hide() + + def _joined_cb(self, activity): + #self.activity.gameToolbar.grey_out_size_change() + #self.activity.gameToolbar.grey_out_restart() + #self.activity.gameToolbar.grey_out_ai() + self._sharing_setup() + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + self.is_initiator = False + #self.activity.undo_button.hide() + + def _sharing_setup(self): + if self.activity._shared_activity is None: + logger.error('Failed to share or join activity') + return + + self.conn = self.activity._shared_activity.telepathy_conn + self.tubes_chan = self.activity._shared_activity.telepathy_tubes_chan + self.text_chan = self.activity._shared_activity.telepathy_text_chan + + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( + 'NewTube', self._new_tube_cb) + + self.activity._shared_activity.connect('buddy-joined', self._buddy_joined_cb) + self.activity._shared_activity.connect('buddy-left', self._buddy_left_cb) + + # Optional - included for example: + # Find out who's already in the shared activity: + for buddy in self.activity._shared_activity.get_joined_buddies(): + logger.debug('Buddy %s is already in the activity', + buddy.props.nick) + + def participant_change_cb(self, added, removed): + logger.debug('Tube: Added participants: %r', added) + logger.debug('Tube: Removed participants: %r', removed) + for handle, bus_name in added: + buddy = self._get_buddy(handle) + if buddy is not None: + logger.debug('Tube: Handle %u (Buddy %s) was added', + handle, buddy.props.nick) + for handle in removed: + buddy = self._get_buddy(handle) + if buddy is not None: + logger.debug('Buddy %s was removed' % buddy.props.nick) + if not self.entered: + if self.is_initiator: + logger.debug("I'm initiating the tube, will " + "watch for hellos.") + self.add_hello_handler() + else: + logger.debug('Hello, everyone! What did I miss?') + self.Hello() + self.entered = True + + + # This is sent to all participants whenever we join an activity + @signal(dbus_interface=IFACE, signature='') + def Hello(self): + """Say Hello to whoever else is in the tube.""" + logger.debug('I said Hello.') + + # This is called by whoever receives our Hello signal + # This method receives the current game state and puts us in sync + # with the rest of the participants. + # The current game state is represented by the game object + @method(dbus_interface=IFACE, in_signature='', out_signature='') + def World(self): + """To be called on the incoming XO after they Hello.""" + if not self.world: + logger.debug('Somebody called World on me') + #self.activity.board_size_change(None, size) + self.world = True # En vez de cargar el mundo, lo voy recibiendo jugada a jugada. + #self.activity.set_player_color(self.activity.invert_color(taken_color)) + #self.players = players + # now I can World others + self.add_hello_handler() + else: + self.world = True + logger.debug("I've already been welcomed, doing nothing") + + @signal(dbus_interface=IFACE, signature='ii') + def Play(self, x, y): + """Say Hello to whoever else is in the tube.""" + logger.debug('Signaling players of stone placement at:%s x %s.', x, y) + + def add_hello_handler(self): + logger.debug('Adding hello handler.') + self.tube.add_signal_receiver(self.hello_signal_cb, 'Hello', IFACE, + path=PATH, sender_keyword='sender') + self.tube.add_signal_receiver(self.play_signal_cb, 'Play', IFACE, + path=PATH, sender_keyword='sender') + + def hello_signal_cb(self, sender=None): + """Somebody Helloed me. World them.""" + if sender == self.tube.get_unique_name(): + # sender is my bus name, so ignore my own signal + return + logger.debug('Newcomer %s has joined', sender) + logger.debug('Welcoming newcomer and sending them the game state') + + # No le mandamos nada, la solo interesa mandar jugadas y reflejar el resultado {tocado, agua, hundido} + self.tube.get_object(sender, PATH).World(dbus_interface=IFACE) + + def play_signal_cb(self, x, y, sender=None): + """Somebody placed a stone. """ + if sender == self.tube.get_unique_name(): + return # sender is my bus name, so ignore my own signal + logger.debug('Buddy %s placed a stone at %s x %s', sender, x, y) + # Call our Play callback + #self.Play_cb(x, y, sender) + self.Play_cb(x, y) # En principio no importa quien lo mandó + + def _list_tubes_error_cb(self, e): + logger.error('ListTubes() failed: %s', e) + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + 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) + self.tube = SugarTubeConnection(self.conn, + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], + id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + super(CollaborationWrapper, self).__init__(self.tube, PATH) + self.tube.watch_participants(self.participant_change_cb) + + def _buddy_joined_cb (self, activity, buddy): + """Called when a buddy joins the shared activity. """ + logger.debug('Buddy %s joined', buddy.props.nick) + if self.buddy_joined: + self.buddy_joined(buddy) + + def _buddy_left_cb (self, activity, buddy): + """Called when a buddy leaves the shared activity. """ + if self.buddy_left: + self.buddy_left(buddy) + + def _get_buddy(self, cs_handle): + """Get a Buddy from a channel specific handle.""" + logger.debug('Trying to find owner of handle %u...', cs_handle) + group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP] + my_csh = group.GetSelfHandle() + logger.debug('My handle in that group is %u', my_csh) + if my_csh == cs_handle: + handle = self.conn.GetSelfHandle() + logger.debug('CS handle %u belongs to me, %u', cs_handle, handle) + elif group.GetGroupFlags() & telepathy.CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES: + handle = group.GetHandleOwners([cs_handle])[0] + logger.debug('CS handle %u belongs to %u', cs_handle, handle) + else: + handle = cs_handle + logger.debug('non-CS handle %u belongs to itself', handle) + # XXX: deal with failure to get the handle owner + assert handle != 0 + return self.presence_service.get_buddy_by_telepathy_handle( + self.conn.service_name, self.conn.object_path, handle) diff --git a/BatallaNaval.activity/MANIFEST b/BatallaNaval.activity/MANIFEST new file mode 100644 index 0000000..3da8118 --- /dev/null +++ b/BatallaNaval.activity/MANIFEST @@ -0,0 +1,6 @@ +Collaboration.py +setup.py +BatallaNavalActivity.py +BatallaNaval.py +activity/activity.info +activity/batalla-naval-icon.svg diff --git a/BatallaNaval.activity/activity/activity.info b/BatallaNaval.activity/activity/activity.info new file mode 100644 index 0000000..7cb17e8 --- /dev/null +++ b/BatallaNaval.activity/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = BatallaNaval +bundle_id = org.ceibaljam.BatallaNaval +class = BatallaNavalActivity.BatallaNavalActivity +icon = batalla-naval-icon +activity_version = 2 +host_version = 1 +show_launcher = no +license = GPLv2+ diff --git a/BatallaNaval.activity/activity/batalla-naval-icon.svg b/BatallaNaval.activity/activity/batalla-naval-icon.svg new file mode 100644 index 0000000..432bd56 --- /dev/null +++ b/BatallaNaval.activity/activity/batalla-naval-icon.svg @@ -0,0 +1,68 @@ + + + +]> + + + + + + + + + + + + + + + + + + -- cgit v0.9.1