jueves, 31 de julio de 2014

Diferencia entre iteración y recursión en ensamblador (MIPS)

Buenas gente. Últimamente os tenemos demasiado abandonados. Mea culpa. Estamos los dos de viajes a todos lados (me fuí a Valencia a la DreamHack) y a la vuelta empecé a trabajar en una academia. No tengo tiempo para nada. Es totalmente estresante. Trabajo de mañana y de tarde pero bueno.., hay que comer no?

Hoy os traigo un code simple en MIPS tanto en forma iterativa como recursiva para que veais las diferencias entre ambos así como el uso de la pila. Debo decir que (en éste codigo concreto) la versión iterativa es más eficiente que la recursiva, pero no siempre es así.
El código lo único que hace es coger y multiplicar los registros $4 y $5 y guardar el resultado en $2, pero lo hace simando $5 veces $4.
Dicho ésto, aquí vuestro código iterativo:

Y aquí vuestro código recursivo:


Espero que veais la diferencia. Hay que añadir que en todos los códigos recursivos (ya que hay que usar la pila) es necesario hacer push y pop, donde push siempre es restar un múltiplo de 4 a $29 ($sp) y guardar los valores con sw en esa memoria; y pop es la operación inversa.

Espero que os haya quedado claro y que nos perdoneis por no ser puntuales :P
Un saludo y gracias ;)

viernes, 18 de julio de 2014

Ejercicios C++: Strings (I)

Aquí unos ejercicios en los que usamos strings y todo lo aprendido anteriormente sobre Registros y arrays.

Diseñar un algoritmo (y desarrollar los subalgoritmos C++ correspondientes y el main para probarlo) que permita traducir una cadena numérica al número natural que representa:
Entrada: 7805 (hay que leer el dato como un string)
Salida: 7805 (hay que mostrar el dato como un unsigned)
#include <iostream>
#include <string>


using namespace std;

void leerCadena(string & cadena){
 cout << "Introduce cadena numérica: ";
 cin >> cadena;
}

unsigned potenciaDiez(unsigned exp){
 unsigned resultado=1;
 for(unsigned i = 1; i <= exp; i++){
 resultado = resultado * 10;
 }
 return resultado;
}

void transformar (string cadena, unsigned& num){
 num = 0;
 unsigned n;
 for(unsigned i= 0 ; i < cadena.size(); i++){
  n = (int(cadena[i]))-(int('0'));
  num = num + n * potenciaDiez(cadena.size()-1-i);
 }
}

int main() {
 string cadena;
 unsigned num;
 leerCadena(cadena);
 transformar(cadena, num);
 cout << "El número con el que se corresponde es el: "<< num;
 return 0;
}


Una palabra w es un anagrama de la palabra v, si podemos obtener w cambiando el orden de las letras de v. Por ejemplo, VACA es un anagrama de CAVA.
Diseñar un algoritmo (y desarrollar los subalgoritmos C++ correspondientes y el main para probarlo) que lea un texto y determine de cuántas palabras es anagrama la primera que aparece dentro de dicho texto. Hay que tener en cuenta que:

  • El texto contiene un número indefinido de palabras.
  • El texto termina con la palabra FIN.
  • Cada palabra tiene un número indefinido pero limitado de caracteres (todos alfabético mayúsculas).
  • El carácter separador de palabras es el espacio en blanco.

#include <iostream>
#include <string>
using namespace std;


void esAnagrama(string palabra, string texto){
 unsigned contador = 0;
 bool loes = false;
 string aux = texto;
 if(palabra.size() == texto.size()){

  for(unsigned i= 0; i < palabra.size(); i++){

   unsigned j= 0;
   loes = false;


   while(j < texto.size() && loes== false){

     if(palabra[i]== aux[j]){
      loes = true;
      contador++;
      aux[j]= ' ';
     }

    j++;
   }
  }

 }

 if(contador == palabra.size()){
  cout << texto << " es anagrama de "<< palabra << endl;
 }else{
  cout << texto << " no es anagrama de "<< palabra << endl;
 }
}


void introducirTexto(){
 string palabra, texto;
 cout << "Introduce texto: "<< endl;
 getline(cin, palabra, ' ');
 //getline(cin, texto, ' ');
 cin >> texto;
 while(texto != "FIN"){
  esAnagrama(palabra, texto);
  cin >> texto;
 }
}


int main() {
 introducirTexto();
 return 0;
}


Supongamos que deseamos evaluar a un determinado número de alumnos siguiendo el criterio de que aprobará una determinada evaluación aquel que supere o iguale la nota media de la clase en dicha evaluación.
Diseñar un algoritmo (y desarrollar los subalgoritmos C++ correspondientes y el main para probarlo) que lea por teclado un número de alumnos, N_alumnos (como máximo podrá tomar el valor de MAX_ALUMNOS=20), y las notas de tres evaluaciones para cada alumno, N_EVALUACIONES=3, y como resultado emita un informe indicando para cada alumno el resultado de cada evaluación (Aprobado o Suspenso).
#include <iostream>
using namespace std;

const unsigned MAX_ALUMNOS=20;
const unsigned N_EVALUACIONES=3;

typedef double notasAlumno[N_EVALUACIONES];

struct TAlumnos{
 string nombre;
 notasAlumno notas;
};

typedef TAlumnos ArrayAlumnos[MAX_ALUMNOS];

unsigned leerNalumnos(){
 unsigned N_alumnos;
 do{
  cout << "Introduce el número de alumnos (<= "<<MAX_ALUMNOS<<"): ";
  cin >> N_alumnos;
 }while(N_alumnos > MAX_ALUMNOS);
 return N_alumnos;
}

void leerAlumnos(unsigned N_alumnos, ArrayAlumnos& alumnos){
 for(unsigned i=0; i < N_alumnos; i++){

  cout << "Introduce el nombre y 3 notas: ";
   getline(cin, alumnos[i].nombre, ' ');
   for(unsigned j = 0; j < N_EVALUACIONES; j++){
    cin >> alumnos[i].notas[j];
   }
 }
}

double notaMedia (unsigned N_alumnos, const ArrayAlumnos& alumnos, unsigned evaluacion){
 double suma = 0;
 for(unsigned i= 0; i < N_alumnos; i++){
  suma = suma + alumnos[i].notas[evaluacion-1];

 }
 return suma/N_alumnos;
}






void notasAlumnos(unsigned N_alumnos, const ArrayAlumnos& alumnos){
 cout << "Alumno \t Nota 1 \t Nota 2 \t Nota 3" << endl;
 cout << "-------------------------------------------------------";
 for(unsigned i=0; i < N_alumnos; i++){

  cout << alumnos[i].nombre << "\t";

  for(unsigned ev = 1; ev <= N_EVALUACIONES; ev++){
   double media = notaMedia(N_alumnos, alumnos, ev);
   //cout << media << endl;
   if(alumnos[i].notas[ev-1] >= media){
    cout << "Aprobado \t";
   }else{
    cout << "Suspenso \t";
   }

  }

  cout << endl;


 }
}

int main() {
 
 unsigned N_alumnos = leerNalumnos();

 ArrayAlumnos alumnos;
 leerAlumnos(N_alumnos, alumnos);
 notasAlumnos(N_alumnos, alumnos);

 return 0;
}
Diseñar un algoritmo (desarrollando los subalgoritmos C++ correspondientes y el main para probarlo) que se comporte como una calculadora que pida repetidamente un operador de conjuntos y dos operandos que sean conjuntos de letras minúsculas y que escriba el resultado de la operación. Las operaciones se expresan como caracteres, siendo válidas las siguientes:
+ Unión de conjuntos
- Diferencia de conjuntos
* Intersección de conjuntos
El proceso se repetirá hasta que se introduzca como código de operación el carácter ‘&’. Los operadores y el resultado se expresan como cadenas de caracteres. Ejemplo:
Operación = *
Operando1 = azufre
Operando2 = zafio
Resultado = afz
Operación = -
Operando1 = abril
Operando2 = arco
Resultado = bil
Operación = &
FIN
#include <iostream>
#include <string>
using namespace std;

string restaString(string op1, string op2){
 string res;
 bool esta;
 for(unsigned i=0; i < op1.size(); i++){
  esta = false;
  unsigned j = 0;
  while(esta == false && j < op2.size()){
   if(op1[i] == op2[j]){
    esta = true;
   }

   j++;
  }

  if(!esta){
   res = res + op1[i];
  }

 }
 return res;
}

string interseccion(string op1, string op2){
 string res;
  bool esta;
  for(unsigned i=0; i < op1.size(); i++){
   esta = false;
   unsigned j = 0;
   while(esta == false && j < op2.size()){
    if(op1[i] == op2[j]){
     esta = true;
    }

    j++;
   }

   if(esta){
    res = res + op1[i];
   }

  }
  return res;
}

string realizarOperacion(char operacion, string op1, string op2){
 string resultado;
 switch (operacion){
 case '+': resultado = op1 + op2;;
  break;
 case '-': resultado = restaString(op1,op2);
  break;
 case '*': resultado = interseccion(op1, op2);
  break;
 }

 return resultado;
}


void introducirDatos(){
 char operacion;
 string op1, op2;




 do{
 cout << "Operación: ";
 cin >> operacion;
 } while(operacion != '+' && operacion != '-' && operacion != '*' && operacion != '&');

 while(operacion != '&'){
 cout << "Operando 1: ";
 cin >> op1;
 cout << "Operando 2: ";
 cin >> op2;


 string resultado = realizarOperacion(operacion, op1, op2);
 cout << "Resultado: "<< resultado << endl;

 do{
  cout << "Operación: ";
  cin >> operacion;
  } while(operacion != '+' && operacion != '-' && operacion != '*' && operacion != '&');

 if(operacion == '&'){
  cout << "FIN";
 }
 }


}




int main() {
 cout << "Operaciones con strings" << endl; 
 introducirDatos();
 return 0;
}

viernes, 11 de julio de 2014

Mi primera App con Android. BPCalculator.

Buenas gente, ¿qué tal? Acabo de depurar la aplicación que hice antes de ayer y ya os puedo mostrar de qué va. Os cuento... Resulta que por ser de la UMA, tengo una tarjeta para la BP mediante la cual me descuentan 5 céntimos por litro, pero me los dan en mano. Pues como hay veces que llevo 4,60 y pocos euros y cosas así, me decidí a hacer una calculadora con la que poder ver lo que me iba a costar y cuánto me iban a echar. Ésta es la version 1.0. Quiero poder hacer que busque directamente el precio, pero eso será más adelante cuando aprendamos a conectar la aplicación con la web. El proyecto en git lo tenéis por aquí.

