Tuesday, July 7, 2015

Dominância em grafos

Aproveitando a tarefa da matéria de estruturas de dados, o próximo post será voltado para o conceito de dominância de grafos. A atividade envolvia encontrar número mínimo de aldeias de emergência que seriam necessárias para cobrir todas as aldeias em situação de emergência. Na simplificação assumida, todas as aldeais que se conectam tem arestas com peso unitário e a distância de auxílio é apenas uma aresta. Assim, a resposta ideal então é o menor número possível de se colocar aldeias tal que todas aldeias são ou aldeias-resposta (prestadoras de auxílio) ou vizinhas a uma dessas.

Depois de pesquisar esse problema NP-Completo (que, portanto, não tem uma solução eficiente conhecida), chegamos a um algorítmo "ganansioso", que não promete a melhor resposta possível, mas é de O(n^2*m), onde n é o número de vértices e m, o número de arestas.

Segue o código de tal algorítmo, comentado.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package greedyDSPcustom;


/* Esse código procura o vértice com maior número de cidades vizinhas sem auxílio e o torna em ponto de emergência
 * O algoritmo continua criando pontos de emergência até não existirem mais cidades sem auxílio
 */
 


public class algorithm {

 
 public static int[][] g = {
  {0, 1, 0, 0, 0, 0, 0, 0, 0},
  {1, 0, 1, 1, 0, 0, 0, 1, 0},
  {0, 1, 0, 1, 0, 1, 1, 0, 0},
  {0, 1, 1, 0, 1, 0, 0, 0, 1},
  {0, 0, 0, 1, 0, 1, 0, 0, 0},
  {0, 0, 1, 0, 1, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0},
  {0, 1, 0, 0, 0, 0, 0, 0, 1},
  {0, 0, 0, 1, 0, 0, 0, 1, 0},};
 
 public static int[] state = {0,0,0,0,0,0,0,0,0};
 
 public static void main(String[] args) {
  int vertexMaxOrder = -1;
  while(!done())
  {
   vertexMaxOrder = maxOrder();
   state[vertexMaxOrder] = 2; //É ponto de emergência! Recebe auxílio por ser ponto de emergência
   for(int i=0; i<g[vertexMaxOrder].length; i++)
   {
    if(g[vertexMaxOrder][i] == 1 && state[i]==0)
     state[i] = 1;//É vizinho a um ponto de emergência, portanto recebe auxílio
   }
  }
  
  for(int i=0; i<state.length;i++)
   if(state[i]==2)
    System.out.println(i+1);

 }
 
 //Retorna o número de vizinhos sem auxílio para o vértice pedido
 public static int getOrder(int vertex)
 {
  int counter=0;
  for(int i=0; i<g[vertex].length;i++)
   if(g[vertex][i]==1 && state[i] == 0)
    counter++;
  return counter;
 }
 
 //Retorna o maior getOrder(vertex) dentre todos os vértices
 public static int maxOrder()
 { 
  int max=-1;
  int vertex=-1;
  for(int i=0; i<g[0].length;i++)
  {
   if(getOrder(i)>max)
   {
    max = getOrder(i);
    vertex = i;
   }
  }
  return vertex;
 }
 
 //Retorna se o algoritmo deve acabar. A condição é não existir mais vértices sem auxílio (isto é, não serem pontos de emergência NEM vizinhos a um ponto de emergência)
 public static boolean done()
 {
  boolean done = true;
  for(int i=0; i<state.length;i++)
   if(state[i] == 0)
    done = false;
  return done;
 }

}

O trabalho completo pode ser visto em https://goo.gl/Bg3HQf, inclusive com um método de força bruta desenvolvido pelo genial aluno Matheus Mattos Moller.

Monday, June 22, 2015

Gamification e Game Design

