quinta-feira, 31 de maio de 2012

Delegates


Um delegate pode ser definido como um ponteiro para uma função. O delegate encapsula o endereço de uma função no seu código.

As funções associadas a eventos são um exemplo da utilização de delegates, quando o evento “acontece” é chamada a função para a qual o delegate aponta.

Os delegates podem agrupar funções que são chamadas em conjunto.


Para utilizarmos delegates começamos por definir um:
Private Delegate Sub MinhaFuncao()

Agora vamos criar a função que será associada ao delegate:
    Private SubEscreveDebug()
        Debug.Print("Olá mundo")
    End Sub

De seguida criamos uma variável do tupo do delegate e associamos à função:
     Dim funcDel As MinhaFuncao 

Depois associamos a variável à respectiva função:
     funcDel = New MinhaFuncao(AddressOf EscreveDebug)

Por fim executamos a função através do delegate:
     funcDel.Invoke()


Delegates Multicast
Os delegates multicast permitem criar uma sequência de funções a serem chamadas quando o delegate é invocado.

De momento não é possível definir a ordem em que são executadas somente que estas são executadas.

Para começar definimos mais uma função:
    Private SubEscreveDebug2()
        Debug.Print("Olá mundo, outra vez")
    End Sub

De seguida criamos outra variável para associar a esta função:
    Dim funcDel2 As MinhaFuncao

Associamos:
    funcDel2 = New MinhaFuncao(AddressOfEscreveDebug2)

Agora criamos um delegate multicast que vai invocar os outros dois:
    Dim funcDelTodas As [Delegate]

Para isso temos de associar este aos outros dois:
    funcDelTodas = MulticastDelegate.Combine(funcDel, funcDel2)

Por fim, executamos:
    funcDelTodas.DynamicInvoke(Nothing)

Com os delegates podemos criar diferentes versões de uma função e associar ao delegate a versão que pretendemos utilizar de acordo com o desenvolvimento da aplicação.

Os delegates também permitem passar uma função para outra como um parâmetro.

Por exemplo podemos ter uma função chamada operador que invoca um delegate passado por parâmetro a dois valores para executar uma operação.

Declaração da função delegate:
   Public Delegate Function Operador(ByVal x As Double, ByVal y As Double) As Double

Assim podemos definir a seguinte operação:
    FunctionSoma(ByVal x AsDouble, ByVal y As Double) As Double
        Return x + y
    End Function

E ainda outra assim:
    FunctionSubtrai(ByVal x AsDouble, ByVal y As Double) As Double
        Returnx - y
    End Function

Agora podemos criar a função calcular que recebe o delegate a chamar bem como os parâmetros a passar ao delegate:
    FunctionCalcular(ByVal op AsOperador, ByValx As Double, ByVal y As Double) As Double
        Dim devolver As Double

        devolver = op.Invoke(x, y) 'invoca a função associada

        Return devolver
    End Function

Para testar este conceito criamos um formulário assim:

Agora podemos chamar a função calcular a partir do clique no botão:
        Dim x, y As Integer

        x = Me.TextBox1.Text
        y = Me.TextBox2.Text

        Me.Label3.Text = Calcular(AddressOf Soma, x, y)

        Me.Label4.Text = Calcular(AddressOf Subtrai, x, y)


O projeto pode ser descarregado aqui.

sábado, 26 de maio de 2012

Hashtables - Tabelas de Dispersão

As hashtables são, nas linguagens de programação modernas, uma das estruturas mais úteis para armazenar grandes quantidades de informação.

As suas principais características são:
  • Não são ordenadas, ou seja um ciclo de pesquisa sequencial pode obter os dados por qualquer ordem;
  • São muito eficientes nas pesquisas, na maior parte das situações basta uma comparação para obter o valor a pesquisar, independentemente do número de elementos na tabela;
  • Internamente cada elemento representa uma lista ligada;
  • Ao inserir um elemento deve ser indicada uma chave da qual é calculado o código hash que servirá para indexar o elemento na tabela, muito importante, o código hash é obtido a partir da chave e não dos valores armazenados;
  • Caso existe um elemento com a mesma chave o novo elemento inserido substitui o antigo;
  • Para obter um elemento basta indicar a chave associada;
  • A principal desvantagem é o espaço em memória que a tabela necessita.

