giovedì 5 luglio 2012

Il file ECW

Il file ECW (Enhanced Compression Wavelet)è un file raster compresso, senza perdita di informazioni, adatto a memorizzare ortofoto, solitamente georeferenziato, e può contenere anche informazioni sull'SR, il sistema di riferimento (proiezione cartografica). Il rapporto di compressione può variare tra 1:2 a 1:100. Sviluppato dalla ERDAS, è un formato proprietario ma nonostante ciò esiste un SDK per la lettura dei file ECW, ergo può essere leggibile da software liberi. La creazione/modifica di file ECW necessita invece una licenza, quindi a pagamento.

 Da qui si possono scaricare i SDK per la creazione, modifica o sola lettura di file ECW.

 Tra i programmi più diffusi che supportano questo formato ricordiamo Adobe Photoshop, AutoCAD (solo alcune versioni), ArcView, ArcGIS, mentre fra i software liberi ricordiamo QuantumGIS.

 Degli esempi di carte geologiche in formato ECW si possono scaricare da questo sito: http://www.cartografiarl.regione.liguria.it/SiraWebGis/IndiceCarte.asp?idCanale=Geologia&cod_repertorio=03&modalita=LIGHT&ambiente=I&utente=&ruolo

N.B. sono mappe georeferenziate ma non contengono informazioni sul tipo di proiezione cartografica. Impostare dunque il SR Gauss Boaga fuso Ovest (EPSG: 3003)

venerdì 6 aprile 2012

Wikipedia passa a OpenStreetMap

Wikimedia, fondazione che gestisce il sito Wikipedia (e altri), ha deciso di non utilizzare più Google Maps in favore di OpenStreetMap , ovvero un servizio simile a Google Maps ma completamente libero, che si avvicina di più allo spirito di Wikipedia, ovvero rendere la conoscenza accessibile gratuitamente a tutti. Infatti, come Wikipedia, anche OpenStreetMap viene supportato dalle donazioni. Devo dire che non conoscevo questo servizio, ma dopo avergli dato un'occhiata veloce, ho constatato che non è niente male: ci da mediamente più informazioni di una mappa di base di Google Maps (come aree protette, elettrodotti, confini territoriali, demani, e addirittura pale eoliche), ma la pecca principale è la mancanza di una mappa satellitare.

sabato 31 marzo 2012

Java: visualizzare gli elementi di una JTable come degli JCkeckBox

Se si vuole una JTable (una tabella swing di java) dove gli elementi (righe) selezionate sono visualizzate con un checkbox selezionato  e viceversa, con questo risultato:
è possibile modificare una proprietà della JTable tramite il metodo setDefaultRenderer(). Nel caso dello screenshot sopra, la tabella è divisa in un numero indeterminato di righe, e n° 6 colonne. La colonna interessata a visualizzare le celle con una checkbox è la prima, quindi il mio interesse è di modificare la colonna in questione. Per prima cosa definiamo la classe CheckBoxTableCellRenderer, ovvero una classe che eredita da JCheckBox e che si appoggià all'interfaccia JTableCellRenderer. Quest'ultima contiene solamente un metodo: getTableCellRendererComponent(), che noi definiamo a nostro piacere e che deve ritornare il componente (oggetto grafico) che vogliamo visualizzare nella casella, dunque il JCkeckBox

import javax.swing.JCheckBox;
import javax.swing.table.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.SwingConstants;
import java.awt.Component;
import java.awt.Insets;
import javax.swing.JTable;
import javax.swing.UIManager;

class CheckBoxTableCellRenderer
 extends JCheckBox
 implements TableCellRenderer {

 Border noFocusBorder;
 Border focusBorder;

 public CheckBoxTableCellRenderer() {
   super();
   setContentAreaFilled(true);  // sfondo opaco
   setBorderPainted(true);
   setHorizontalAlignment(SwingConstants.CENTER); (allineamento orizzontale centrato)
   setVerticalAlignment(SwingConstants.CENTER);   (allineamento verticale centrato)
 }

    @Override
 public Component getTableCellRendererComponent(JTable table, Object value,
                                                boolean isSelected,
                                                boolean hasFocus,
                                                int row, int column) {

   if(table == null) {
     //operazioni da fare nel caso la tabella non contenga alcun record
   }
   else { 
     if (isSelected) { //variabile locale che è vera se la riga è selezionata

         setForeground(table.getSelectionForeground());
         setBackground(table.getSelectionBackground());
     }
     else {

         setForeground(table.getForeground());
         setBackground(table.getBackground());
     }

     setEnabled(table.isEnabled());
     //questo pezzo di codice agisce sul bordo, per eliminare l'effetto "tasto"
     if (hasFocus) {
       if (focusBorder == null) {
         focusBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
       }
       setBorder(focusBorder);
     }
     else {
       if (noFocusBorder == null) {
         if (focusBorder == null) {
           focusBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
         }
         if (focusBorder != null) {
           Insets n = focusBorder.getBorderInsets(this);
           noFocusBorder = new EmptyBorder(n);
         }
       }
       setBorder(noFocusBorder);
     }

     //seleziona o meno il JCheckBox, in base al valore della cella (riga selezionata o meno)
     setSelected(Boolean.TRUE.equals(value));
   }
  //ritorna il JCheckBox 
   return this;
 }
}