Antes de nada os aconsejaría que en vez de usar las máquinas virtuales de Android uséis vuestro teléfono, que irá mil veces mejor (ayer me dijeron que en el eclipse de Linux, las máquinas van perfectas; pero aún no lo he podido probar). Si no sabéis cómo conectar el móvil, preguntad ;)

Aquí tenéis una imagen del aspecto de la aplicación:


Ésto lo podéis hacer editando el activity.xml (es uno de los dos archivos que se os abren). De todos modos, aquí os dejo el mío:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.NikNitro.bpcalculator.Calculator" >

    <GridLayout
        android:id="@+id/gridLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="25dp"
        android:alignmentMode="alignBounds"
        android:columnCount="2"
        android:useDefaultMargins="true" >

        <TextView
            android:id="@+id/textView1"
            android:layout_width="102dp"
            android:layout_column="0"
            android:layout_gravity="left|top"
            android:layout_row="0"
            android:text="Precio"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/textView2"
            android:layout_column="1"
            android:layout_gravity="center"
            android:layout_row="0"
            android:text="Cantidad(€)"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <EditText
            android:id="@+id/Cantidad"
            android:layout_width="106dp"
            android:layout_column="0"
            android:layout_gravity="left|center_vertical"
            android:layout_row="1"
            android:ems="10"
            android:inputType="numberDecimal" />

        <EditText
            android:id="@+id/Precio"
            android:layout_width="121dp"
            android:layout_column="1"
            android:layout_gravity="center"
            android:layout_row="1"
            android:ems="10"
            android:inputType="numberDecimal" />

    </GridLayout>

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/gridLayout1"
        android:layout_below="@+id/gridLayout1"
        android:layout_marginTop="26dp"
        android:text="Aceptar" />

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/button"
        android:layout_below="@+id/button"
        android:layout_marginTop="75dp"
        android:columnCount="2"
        android:useDefaultMargins="true" >

        <TextView
            android:id="@+id/textView3"
            android:layout_width="126dp"
            android:layout_column="0"
            android:layout_gravity="right|top"
            android:layout_row="0"
            android:text="Vas a Pagar"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/textView4"
            android:layout_width="124dp"
            android:layout_gravity="left"
            android:text="Vas a Tener"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/Pago"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <TextView
            android:id="@+id/Compra"
            android:layout_gravity="left"
            android:textAppearance="?android:attr/textAppearanceLarge" />

    </GridLayout>

</RelativeLayout>
Como veis no es nada difícil de comprender (no digo nada de hacerlo, solo de entenderlo :P). Bueno, aquí os dejo ahora el código de la aplicación (el cual es más sencillo aún).

package com.NikNitro.bpcalculator;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class Calculator extends Activity implements OnClickListener{
 //Botón
 private Button boton;
 //Texto Editable por el usuario
 private EditText litros, precio;
 //Texto SÓLO legible por el usuario
 private TextView compra, preciofinal;
 // Variables enteras
 private double dlitros, dprecio, dcompra, dpreciofinal;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  //Éste método se ejecuta el primero al iniciar la aplicación.
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_calculator);
  
  initialize();
 }

 private void initialize() {
  // TODO Auto-generated method stub
  boton = (Button)findViewById(R.id.button);
  //Añado al botón el controlador.
  boton.setOnClickListener(this);
  //Así es como se inicializan los objetos para que queden
  // linkeados con el activity.
  litros = (EditText)findViewById(R.id.Cantidad);
  precio = (EditText)findViewById(R.id.Precio);
  compra = (TextView)findViewById(R.id.Compra);
  preciofinal = (TextView)findViewById(R.id.Pago);
  
  
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.calculator, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_settings) {
   return true;
  }
  return super.onOptionsItemSelected(item);
 }

 @Override
 public void onClick(View v) {
  // Al hacer click en algún botón, se llama aquí.
  //Si tuvieramos más de un botón tendríamos que ver a cual se refiere. Para ello usamos la v que nos pasan.
  calcular();
  mostrarToast("Cálculo completo");
 }

 private void mostrarToast(String string) {
  //Muestra un mensaje
  Toast texto1 = Toast.makeText(getBaseContext(), string, Toast.LENGTH_SHORT);
  texto1.show();
  
 }

 private void calcular() {
  try {
   dlitros = Double.parseDouble(litros.getText().toString());
   dprecio = Double.parseDouble(precio.getText().toString());
   double prec = Math.rint(dprecio*100/dlitros)/100;
   compra.setText(prec+"");
   double desc = prec*0.05;
   preciofinal.setText((dprecio-desc)+"");
  } catch (Exception e) {
   mostrarToast("Rellene todos los campos correctamente, por favor");
  }
  
 }
 
 
}

Jeje, bueno, espero que no os queden dudas y (si os quedan) las preguntéis, pues estaré encantado de responderos. Un saludo y hasta la próxima ;)

miércoles, 9 de julio de 2014

Arduino: Control de un motor con velocidad variable.

Qué pasa gente? Aquí os traigo una nueva curiosidad si estáis empezando en Arduino. Si queréis hacer algo con motores, lo primero que notaréis es que el motor corre a una velocidad exagerada. Hoy aprenderemos a controlarla con un sencillo programita. Para ello utilizaremos los pines PWD (los que tienen un signo ~ junto al número) pues éstos nos permiten variar la velocidad. Antes de nada os dejo un video del proyecto, para que veais cómo va:

El esquema que usaremos será el siguiente:

Conexiones.
Por si no se ve bien, el amarillo va a la salida de 5V, el negro al GND y el verde a la salida ~3.
Ahora os dejo el código:

int motor=3;     //Declara Pin del motor

void setup() {
  Serial.begin(9600); 
  Serial.println("Eliga de 0 a 9 la velocidad de giro del motor");
}


void loop() {

  // Si hay algun valor en la Consola Serial
  if (Serial.available()){

    //Variable donde se guarda el caracter enviado desde teclado
    char a = Serial.read();

    // Si el caracter ingresado esta entre 0 y 9
    if (a>='0' && a<='9'){ 

      //Variable para escalar el valor ingresado a rango de PWM
      int velocidad = map(a,'0','9',0,255);
      //Escritura de PWM al motor
      analogWrite(motor,velocidad);
      //Mensaje para el usuario
      Serial.print("El motor esta girando a la velocidad ");
      Serial.println(a);

    } else { 

      Serial.print("Velocidad invalida");
      Serial.println(a);

    }
  }
}

Como veis no es nada demasiado complicado. Una vez hecho tenéis que abrir la consola desde el programa de Arduino (Herramientas->Monitor Serial ó Control+Mayus+M).

Eso es todo. Espero que os hayais enterado bien y hasta la próxima.
Saludos;)

lunes, 7 de julio de 2014

Empezando con Android. Instalación.

Buenas gente. Llevo todo el año con un par de proyectillos en mente así que éste finde me decidí a empezar a darle caña a Android... Hasta hoy no he podido programar, por lo que espero dejaros medianamente claro cómo hay que instalarlo.

Comencé con Android Studio, pero me daba bastantes fallos (Muchos de ellos al buscar en Google solo encontrabas solución para otras versiones). Tiene mucho potencial pero aún está en BETA (versión 0.8.1).

Al fin (tras cargarme, Eclipse, Java...) empecé de 0. Instalé JRE, JDK, Instalé Eclipse (me decía que no podía abrir, pero busqué y abrí el archivo javaw.exe y justo después me dejaba abrir Eclipse. No me ha vuelto a dar fallos).

Ahora que tenemos Eclipse, nos descargamos e instalamos el SDK de Android de aquí (http://developer.android.com/sdk/index.html) dándole a abajo donde pone "GET THE SDK FOR AN EXISTING IDE". Instalamos donde nos dé la gana (en mi caso C:/Android) y abrimos Eclipse. Ahora nos vamos a Help->Install New Software y le damos a Add. Como nombre ponemos lo que queramos (en mi caso ADT) y en la URL ponemos la siguiente: https://dl-ssl.google.com/android/eclipse/ 
Típico Next, Next... Finish. Se instala todo. Reiniciamos Eclipse y VOI LÁ. Objetivo completado.
Para crear proyectos en Android nos vamos a New->Other-> Android -> Android Application Project.

A partir de ahora iré subiendo mis proyectos. Ahora mismo estoy con una especie de "calculadora" para el descuento de la gasolina; es muy simple, pero en cuanto esté acabada la subiré.

Saludos a todos y hasta otra;)

viernes, 4 de julio de 2014

Liga de Pádel. Modelo de Entidad Relación.

Buenas gente. Como ahora empezaré a dar (entre otras) clases de Bases de Datos, voy a ir subiendo ejercicios resueltos. Os dejo aquí el enunciado del mismo:


Se desea diseñar una base de datos para almacenar la información relacionada con la edición de ligas de pádel. Como es bien sabido, el pádel es un deporte similar al tenis el que siempre participan 4 jugadores en cada partido. Cada liga tiene su código y su nombre que son únicos, un organizador y un premio final. Cada liga se celebra como sumo una vez cada año, en lo que se denomina ediciones, cada una con sus propias normas. 
Cada partido de pádel se celebra en un club (aunque es posible que al principio el lugar sea desconocido y no se rellene este dato hasta que los jugadores celebren el encuentro). Por cada partido hay que rellenar la fecha de celebración, la pista donde se juega y el resultado final. La liga tiene convenios con una serie de clubs de los que hay que almacenar un código, un nombre (obligatorio) una dirección y un teléfono de contacto. En un club sólo se puede jugar un partido en una pista en una determinada fecha (en nuestro sistema gestor de bases de datos el tipo fecha contiene también la hora). 
De cada jugador hay que almacenar un código, nombre y teléfono móvil (obligatorios). El móvil debe ser único por jugador. Además, se ha habilitado una página web en la que los jugadores pueden inscribirse como usuarios. Un usuario es un jugador del que hay que almacenar el alias, la password de entrada y la dirección de correo electrónico. Es obligatorio que el organizador de cada liga esté inscrito como usuario. Cada usuario sólo puede organizar una liga. 
Hay que conocer qué jugadores están inscritos en cada una de las ediciones. Éstas, a su vez, se pueden descomponer en varias fases, cada una con su propia fecha de inicio, nombre (fase regular, playoff, finales, etc.) y número de partidos de los que consta, número de partidos que se han jugado realmente y cuáles son. Además, una vez terminada cada edición hay que saber la pareja de jugadores que ha resultado campeona de esa edición. 
Nuestro cliente nos comenta que últimamente se está poniendo de moda el pádel individual, el cual lo juegan 2 jugadores en lugar de 4 y que se están planteando organizar ligas de este estilo. Para ello, bastaría con almacenar el puesto que cada jugador ocupa en cada partido de modo que variará entre 1 y 4 para los partidos de ligas normales y entre 1 y 2 para las ligas de pádel individual.