Recentemente foi concluída a Competição Interna de Games no ITA, em que calouros da iniciativa ITABits, de desenvolvimento de software, montam grupos e fazem o melhor jogo possível em aproximadamente um mês de desenvolvimento. Sendo vice-presidente da iniciativa, estava empolgado pelos jogos a serem apresentados e, apesar de satisfeito com a qualidade técnica (bem polidos, completos e com mecânicas interessantes), não pude deixar de notar como muitos pecaram em conceitos básicos de game design.

Para os que não conhecem, game design são as regras gerais de como um jogo deve ser feito a fim de ser o mais agradável possível ao jogador. Game design dá um guia geral de como estruturar seu programa de tal forma a torná-lo mais intuitivo, simples e divertido. Como se trata da relação de um desenvolvedor com um usuário, acho que é no mínimo interessante se ter uma noção básica de game design mesmo não sendo um programador de jogos, pois muitos do conceitos se aplicam também à outros projetos de desenvolvimento de software.

Percebendo essa falha nos jogos do CIG, me candidatei a dar aulas de Game Design básico para os calouros da T20 e quaisquer veteranos interessados. Essa plataforma seria excelente para marcar o progresso enquanto eu preparo as aulas e adquiro mais conhecimento sobre o assunto.

Em outras notícias, vou começar logo depois das férias uma Iniciação Científica em Gamification (uso de mecânicas de jogos e competitividade, como leaderboards e achievements, para incentivar usuários de software que não são jogos). Vou trabalhar com a Esfinge Framework em Java, e também pretendo comentar sobre o que aprendo nessa Iniciação Científica intrigante.

Até lá!

Saturday, May 2, 2015

O problema do apostador - Uma aplicação