Definita la classe CheckBoxTableCellRenderer, dobbiamo adesso adottarla nella JTable.
Supponiamo di avere una JTable di nome "table", quindi:



        TableColumnModel tcm = table.getColumnModel();
        TableColumn tc = tcm.getColumn(0); //la prima colonna è quella che ci interessa, in questo esempio
        tc.setCellRenderer(new CheckBoxTableCellRenderer());  

Fatto ciò, la prima colonna dunque apparirà come una lista di JCheckBox ordinati in verticale, proprio come nello screenshot sopra. Ma c'è ancora una cosa che manca: l'editor. Infatti se proviamo a modificare il valore di una JCheckBox (quindi il selezionare/deselezionare una riga), notiamo un'anomalia: compare la scritta "true" o "false". Perché? Perchè una JTable di default implementa una DefaultCellEditor, che mostra le celle come dei JTextBox, quindi mostra "true" o "false", in modo testo. Dunque un piccolo passaggio è doveroso:
tc.setCellEditor(new DefaultCellEditor(new JCheckBox()));
this.setCellSelectionEnabled(false);

Infatti (pace all'anima nostra), DefaultCellEditor prevede un costruttore con una JCheckBox. Mentre setCellSelectionEnabled() evita che si selezionino (con conseguente cambiamento del colore) le righe intere, infatti  a noi interessa distinguere se una riga è selezionata o meno guardando il JCheckBox.

Ricapitolando, i due passaggi fondamentali da compiere sulla colonna sono 2: modificare il suo renderer, e modificare il suo editor . Il renderer viene eseguito solamente quando la tabella viene disegnata, mentre il comportamento (editor) viene eseguito ogni qual volta modifichiamo il valore di una JCheckBox.
E' tutto.

Alberto

venerdì 30 marzo 2012

Facebook: ultimate mass control

Bene, finalmente un post diverso dalla programmazione, ma pur sempre inerente all'informatica.

Il titolo dice tutto: da buon complottista quale tendo sempre a essere, ho come la vaga impressione che dietro facebook e il suo apparentemente innocuo web 2.0 ci sia dietro un sistema di controllo delle masse al servizio di qualche potente (non necessariamente una persona fisica) per perseguire i propri obiettivi.

La cosa che sicuramente mi ha dato più fastidio, è la mancanza nelle opzioni dell'account della funzione "Cancella il tuo account"; infatti è possibile eliminare l'account SOLO se si conosce l'indirizzo in cui fare la richiesta. Nelle opzioni account di facebook trovi solo "Disattiva il tuo account", che è una disattivazione (temporanea) dell'account, come se fosse cancellato (vanno via commenti, pagine che possiedi, ogni riferimento al tuo profilo), ma che in realtà c'è sempre, basta infatti loggarsi nuovamente e tutto ricomparirà magicamente come lo avevi lasciato al momento della disattivazione. Non è forse una caratteristica che fa storcere un po' il naso? Perché non hanno messo il link per cancellare la pagina direttamente nelle opzioni? Devo per forza rimanere su facebook? Ad ogni modo, metto qui il link per cancellare il proprio account da facebook, chiunque lo voglia fare basta che clicchi:
http://www.facebook.com/help/contact.php?show_form=delete_account


Perfetto, adesso passiamo all'altra motivazione, ben più grave. Sembra che facebook, anche dopo aver cancellato definitivamente l'account, mantenga alcune informazioni lesive della privacy per, secondo me, un periodo indeterminato di tempo. Vi spiego la mia situazione: mi sono iscritto su facebook nel novembre 2008, con una vecchia email che ho abbandonato a causa dell'elevato spam a cui era soggetta, creandone uno nuovo. Quindi ho aggiornato il mio profilo facebook aggiungendo una mail secondaria. E' possibile quindi mantenere più indirizzi email associati all'account facebook (perché poi?), ma visto che a me ne bastava uno, ho reso la nuova email "principale" e ho cancellato la vecchia email da facebook. Ma andiamo al dunque: dopo aver eliminato il mio account facebook, ho provato a crearne uno fake, e dal momento che non potevo ancora utilizzare la mia nuova email (ci vogliono 14 giorni per l'eliminazione), ho dovuto utilizzare la mia vecchia email (quella piena di spam). Sorpresa: come faceva facebook, dal momento che era la prima volta che entravo, a sapere "le persone che potrei conoscere"? Bene, quelle persone che avrei potuto conoscere le conoscevo effettivamente ed erano tutti miei amici. Ma non avevo eliminato la mail da facebook? Eh...mi sa che facebook conserva questi dati sensibili per chissà quanto tempo (forse illimitatamente). Quindi lui ancora conosce le mie email e tutti i miei amici. Bella merda (passatemi il termine).
Bene, alla facciazza di facebook io l'account fake l'ho ricreato, e come mail ne ho utilizzata una anch'essa fake (che più fake non si può): ho utilizzato uno dei tanti servizi web che ti creano una mail dal nome random che dura 10 minuti.
Consiglio: se dovete iscrivervi a facebook, utilizzate un servizio per creare mail fake, per esempio questo:
http://www.emailtemporanea.net/
oppure
http://10minutemail.com/10MinuteMail/index.html
Ed ecco che quel simpaticone di facebook ti avvisa:
ah feisbù, ma che te frega della mia mail?