Bueno, vamos a ello. En una primera lectura de la especificación podemos identificar fácilmente las
entidades (objetos de interés). Éstos serán seres distinguibles del mundo real; pudiendo ser reales o abstractos y vienen representados por un nombre o sustantivo.
Normalmente las entidades suelen ser algo sobre lo que la organización necesita información.
Identificamos las Entidades como:
Liga, Edición, Club, Partido, Jugador, Usuario y Fase. Ahora vamos a crearlas con sus atributos.

Hay que tener en cuenta que un atributo nunca puede tener 'propiedades', pues en ese caso se trataría de otra entidad más. Además por la propiedad de redundancia, no debemos tener atributos que podamos calcular (por ejemplo, si tenemos una fecha de nacimiento, no pongamos la edad puesto que podría darse el caso de que una de las dos estuviera mal y no cuadraran las cuentas).

Ahora debemos fijarnos en qué atributos pueden ser únicos y elegir una clave para cada Entidad. Una clave es un atributo único que representa inequívocamente a su entidad (Por ejemplo, en la entidad Español, la clave debería ser DNI, pues es único).

Marcaremos con un * los valores obligatorios, con una U los únicos y con un # las claves. Una entidad puede tener varias claves (#1, #2...) o una clave compuesta (marcaremos los atributos con el mismo #) como en el caso de la Entidad Partido.

Ahora nos queda definir las relaciones. En ése momento nos daremos cuenta de que todos los Usuarios son Jugadores, por lo que Usuario es una Subentidad de Jugador. Además nos damos cuenta de que Fase es una entidad débil de Edición y ésta, a su vez, de Liga. Una entidad débil es aquella la cual sus atributos sólo la identifican de forma parcial (para saber de que fase estamos hablando necesitamos saber a qué edición pertenece y a qué liga).

Al fin, nuestro esquema quedaría de la siguiente manera:

Espero que si le echáis sus 5 minutos lo veáis todo claro, en otro caso me hallaré encantado de resolver vuestras dudas. Un saludo a todos y hasta la próxima ;)

miércoles, 2 de julio de 2014

Servidor de Echo en C++ para Linux.

Buenas gente. Ésta es la última práctica que se pidió para redes. Montar un servidor de Echo en C++ para Ubuntu. Me ha sido super trabajoso conseguirlo porque a mi ordenador le ha costado vida y media tirar de la máquina virtual (para compilar) y del Visual Studio para programar. Bueno, el servidor debe seguir el siguiente esquema:

Como veis parece ser seguir una estructura, pero el averiguar de donde sacar los datos nos costó (tanto a Cristina como a mi) demasiado tiempo. Seguramente haya códigos mejores que el mio, pues no soy perfecto. Pero espero que os sirva de ayuda. Aquí los teneis:

Código del Cliente:
#include <iostream>
#include <sys/socket.h>    //socket
#include<stdio.h> //printf
#include<netinet/in.h> 
#include<arpa/inet.h>  //inet_addr
#include<netdb.h>
#include<string.h>    //strlen
#include <stdlib.h>  // para atoi


using namespace std;





int main(int argc, char *argv[]) {
 int sck, rtn;
// const int PUERTO = 5050;
 const int PUERTO = atoi(argv[argc-1]);
 cout << argv[1] << ":" << PUERTO << "\n\n";

 struct sockaddr_in server;
 char bufferIn[2000], bufferOut[2000], bufferAux[2000];
 //creamos socket
 sck = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 if (sck == -1) {
  cout << "No se puede crear el socket";
 }
 puts("Socket creado");

 server.sin_family = PF_INET;
 server.sin_port = htons(PUERTO);
 server.sin_addr.s_addr = inet_addr(argv[1]);
// server.sin_addr.s_addr = inet_addr("127.0.0.1");

 //Conectando al servidor
 if (connect(sck, (struct sockaddr*) (&server), sizeof(server)) < 0) {
  perror("Conexion fallida. error");
  return 1;
 }
 puts("Conectado\n");
 
 //Obtenemos la string a enviar, la separamos en caracteres y la enviamos.
 bool noAcaba = true;
 while (noAcaba) {
  cout << "Conectado al servidor " << inet_ntoa(server.sin_addr) << ":" << PUERTO << ".\n";
  cout << "Que desea enviar?\n";
  scanf("%s", bufferOut);
  if (!strcmp(bufferOut, "FIN")) {
   noAcaba = false; 
   if (write(sck, bufferOut, strlen(bufferOut)) < 0) {
    puts("envio fallido");
    return 1;
   }
  } else {
   strcat(bufferOut, "\n");

   if (write(sck, bufferOut, strlen(bufferOut)) < 0) {
    puts("envio fallido");
    return 1;
   }
   rtn = read(sck, bufferIn, 2000);
   
   cout << "\nDevuelto " ; 
   for (int i = 0; i < strlen(bufferOut); i++) {
    cout << bufferIn[i];
   }
   cout << "\n";
   strcpy(bufferIn, "\0");
   strcpy(bufferOut, "\0");
  }
  

 }
 cout << "Desconectado Correctamente.\n";
 

 return 0;
}

Código del Servidor:
// EchoServer.cpp: define el punto de entrada de la aplicación de consola.
//

#include <iostream>
#include <sys/socket.h> 
#include <unistd.h>  //write
#include <stdio.h>  //para perror
#include <stdlib.h>  // para atoi

//Otros includes:
#include<netinet/in.h> 
#include<arpa/inet.h> //inet_addr
#include<netdb.h>
#include<string.h>  //strlen

using namespace std;




int main(int argc, char *argv[]) {

 int listen_socket, cliente, rtn;
 struct sockaddr_in server, clientinfo;
// const int PUERTO = 5050;
 const int PUERTO = atoi(argv[1]);

 //SOCKET
 listen_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
 if (listen_socket == -1) {
  perror("No se puede crear el socket");
  return 1;
 }
 cout << "Socket creado\n";

 //preparar la estructura sockaddr_in
 server.sin_family = AF_INET;
 server.sin_addr.s_addr = INADDR_ANY;
 server.sin_port = htons(PUERTO);
 
 //BIND
 rtn = bind(listen_socket, (struct sockaddr*)&server, sizeof(server));
 if (rtn < 0) {
  perror("Error en el bind");
  return 1;
 }
 puts("Bind hecho");
 
 //LISTEN
 rtn = listen(listen_socket, 1); //1 es el maximo de conexiones a la vez
 cout << "Esperando conexiones entrantes en el puerto "<< PUERTO << "\n";
 /*if (rtn < 0) {
  perror("Error en el listen");
  return 1;
 }*/

 while (true) {

 sockaddr client;
 socklen_t c = sizeof(client);
 //ACCEPT
 cliente = accept(listen_socket, &client, &c);
 if (cliente < 0) {
  perror("Error en el accept");
  return 1;
 }
 cout << "Conexion aceptada en el puerto " << PUERTO << "\n";
 fflush(stdout);

 //READ
 char buffer[2000], bufferAux[2000];
//  cout << "Al menos intenta leer \n";
 bool noAcaba = true;
 while ((rtn = read(cliente, buffer, 2000)) > 0 && noAcaba) {
   cout << "Conectado en el puerto " << PUERTO << ". Esperando accion.\n";
//   cout << "RECIBIDO: " << buffer << "\n";
   if (!strcmp(buffer, "FIN")) {
    noAcaba = false;
   }
   else {
    fflush(stdout);
 //   cout << "Al menos lee \n";
 //   strcat(buffer, "  Servidor");
    for (int i = 0; i < rtn; i++) {
     buffer[i] = toupper(buffer[i]);
    }
    fflush(stdout);
    if (write(cliente, buffer, strlen(buffer)) < 0) {
     perror("Envio fallido");
     return 1;
    }
//    cout << "ENVIADO: " << buffer;
    fflush(stdout);
    //Reiniciarlo
    strcpy(buffer, "\0");
   }

   
  }
  if (rtn == 0) {
   cout << "Recibido FIN. \nCerrando la conexión con el cliente\n";
   cout<<"Cliente desconectado. Esperando conexion en el puerto "<< PUERTO << "\n";
   fflush(stdout);
  }
  else if (rtn == -1)
  {
   perror("Error en Read");
  }
  cout << "Esperando otro cliente.\n \n";

 }
 
 
 return 0;
}

Como podéis ver son algo extensos y programados de forma estructurada. De todos modos aquí teneis el proyecto en Git donde podréis ver los cambios así como otras dos posibilidades de crearlo llamadas "client2" y "server2".

Espero que os haya gustado. El contenido de éste post tiene relación con el "intento de chat" que aún está por acabar, pues ése empezó siendo un servidor de echo.

Saludos y hasta la próxima;)

domingo, 29 de junio de 2014

Crear un sistema de videovigilancia con Raspberry, Webcam y Dropbox (Parte II)

Buenas gente. ¿Acabando los exámenes, no? Os traigo la segunda parte del sistema de vigilancia. Esto también lo podéis usar para tener una copia de seguridad 'autoactualizable' de vuestra carpeta de dropbox en la Raspberry Pi y viceversa.

Si te perdistes la primera parte, aquí la tienes.

Lo primero que vamos a hacer es descargarnos una pequña aplicación de Dropbox creada por Andrea Fabrizi. Para ello nos vamos a la shell (o entramos via ssh como hago yo) y escribimos lo siguiente:
cd /usr/local/bin/

sudo wget https://raw.github.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh

sudo chmod +x dropbox_uploader.sh

Esto descargará la aplicación en /usr/local/bin/ y le dará los permisos necesarios. Si ejecutaramos dropbox_uploader.sh (escribiendolo tal cual) nos saldrían las opciones necesarias, pero aún no podemos ejecutarlo. Necesitamos explicarle con que cuenta debe comunicarse. Para ello nos vamos a https://www.dropbox.com/developers/apps y creamos una nueva app "Dropbox API app". Le decimos que trabajaremos con "files and datastores" y que necesitamos acceder también a archivos de dropbox (por si nos hiciera falta) y para lo cual hay que pulsar en el No. Especificamos como archivos fotos (y el video es opcional) y le damos un nombre a la app. No cerreis la ventana que os sale. Ahora si teneis que ejecutar el script instalado anteriormente. Os pedira una "app key" y una "app secret" las cuales son las que os dice la ultima ventana de dropbox. Si os pregunta si darle acceso full, aceptad.
Si ahora escribierais en la consola:
dropbox_uploader.sh list