Como foi introduzido no post passado, hoje vamos falar de uma aplicação prática do problema do apostador. O puzzle do Facebook para o famoso hackathon anual (https://www.facebook.com/events/828648753872660/) finalmente foi encerrado e podemos divulgar ideias e métodos. A questão era a seguinte:

Temos um vetor "arr" com n inteiros, sendo cada elemento 1 ou 0. Queremos achar as posições L e R tal que, invertendo todos os elementos de índice L a R, inclusive, temos o maior número possível de uns. A resposta para o problema deve ser um inteiro, o número de uns total do vetor depois dos elementos entre L e R serem invertidos.

Lembrando que "inverter" um inteiro nesse vetor significa transformar zeros em uns, e uns em zeros.

Para resolver esse problema, voltamos às técnicas do post anterior. Podemos variar L e R para todos os valores possíveis e armazenar o número de uns antes de L e depois de R, e o número de zeros entre L e R, somando os dois. Depois vemos para que valores de L e R temos a soma máxima e, pronto, temos nossa resposta!

Infelizmente esse método é extremamente ineficiente, e portanto nem vou botar a ideia em código. Fica muito evidente os problemas de tempo quando se fala em vetores com um número grande de elementos.

Portanto, vamos ao segundo método. Foi minha primeira ideia para resolver o puzzle: Criar uma lista encadeada de ilhas e unir os elementos que valem a pena. Segue o código que fiz, comentado.



  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
import java.util.ArrayList;
import java.util.Scanner;


public class island {

 int start; //indice do começo da ilha
 int finish; //indice do fim da ilha
 int size; //lucro da ilha, quantos zeros tem a mais que 1
 int type; //0 ou 1
 
 public static ArrayList<island> verifyWorth(ArrayList<island> list)
 {
  boolean done = false; 
  while(!done) //quando nenhuma modificacao for feita, encerramos
  {
   done = true;
   for(int i=0; i<list.size()-2; i++)
   {
    if(list.get(i).type == 0 && list.get(i+2).size > list.get(i+1).size)
    {
     done = false;
     list.get(i).finish = list.get(i+2).finish;
     list.get(i).size += (list.get(i+2).size - list.get(i+1).size);
     list.get(i).mixed = true;
     list.remove(i+1);
     list.remove(i+1);
     i--; //como estamos modificando os elementos da lista, precisamos modificar o i adequadamente
    }
   }
  }
  return list;
 }
 
 public static ArrayList<island> buildList(int[] raw) //constroi a lista a partir do vetor dado
 {
  ArrayList<island> list = new ArrayList<island>();
  island temp = new island();
  int start;
  start = 0;
  
  for(int i=0; i<raw.length-1; i++)
  {
   if(raw[i]!=(raw[i+1]))
   {
    temp.start = start;
    temp.finish = i;
    temp.type = raw[start];
    start = i+1;
    temp.size = 1 + temp.finish - temp.start;
    list.add(temp);
    temp = new island(); //precisamos reiniciar essa ilha para ela não modificar um elemento já adicionado
   }
  }

  temp.start = start; //por causa das restrições do for, o último elemento tem que ser tratado separadamente
  temp.finish = raw.length-1;
  temp.type = raw[start];
  temp.size = 1 + temp.finish - temp.start;
  list.add(temp);
  temp = new island(); 
  
  return list;
 }
 
 public static island findMax(ArrayList<island> list) //retorna o máximo da lista já reduzida
 {
  island max;
  if(list.get(0).type == 0)
   max = list.get(0);
  else
   max = list.get(1);
  for(int i=0; i<list.size(); i++)
  {
   if(list.get(i).type == 0 && max.size < list.get(i).size)
    max = list.get(i);
  }
  return max;
 }
 
 public static int Result(int[] raw)
 {
  ArrayList<island> list = new ArrayList<island>();
  island max;
  int counter=0;
  
  list = buildList(raw); //constrói o ArrayList
  list = verifyWorth(list); //Reduz
  max = findMax(list); //Retorna a ilha máxima
  for(int i=0; i<raw.length(); i++) //conta quantos uns temos
  {
   if((i<max.start || i>max.finish) && raw[i] == '1')
    counter++;
   else if((i>=max.start && i<=max.finish) && raw[i] == '0')
    counter++;
  }
  return counter;
 }

}


Esse código é funcional, porém bastante longo e complexo. Também não é muito eficiente em questão de tempo de execução. Podemos fazer melhor.

Chegamos então, finalmente, na última ideia. Segue o código, comentado.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void Solution(int[] arr)
{
 int ones=0; //quantos uns tem na string, ao todo, sem inversão
 int score=0; //lucro do elemento sendo analisado
 int maxscore=0; //lucro maximo encontrado ate o momento
  
 for(int i=0; i<arr.length;i++) //percorremos o vetor
 {
  if(arr[i]==0) //se o dia tiver lucro
  {
   score++; //incremente o lucro atual
   if(score>maxscore) //se maior que o lucro máximo, temos um novo máximo
    maxscore = score;
  }
  if(arr[i]==1) //se for prejuízo
  {
   score--; //decremente o lucro atual
   ones++; //conte quantos uns existem no vetor
   if(score<0) //se o lucro for negativo, nao compensa começar o dia até hoje
    score = 0; //L se torna esse
  }
 }
 
 System.out.println(ones+maxscore); //maxscore sao todos os zeros trocados MENOS os uns trocados. Entao
 //para a resposta temos o numero de uns total, mais o numero de zeros entre L e R, MENOS o numero de uns entre L e R. 
 return;
}

E pronto! Temos a resposta em pouquíssimas linhas, em uma única função e em ordem n, não sendo problemática para vetores grandes.

Quaisquer dúvidas, estou à disposição.

Até depois!

Wednesday, April 29, 2015

O problema do apostador

Esse post será puramente teórico, por se tratar de um assunto de uma determinada competição que só se encerra dia primeiro de maio. O código da solução do problema será postado assim que que a competição se encerrar, para não garantir a vantagem de ninguém que, por um acaso, leia o blog.

Vamos falar do problema do apostador. A situação é a seguinte: Temos um apostador que, de alguma forma, sabe exatamente quanto ele ganharia ou perderia no cassino em cada dia. No entanto, ele será banido assim que coletar seus lucros, podendo ficar dentro do cassino quantos dias quiser.

Deseja-se maximizar o lucro nessa situação. Mas como fazer isso de forma que não se precise de muito poder computacional? Poderia-se testar todas as possibilidades: percorrer o vetor que armazena os lucros e perdas com dois vetores (um representando o dia de chegada e um o dia de saida) de tal forma que o período com lucro máximo seja armazenado. Porém essa ideia da forma bruta é da ordem de n! operações, o que dificulta muito quando tem-se um número grande de dias.

Outra ideia seria utilizando uma noção de "ilhas". Contrói-se um ArrayList (ver post anterior) que contém um elemento para cada sequência de dias em que se lucra ou se perde dinheiro. Em seguida, percorre-se a lista fazendo diversos merges em três elementos de cada vez, sempre que compensar (ou seja, sempre que o elemento n+2, que é uma sequência de lucros, tiver valor maior que o elemento n+1, prejuízo, pode-se unir os elementos n, n+1 e n+2). Assim, teremos no final apenas alguns elementos isolados e o maior deles contém o máximo lucro. Porém esse algorítmo é O(n^2). Pode-se melhorar.

A solução final envolve a dependência com o dia anterior. Pode-se dizer que o lucro máximo do dia n será o lucro máximo do dia n-1 mais o lucro (ou prejuízo) do dia n. Se esse valor acumulado em algum momento for negativo, não compensa entrar no cassino até esse dia, e pode-se começar do lucro do zero novamente. Assim é possível percorrer o vetor uma única vez e saber qual o valor máximo de lucro, além do período em que se encontra, que é do dia em que se tem lucro máximo até o primeiro zero anterior a esse dia.

Uma aplicação será mostrada no próxmo post. Até lá!

Sunday, March 29, 2015

ArrayLists e Serialização

Devido à um projeto externo, tive que trabalhar bastante com estruturas de dado e maneiras de armazená-los em formato de arquivo. Esse projeto envolvia o armazenamento e procura de hyperlinks (cada um deles com um nome, um comentário, uma metatag, uma data de criação e uma possível data de modificação).

Para facilitar a procura pelo database usando qualquer um desses critérios como parâmetro, a estrutura de dados necessariamente teria que ser algo simples de se percorrer e retornar os resultados positivos, por isso resolvi que um ArrayList era a melhor solução.

Um ArrayList funciona como é esperado: É uma sequência de objetos (no meu caso, hyperlink, uma estrutura formada por 5 strings, nome, comentário, etc), entre elas tendo um ponteiro apontando para o próximo objeto. O java já tem uma implementação boa, com diversas funções para manipular sua lista de dados, como Add, Insert e Remove.


No entanto, como se trata de uma database de hyperlinks, não se pode perder o conteúdo quando se fecha o programa. Se ainda estivesse trabalhando com C, seria necessário implementar uma lógica para armazenar toda a informação em plain-text em um arquivo de texto e por fim gerar uma ArrayList à partir de qualquer arquivo de texto dentro desses parâmetros que foram definidos.

Felizmente, existe em java um método chamado serialização. Ele basicamente faz uma cópia do estrutura serializada em arquivo binário, e permite a deserialização (conversão do arquivo binário em estrutura de dado) sem maiores complicações.


Se o assunto continuar pertinente, entrarei em detalhes sobre a implementação da serialização e funções mais avançadas de ArrayList no próximo post. Até lá!

Sunday, March 22, 2015

Superclasses e Subclasses

No último projeto nós trabalhos com JFrame e interfaces gráficas, bem como definição e chamada de funções. Nessa semana o foco foi em classes, subclasses e hereditariedade. Em termos simples, na linguagem java é possível criar objetos segundo uma classe, assim tendo todas as propriedades e funções inerentes a essa classe. As coisas começam a ficar interessantes quando se falam de subclasses e superclasses. Funciona assim: Subclasses necessariamente possuem uma superclasse, podendo herdar propriedades e funções da classe-pai. Dessa forma, é possível ter várias classes com propriedades diferentes, porém herdando uma ou mais atributos de uma superclasse em comum.

Para se mostrar a definição da subclasse, usa-se a sintaxe extends, e.g. “class Circle extends Shape”

O projeto dessa semana foca justamente nessa habilidade de herança. Foi construída uma superclasse Shape com os atributos aName e as funções getName e calculateArea (que, apenas em Shapes, retorna zero). Então fez-se as subclasses Circle, Square e Triangle, herdando todas as propriedades e funções de Shape, porém cada uma com os atributos específicos de sua forma geométrica, bem como uma redefinição da função calculateArea. Para isso, basta programar a função calculateArea dentro de cada subclasse, utilizando-se a extensão super.calculateArea caso queira-se chamar a função da superclasse Shape.

A redefinição foi feita para se poder chamar a função de todas as subclasses dentro de um único for, permitindo assim a fácil edição do funcionamento do programa. É desnecessário em uma aplicação tão simples, mas é análogo ao processo que permitiria a redefinição em programas muito maiores e mais complexos.

Segue agora o código completo, comentado.

1:  public class Shape {  
2:  private String name;  
3:  Shape(String aName) {name=aName;} //propriedade do nome, inerente à todas as subclasses  
4:  public String getName() {return name;}  
5:  public float calculateArea() {return 0.0f;} //percebe-se aqui o funcionamento da redefinição de funções nas subclasses  
6:  public static void main (String argv[])  
7:  {  
8:   Circle C = new Circle("Circle C"); //Cria os objetos  
9:   Square S = new Square("Square S");  
10:   Triangle T = new Triangle("Triangle T");  
11:   Shape shapeArray[] = {C,S, T};  
12:   //aqui pode-se rodar funções completamente diferentes sob um mesmo nome (calculateArea) pois cada uma é chamada enquanto dentro do escopo de uma subclasse específica  
13:   for (int i=0; i System.out.println("The area of " + shapeArray[i].getName() + " is " + shapeArray[i].calculateArea() + " sq. cm.\n");  
14:   }  
15:  }  
16:  }  
17:  class Circle extends Shape {  
18:   private int radius;  
19:   Circle(String aName){  
20:   super(aName); //Super é chamado para usar a propriedade do pai  
21:   radius = 3;  
22:   }  
23:   public float calculateArea(){  
24:   float area;  
25:   area = (float) (3.14*radius*radius);  
26:   return area;  
27:   }  
28:  }  
29:  class Square extends Shape {  
30:   private int side;  
31:   Square(String aName){  
32:   super(aName);  
33:   side = 3;  
34:   }  
35:   public float calculateArea(){  
36:   int area;  
37:   area = side*side;  
38:   return area;  
39:   }  
40:   }  
41:  class Triangle extends Shape {  
42:   private int side1, side2;  
43:   private double angle;  
44:   Triangle(String aName){  
45:   super(aName);  
46:   side1 = 2;  
47:   side2 = 3;  
48:   angle = 30.0;  
49:   }  
50:   public float calculateArea() {  
51:   float area;  
52:   area = (float)(0.5*side1*side2*Math.sin(Math.toRadians(angle)));  
53:   return area;  
54:   }  
55:  }  


Depois de executado, tem-se o output:

The area of Circle C is 28.26 sq. cm.

The area of Square S is 9.0 sq. cm.

The area of Triangle T is 1.5 sq. cm.

Até o próximo projeto!

Sunday, March 15, 2015

Continuação: Calculadora com interface gráfica!

Continuando com a atividade de anteontem, hoje implementei os métodos para as funções da calculadora, bem como o funcionamento dos botões. Alguns problemas ainda podem ser vistos por causa da transformação entre string e double da calculadora, por isso vou listar alguns detalhes que poderiam ser melhorados:
  • A função +/- inclui o sinal do negativo à direita do número. Poderia usar a excessão que usei na função getResult para resolver a estética, mas então seria impossível usar o número com as outras funções (pois a conversão de string para double supõe aquela posição do sinal negativo). Então esse detalhe foi ignorado para permitir a funcionalidade da calculadora.
  • Quando se aperta o botão ".", o visor mostra os números de forma errada. Ainda é um mistério para mim o porquê da chamada display.append("."); colocar o ponto antes dos números. Funcionalmente, no entanto, está tudo perfeito, e o resultado sempre é imprimido de maneira correta.
  • Contas com inteiros sempre contém casas decimais, por causa da conversão para double. Isso pode ser consertado facilmente, mas não achei necessário para o exercício.
Para que os leitores possam rodar o código com mais facilidade, postarei todo o projeto. As partes modificadas desde sexta-feira são dentro da função actionPerformed, mais todas as funções à partir da clear.

1:  package calculator;  
2:  import java.awt.*;  
3:  import javax.swing.*;  
4:  import java.awt.event.*;  
5:   //@author Gabriel  
6:  public class Calculator extends JFrame implements ActionListener{  
7:    //Declarando variáveis  
8:    JPanel[] row = new JPanel[5];  
9:    JButton[] button = new JButton[19];  
10:  //colocando todas as strings em um único vetor  
11:    String[] buttonString = {"7","8","9","+",   
12:       "4","5","6","-",   
13:       "1","2","3","*",  
14:       ".","/","C","sqrt",  
15:       "+/-","=","0"};  
16:    //Definindo tamanho do display e botões  
17:    Dimension displayDimension = new Dimension(300, 35);  
18:    Dimension regularDimension = new Dimension(45, 40);  
19:    Dimension rColumnDimension = new Dimension(60, 40);  
20:    Dimension zeroButtonDimension = new Dimension(90, 40);  
21:    boolean[] function = new boolean[4]; //vetor para armazenar a função pedida pelo usuário  
22:    double[] temporary = {0,0}; //variáveis auxiliares para calcular resultados  
23:    JTextArea display = new JTextArea(1,20);  
24:    boolean decimal = false; //para garantir que a função "." não possa ser inserida mais de uma vez  
25:    public static void main(String[] args)  
26:    {  
27:      Calculator c = new Calculator();   
28:    }  
29:    Calculator()  
30:    {  
31:      //o tutorial não explica a necessidade dessa função super. No entanto, procurando na internet, parece que  
32:      //ela é responsável por invocar o construtor pai  
33:      //super("Calculator");  
34:      setDesign();  
35:      setSize(380,250);  
36:      setResizable(false); //se o windows não deixa ajustar o tamanho da calculadora, por que eu iria?  
37:      setDefaultCloseOperation(EXIT_ON_CLOSE);  
38:      //Como vou querer 5 linhas e 5 colunas  
39:      GridLayout grid = new GridLayout(5,5);  
40:      setLayout(grid);  
41:      //inicializando o vetor de funções  
42:      for(int i=0;i<4 br="" i="">        function[i] = false;  
43:      FlowLayout f1 = new FlowLayout(FlowLayout.CENTER); //utilizamos a função FlowLayout para posicionar as colunas e linhas  
44:      FlowLayout f2 = new FlowLayout(FlowLayout.CENTER,1,1); //1,1 são os espaçamentos horizontais e verticais em relação a f1  
45:      for(int i=0; i<5 br="" i="">        row[i] = new JPanel();  
46:      row[0].setLayout(f1);  
47:      for(int i = 1; i<5 br="" i="">        row[i].setLayout(f2);  
48:      //agora as linhas e colunas já podem ser usadas!  
49:      for(int i = 0; i < 19; i++) //coloca as características dos botões  
50:      {  
51:        button[i] = new JButton();  
52:        button[i].setText(buttonString[i]);  
53:        button[i].addActionListener((ActionListener) this); //será usado para implementação do funcionamento do botão no futuro  
54:      }  
55:      display.setEditable(false); //não seria bom permitir o usuário editar o display  
56:      display.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); //Por alguma razão desconhecida por mim, calculadoras geralmente tem o display escrevendo da direita para a esquerda  
57:      //definindo tamanhos!  
58:      display.setPreferredSize(displayDimension);  
59:      for(int i = 0; i < 14; i++)  
60:        button[i].setPreferredSize(regularDimension);  
61:      for(int i = 14; i < 18; i++)  
62:        button[i].setPreferredSize(rColumnDimension);  
63:      button[18].setPreferredSize(zeroButtonDimension);  
64:      //colocando o display e os botões nas linhas e colunas  
65:      row[0].add(display);  
66:      add(row[0]);  
67:      for(int i = 0; i < 4; i++)  
68:        row[1].add(button[i]);  
69:      row[1].add(button[14]);  
70:      add(row[1]);  
71:      for(int i = 4; i < 8; i++)  
72:        row[2].add(button[i]);  
73:      row[2].add(button[15]);  
74:      add(row[2]);  
75:      for(int i = 8; i < 12; i++)  
76:        row[3].add(button[i]);  
77:      row[3].add(button[16]);  
78:      add(row[3]);  
79:      row[4].add(button[18]);  
80:      for(int i = 12; i < 14; i++)  
81:        row[4].add(button[i]);  
82:      row[4].add(button[17]);  
83:      add(row[4]);  
84:      setVisible(true);  
85:    }  
86:    //necessário para possibilitar a compilação  
87:    public final void setDesign() {  
88:      try {  
89:        UIManager.setLookAndFeel(  
90:            "com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");  
91:      } catch(Exception e) {   
92:      }  
93:    }  
94:   public void actionPerformed(ActionEvent ae)  
95:   {  
96:    if(ae.getSource() == button[0]) //7  
97:      display.append("7");  
98:    if(ae.getSource() == button[1]) //8  
99:      display.append("8");  
100:    if(ae.getSource() == button[2]) //9  
101:      display.append("9");  
102:    if(ae.getSource() == button[3]) //Soma  
103:    {  
104:      temporary[0] = Double.parseDouble(display.getText());  
105:      for(int i = 0; i < 4; i++) //Para evitar conflito quando multiplas teclas de função são apertadas  
106:        function[i] = false;  
107:      function[0] = true;  
108:      decimal = false;  
109:      display.setText("");  
110:    }  
111:    if(ae.getSource() == button[4]) //4  
112:      display.append("4");  
113:    if(ae.getSource() == button[5]) //5  
114:      display.append("5");  
115:    if(ae.getSource() == button[6]) //6  
116:      display.append("6");  
117:    if(ae.getSource() == button[7]) //Subtração  
118:    {  
119:      temporary[0] = Double.parseDouble(display.getText());  
120:      for(int i = 0; i < 4; i++)  
121:        function[i] = false;  
122:      function[1] = true;  
123:      decimal = false;  
124:      display.setText("");  
125:    }  
126:    if(ae.getSource() == button[8]) //1  
127:      display.append("1");  
128:    if(ae.getSource() == button[9]) //2  
129:      display.append("2");  
130:    if(ae.getSource() == button[10]) //3  
131:      display.append("3");  
132:    if(ae.getSource() == button[11]) //Multiplicação  
133:    {  
134:      temporary[0] = Double.parseDouble(display.getText());  
135:      for(int i = 0; i < 4; i++)  
136:        function[i] = false;  
137:      function[2] = true;  
138:      decimal = false;  
139:      display.setText("");  
140:    }  
141:    if(ae.getSource() == button[12]) //Decimal  
142:    {  
143:      if(!decimal)  
144:      {  
145:        decimal = true;  
146:        display.append(".");  
147:      }  
148:    }  
149:    if(ae.getSource() == button[13]) //Divisão  
150:    {  
151:      temporary[0] = Double.parseDouble(display.getText());  
152:      for(int i = 0; i < 4; i++)  
153:        function[i] = false;  
154:      function[3] = true;  
155:      decimal = false;  
156:      display.setText("");  
157:    }  
158:    if(ae.getSource() == button[14]) //C  
159:      clear();  
160:    if(ae.getSource() == button[15]) //Sqrt  
161:      getSqrt();  
162:    if(ae.getSource() == button[16]) //+/-  
163:      getPosNeg();  
164:    if(ae.getSource() == button[17]) //=  
165:      getResult();  
166:    if(ae.getSource() == button[18]) //0  
167:      display.append("0");  
168:  }  
169:    //Agora, para implementar o funcionamento dos botões, vou criar diversos métodos  
170:    //Para limpar a tela e resetar a calculadora  
171:    public void clear() {  
172:      try  
173:      {  
174:        display.setText(""); // Apaga o visor  
175:        for(int i = 0; i < 4; i++)  
176:          function[i] = false; // Reseta o estado de todas as funções  
177:        for(int i = 0; i < 2; i++)  
178:          temporary[i] = 0; // Apaga as variáveis auxiliares  
179:        decimal = false;  
180:      } catch(NullPointerException e) {   
181:      }  
182:    }  
183:    //Para calcular a raiz quadrada  
184:    public void getSqrt() {  
185:      try {  
186:        double value = Math.sqrt(Double.parseDouble(display.getText())); //Cria uma variável (double) e calcula a raiz do display usando a biblioteca math  
187:        display.setText(Double.toString(value)); //Modifica o valor do display  
188:      } catch(NumberFormatException e) {  
189:    }  
190:    }  
191:    //Para o botão +/-  
192:    public void getPosNeg() {  
193:      try {  
194:        double value = Double.parseDouble(display.getText()); //Cria a variável (por simplicidade)  
195:        value = value * (-1);//Não é necessário se preocupar com o zero por (-1)*0 = 0  
196:        display.setText(Double.toString(value));//Escreve o resultado  
197:      } catch(NumberFormatException e) {  
198:    }  
199:    }  
200:    public void getResult() {  
201:      double result = 0;  
202:      temporary[1] = Double.parseDouble(display.getText());  
203:      String temp0 = Double.toString(temporary[0]); //Duas variáveis necessárias para a conversão string -> double dos dois números  
204:      String temp1 = Double.toString(temporary[1]);  
205:      try {  
206:        if(temp0.contains("-")) { //Se o número é negativo  
207:          String[] temp00 = temp0.split("-", 2); //Divide a string em dois  
208:          temporary[0] = (Double.parseDouble(temp00[1]) * -1); //Transforma em double como negativo  
209:        }  
210:        if(temp1.contains("-")) { //Analogamente  
211:          String[] temp11 = temp1.split("-", 2);  
212:          temporary[1] = (Double.parseDouble(temp11[1]) * -1);  
213:        }  
214:      } catch(ArrayIndexOutOfBoundsException e) {  
215:      }  
216:      try {  
217:        if(function[2]) //Multiplicação  
218:          result = temporary[0] * temporary[1];  
219:        else if(function[3]) //Divisão  
220:          result = temporary[0] / temporary[1];  
221:        else if(function[0]) //Soma  
222:          result = temporary[0] + temporary[1];  
223:        else if(function[1]) //Subtração  
224:          result = temporary[0] - temporary[1];  
225:        if(result>=0) //Imprime resultado  
226:          display.setText(Double.toString(result));  
227:        else //evita um bug com o sinal negativo aparecendo depois do número  
228:        {  
229:          display.setText(Double.toString((-1)*result));  
230:          display.append("-");  
231:        }  
232:        for(int i = 0; i < 4; i++)  
233:          function[i] = false; //Reseta variáveis de função  
234:        decimal = false;  
235:      } catch(NumberFormatException e) {  
236:      }  
237:    }  
238:  }  


Cuidado apenas na hora de copiar e colar o código em um IDE, pois a formatação pode ter sido modificada pelo código html do blog. Vou incluir uma animação da calculadora funcionando para os menos interessados em programação.




Obrigado, até o próximo projeto!