Vogliamo dunque la ciliegina sulla torta? E' una notizia un po' datata (25 settembre 2011), ma comunque non credo che qualcosa sia cambiato. Un blogger australiano, Nik Cubrilovic, ha scoperto che facebook deposita sul computer dei cookie che non verranno mai più cancellati. I cookie sono dei file che un sito web chiede che vengano memorizzati sul computer di un client, affinché possa riconoscere chi è che si sta collegando al sito. La motivazione più giustificata per utilizzare dei cookie è quella di memorizzare l'accesso a un sito web anche dopo la chiusura del browser, in modo tale di non riscrivere a ogni accesso nome utente e  password; quindi, a buon ragione, tutti i cookie dovrebbero essere cancellati nel momento in cui l'utente fa "log out". Ma non è il caso di facebook, che continua tramite questi cookie a controllare l'utente sapendo buona parte delle pagine che visita. Perché "buona parte"? Beh, tecnicamente non è possibile sapere solamente con un cookie le pagine che un client visita, almeno che nella pagina non ci sia uno script che legga il cookie; questo è il caso di facebook: avete presente i tasti "Consiglia", "Condividi" e "Mi piace" sparsi per i vari siti che si riferiscono a facebook? Ecco, questi tasti incorporano codice che legge questo cookie, inviando al server di facebook il sito che l'utente sta visitando. 

Nel blog di Nik Cubrilovic è riportato un "esperimento". Il blogger ha creato un certo numero di account facebook, ha fatto il log out, e ha navigato per un po' sul web. Quando è rientrato su facebook, (log in), ha notato che il sistema gli suggeriva gli altri account fake che stava utilizzando. Esperimento riuscito. Facebook sa anche quanti account possediamo, e conosce tutti i browser dal quale facciamo accesso. Bello no? 
Per altre informazioni vi rimando al post originario di Nik Cubrilovic (in inglese): http://nikcub.appspot.com/posts/logging-out-of-facebook-is-not-enough

giovedì 29 marzo 2012

Convertitore UNIX Timestamp - Data in Java

Bene, per una mia applicazione Java avevo sempre questo problema del UNIX timestamp (ovvero il numero di secondi trascorsi dal 1 Gennaio 1970 alle ore 00:00:00), ovvero convertire una data in formato anno, mese, giorno, ore minuti e secondi in UNIX timestamp. Forse non ho cercato bene nella libreria di Java o forse non ho googlato per bene, ma una funzione che facesse ciò non l'ho trovata, solo timestamp dell'ora corrente (che non mi interessava).

Avevo urgentemente bisogno dell'algoritmo per calcolarlo che per pigrizia non ho mai scritto; adesso con il cambiamento all'ora legale è seguito un nuovo malfunzionamento del mio programma e quindi ho deciso di scriverlo una volta per tutte e condividerlo con chi ne avesse bisogno. 

L'algoritmo si compone di due parti: data una data (scusate il gioco di parole), la prima parte calcola il numero di secondi trascorsi fino al primo gennaio dell'anno corrente alle 00:00:00, la seconda parte aggiunge il "resto", ovvero il numero di secondi mancante. 

Specularmente, ho riciclato il suddetto algoritmo per fare il procedimento inverso, e ne è uscita fuori una bella classe che contiene i due metodi statici. 
Innanzitutto ho creato una classe ausiliaria Data, invece di utilizzare la java.util.Date.

package it.albe;

/**
 *
 * @author Alberto
 * 29/03/2012
 */
public class Data {
    public Data(int a,int m,int g, int o, int min, int sec){
        anno = a;
        mese = m;
        giorno = g;
        ore = o;
        minuti = min;
        secondi = sec;
    }
    public String toString(){
        String ms;
        switch(mese){
            case 1: ms = "Gennaio";break;
            case 2: ms = "Febbraio";break;
            case 3: ms = "Marzo";break;
            case 4: ms = "Aprile";break;
            case 5: ms = "Maggio";break;
            case 6: ms = "Giugno";break;
            case 7: ms = "Luglio";break;
            case 8: ms = "Agosto";break;
            case 9: ms = "Settembre";break;
            case 10: ms = "Ottobre";break;
            case 11: ms = "Novembre";break;
            case 12: ms = "Dicembre";break;
            default: ms="";
        }
        return String.valueOf(giorno)+" "+ms+" "+
                String.valueOf(anno)+" "+
                String.valueOf(ore)+":"+
                String.valueOf(minuti)+":"+
                String.valueOf(secondi);
    }
    public int anno;
    public int mese;
    public int giorno;
    public int ore;
    public int minuti;
    public int secondi;
}
Fatto ciò, ecco allora la classe Timestamp:

package it.albe;

/**
 *
 * @author Alberto
 * 29/03/2012
 */
public class Timestamp {
    public static long toUNIXtimestamp(Data data){
        long timestamp = 0;     
        for (int a=1970;a < data.anno;a++){ //anno
            if (a % 4 == 0 && (a % 100 != 0 || a % 400 == 0)){  //bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            for (g=1;g <= 31;g++)
                               timestamp+= 86400;
                        }break;
                        case 2: {
                            int g;
                            for (g=1;g <= 29;g++)
                               timestamp+= 86400;
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            for (g=1;g <= 30;g++)
                               timestamp+= 86400;
                        }break;
                    } //switch                
            } //if
            else {      //non bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            for (g=1;g <= 31;g++)
                               timestamp+= 86400;
                        }break;
                        case 2:{
                            int g;
                            for (g=1;g <= 28;g++)
                               timestamp+= 86400;
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            for (g=1;g <= 30;g++)
                               timestamp+= 86400;
                        }break;
                    } //switch
            } //else
        }    //for anno
        /***********************************************************/
        /*                   AGGIUNGO RESIDUO                      */
        /***********************************************************/
        if (data.anno % 4 == 0 && (data.anno % 100 != 0 || data.anno % 400 == 0)){
            int m;
            for (m=1;m < data.mese;m++)
                switch(m){
                    case 1:
                    case 3:
                    case 5:
                    case 7:
                    case 8:
                    case 10:
                    case 12: {
                        int g;
                        for (g=1;g <= 31;g++)
                           timestamp+= 86400;
                    }break;
                    case 2: {
                        int g;
                        for (g=1;g <= 29;g++)
                           timestamp+= 86400;
                    }break;
                    case 4:
                    case 6:
                    case 9:
                    case 11:{
                        int g;
                        for (g=1;g <= 30;g++)
                           timestamp+= 86400;
                    }break;
               } //switch  
           int g;
           for (g=1;g < data.giorno;g++)
               timestamp+= 86400;
           timestamp += data.secondi+data.minuti*60+data.ore*3600;             
        }
        else {
             int m;
            for (m=1;m < data.mese;m++)
                switch(m){
                    case 1:
                    case 3:
                    case 5:
                    case 7:
                    case 8:
                    case 10:
                    case 12: {
                        int g;
                        for (g=1;g <= 31;g++)
                           timestamp+= 86400;
                    }break;
                    case 2: {
                        int g;
                        for (g=1;g <= 28;g++)
                           timestamp+= 86400;
                    }break;
                    case 4:
                    case 6:
                    case 9:
                    case 11:{
                        int g;
                        for (g=1;g <= 30;g++)
                           timestamp+= 86400;
                    }break;
               } //switch  
           int g;
           for (g=1;g < data.giorno;g++)
               timestamp+= 86400;
           timestamp += data.secondi+data.minuti*60+data.ore*3600;  
        }
        return timestamp;
    }
    public static Data toDate(long Ptimestamp){
        int a;
        long timestamp = 0;
        for (a=1970;a < 2038;a++){ //anno
            if (a % 4 == 0 && (a % 100 != 0 || a % 400 == 0)){  //bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 31;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            
                                            }
                                        }
                        }break;
                        case 2: {
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 29;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 30;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                    } //switch                
            } //if
            else {      //non bisestile
                int m;
                for (m=1;m <= 12;m++)
                    switch(m){
                        case 1:
                        case 3:
                        case 5:
                        case 7:
                        case 8:
                        case 10:
                        case 12: {
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 31;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                        case 2:{
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 28;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                        case 4:
                        case 6:
                        case 9:
                        case 11:{
                            int g;
                            int o;
                            int min;
                            int s;
                            for (g=1;g <= 30;g++)
                                for (o=0;o < 24;o++)
                                    for(min=0;min < 60;min++)
                                        for (s=0;s < 60;s++){
                                            timestamp++;
                                            if (Ptimestamp==timestamp){
                                                return new Data(a,m,g,o,min,s+1);
                                            }
                                        }
                        }break;
                    } //switch
            } //else
        }    //for anno 
        return new Data(0,0,0,0,0,0);
    }
    
}
In pratica il metodo toUNIXtimestamp() nella prima parte fa un ciclo dove anno per anno controlla innanzitutto se è bisestile, e mese per mese controlla se è un 31 o un 28 o un 30, e poi giorno per giorno aumenta il timestamp di 86400 (numero di secondi in un giorno), mentre la seconda parte procede in maniera analoga tenendo conto della data di cui si vuol calcolare il timestamp.