Os devolvería todas las carpetas y archivos que tenéis en la raíz.

Bueno, como usaremos un solo comando para subir (en este caso) los archivos de motion a dropbox, vamos a usar un alias en vez de un script (por hacerlo más liviano).
Un alias en Linux es una palabra que tu reservas y que significará uno o varios comandos seguidos. Por ejemplo, si pusieramos como alias a='cd /' cada vez que escribieramos 'a' y pulsaramos enter nos llevaría a la carpeta raíz del disco. Bueno, nuestro comando será el siguiente:

/usr/local/bin/./dropbox_uploader.sh upload /tmp/motion/* Motion/$(date +%Y%m%d)


El cual lo que hace es ejecutar dropbox_uploader.sh con la opcion upload, la carpeta de origen de motion y como destino una carpeta en Dropbox llamada Motion (si no existe, la crea) y dentro de ella una carpeta con la fecha actual. Para crear el alias editaremos un archivo llamado bashrc. Para ello ejecutamos:

sudo nano /home/pi/,bashrc


y bajamos abajo del todo. Allí escribimos :

alias subidaMotion='/usr/local/bin/./dropbox_uploader.sh upload /tmp/motion/* Motion/$(date +%Y%m%d)'


Cerramos con Control+X, nos pregunta si guardar, escribimos 'y' y dejamos el nombre tal y como está. Deberemos reiniciar para que surta efecto el cambio. A partir de ese momento cada vez que escribais subidaMotion os copiará todas las imagenes de /tmp/motion a la carpeta de dropbox; la cual depende de la fecha.

Bueno gente, eso es todo. Ya solo os queda toquetear. Quizás en un futuro próximo me de por hacer un script que cada 5 minutos mirase el contenido de la carpeta /tmp/motion y si fuera distinto de vacío subiera automáticamente. También cabe la posibilidad de hacer que nos enviara un correo o nos avisara vía Twitter. Son cosas sencillas que iré poniendo en cuanto tenga tiempo.

Saludos y espero que os haya gustado;)

viernes, 27 de junio de 2014

Examen final Programación de Sistemas y Concurrencia. Junio 2012.

Os dejo mi solución al ejercicio de C del examen final de Programación de Sistemas y Concurrencia de la UMA de 2012.

Se desea implementar una lista enlazada circular para representar la lista de procesos que están disponibles para ejecución. El puntero externo apuntará al proceso que le corresponde la ejecución (ver figura).  

Definir la estructura de datos e implementar las siguientes operaciones: 
void Crear (LProc *lista) 
Crea una lista de procesos vacía. 
void AñadirProceso (LProc *lista, int idproc) 
Añade el proceso con identificador idproc a la lista de procesos disponibles para ejecución. Este proceso se añade como nodo anterior al nodo al que apunta ejecución. Si la lista está vacía el puntero externo apuntará a l único nodo. Dada la figura anterior, si queremos añadir el proceso 5, la lista quedaría como aparece en la figura: 


void MostrarLista( LProc lista) 
Muestra la lista de los procesos que están disponibles para la ejecución. 
void EjecutarProceso(LProc *lista) 
Simula la ejecución del proceso apuntado por ejecución, eliminándolo de la lista de procesos.  Así, partiendo de la figura anterior, la lista quedaría :


ListaCircular.h
#ifndef LISTACIRCULAR_H_
#define LISTACIRCULAR_H_


typedef struct Lista LProc;

struct Lista{
 struct TNodo * ejecucion;
};
struct TNodo{
 int valor;
 struct TNodo * sig;
};

void Crear (LProc *lista);
//Crea una lista de procesos vacía.
void AnadirProceso (LProc *lista, int idproc);
void MostrarLista( LProc lista);
//Muestra la lista de los procesos que están disponibles para la ejecución.
void EjecutarProceso(LProc *lista);

#endif /* LISTACIRCULAR_H_ */

ListaCircular.c
#include <stdio.h>
#include <stdlib.h>
#include "ListaCircular.h"

void Crear (LProc *lista){
 //Crea una lista de procesos vacía.
 (*lista).ejecucion = NULL;
}
void AnadirProceso (LProc *lista, int idproc){

 struct TNodo* nuevo = malloc(sizeof(struct TNodo));
 struct TNodo * auxiliar = (*lista).ejecucion;
 struct TNodo * anterior = NULL;
 nuevo -> valor = idproc;
 int insertado = 0;
 if((*lista).ejecucion == NULL){ //lista vacía
  nuevo ->sig = nuevo;
  (*lista).ejecucion = nuevo;
  insertado = 1;
 }
 while(!insertado ){
  if(auxiliar -> sig == (*lista).ejecucion){ // se añade detras de auxiliar
   nuevo -> sig = (*lista).ejecucion;
   auxiliar -> sig = nuevo;
   insertado = 1;
  }else{
   anterior = auxiliar;
   auxiliar = auxiliar -> sig;
  }
 }

}
void MostrarLista( LProc lista){
 //Muestra la lista de los procesos que están disponibles para la ejecución.
 struct TNodo * aux = lista.ejecucion;
 if(lista.ejecucion == NULL){
  printf("No hay procesos disponibles para la ejecución. \n");
 }else{
  printf("Procesos disponibles para la ejecución: \t");


   printf(" %d -> ",lista.ejecucion ->valor );
   lista.ejecucion = lista.ejecucion -> sig;
   while(lista.ejecucion != aux){
    printf(" %d -> ",lista.ejecucion ->valor );
    lista.ejecucion = lista.ejecucion -> sig;
   }
   printf("\n");
 }

}
void EjecutarProceso(LProc *lista){
 /*
  * Simula la ejecución del proceso apuntado por ejecución, eliminándolo de la lista de procesos.
Así, partiendo de la figura anterior, la lista quedaría :
  */
 struct TNodo * aux;
 struct TNodo * auxiliar = (*lista).ejecucion ;

 while(auxiliar -> sig != (*lista).ejecucion){
   auxiliar = auxiliar -> sig;
 }
 aux = (*lista).ejecucion ->sig;
 printf("Se ejecuta el proceso: %d \n", (*lista).ejecucion -> valor);
 free((*lista).ejecucion);
 (*lista).ejecucion = aux;
 auxiliar -> sig = (*lista).ejecucion;
}

int main(void) {
 LProc lista;
 Crear(&lista);
 MostrarLista(lista);
 AnadirProceso(&lista, 4);
 AnadirProceso(&lista, 6);
 AnadirProceso(&lista, 1);
 AnadirProceso(&lista, 8);
 AnadirProceso(&lista, 3);
 AnadirProceso(&lista, 5);
 MostrarLista(lista);
 EjecutarProceso(&lista);
 MostrarLista(lista);

 return EXIT_SUCCESS;
}


miércoles, 25 de junio de 2014

Ejercicios C++: Registros y Arrays (III)

Hace ya tiempo que no escribía una entrada de ejercicios de C++, así que aquí os dejo la tercera que vemos sobre registros y arrays, pues es lo más complicado que hemos visto hasta ahora de este lenguaje. En las (dos) próximas entradas de C++ trabajaremos con strings.


Se dispone de un array de 80 números enteros en el que al menos hay dos números que son iguales y dos que son distintos. Obtenga una función que tomando como parámetro dicho array, devuelva un elemento del array que sea mayor que el mínimo de éste.
int minimo (const TArray& array){
 int min = array[0];
 for(unsigned i = 1; i < TAMANO; i++){
  if(array[i] < min){
   min = array[i];
  }
 }
 return min;
}

int ejercicio (const TArray& array){
 int res;
 bool encontrado = false;
 unsigned i= 0;
 minimo = minimo(array);
 while(i < TAMANO && !encontrado){
  if(array[i] > minimo){
   encontrado = true;
   res = array[i];
  }
  i++;
 }

 return res;
}

Diseña un algoritmo que permita invertir el contenido de un array.
#include <iostream>
using namespace std;

const unsigned TAMANO = 10;

typedef int TArray[TAMANO];


void leerArray(TArray& array){
 for(unsigned i=0; i < TAMANO; i++){
  //cout << "v["<<i<<"]->";
  cin >> array[i];
 }
}

void invertir(const TArray& array, TArray& inv){
 unsigned j = TAMANO -1;
 for(unsigned i = 0; i < TAMANO; i++){
  inv[i] = array[j];
  j--;
 }
}



void mostrarArray(const TArray& unarray){
 for(unsigned i =0; i < TAMANO; i++){
  cout << "["<<unarray[i]<<"]";
 }
}
int main() {
 TArray array, inv;
 leerArray(array);
 invertir(array, inv);
 mostrarArray(inv);
 return 0;
}


Escriba un programa que efectúe la conversión de un número natural en base 10 a otra determinada base, sabiendo que el resultado no sobrepasará los 50 dígitos. El usuario introducirá primero el número en base 10 y después la base a la que convertirlo (el programa debe asegurarse de que la base no sea ni menor de 2 ni mayor de 9).
#include <iostream>
using namespace std;

const unsigned MAXIMO = 50;

typedef unsigned TArray[MAXIMO];

void leerDatos(unsigned &num, unsigned& base){
 cout << "Introduce el número a convertir: ";
 cin >> num;
 do{
  cout << "Introduce la base: ";
  cin >> base;
 }while(base < 2 || base > 9);
}

void convertir(unsigned num, unsigned base, TArray array, unsigned&  i){
 i=0;
 while(num != 0){
  array[i] = num%base;
  num = unsigned(num/base);
  i++;
 }
}
void invertir(const TArray& array, TArray& inv, unsigned contador){
 unsigned j = contador -1;
 for(unsigned i = 0; i < contador; i++){
  inv[i] = array[j];
  j--;
 }
}

void mostrarSolucion(const TArray& array, unsigned contador){
 cout << "El número ya convertido es ";
 for(unsigned i=0; i < contador  ; i++){
  cout << array[i];
 }
}

int main() {
 unsigned num, base, i;
 TArray primero, solucion;
 leerDatos(num, base);
 convertir(num, base, primero, i);
 invertir(primero, solucion, i);
 mostrarSolucion(solucion, i);
 return 0;
}

Dados los siguientes tipos (para una constante MAX cualquiera):

typedef char Componentes[MAX];

struct Vector {

Componentes datos; // array de caracteres

unsigned tam; // numero de celdas ocupadas

};