A plataforma .NET implementa dois tipos de hashtables:
  • Dictionary: para utilização genérica;
  • Hashtable: para utilizar referências a objetos;

Para aprendermos a utilizar as hashtables vamos criar um pequeno programa para guardar nomes e números de telefone.



Assim declaramos a nossa hashtable:
     Dictionary listaTelefonica<string,int> = new Dictionary();


Ao fazermos a declaração desta forma indicamos que vamos armazenar um inteiro e utilizar uma chave do tipo string.


Para adicionar um elemento basta escrever:
    listaTelefonica["joaquim"]=919191919;


A pesquisa é conseguida da seguinte forma:
    int numTelefone=listaTelefonica["joaquim"];


Neste caso se a chave indicada não existir é lançada uma excepção do tipo KeyNotFoundException.


Outra forma de pesquisar:
   int numTelefone;
   if(listaTelefonica.TryGetValue("joaquim",out numTelefone)
        Console.WriteLine("{0}",numTelefone);
   else 
        Console.WriteLine("Nome não foi encontrado!");

Para remover um elemento basta executar a linha:
    listaTelefonica.Remove("joaquim");

Listar toda a hastable:
    foreach (KeyValuePair elemento in listaTelefonica) 
         Console.WriteLine("{0} - {1}", elemento.Key, elemento.Value);


Para terminar temos a função clear que permite limpar a hashtable toda:
    listaTelefonica.Clear();


A principal vantagem da hashtable é a sua excepcional capacidade de pesquisar elementos sem tempos de espera, literalmente 0 (zero) milissegundos de espera, para provar esta potencialidade comparei os tempos de execução de uma hashtable com um vector de duas dimensões, uma tabela tradicional.




Como se pode ver tanto na inserção de dados como na pequisa a hashtable é mais rápida.
Enquanto que na tabela a pesquisa é linear e por isso depende da posição do elemento a encontrar na hashtable o tempo de pesquisa é sempre o mesmo.


O seguinte código corresponde ao programa que criado para executar este teste.
O projeto está aqui.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;


namespace tabelahash
{
    class Program
    {
        const int MAX = 10000000;


        static void Main(string[] args)
        {
            Dictionary<string, int> listaTelefonica = new Dictionary<string, int>();
            string[,] listaTelefonicas= new string[MAX,2];
            
            Console.WriteLine("Numero de elementos: {0}", MAX);
            var sw = new Stopwatch();
            //preencher a tabela de hash
            sw.Start();
            for (int i = 0; i < MAX; i++)
            {
                listaTelefonica["joaquim"+i]=i;
            }
            sw.Stop();
            Console.WriteLine("Tempo necessário para preencher a hashtable {0} milissegundos", sw.ElapsedMilliseconds);
            //preencher a tabela de strings
            sw.Reset();
            sw.Start();
            for (int i = 0; i < MAX; i++)
            {
                listaTelefonicas[i, 0] = "joaquim" + i;
                listaTelefonicas[i, 1] = i.ToString();
            }
            sw.Stop();
            Console.WriteLine("Tempo necessário para preencher a hashtable {0} milissegundos", sw.ElapsedMilliseconds);


            string nome;
            Console.Write("Nome a pesquisar:");
            nome=Console.ReadLine();
            sw.Reset();
            sw.Start();
            try{
                int telefone = listaTelefonica[nome];
                sw.Stop();
                Console.WriteLine("{0} - {1}", nome, telefone);
            }
            catch(KeyNotFoundException erro){
                Console.WriteLine("Elemento não encontrado: {0}",erro.Message);
            }
            Console.WriteLine("Tempo necessário para pesquisar a hashtable {0} milissegundos", sw.ElapsedMilliseconds);


            Console.ReadLine();
            sw.Reset();
            sw.Start();
            for (int i = 0; i < MAX; i++)
            {
                if (listaTelefonicas[i,0] == nome)
                {
                    sw.Stop();
                    Console.WriteLine("{0} - {1}", nome, listaTelefonicas[i, 1]);
                    break;
                }
            }
            Console.WriteLine("Tempo necessário para pesquisar a hashtable {0} milissegundos", sw.ElapsedMilliseconds);


            Console.ReadLine();
                //listar todos
            sw.Reset();
            sw.Start();
            foreach (KeyValuePair<string, int> elemento in listaTelefonica)
                Console.WriteLine("{0} - {1}", elemento.Key, elemento.Value);
            sw.Stop();
            Console.WriteLine("Tempo necessário para listar a hashtable {0} milissegundos", sw.ElapsedMilliseconds);


            Console.Read();
        }
    }
}

sexta-feira, 11 de maio de 2012

PONG em Flash AS3.0

Mais um pequeno jogo para demonstrar algumas das funcionalidades do AS3.0.

Para este exemplo vamos implementar uma versão do Pong.




Para este Pong vamos criar a possibilidade de acelerar a bola com a raqueta e, para tornar o jogo mais difícil, quando se atingir uma determinada pontuação fazemos aparecer uma parede no meio do campo de jogo.

O código é muito parecido com o jogo do post anterior, mas um pouco mais complicado.

Para controlar a nossa raqueta utilizamos a seguinte função:


function teclado(e:KeyboardEvent):void{
dir_j1=0;
if (e.keyCode == Keyboard.UP){
if(jogador1.y>0) jogador1.y -=5;
dir_j1=-5;
}
if (e.keyCode == Keyboard.DOWN){
if(jogador1.y<370) jogador1.y +=5;
dir_j1=5;
}

}
Agora está mais simples pois só percorremos as linhas, ou seja, a coluna nunca muda.

A raqueta que é controlada pelo computador depende do seguinte código:



function movepc():void {

if (bola.y>jogador2.y) jogador2.y = jogador2.y + velocidade_y;
if (bola.y<jogador2.y) jogador2.y = jogador2.y - velocidade_y;

}
Como é fácil de perceber sempre que a bola está acima da posição da raqueta esta move para cima, quando a bola está mais para baixo a raqueta move-se para baixo.

Para mostrar a pontuação utilizamos dois textfields, assim:


var lb_pontos1:TextField = new TextField();
lb_pontos1.x=400;
addChild(lb_pontos1);
lb_pontos1.text = pontos1.toString();
var lb_pontos2:TextField = new TextField();
lb_pontos2.x=20;
addChild(lb_pontos2);
lb_pontos2.text=pontos2.toString();

Por fim a parte mais complicada o código que controla a bola:


function movebola():void {
if(v_bolax>0){
if(v_bolax>v_bolamax){ v_bolax--; v_bolay--;}
}else{
if(v_bolax*(-1)>v_bolamax) {v_bolax++; v_bolay++;}
}
if(v_bolay>0){
if(v_bolay>v_bolamax){ v_bolax--; v_bolay--;}
}else{
if(v_bolay*(-1)>v_bolamax){ v_bolax++; v_bolay++;}
}
bola.x = bola.x + v_bolax;
bola.y = bola.y + v_bolay;
/*verifica limites do stage*/
if (bola.y<=0) v_bolay =5;// v_bolay * (-1);
if (bola.y>=400) v_bolay =-5;// v_bolay * (-1);
/*Saiu do palco*/
if (bola.x<=0){
pontos1 = pontos1 + 1;
lb_pontos1.text = pontos1.toString();
bola.x=200;
if (v_bolax>0)
v_bolax=v_bolamax;
else
v_bolax=v_bolamax*(-1);
if (v_bolay>0)
v_bolax=v_bolamax;
else
v_bolay=v_bolamax*(-1);
if(pontos1>5 && parede.alpha<=100) parede.alpha=100;
}
if (bola.x>=550){
pontos2 = pontos2 + 1;
lb_pontos2.text=pontos2.toString();
bola.x=200;
if (v_bolax>0)
v_bolax=v_bolamax;
else
v_bolax=v_bolamax*(-1);
if (v_bolay>0)
v_bolax=v_bolamax;
else
v_bolay=v_bolamax*(-1);
if(pontos2>5 && parede.alpha<=100) parede.alpha=100;
}

/*testar colisão */
if(bola.hitTestObject(jogador1)){
if (v_bolax>0) v_bolax = v_bolax * (-1);
if(v_bolay>0 && dir_j1>0){
v_bolax=-20;
v_bolay=20;
}
if(v_bolay<0 && dir_j1<0){
v_bolax=-20;
v_bolay=-20;
}
bola.x = bola.x + v_bolax;

}
if(bola.hitTestObject(jogador2)){
if (v_bolax<0) v_bolax = v_bolax * (-1);
bola.x = bola.x + v_bolax;
}
if(parede.alpha>0){
if(bola.hitTestObject(parede)){
v_bolax = v_bolax * (-1);
bola.x = bola.x + v_bolax;
}
}
}

Então vamos estudar esta função por partes.

A primeira parte da função serve para ver se a bola está acelerada, se sim, abranda:

if(v_bolax>0){
if(v_bolax>v_bolamax){ v_bolax--; v_bolay--;}
}else{
if(v_bolax*(-1)>v_bolamax) {v_bolax++; v_bolay++;}
}
if(v_bolay>0){
if(v_bolay>v_bolamax){ v_bolax--; v_bolay--;}
}else{
if(v_bolay*(-1)>v_bolamax){ v_bolax++; v_bolay++;}
}

Depois vem o código que atualiza a posição da bola e verifica se sai do palco para fazer ricochete:

bola.x = bola.x + v_bolax;
bola.y = bola.y + v_bolay;
/*verifica limites do stage*/
if (bola.y<=0) v_bolay =5;// v_bolay * (-1);
if (bola.y>=400) v_bolay =-5;// v_bolay * (-1);

O código que se segue serve para verificar o limite direito e esquerdo do palco para atribuir pontos aos jogadores:

if (bola.x<=0){
pontos1 = pontos1 + 1;
lb_pontos1.text = pontos1.toString();
bola.x=200;
if (v_bolax>0)
v_bolax=v_bolamax;
else
v_bolax=v_bolamax*(-1);
if (v_bolay>0)
v_bolax=v_bolamax;
else
v_bolay=v_bolamax*(-1);
if(pontos1>5 && parede.alpha<=100) parede.alpha=100;
}
if (bola.x>=550){
pontos2 = pontos2 + 1;
lb_pontos2.text=pontos2.toString();
bola.x=200;
if (v_bolax>0)
v_bolax=v_bolamax;
else
v_bolax=v_bolamax*(-1);
if (v_bolay>0)
v_bolax=v_bolamax;
else
v_bolay=v_bolamax*(-1);
if(pontos2>5 && parede.alpha<=100) parede.alpha=100;
}
Esta parte do código faz aparecer a parede quando algum jogador ultrapassa os 5 pontos.

Por fim verificamos a colisão com as raquetas e com a parede se ela estiver visível:

/*testar colisão */
if(bola.hitTestObject(jogador1)){
if (v_bolax>0) v_bolax = v_bolax * (-1);
if(v_bolay>0 && dir_j1>0){
v_bolax=-20;
v_bolay=20;
}
if(v_bolay<0 && dir_j1<0){
v_bolax=-20;
v_bolay=-20;
}
bola.x = bola.x + v_bolax;

}
if(bola.hitTestObject(jogador2)){
if (v_bolax<0) v_bolax = v_bolax * (-1);
bola.x = bola.x + v_bolax;
}
if(parede.alpha>0){
if(bola.hitTestObject(parede)){
v_bolax = v_bolax * (-1);
bola.x = bola.x + v_bolax;
}
}


O ficheiro fonte está aqui.
E o jogo pode ser jogado aqui.


Partilhar no Facebook




segunda-feira, 7 de maio de 2012

PHP e base de dados MS Access

O PHP tem uma ligação natural ao MySQL no entanto é possível utilizar outros formatos de base de dados, nomeadamente o MS Access.
A via de ligação será o ODBC - Open Data Base Connectivity.
No Windows através do Painel de Controlo acedemos às Ferramentas Administrativas e escolhemos Origem de Dados, no Windows 7 a opção está em Sistema e Segurança e depois Ferramentas Administrativas.



Um problema que pode surgir nesta ferramenta é não existirem drivers (controladores) para o MS Access, mesmo tendo o MS Access instalado! Isto deve-se ao facto de o Windows ser 64bits e a aplicação e os respectivos drivers serem 32 bits, para resolver este problema devemos correr a mesma aplicação mas a versão de 32 bits que está em
Agora que temos a versão certa da ferramenta devemos selecionar o separador Controladores para verificar se temos ou não o controlador do MS Access
Confirmada que está a existência do driver vamos então criar uma ligação à nossa base de dados para que o PHP a possa manipular, assim vamos ao separador DSN de sistema e escolhemos o botão adicionar
De seguida escolhemos o formato da base de dados
E por fim indicamos o ficheiro da base de dados e um nome que posteriormente vamos utilizar no código PHP
Neste formulário preenchemos:
Nome da origem de dados com o nome que vamos utilizar no código PHP, Descrição com uma breve descrição que permita identificar a base de dados, depois escolhemos o botão seleccionar para procurar o ficheiro da base de dados propriamente dita.

Feito isto podemos então passar ao código PHP.

Para criar uma ligação à base de dados escrevemos:

$conn=odbc_connect("nome","utilizador","palavra_passe");

Esta instrução cria a ligação à base de dados, os parâmetros são nome que deve ser igual ao nome da origem de dados, não o nome da base de dados, utilizador e palavra_passe são as credenciais necessárias para aceder à base de dados, se não existirem não se preenche.

Para, por exemplo, listar os dados de uma tabela basta fazer:


if($conn){
$sql="Select * from clientes";
$linha=odbc_exec($conn,$sql);
while(odbc_fetch_row($linha)){
$ncliente=odbc_result($linha,1);
$nome=odbc_result($linha,2);
print('NºCliente:'.$ncliente.'</p>Nome:'.$nome.'</p>');
}
}

A instrução odbc_exec executa um comando SQL na base de dados, seja uma consulta ou uma alteração dos dados (inserção, actualização ou remoção).
A função odbc_fetch_row permite controlar o ciclo e recolher um registo.
Para referenciar os campos utilizamos a função odbc_result que recebe a variável associada ao registo e o número do campo a referenciar.

Partilhar no Facebook

terça-feira, 1 de maio de 2012

Um jogo simples em Flash e AS3.0

Agora para uma coisa totalmente diferente, um pouco de Flash e AS3.0.
Este jogo muito simples tem como principal objetivo introduzir um pouco de código que permite gerir eventos do teclado, controlar imagens (sprites) totalmente por código, definir eventos associados a temporizadores (timers) e ainda gerar números aleatoriamente.
O resultado final será qualquer coisa como isto:


Então vamos a isso...
Para começar vamos criar duas camadas (layers) na linha de tempo (Timeline) como se apresenta a seguir:

Uma camada vai servir para inserir os objetos, neste caso três Clipes de Filme (Movieclips): a nossa nave, o inimigo e um projetil.

Na outra camada vamos inserir o código que vamos utilizar.

O movieclip que representa o inimigo vai ter o nome inimigo, o nosso será a nave e o projetil chamasse bala.
O movieclip bala vai ficar invisível inicialmente, para isso utilizamos a propriedade alpha e definimos em 0.

Começamos por associar uma função ao que teclamos, para isso fazemos:
     stage.addEventListener(KeyboardEvent.KEY_DOWN,teclado);

Esta linha associa uma função chamada teclado aos eventos gerados pelas teclas que vamos premindo.
A função fica com este aspeto:

function teclado(e:KeyboardEvent):void{
if (e.keyCode == Keyboard.LEFT){
if(nave.x>30) nave.x -=5;
}
if (e.keyCode == Keyboard.RIGHT){
if(nave.x<500) nave.x +=5;
}
if (e.keyCode == Keyboard.UP){
if(nave.y>200) nave.y -=5;
}
if (e.keyCode == Keyboard.DOWN){
if(nave.y<370) nave.y +=5;
}
if (e.keyCode == Keyboard.SPACE){
if(estadobala==1) return;
estadobala=1;
bala.alpha=100;
bala.x=nave.x+nave.width/2+bala.width/2;
bala.y=nave.y;
}
}
Basicamente controlamos as teclas CIMA, BAIXO, ESQUERDA e DIREITA para movimentar a nave com base nas propriedades x e y que indicam a localização da nave no palco (stage).

As condições dentro de cada if evitam que a nave saia do stage.

Por fim controlamos a BARRA DE ESPAÇO para fazer disparar o projetil, para isso temos uma variável que indica o estado da bala, isto porque só vamos deixar disparar uma de cada vez, assim se a bala está a percorrer o palco não dispara outra, se dispara a bala então coloca o alpha da bala a 100 (torna a bala visível) e posicionamos a bala à frente da nave para esta iniciar o seu trajeto.

A variável estadobala é definida assim:

var estadobala:int=0;

Vamos ter ainda outras variáveis:
var velocidade_inimigo:int=10;
var velocidade_bala:int=15;
var pontuacao:int = 0;

A primeira e a segunda indicam a velocidade a que se movimentam o inimigo e a bala, respetivamente e por fim a pontuação do jogador.

Agora os eventos que controlam o inimigo e a bala, para isso temos de iniciar dois temporizadores para essa tarefa:
var timer_inimigo:uint =setInterval(moveinimigo,100);
var timer_bala:uint =setInterval(movebala,100);

As variáveis definidas permitem controlar os eventos, para este exemplo nãos as vamos utilizar e podiam não existir.

Por fim a 'inteligência' do inimigo:
function moveinimigo():void {
inimigo.x+=velocidade_inimigo;
if(inimigo.x>520 && velocidade_inimigo>0){
velocidade_inimigo*=-1;
inimigo.x+=velocidade_inimigo;
}
if(inimigo.x<30 && velocidade_inimigo<0){
velocidade_inimigo*=-1;
inimigo.x+=velocidade_inimigo;
}
}

Que se resume a mover de um lado para o outro, não saindo do palco.

E ainda a bala:
function movebala():void {
if(estadobala==0) return;
bala.y-=velocidade_bala;
/*testar colisão da bala com o inimigo*/
if(bala.hitTestObject(inimigo)){
estadobala=0;
inimigo.x=sortear(5,500);
bala.alpha=0;
pontuacao+=10;
label1=pontuacao.toString();
label.text = String("Pontuação: " + label1);
}
//A bala saiu do palco?
if(bala.y<0){
estadobala=0;
bala.alpha=0;
}
}

Antes que me esqueça o código que faz aparecer a pontuação:
var label:TextField = new TextField();
var label1:String = pontuacao.toString();
addChild(label);
label.text = String("Pontuação: " + label1);

O código que atualiza a pontuação está na função que movimenta a bala.

Por fim a função que sorteia a coluna onde o inimigo aparece quando lhe acertamos:
function sortear(low:Number=0, high:Number=1):Number
{
  return Math.floor(Math.random() * (1+high-low)) + low;
}