L'altro metodo è analogo e non c'è nemmeno bisogno che lo spieghi.

Ed ecco un utilizzo esemplare di questa classe:


        Data data = new Data(2012,3,29,21,48,45); //29 marzo 2012 alle 21:48 e 45 secondi
        long ts = toUNIXtimestamp(data);
        System.out.print(ts+"\n");
        data = toDate(ts);
        System.out.print(data);

giovedì 1 marzo 2012

Risolutore "Solitario Klondike", codice sorgente e spiegazione.

Per completare il precedente post, ecco questa parte dedicata al codice.

Il “Risolutore solitario Klondike” è stato scritto in C++, con alcune parti scritte in C (è questa la potenza e versatilità del C++…mantenere la compatibilità col caro vecchio C), soprattutto quelle parti di codice inerenti all’interfaccia grafica.
La collezione di classi principale è in C++ standard quindi OS indipendente, le parti di codice della GUI sono molto limitate. Quindi il programma è stato pensato per funzionare su Windows (Win32) e Linux(GTK+/Gnome).
Analizziamo prima il cuore del programma, ovvero la collezione di classi OS indipendente che creano la parte “logica” dell’applicazione,
Comincio a presentare la gerarchia, e poi analizzo le classi una per una.
 e invece questa è la classificazione fatta da me dei gruppi di carte che sono sul tavolo:
p1,p2,p3 e p4 sono di tipo Pila.
master è di tipo MazzoMaster.
terra è di tipo GruppoDiCarte.
 s2,s3,s4,s5,s6 ed s7(ovvero le carte sotto le scale, coperte) sono di tipo GruppoDiCarte.
g1,g2,g3,g4,g5,g6,g7 (ovvero le scale) sono di tipo GruppoOrdinato.
La classe fondamentale di cui si servono tutte le altre è Carta, di cui riporto il file "Carta.h" a cui appartiene:



#pragma once
#include 
#include 
using namespace std;
enum Seme {
 CUORI,QUADRI,PICCHE,FIORI,
};
enum Colore {
 ROSSO,NERO,
};
class Carta {
friend ostream& operator<<(ostream&,Carta&);
public:
 Carta(unsigned int,Seme,string);
 Carta(Carta&);
 unsigned int getValore() const;
 Seme getSeme() const;
 Colore getColore() const;
 void stampa() const;
 string getResource() const;
private:
 unsigned int valore;
 Seme seme;
 string risorsa;
 
 string getStringaSeme() const;
};
La classe presenta due costruttori, uno che crea la carta con il suo valore e il suo seme, e un altro che è un costruttore di copie (che comunque potrei togliere perché non ne faccio uso). Da notare l'importante metodo string getResource() const; che restituisce il percorso del file che contiene l'immagine associata alla carta, scelta appositamente in base al valore e il seme. L'overloading del'operatore << non fa altro che passare una sua stringa rappresentativa a uno stream (per esempio "2 di cuori"). Adesso presento la classe GruppoDiCarte che è una classe quasi-astratta, che rappresenta un gruppo generico di carte messe alla rinfusa.
#pragma once
#include "Carta.h"
#include 
using namespace std;
class GruppoOrdinato;
#define MAX_CARTE 52

class GruppoDiCarte{
friend ostream& operator<<(ostream&,GruppoDiCarte&);
public:
 GruppoDiCarte();
 GruppoDiCarte(unsigned int);
 unsigned int getNCarte(){return nCarte;}
 void setCarta(unsigned int);
 void mescola();
 virtual void ordina();
 virtual Carta* getPrimo(){return NULL;}
 virtual bool attaccaGruppo(GruppoOrdinato*){return NULL;}
 void stampa();
       Carta* pop();
 void push(Carta*);
 Carta* vediCarta();
 virtual bool attacca(Carta*);
 void reverse();
protected:
 unsigned int nCarte;
 Carta* carte[MAX_CARTE];
};
Da notare già quanti metodi virtuali ho inserito. Sono dei metodi "vuoti", potevo anche farli virtuali puri, ma poi non mi avrebbe permesso di instanziare oggetti dalla classe. Ad ogni modo questi metodi verranno chiamati solamente dalle classi figlie. Presenta due costruttori, uno che crea un gruppo di carte con 0 elementi (vuoto) e un altro che crea un gruppo di carte con un numero determinato di elementi, che saranno ovviamente inizializzati al valore delle carte tramite il metodo void setCarta(unsigned int); Poi ci sono i metodi void mescola(); e virtual void ordina(); inseriti per completezza, visto che non ne faccio uso nel programma. Per adesso tralasciamo i metodi virtual bool attaccaGruppo(GruppoOrdinato*); virtual bool attacca(Carta*); che saranno specializzati nelle classi figlie. Particolare importanza hanno i metodi Carta* pop(); void push(Carta*); e Carta* vediCarta(); visto che saranno utilizzati frequentemente in tutto il programma. push(Carta*) aggiunge una carta al mazzetto, mentre pop() la preleva, invece vediCarta() mostra la carta che si trova in cima alla pila (infatti ogni gruppo di carte, eccetto alcuni, viene trattato come se fosse una struttura di tipo LIFO), senza modificare il mazzetto, ovvero senza prelevarla. Una regola categorica per il corretto uso di queste classi è che ad ogni istruzione pop() eseguita su qualsiasi gruppo di carte, deve seguire una istruzione push() su un altro gruppo di carte, per conservare il numero totale di carte. Infatti se tolgo una carta a un gruppo senza inserirla in un altro gruppo, succede che perdo quella carta, scalando di uno il numero totale di carte. Un classico uso che ne faccio è questo: supponiamo un gruppo di carte di nome "g1" e una ltro di nome "g2", e che io voglia passare una carta da g2 a g1. g1.push(g2.pop()); oppure, verificando se effettivamente, stando alle regole del gioco, posso eseguire la suddetta operazione:
Carta* c = g2.vediCarta();  //guardo la carta
if (c==NULL)                //se il mazzo è vuoto ritorno
 return false;