a) La moda de un array de caracteres es el carácter del array que se repite más frecuentemente. Si varios caracteres se repiten con la misma frecuencia máxima, entonces no hay moda. Escribe un procedimiento con tres parámetros. El primero es de entrada para recibir un registro de tipo Vector que contiene el array datos con tam caracteres. El segundo parámetro es de salida e indicará si se ha encontrado la moda en el array o no. El tercer parámetro es de salida y será el carácter que representa la moda (si es que existe).

#include <iostream>
using namespace std;

const unsigned MAX =50;

typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

void apartadoA(const Vector& vector, bool& existeModa, char& moda){
 int apariciones;
 int mayor=0;
 char m;
 existeModa = false;
 for(unsigned i=0; i < vector.tam; i++){
  apariciones = 1;
  for(unsigned j=i+1; j< vector.tam;j++){
   if(vector.datos[i] == vector.datos[j]){
    apariciones++;
   }
  }
  if(apariciones != mayor){
   if(mayor < apariciones){
    mayor = apariciones;
       m = vector.datos[i];
       existeModa = true;
   }
  }else{
   existeModa = false;
  }
 }
 if(existeModa){
  moda = m;
 }
}

void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}

int main() {
 Vector vector;
 bool existeModa;
 char moda;
 leerVector(vector);
 apartadoA(vector, existeModa, moda);
 if(existeModa){
  cout << "La moda es '"<< moda<<"'";
 }else{
  cout << "No existe la moda.";
 }
 return 0;
}

b) Diseña una función booleana que dados dos registros de tipo Vector como parámetros, devuelva TRUE si son iguales y FALSE en otro caso. Dos vectores son iguales si contienen los mismos elementos y en el mismo orden relativo, suponiendo que el primer elemento sigue al último.
#include <iostream>
using namespace std;

const unsigned MAX=50;
typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

bool vectoresIguales(Vector& vector1, Vector& vector2){
 bool iguales = false;
 if(vector1.tam == vector2.tam){

 iguales = true;
 unsigned i=0;

 bool encontrado=false;
 unsigned j=0;
  while(j < vector2.tam && !encontrado){
   if(vector1.datos[0] == vector2.datos[j]){
    encontrado = true;
   }else{
    j++;
   }
  }

 while(i < vector1.tam && iguales){
  iguales = false;
   if(vector1.datos[i] == vector2.datos[j]){
    i++;
    if(j == vector2.tam -1){
     j = 0;
    }else{
     j++;
    }
    iguales = true;
   }

 }

 }
 return iguales;

}


void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cout << "Introduce cadena: ";
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}


int main() {
 Vector v1, v2;
 leerVector(v1);
 leerVector(v2);
 if(vectoresIguales(v1,v2)){
  cout << "Los vectores son iguales.";
 }else{
  cout << "No son iguales";
 }

 return 0;
}

c) Diseña un procedimiento que tome como parámetros de entrada dos vectores con los arrays ordenados y devuelva (con un parámetro de salida) el vector resultado de realizar la mezcla ordenada de los dos arrays contenidos en los vectores de entrada.
#include <iostream>
using namespace std;

const unsigned MAX =50;

typedef char Componentes[MAX];
struct Vector {
 Componentes datos; // array de caracteres
 unsigned tam; // numero de celdas ocupadas
};

void apartadoC(const Vector& v1, const Vector& v2, Vector& res){
 unsigned i=0;
 unsigned j=0;
 unsigned cnt =0;
 res.tam = v1.tam+v2.tam;
 while(cnt < res.tam && i < v1.tam && j < v2.tam){
  if(v1.datos[i] < v2.datos[j]){
   res.datos[cnt] = v1.datos[i];
   i++;
  }else{
   res.datos[cnt] = v2.datos[j];
   j++;
  }
  cnt++;
 }
 if(i == v1.tam){
  while(cnt < res.tam){
   res.datos[cnt] = v2.datos[j];
   j++;
   cnt++;
  }
 }else if (j == v2.tam){
  while(cnt < res.tam){
     res.datos[cnt] = v1.datos[i];
     i++;
     cnt++;
  }
 }
}

void leerVector(Vector& vector){
 vector.tam = 0;
 char a;
 cout << "Introduce los datos: ";
 cin >> a;
 while(a != '.'){
  vector.datos[vector.tam]= a;
  vector.tam++;
  cin >> a;
 }
}

void mostrarArray(const Vector& res){
 for(unsigned i=0; i < res.tam; i++){
  cout <<"["<<res.datos[i]<<"]";
 }
}


int main() {
 Vector v1, v2, res;
 leerVector(v1);
 leerVector(v2);
 apartadoC(v1,v2, res);
 mostrarArray(res);
 return 0;
}

Diseña un algoritmo para calcular la suma de los elementos de la diagonal principal de una
matriz cuadrada.
include <iostream>
using namespace std;

typedef double TFilas[100];
typedef TFilas TMat[100];

struct TMatriz{
 unsigned dim;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 cout << "Introducir número de filas y columnas: ";
 cin >> mat.dim;
}

void leerMatriz(TMatriz& mat){
 cout << "Introduce los valores de la matriz."<< endl;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << "Matriz["<<f+1<<"]["<<c+1<<"]->";
   cin >> mat.matriz[f][c];
  }
 }
}

void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << mat.matriz[f][c]<<" ";
  }
  cout << endl;
 }
}

double sumaDiagonal(const TMatriz& mat){
 double suma=0;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
    if(c==f){
     suma = suma + mat.matriz[f][c];
    }
  }
 }
 return suma;
}

int main() {
 cout << "Suma de la diagonal de una matriz." << endl; 
 TMatriz matriz;
 leerDimension(matriz);
 leerMatriz(matriz);
 mostrarMatriz(matriz);
 cout << "La suma de la diagonal es "<< sumaDiagonal(matriz);

 return 0;
}


Una matriz tiene un punto silla en una de sus componentes, si ese componente es el mayor valor de su columna y el menor de su fila. Diseña un algoritmo que recogiendo de teclado los componentes de una matriz cuadrada de enteros de hasta un máximo de 10x10, muestre en la pantalla las coordenadas de todos sus puntos silla.
##include <iostream>
using namespace std;

typedef double TFilas[10];
typedef TFilas TMat[10];

struct TMatriz{
 unsigned dim;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 do{
 cout << "Introducir número de filas y columnas: ";
 cin >> mat.dim;
 }while(mat.dim > 10);
}

void leerMatriz(TMatriz& mat){
 cout << "Introduce los valores de la matriz."<< endl;
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << "Matriz["<<f+1<<"]["<<c+1<<"]->";
   cin >> mat.matriz[f][c];
  }
 }
}

void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.dim; f++){
  for(unsigned c =0; c < mat.dim; c++){
   cout << mat.matriz[f][c]<<" ";
  }
  cout << endl;
 }
}

double mayorColumna(const TMatriz& mat, unsigned columna){
 double mayor = mat.matriz[0][columna];
 for(unsigned f=1; f < mat.dim; f++){
  if(mayor < mat.matriz[f][columna]){
   mayor =  mat.matriz[f][columna];
  }
 }
 return mayor;
}

double menorFila(const TMatriz& mat, unsigned fila){
 double menor = mat.matriz[fila][0];
  for(unsigned c=1; c< mat.dim; c++){
   if(menor > mat.matriz[fila][c]){
    menor =  mat.matriz[fila][c];
   }
  }
  return menor;
}


void buscarPuntos(const TMatriz& mat){
 for(unsigned f=0; f < mat.dim; f++){
  for(unsigned c=0; c < mat.dim; c++){
   if(mat.matriz[f][c] == menorFila(mat, f) && menorFila(mat, f)==mayorColumna(mat, c)){
    cout << "El elemento de la fila "<< f+1 << " y la columna "<< c+1 << " es un punto de silla."<< endl;
   }
  }
 }
}
int main() {
 cout << "Buscar punto de silla." << endl; // prints !!!Hello World!!!
 TMatriz matriz;
  leerDimension(matriz);
  leerMatriz(matriz);
  mostrarMatriz(matriz);
  buscarPuntos(matriz);
 return 0;
}

Un tablero n-goro es un tablero con n x (n+1) casillas. Una propiedad interesante es que se pueden visitar todas sus casillas haciendo el siguiente recorrido por diagonales. Empezamos por la casilla (1,1) y recorremos la diagonal principal hacia la derecha y hacia abajo hasta llegar a la casilla (n,n). La siguiente casilla a visitar sería la (n+1,n+1) que no existe porque nos saldríamos del tablero por abajo. En estos casos siempre se pasa a la casilla equivalente en al primera fila, es decir, la (1,n+1). Ahora seguimos moviéndonos hacia la derecha y hacia abajo. Pero la siguiente casilla sería la (2,n+2) que no existe porque nos hemos salido por la derecha. En estos casos se continúa por la casilla equivalente de la primera columna, es decir, la (2,1). De nuevo nos movemos hacia la derecha y hacia abajo, hasta alcanzar la casilla (n,n-1). La siguiente casilla sería la (n+1,n), pero como nos saldríamos por abajo pasamos a la casilla equivalente de la primera fila (1,n). Si se continúa con este proceso se termina visitando todas las casillas del tablero.
Diseñe un procedimiento que dada una constante N devuelve como parámetro de salida un
tablero N-goro con sus casillas rellenas con el número correspondiente al momento en que se
visitan.
#include <iostream>
using namespace std;

typedef double TFilas[10];
typedef TFilas TMat[10];

struct TMatriz{
 unsigned nco, nfi;
 TMat matriz;
};

void leerDimension(TMatriz& mat){
 cout << "Introducir número de filas: ";
 cin >> mat.nfi;
 mat.nco= mat.nfi+1;
}
void inicializar(TMatriz& mat){
 for(unsigned i=0; i < mat.nfi; i++){
  for(unsigned j= 0; j < mat.nco; j++){
   mat.matriz[i][j]= 0;
  }
 }
}

void escribirMatriz(TMatriz& mat){
 unsigned fila = 0;
 unsigned columna =0;
 for(unsigned i = 1; i <= mat.nfi*mat.nco; i++){
  if(mat.matriz[fila][columna]== 0){
   mat.matriz[fila][columna]= i;

   fila++;
   columna++;

   if(fila >= mat.nfi){
   fila = 0;
   }
   if(columna >= mat.nco){
   columna =0;
   }
  }
 }

}