bool result = g1.attacca(c);     //verifico se posso fare l'operazione
if (result) {               // in caso affermativo...
 g2.pop();            //prelevo la carta da g2, visto che l'ho già attaccata a g1
 return true;
}
Adesso analizziamo le classi che ereditano da GruppoDiCarte, ovvero GruppoOrdinato,MazzoMaster,Pila,Mazzo. Analizziamo prima mazzo, che rappresenta un mazzo intero di 52 carte.
#pragma once
#include "GruppoDiCarte.h"
#include 
using namespace std;

class Mazzo : public GruppoDiCarte {
public:
 Mazzo();
 void ordina();
private:
 string toString(int);
};
La classe (che ricordiamo eredita da GruppoDiCarte) non fa altro che creare un gruppo di carte di 52 elementi e mescolarli. Riporto il costruttore di questa classe che crea il mazzo e simula il mescolamento delle carte:
#include "Mazzo.h"
#include 
#include 

Mazzo::Mazzo() {
 nCarte = 52;
 for (int i=0;i<13;i++){
  carte[i] = new Carta(i+1,CUORI,"carte\\"+toString(i+1)+"c.bmp");
  carte[i+13] = new Carta(i+1,QUADRI,"carte\\"+toString(i+1)+"q.bmp");
  carte[i+26] = new Carta(i+1,PICCHE,"carte\\"+toString(i+1)+"p.bmp");
  carte[i+39] = new Carta(i+1,FIORI,"carte\\"+toString(i+1)+"f.bmp");
 }
 /*          mescolo le carte    */
 srand(time(NULL));
 int c;
 int d;
 Carta* app;  //carta di "appoggio"
 for (int i=0;i<208;i++){
  c = rand() % 52;
  d = rand() % 52;
  app = carte[c];
  carte [c] = carte[d];
  carte [d] = app;
 }
}
Passiamo a MazzoMaster, ovvero il mazzo posto in alto a sinistra:
#pragma once
#include "GruppoDiCarte.h"

class MazzoMaster : public GruppoDiCarte {
public:
 Carta* daiCarta();
 void setTerra(GruppoDiCarte*);
private:
 GruppoDiCarte* terra;
};
che aggiunge i metodi Carta* daiCarta(); e void setTerra(GruppoDiCarte*); dove il primo serve per scoprire una carta e il secondo serve per passare il gruppo di carte già scoperte alla classe, in modo che se MazzoMaster si esaurisce, "capovolga" le carte che ci sono a terra. Passiamo ancora a Pila (il nome non ha il noto significato in informatica, ma non sapevo che nome scegliere). Le pile sul tavolo sono quattro, si trovano in alto a destra e raggruppano le carte di uno stesso seme in ordine crescente.
#pragma once
#include "GruppoDiCarte.h"
#include 
#include 

class Pila : public GruppoDiCarte {
friend ostream& operator<<(ostream&, Pila&);
public:
 Pila();
 bool attacca(Carta* c);
 Seme getSeme();
 Carta* getPrimo(){return NULL;}
 bool attaccaGruppo(GruppoOrdinato*){return false;}
private:
 Seme seme;
};
bool attacca(Carta* c); è un metodo specializzato che è virtual della classe madre, e restituisce true se la carta può effettivamente essere attaccata al gruppo (stando alle regole del gioco), false altrimenti. Seme getSeme(); ritorna il seme del gruppo (visto che sono tutte carte di uno stesso seme),Carta* getPrimo() è una funzione ridefinita in modo che ritorni sempre NULL, e allo stesso modo ridefinisco bool attaccaGruppo() visto che non ha senso attaccare un gruppo di carte a questo, dato che andranno attaccate una per una. Passiamo all'ultima classe che rappresenta un gruppo di carte, ovvero GruppoOrdinato, quei gruppi di carte scoperti (7 in tutto) e ordinati in ordine decrescenti a colori alterni:
#pragma once
#include "GruppoDiCarte.h"
#include 
using namespace std;

class GruppoOrdinato : public GruppoDiCarte {
friend ostream& operator<<(ostream&, GruppoOrdinato&);
public:
 GruppoOrdinato();
 bool attacca(Carta*);
 bool attaccaGruppo(GruppoOrdinato*);
 Carta* getPrimo();
 void setAttaccaPrimaVolta(); //uso riservato solamente a Tavolo
private:
 bool attaccaPrimaVolta;
};
bool attacca(Carta*); e bool attaccaGruppo(GruppoOrdinato*); sappiamo già a cosa servono, bool attaccaPrimaVolta; è una variabile usata per "capire" se la carta che verrà attaccata è attaccata dal primo utilizzo o meno. void setAttaccaPrimaVolta(); resetta questa variabile al valore originario, pertanto non andrebbe utilizzata e il suo utilizzo è riservato alla classe Tavolo. Utilizzare questo metodo sarebbe deleterio e il programma non funzionerebbe come dovrebbe. Rimane l'ultima classe da analizzare (oltre a Risolutore), ovvero Tavolo.
#pragma once
#include "Mazzo.h"
#include "GruppoOrdinato.h"
#include "Pila.h"
#include "MazzoMaster.h"
#include 
using namespace std;
class Tavolo {
friend ostream& operator<<(ostream&,Tavolo&);
public:
 Tavolo(Mazzo*);
 void info();
 void checkGruppiOrdinati();
 unsigned int contaCarte();
 MazzoMaster& getMaster(){return master;}
 GruppoDiCarte& getTerra(){return terra;}
 GruppoOrdinato& getg1(){return g1;}
 GruppoOrdinato& getg2(){return g2;}
 GruppoOrdinato& getg3(){return g3;}
 GruppoOrdinato& getg4(){return g4;}
 GruppoOrdinato& getg5(){return g5;}
 GruppoOrdinato& getg6(){return g6;}
 GruppoOrdinato& getg7(){return g7;}
 GruppoDiCarte& gets2(){return s2;}
 GruppoDiCarte& gets3(){return s3;}
 GruppoDiCarte& gets4(){return s4;}
 GruppoDiCarte& gets5(){return s5;}
 GruppoDiCarte& gets6(){return s6;}
 GruppoDiCarte& gets7(){return s7;}
 Pila& getp1(){return p1;}
 Pila& getp2(){return p2;}
 Pila& getp3(){return p3;}
 Pila& getp4(){return p4;}

private:
 MazzoMaster master;
 GruppoDiCarte terra;

 GruppoOrdinato g1;
 GruppoOrdinato g2;
 GruppoOrdinato g3;
 GruppoOrdinato g4;
 GruppoOrdinato g5;
 GruppoOrdinato g6;
 GruppoOrdinato g7;
 

 GruppoDiCarte s2;
 GruppoDiCarte s3;
 GruppoDiCarte s4;
 GruppoDiCarte s5;
 GruppoDiCarte s6;
 GruppoDiCarte s7;

 Pila p1;
 Pila p2;
 Pila p3;
 Pila p4;
};
Praticamente il tavolo contiene i puntatore a tutti i gruppi di carte che sono presenti sul tavolo di un giocatore che gioca al solitario. Potete confrontare i membri di questa classe con l’immagine postata sopra. Da notare i metodi info(), che stampa su console informazioni sulle carte che contiene ogni gruppo; checkGruppiOrdinati() che controlla se c’è qualche GruppoOrdinato che non contiene alcuna carta, e quindi deve essere scoperta una carta del GruppoDiCarte che sta proprio sotto lo stesso; contacarte() che conta il numero totale di carte, mi è stato utile in debug per evitare la “scomparsa” (o l’”apparizione”) di alcune carte. Ultima classe fondamentale: il Risolutore, ovvero colui che secondo una logica ben precisa, muove le carte per vincere il giuoco.
#pragma once
#include "costanti.h"
#include "Tavolo.h"
#include "Interfaccia.h"
class InterfacciaWin;


class Risolutore {
public:
 Risolutore(Tavolo* t,Interfaccia*);
 bool mossa();
 bool getVincita(){return vincita;}
private:
 Tavolo* tavolo;
 int chiamateMaster; //tiene conto del numero di volte che io prendo una carta dal master
 int chiamateCiclo; //tiene conto del numero di volte che io chiamo mossa()
 GruppoDiCarte* gruppi[11];