void mostrarMatriz(const TMatriz& mat){
 for(unsigned f =0; f < mat.nfi; f++){
  for(unsigned c =0; c < mat.nco; c++){
   cout << mat.matriz[f][c]<<"  ";
  }
  cout << endl;
 }
}


int main() {
 
 TMatriz matriz;
 leerDimension(matriz);
 inicializar(matriz);
 escribirMatriz(matriz);
 mostrarMatriz(matriz);
 return 0;
}



domingo, 22 de junio de 2014

Examen parcial de Programación de Sistemas y Concurrencia. Curso 2013/2014

Después de más de dos meses sin publicar ninguna entrada de lenguaje C (que sí de C++) aquí os dejo una con el parcial de Programación de Sistemas y Concurrencia de la UMA de este curso.

Os dejo el enunciado y mi solución que, como tantas veces he dicho, es una de las posibles pues siempre hay varias.

Una lista doblemente enlazada es una lista enlazada en la que cada nodo tiene dos punteros, uno que apunta al siguiente nodo de la lista y otro que apunta al nodo anterior. Además, las listas doblemente enlazadas tienen una referencia a la cabeza de la lista y a la cola (ver figura).


a) Implementar en el archivo Lista.h el tipo T_Lista que representa una lista doblemente enlazada, donde cada nodo almacena el valor de un número entero. 
NOTA: T_Lista debe tener referencias a la cabeza y a la cola de la lista.

b) Implementar en el archivo Lista.c las operaciones especificadas en el archivo Lista.h: 

void crear(T_Lista *lista) 
Crea una lista doblemente enlazada vacía. 

void destruir(T_Lista *lista) 
Destruye completamente la estructura de la lista. 

void insertar(T_Lista *lista, int valor) 
Inserta un nodo con el valor especificado como parámetro de forma ordenada 
ascendente, para ello se deberá recorrer la lista desde la cabeza y desde la cola con 
punteros auxiliares, insertándolo en el sitio correcto desde el primer puntero que lo 
encuentre. 

int eliminar(T_Lista *lista, int valor) 
Elimina un nodo con el valor especificado como parámetro, para ello se deberá recorrer 
la lista desde la cabeza y desde la cola con punteros auxiliares, eliminándolo desde el 
primer puntero que lo encuentre. En caso de que exista más de un nodo con ese valor se 
eliminará el primero encontrado, si se encuentran a la vez por la cabeza o por la cola se 
eliminará solamente uno de los dos. Si no existe en la lista el valor no se eliminará nada. 
El valor devuelto por la función indica si se ha eliminado o no el valor. 

void imprimir(T_Lista lista, int direccion) 
Escribe por pantalla la lista ordenada de forma ascendente si direccion == 0 y de 
forma descendente en caso contrario. 

Lista.h
#ifndef LISTA_H_ 
#define LISTA_H_

typedef struct Lista T_Lista;

struct Lista {
 struct TNodo * cabeza;
 struct TNodo * cola;
};

struct TNodo{
 int valor;
 struct TNodo* sig;
 struct TNodo* ant;
};

void crear(T_Lista *lista);//Crea una lista doblemente enlazada vacía.
void destruir(T_Lista *lista); //Destruye completamente la estructura de la lista.
void insertar(T_Lista *lista, int valor);
int eliminar(T_Lista *lista, int valor);
void imprimir(T_Lista lista, int direccion);

#endif /* LISTA_H_ */


Lista.c
#include <stdio.h>
#include <stdlib.h>
#include "Lista.h"



void crear(T_Lista *lista){
 //Crea una lista doblemente enlazada vacía.
 (*lista).cabeza = NULL;
 (*lista).cola = NULL;
}
void destruir(T_Lista *lista){
 //Destruye completamente la estructura de la lista.
 struct TNodo* auxiliar;
 while((*lista).cabeza != NULL){
  auxiliar = (*lista).cabeza ->sig;
  free((*lista).cabeza);
  (*lista).cabeza = auxiliar;
 }
 (*lista).cola = NULL;
}
void insertar(T_Lista *lista, int valor){
 int insertado = 0;
 struct TNodo* auxcab = (*lista).cabeza;
 struct TNodo* auxcola = (*lista).cola;
 struct TNodo * nuevo = malloc(sizeof(struct TNodo));
 nuevo ->valor = valor;

 if((*lista).cabeza == NULL && (*lista).cola == NULL){
  nuevo -> sig = NULL;
  nuevo -> ant = NULL;
  (*lista).cabeza  = nuevo;
  (*lista).cola = nuevo;
  insertado = 1;
 }

 while(!insertado){
  if(auxcab != NULL){
   if(auxcab ->valor < valor){
    auxcab = auxcab -> sig;
   }else{ // lo tengo que insertar
    if(auxcab ->ant == NULL){ // es el primero
     nuevo -> sig = auxcab;
     nuevo -> ant = NULL;
     auxcab -> ant = nuevo;
     (*lista).cabeza = nuevo;
    }else{
     nuevo -> sig = auxcab;
     nuevo -> ant = auxcab->ant;
     auxcab->ant -> sig =nuevo;
     auxcab->ant = nuevo;
    }
    insertado = 1;
   }
  }
  if(auxcola != NULL && !insertado){
   if(auxcola ->valor > valor){
    auxcola = auxcola -> ant;
   }else{ // lo tengo que insertar
    if(auxcola-> sig == NULL){ // es el ultimo
     auxcola -> sig = nuevo;
     nuevo -> sig = NULL;
     nuevo -> ant = auxcola;
     (*lista).cola = nuevo;
    }else{

     nuevo -> sig = auxcola -> sig;
     auxcola -> sig -> ant = nuevo;
     nuevo -> ant = auxcola;
     auxcola -> sig = nuevo;
    }
    insertado = 1;
   }
  }


 }

}
int eliminar(T_Lista *lista, int valor){
 struct TNodo* auxcab = (*lista).cabeza;
 struct TNodo* auxcola = (*lista).cola;
 struct TNodo * aux;

 while(auxcab != NULL || auxcola != NULL){

   if(auxcab != NULL && auxcab -> valor == valor){ // lo tengo que eliminar
    if(auxcab -> ant == NULL){ // es el primero
     aux = (*lista).cabeza -> sig;
     free((*lista).cabeza);
     (*lista).cabeza = aux;
     (*lista).cabeza -> ant = NULL;
     if(aux == NULL){
      (*lista).cola = NULL;
     }
    }else{
     auxcab -> ant -> sig = auxcab -> sig;
     auxcab -> sig -> ant = auxcab -> ant;
     free(auxcab);
    }
    return 1;

   }else if(auxcola != NULL && auxcola -> valor == valor){
    if(auxcola -> sig == NULL){ // es el ultimo
     aux = (*lista).cola -> ant;
     free((*lista).cola);
     (*lista).cola = aux;
     (*lista).cola -> sig = NULL;
    }else{
     auxcola -> ant -> sig = auxcola -> sig;
     auxcola -> sig -> ant = auxcola -> ant;
     free(auxcola);
    }
    return 1;
   }else{
    if(auxcola != NULL) auxcola = auxcola -> ant;
    if(auxcab != NULL)auxcab = auxcab -> sig;
   }
 }
 return 0;
}
void imprimir(T_Lista lista, int direccion){
 /*Escribe por pantalla la lista ordenada de forma ascendente si direccion == 0
   y de forma descendente en caso contrario. */
 struct TNodo * aux;
 if(direccion == 0){
  aux = lista.cabeza;
  while(aux != NULL){
   printf("%d, ", aux ->valor);
   aux = aux -> sig;
  }
  printf("\n");
 }else{
  aux = lista.cola;
  while(aux != NULL){
   printf("%d, ", aux ->valor);
   aux = aux ->ant;
  }
  printf("\n");
 }
}
int main(void) {
 T_Lista lista;
  crear(&lista);

  insertar(&lista, 1);

  insertar(&lista, 2);


  insertar(&lista, -1);


  insertar(&lista, 0);

  imprimir(lista, 0);
 imprimir(lista, 1);
  eliminar(&lista, 2);
  imprimir(lista, 0);
  imprimir(lista, 1);

  destruir(&lista);
  imprimir(lista, 0);
  imprimir(lista, 1);
 return EXIT_SUCCESS;
}

Pues ahí lo tenéis, podeis probar en el main con los diferentes métodos creados ;)

viernes, 20 de junio de 2014

Crear un sistema de videovigilancia con Raspberry, Webcam y Dropbox (Parte I)

Buenas gente. Hoy os traigo algo diferente, un pequeño sistema de videovigilancia que (en caso de notar movimiento) subirá fotos a tu cuenta de dropbox señalando la fecha y hora así como el lugar (dentro de la foto) donde se ha realizado el cambio.

Aquí la segunda parte del proyecto.

Para ésto vamos a usar una Raspberry Pi, una webCam (aquí os dejo un enlace con una lista de cámaras que, seguro, funcionarán).

Lo primero de todo que debéis hacer es actualizar vuestra Raspberry. Para ello:
sudo apt-get update
sudo apt-get upgrade
Lo siguiente a hacer será instalar motion. Motion será el software encargado de usar la webcam y procesar las imágenes.
sudo apt-get install motion
Ahora lo que tenemos que hacer es configurar motion. para ello hacemos lo siguiente:
sudo nano /etc/motion/motion.conf
Vamos a cambiar los siguientes valores:

#Para que se ejecute en segundo plano
Daemon = OFF ==> Daemon ON
#Lo que capturará antes y después de cada toque.
pre_capture 2
post_capture 4
#Muestra en la imagen un rectángulo con el cambio.
locate on
#Para poder acceder desde fuera:
webcam_port 9091
webcam_localhost off
#Para poder acceder al control desde fuera:
control_port 9090
control_localhost off
#Contraseña para control: Elegid la que queráis
control_authentication user:pass
Ahora nos vamos a
sudo nano /etc/default/motion
Y cambiamos el 'no' a 'yes'. Esto hará que se arranque al inicio.

Hasta aquí ya hemos instalado y configurado correctamente la cam. Podeis probarlo poniendo en la consola:
sudo service motion start
Si ahora pasárais por delante, podríais ver como aparece una carpeta llamada 'motion' en /tmp/ con imágenes. Para parar usad el comando de antes con stop. Las imágenes que guardais (al estar en la carpeta /tmp) se borrarán al apagar la raspberry, por lo que tendríamos que guardarlas. De eso nos encargaremos en la segunda mitad del tutorial, la conexión con Dropbox. Saludos y gracias ;)

viernes, 13 de junio de 2014

PROYECTO Creación de un Chat. Parte III: Petición de datos desde PHP a MySQL

Buenas gente, aquí sigo sin vida social :p. Os traigo ya los tres códigos en PHP que vamos a usar recién sacados del horno... Vereis que son similares. Todos consisten en recoger ( o no ) una variable mediante GET, enviar una instrucción SQL a la BD y mostrar por pantalla el resultado (Si lo hubiera).

Antes de nada aquí tenéis los enlaces a las otras partes:
Parte 1: Creación de la base de datos gratuita MySQL.
Parte 2: Actualización de datos en MySQL desde PHP

Os voy a recordar el código que usamos para actualizar con un nuevo nombre, el archivo userReg.php:

  <?php 
  $nick    = $_GET['nick'];
  $ip     = $_SERVER['REMOTE_ADDR'];
  $server  = 'mysql.hostinger.es';
  $usuario = 'usuario';
  $pass  = 'password';
  $BD  = 'baseDeDatos';
  
  $link = mysql_connect($server, $usuario, $pass);
  mysql_select_db($BD, $link);
  
  $inser = "INSERT INTO Usuario (Nick, IP) VALUES ('".$nick."', '".$ip."') ON DUPLICATE KEY UPDATE IP= '".$ip."'";
 
  mysql_query($inser, $link);
  mysql_close($link); // Cerramos la conexion con la base de datos
  
 ?>
Ahora vamos con uno que nos liste todos los nombres de usuario que hay (He hecho que los liste todos seguidos separados por un '&' porque era una forma sencilla de luego poder enviarselos a Java. userList.php.
 <?php 
 $server  = 'mysql.hostinger.es';
 $usuario = 'usuario';
 $pass  = 'password';
 $BD  = 'baseDeDatos';
 $link = mysql_connect($server, $usuario, $pass);
 mysql_select_db($BD, $link);
  
 $lalala = "Select Nick from Usuario";
  
 $result = mysql_query($lalala, $link);
 while($row = mysql_fetch_array($result)) {
  echo $row['Nick']."&";
 }
 mysql_close($link); // Cerramos la conexion con la base de datos 
?>
Como veis son muy similares. Solo cambia que le enviamos la instrucción y luego mostramos por pantalla lo que nos devuelve. Ya por último teneis el que va a recibir un nombre de usuario y va a devolver su IP. userBusc.php.
 <?php 
  $nick    = $_GET['nick'];
  $server  = 'mysql.hostinger.es';
  $usuario = 'usuario';
  $pass  = 'password';
  $BD  = 'baseDeDatos';
  $link = mysql_connect($server, $usuario, $pass);
  mysql_select_db($BD, $link);
  
  $lalala = "Select IP from Usuario Where Nick like '".$nick."'";
  
  $result = mysql_query($lalala, $link);
  
  $row = mysql_fetch_array($result);
  echo $row["IP"]; 
  mysql_close($link); // Cerramos la conexion con la base de datos 
  
  
 ?>
Ya podeis daros cuenta de que siempre son los mismos. Es una forma un poco fea de conectarse a una base de datos (Podríamos hacerlo orientado a objetos, con métodos y todo) pero es muy intuitiva y te puede sacar fácilmente de un apuro.

El próximo día os ensñaré a integrar esto con Java, así que estad atentos ;)

Saludos ;)

miércoles, 11 de junio de 2014

Ejercicios de ensamblador MIPS: funciones (sumatorio, factorial), media aritmética ...

Vuelvo con ejercicios de ensamblador MIPS.
En estos ejercicios ya vemos el uso de funciones, y por tanto de las instrucciones jal y jr. Recuerdo que es recomendable, aunque más bien necesario, echar un vistazo a las instrucciones para conocerlas bien y si no tenéis ni idea de ensamblador MIPS recomiendo visitar la anterior entrada en la que os dejé unos ejemplos muy sencillos para entender las instrucciones más usuales.

NOTA: Aunque en algunos ejercicios piden una función, para poder probarla añado un "main" y un segmento de datos de prueba. Pero recordad que la función debe funcionar independientemente de esos datos.

Función factorial en ensamblador MIPS.
# función factorial (argumento en $a1, valor devuelto en $v1)
# número en 'ene'
# valor factorial en 'resul'


.data
ene:      .word  5
             .space 28
resul:    .word  0        

      
.text
main:    lw $a1,ene($0)
         jal  fact          
         sw $v1,resul($0)               
         li $v0,10
         syscall
          
fact:
       addi $v1, $0, 1
       add $8, $a1, $0
      
loop:
  beq $8, $0, fin  
 mul $v1, $v1,$8
        subi $8, $8, 1
        j loop
     
fin:  jr $31


Realizad una función llamada ‘sumatorio” que sume las componentes de un vector de números enteros almacenados en memoria. Los parámetros de entrada a la función son, la dirección de comienzo del vector en $a0 y el número de componentes en $a1. La función devuelve el valor de la suma en $v0.
.data
suma: .word 0
.space 24
tamano: .word 10
datos1: .word 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
 .word 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32

.text
main:  la $a0, datos1
 lw $a1, tamano($0)
 jal sumatorio
 sw $v0, suma($0)
 li $2, 10
 syscall 

sumatorio:  add $10, $0, $0 # inicializo 10
  add $v0, $0, $0 # inicializo 8
  
etq1:  lw $9, 0($a0)
  add $v0,$v0,$9 
  addi $a0,$a0,4 
  addi $10,$10, 1 
  bne $10,$a1,etq1 
  jr $ra


A partir de la posición de memoria etiquetada como ‘A’ hay almacenada una secuencia de ‘n’ números. El valor ‘n’ se encuentra almacenado en la posición previa a ‘A’. Realizad un programa que lea dicha secuencia y la transforme en otra que se almacene a partir de la posición etiquetada como ‘B’ y obedezca la siguiente expresión de transformación:
B(0)=A(0)
B(i)= (A(i)+A(i-1))/2                i=1,2,...,n-1
Utilizad la cabecera de programa que se indica a continuación.

.data
       .space 28
       .word 5
A:     .word 2,4,5,7,4,9,6,8,9,4,5,6,7,8,9,3,4,5,9,7,2,3,4,777
B:     .word 0
      


.text  
 addi $10, $0, -4
 lw $10, A($10) # en el registro 10 tengo n
 
 beq $10, $0, fin
 
 slt $15, $10, $0
 
 bne $15, $0, fin 
 
 add $9, $0, $0
 
 
 lw $8,A($9) # En el registro 8, A
 lw $11, B($9) # En el registro 11, B
 
 sw $8, B($0) # B(0) = A(0)
 
 addi $14, $10, -1
 
loop: addi $9, $9, 4 # incrementar puntero
 
 addi $10, $9, -4
 
 lw $12, A($10)
 lw $8,A($9) 
 
 add $13, $8, $12
 
 div $13, $13, 2
 
 sw $13, B($9) 
 
 addi $14, $14, -1
 
 
 bne $14, $0, loop
 
fin: li $2, 10
 syscall
   


A partir de la posición de memoria etiquetada como ‘datos’ hay almacenada una secuencia de ‘n’ números. El valor ‘n’ se encuentra almacenado en la posición previa a ‘datos’. Realizad un programa que calcule la media aritmética de dicha secuencia y la almacene en la posición etiquetada como ‘med’. Utilizad la cabecera de programa que se indica a continuación.
.data 
 .space 28 
 .word 5
datos: .word 2,4,5,7,4,95,6,8,9,4,555,6,7,8,9,3,4,5,9,7,2,3,4,777 
med: .word 0 


.text 

addi $9, $10, -4
lw $9, datos($9) # tengo n en $9

slti $13, $9, 1


add $10, $0, $0
add $11, $0, $0
add $12, $0, $0

bne $0, $13, fin

suma:
lw $8, datos($12)
add $11, $11, $8
addi $10, $10, 1
addi $12, $12, 4
bne $10, $9, suma
div $11, $11, $9
sw $11, med($0)
fin:
li $2, 10
syscall

domingo, 8 de junio de 2014

Práctica Planificación Sistemas Inteligentes

Pues, por una petición en particular, os dejo la práctica de Planificación de la asignatura Sistemas Inteligentes de 2º de Ingeniería Informática de la UMA.
El problema del mono y los plátanos (the monkey-and-bananas problem) es aquel al que se enfrenta un mono (monkey) en un laboratorio donde hay varios plátanos colgando del techo, fuera de su alcance.
Hay una caja (box) disponible que permitirá el mono alcanzar los plátanos si se sube en ella.
Al principio el mono está en A, los plátanos en B, y la caja en C.
El mono y la caja tienen altura (height) baja (low), pero si el mono se sube a la caja tendrá altura elevada (high), la misma que tienen los plátanos.
Las acciones disponibles para el mono son:
1.  ir (go) de un lugar a otro,
2.  empujar (push) un objeto de un lugar a otro,
3.  subirse (climbUp) o
4.  bajarse (climbDown) de un objeto, y
5.  agarrar (grasp) o
6.  soltar (unGrasp) un objeto.

El resultado de grasp es que el mono obtiene el objeto si el mono y el
objeto están en el mismo lugar y a la misma altura.

Se pide hacer lo siguiente:
a) Escribir la descripción del estado inicial y final.
b) Escribir los seis esquemas de acción.
c) Crea los ficheros mb_domain.txt y mb_problem.txt para el dominio y el problema, respectivamente. A continuación suminístraselos a JavaGP (una implementación del algoritmo GRAPHPLAN) y comprueba que se encuentra un plan correcto. Guarda la salida del programa en el archivo result_trace.txt
d) OPCIONAL: Supón que el mono quiere engañar a los científicos, que han ido a tomarse un café, cogiendo los plátanos, pero dejando la caja en su lugar original. Escribe esto como un objetivo genérico (es decir, sin suponer que la caja está necesariamente en C) en el lenguaje del cálculo de situaciones. ¿Puede resolver este objetivo un sistema de planificación clásico?
Si la respuesta es no, cambia el objetivo general a un objetivo más específico que
pueda ser resuelto por JavaGP.
Crea el archivo mb_problem2.txt con el problema. Ejecute el problema en JavaGP
con mb_domain.txt y mb_problem2.txt. Almacena la salida en result_trace2.txt.

mb_domain.txt
operator go(M,O,F)
pre: monkey(M),place(F), place(O), en(M,O), bajo(M) 
post: ~en(M,O), en(M, F)

operator push(M,O,F,C) 
pre: monkey(M), box(C), place(O), place(F), en(M,O), en(C,O), bajo(C), bajo(M)
post: ~en(M,O), ~en(C,O),en(C,F), en(M,F) 