 bool gruppi_pile();
 bool terra_gruppi();
 bool gruppi_gruppi();
 bool vincita;
 InterfacciaWin* interfaccia;
};
Il risolutore per giocare ha bisogno dell’oggetto Tavolo, e di un’interfaccia grafica, per comunicare gli spostamenti di carte. Il cuore di questa classe è contenuta nel metodo mossa(), che chiama ripetutamente i tre metodi privati della stessa classe. Ok, abbiamo presentato la parte logica della classe, passiamo alla parte fisica, o meglio, alla parte visiva, quella che ci fa vedere che cosa sta facendo il computer: INTERFACCIA GRAFICA L’interfaccia grafica per ora è stata implementata per Win32, ma sto lavorando per la versione Linux (GTK+).
#pragma once
#include "costanti.h"
#include "Tavolo.h"
#include "Risolutore.h"
class Risolutore;

#ifdef WINDOWS_APP
#include 
class InterfacciaWin {
public:
 InterfacciaWin(unsigned int,Tavolo*);
 void aggiungiRisolutore();
 bool mossa();
 char** clickOnMaster();
 void transizione(int,int);
 void aggiungiFinestra(HWND);
private:
 Tavolo* tavolo;
 Risolutore* risolutore;
 bool setRisolutore;
 unsigned int weight;
 
    HWND hwnd;
};
#define Interfaccia InterfacciaWin
#endif

#ifdef LINUX_APP
class InterfacciaLinux {
public:
 InterfacciaLinux(unsigned int,Tavolo*);
 void aggiungiRisolutore();
 bool mossa();
 char** clickOnMaster();
 void transizione(int,int);
private:
 Tavolo* tavolo;
 Risolutore* risolutore;
 bool setRisolutore;
 unsigned int weight;
};
#define Interfaccia InterfacciaLinux
#endif
Come vedete, siccome è OS dipendente, ho provveduto a creare le direttive per il precompilatore in modo tale che se WINDOWS_APP è definito, compila per windows, altrimento se LINUX_APP è definito, compila per LINUX. Nota che PER ORA, la funzione della classe Interfaccia è abbastanza futile, visto che potrei anche farne a meno. Ma l’ho messa nel caso mi servisse più avanti, quando continuerò a sviluppare questo programma permettendo di fare giocare anche un umano (potete infatti notare il metodo aggiungiRisolutore() , che come dice il nome, attiva la modalità automatica, altrimenti se non c’è il risolutore, vuol dire che sarà un umano a giocare. Stay connected!

 Per finire…beh, ecco il codice sorgente per voi: sono dei fottutissimi file .h e .cpp, quindi compilabili con qualunque compilatore all’altezza. Nessun progetto, nessun vincolo di sistema, only raw material. P.S. ci sono solo 2 file che non ho analizzato: “WinMain.c” e “Windows.h”, che sono troppo specifici del sistema operativo, ma comunuque lo trovate nel codice sorgente ;)
Fine del post chilometrico.
Alberto

giovedì 23 febbraio 2012

Risolutore automatico del solitario Klondike

Ho creato un risolutore automatico del famoso solitario di Windows, Apple, Gnome, KDE e chi più ne ha più ne metta. Scritto in C++ per Win32.

La mia intenzione era di crere un risolutore in grado di vincere ogni partita, ma mi sbagliavo di grosso; infatti sappiamo che dipende tutto dalle carte, e possono presentarsi situazioni (frequenti) in cui è impossibile vincere, come in questo caso, per esempio:

 Le carte per "sbloccare" il gioco sono 2: il nove di picche e il quattro di fiori, che quarda caso si trovano sotto il dieci di cuori e il nove di quadri.

Comunque, il computer vince 1 volta su 5, e non sono poche!
Metto il file exe scaricabile per Windows, conm poche modifiche ne farò anche una versione per KDE. Le mosse le fa il computer ad ogni click del mouse (mi è stato utile per seguire le mosse del computer). Risoluzione consigliata: almeno 800x600 pixel.
Scusate le carte ritagliate male e prese da wikipedia XD. Nel prossimo post darò i dettagli tecnici di questo programma.
 Download
Alberto

lunedì 20 febbraio 2012

mercoledì 15 febbraio 2012

Scrobblit

Scrobblit è un programma scritto interamente in Java utile per scrobblare su last.fm le canzoni che noi ascoltiamo con iTunes (su Windows) o con Rythmbox (su linux).E' nato per una mia necessità di scrobblare le canzoni che ascolto col mio iPod, in alternativa alla versione ufficiale del software di last.fm che aveva cominciato a dare fastidiosi problemi.

Scrivo questo post per dare la possibilità a chi cerca su internet di avere un'alternativa al software di last fm, e anche per preannunciare il volere mettere open-source questo programma e quindi rilasciarne il codice sorgente. A breve posterò il progetto di source forge e chi vorrà entrarne a far parte è ben accetto, basta che contribuisca bene. Il programma non presenta grossi bug, quello più rilevante è il problema del fuso orario e del passaggio ora legale / ora solare.  E qualche miglioramento lo richiederebbe pure.

Comunque, la pagina da dove scaricare il programma è questa:
http://prodigious.altervista.org/Scrobblit/indexit.php

P.S. ci sono una decina di utilizzatori assidui del programma, compreso me :)