operator climbUp(M, C, P) 
pre: monkey(M), box(C), place(P), en(M,P), en(C,P), bajo(C), bajo(M) 
post: ~bajo(M) 


operator grasp(M, B, P) 
pre: monkey(M), bananas(B), place(P), en(M,P), en(B,P), ~bajo(B), ~bajo(M) 
post: agarra(M, B), ~en(B, P)

operator unGrasp(M, B,P) 
pre: monkey(M), bananas(B), place(P), en(M,P), en(B,P), agarra(M,B) 
post: ~agarra(M, B), en(B, P)


operator climbDown(M,C,P) 
pre: monkey(M), box(C), place(P), en(M,P), en(C,P), bajo(C), ~bajo(M) 
post: bajo(M)


mb_problem.txt
start(
monkey(m), 
bananas(p), 
box(caja), 
place(a),
place(b),
place(c),
~agarra(m,p),
bajo(m), 
bajo(caja),
~bajo(p),
en(m,a),
en(p, b),
en(caja, c))

goal(
agarra(m,p)
)

mb_problem2.txt
start(
monkey(m),
bananas(p), 
box(caja),
place(a),
place(b),
place(c),
en(m,a),
~bajo(p),
~agarra(m,p),
bajo(m), 
bajo(caja),
en(p, b), 
en(caja, c)) 

goal(
agarra(m,p),
en(caja,c),
en(m, a)
)
 

Para ejecutar se debe abrir la consola de comandos y, estando en el directorio en el que se encuentran tanto javagp.jar como los archivos txt, poner: java -jar javagp.jar –d mb_domain.txt  -p mb_problem.txt

Así se obtiene la salida que nos piden en los ficheros result_trace.txt, pero eso ya os lo dejo a vosotros ^^

viernes, 6 de junio de 2014

Ejercicio Concurrencia con Semáforos en Java.

Os dejo un ejercicio de concurrencia en Java usando semáforos.


En una cadena de montaje existe un robot encargado de colocar productos de 3 tipos diferentes (1, 2 o 3) en la cadena de montaje. Otros robots, retiran los productos de la cadena de montaje para realizar su empaquetado, teniendo en cuenta que están especializados en un solo tipo de producto (1, 2 o 3), ignorando los que no son de su tipo. Finalmente, se quiere llevar un control del total de productos empaquetados (independientemente de su tipo).

Modelar utilizando semáforos el sistema descrito con las siguientes indicaciones:

  • Modelar cada robot como una hebra (1 colocador y 3 empaquetadores, uno para cada tipo de producto).
  • Los productos son colocados de uno en uno en la cadena, y solamente en posiciones libres (se puede considerar que en la cadena de montaje caben un máximo N de elementos). Si no hay posiciones libres el robot colocador tendrá que esperar hasta que algún producto sea retirado de la cadena.
  • Los robots empaquetadores se especializan en un tipo de producto (1, 2 o 3) en tiempo de inicialización.
  •  Los robots empaquetadores comprueban si hay algún elemento de su tipo en la cadena ignorando los productos que no sean de su tipo. Si hay algún producto de su tipo lo retiran de la cadena (sólo 1 producto cada vez) y la posición queda libre para colocar nuevos productos, en caso contrario se quedan a la espera de que haya nuevos productos.
  • Los robots empaquetadores de distinto tipo pueden funcionar a la vez.
  • Tanto el colocador como los empaquetadores nunca acaban.
  • Cada vez que un robot empaquetador procesa un producto, la cuenta total de productos empaquetados debe aumentar y mostrarse un mensaje por pantalla.

public class Robots {
 static int tam = 10;
 static int [] cadena = new int [tam];
 
 static Semaphore coloco=new Semaphore(tam, true);;
 static Semaphore [] puedoemp =  {new Semaphore(0), new Semaphore(0), new Semaphore(0)};
 static int nemp = 0; // necesita un mutex
 static Semaphore mutex = new Semaphore(1, true);
 static Random r = new Random();
 
 public static class Colocador extends Thread{
  public void run(){
   int i, n;
   while(true){
    try {
     n = r.nextInt(3)+1;
     System.out.println("Produciendo tipo "+ n);
     sleep(r.nextInt(1000));
     coloco.acquire();
     i=0;
     while(cadena[i] != 0) i++;
     //cuando encuentra un sitio vacío, sale del bucle y puede colocar
     cadena[i]  = n;
     System.out.println("Coloco un producto "+ n + " en la posición "+ i +"\n"+ Arrays.toString(cadena));
     puedoemp[n-1].release();

    } catch (InterruptedException e) {e.printStackTrace(); }
   }
  }
 }
 
 public static class Empaquetador extends Thread {
  private int tipo;
 
  public Empaquetador (int tipo){
   this.tipo = tipo;
 
  }
  public void run(){
   int i;
   while(true){
   try {
    puedoemp[tipo-1].acquire();
    
     i =0;
    while(cadena[i] != tipo) i++;
    sleep(r.nextInt(1000));
    System.out.println("Robot de tipo "+ tipo + " empaquetando el producto de la posición "+ i);
    cadena[i] = 0; //dejo el hueco
    coloco.release();
    sleep(2000);
    mutex.acquire();  //aumentar nemp
    nemp++;
    
    System.out.println("Aumenta el número de empaquetados: "+ nemp);
    
    mutex.release(); // 
    
    
   } catch (InterruptedException e) {e.printStackTrace();}
   }
  }
 }
 public static void main(String[] args){
  System.out.println("El tamaño de la cadena será de " + tam);
  Empaquetador  empaquetador1 = new Empaquetador(1);
  Empaquetador  empaquetador2 = new Empaquetador(2);
  Empaquetador  empaquetador3 = new Empaquetador(3);
  Colocador colocador = new Colocador();
  colocador.start();
  empaquetador1.start();
  empaquetador2.start();
  empaquetador3.start();
  
 }
}

.

miércoles, 4 de junio de 2014

PROYECTO Creación de un Chat. Parte II: Actualización de Datos desde PHP

Antes de nada os mando a la primera parte por si os la habeis perdido. En ella muestro como crear el Host Gratuito y la Base de Datos:

Parte 1: Creación de Base de Datos MySQL
Parte 3: Petición de datos desde PHP a MySQL

Bueno, pues hoy tocaremos algo de PHP así por encima. Tampoco es nada demasiado complejo e intentaré explicarlo bien. Si nunca habeis programado en PHP yo os recomendaría el notepad++ que es gratuito.

Antes de nada os explicaré un pequeño detalle que me ha tenido comiendome el coco un buen rato. En PHP las Strings se pueden definir con comillas dobles ("hola") o comillas simples('hola').
La diferencia es (si me equivoco avisadme ;) ) que lo que pongas en las comillas simples va tal cual, mientras que con las comillas dobles puedes meter etiquetas como <br> (un salto de línea en  html ) o \n (un salto de linea de String). Además muchas veces uso las dobles cuando necesito usar una string que tenga comillas simples dentro (para sentencias SQL).

Ahora sí, he aquí mi código:

<html>
 <?php 
 
// Creo las variables y las defino. No es necesario poner el tipo
$Nick    = 'yo';
  $ip     = $_SERVER['REMOTE_ADDR'];
  $server  = 'mysql.hostinger.es';
  $usuario = 'nombreDeUsuarioDeMiBD';
  $pass  = 'password';
  $BD  = 'NombreDeLaBD';
  
//Conecto con la BD
$link = mysql_connect($server, $usuario, $pass);
  mysql_select_db($BD, $link);

  //Creo las sentencias que vaya a utilizar
  $inser = "Insert Into Usuario Values ('".$nick."', '".$ip."')";
  $inser2 = "INSERT INTO Usuario (Nick, IP) VALUES ('".$nick."', '".$ip."') ON DUPLICATE KEY UPDATE IP= '".$ip."'";

  //Envio la sentencia
mysql_query($inser2, $link); 
  mysql_close($link); // Cerramos la conexion con la base de datos 
  
  
 ?>
</html>
NOTAS:
-Las variables llevan siempre el símbolo $ delante.
-Para concatenar Strings en PHP se usa el punto.
-Podeis ver como en $inser y $inser2 uso las comillas dobles porque los valores en SQL deben ir entre comilas simples.
-Como veis hay dos sentencias. La primera ($inser) es la que usaba, pero si un Nick ya existía no me lo reemplazaba, por lo que busqué como solución la segunda.
-La IP se calcula directamente y mete la que estamos usando en ese momento.

Este archivo deberéis guardarlo con nombre userReg.php (en realidad el nombre es irrelevante, pero es el que yo uso y así os evitais comederos de cabeza innecesarios :p).

Os acordais en la entrada anterior que marqué un cuadro llamado 'Administrador de Archivos'? Ahí subiremos el archivo que acabamos de crear. Para ejecutarlo nos vamos a nuestro navegador y escribimos la dirección de nuestro host (la que pusimos al crear el nuevo Hosting gratuito). Supongamos que era "pruebasdephp.hol.es". En este caso iriamos al navegador y pondríamos "pruebasdephp.hol.es/userReg.php".


Esto ejecutaría nuestro php (no os dije que la única forma de ejecutar un programa php era subiendolo a un servidor? :P) y en principio nos aparecería la página en blanco (pues no le hemos dicho que nos imprima nada) pero en nuestra base de datos si nos metemos debería estar el resultado.

En mi caso tengo más entradas, pero como tienen IP verdaderas no las voy a mostrar :P

Ahora bien, os voy a enseñar a pasar datos desde el exterior a PHP. Para ello cambiaremos la definición de Nick a la siguiente:
$Nick    = $_GET['nick'];

Resubimos el nuevo archivo modificado al servidor y ahora entraremos a la url de la siguiente forma:
"pruebasdephp.hol.es/userReg.php?nick=holaa" (sin las comillas).

Os explico, al decirle a $Nick que es $_GET['nick] le estamos diciendo que su resultado lo tomará de la url. Luego en la url ponemos una interrogación (esto le dice que van a aparecer las variables) y ponemos nick=holaa, ergo el valor de nick será 'holaa' y te guardará un usuario holaa con tu IP.
Si por algún caso no saliera prueba a borrar de la tabla el que creamos antes. Puede que tengas un conflicto al ser las dos IPs las mismas (Aunque en principio no debería de dar problemas).

Al fin acabo. Esta entrada es muy espesa, tiene mucho contenido y (si no sabes nada de PHP) vas a tener que leértela  un par de veces. Cualquier duda comentadla, estaré encantado de resolverlas.

Saludos y Gracias ;)