Visualizzazione post con etichetta intrattenimento. Mostra tutti i post
Visualizzazione post con etichetta intrattenimento. Mostra tutti i post

venerdì 20 dicembre 2013

LifeGame, il gioco della vita

In realtà erano anni che mi frullava per la testa l'idea di creare una mia versione del gioco della vita, ma per un motivo o per un altro, non l'avevo mai sviluppato.
Però ultimamente sono stato preso dalla documentazione ufficiale del progetto GNOME, e sono venuto a conoscere una libreria di nome clutter la quale viene descritta nel progetto come “una libreria per creare animazioni e interfacce grafiche eleganti”; mi sono così incuriosito e ho dato un'occhiata alla documentazione ufficiale di questa libreria: non mi ci è voluto molto a capire che segue lo stesso stile di GTK+, usa le sue stesse librerie (GObject, GLib, GDK ecc.), è integrata nell'ambiente GNOME e usa le librerie OpenGL.
Così, mani sulla tastiera, ho cominciato a sviluppare con clutter il solitario che ho sviluppato circa due anni fa per windows e creare una versione giocabile, ma il progetto è lungo e continua ancora adesso.
Quindi ho deciso di usare clutter per sviluppare il gioco della vita, che è più veloce e meno impegnativo: 4 giorni e ho implementato la versione 0.8, in cui oltre a usare clutter, mi sono avvalso della libreria clutter-gtk la quale fa da “ponte” per le librerie clutter e gtk.

Più avanti ne approfitterò per avviare un mini-tutorial su clutter descrivendo il codice sorgente del gioco della vita.

Il gioco della vita

Il gioco della vita è un automa cellulare che come il nome stesso dice, simula alcuni comportamenti assimilabili alla vita, alla crescita della popolazione e così via. Personalmente mi piace immaginarlo come popolazioni di umani su una terra che si stanziano (forme stabili) o vagano (forme dinamiche) su un territorio, dove possono entrare in guerra fra loro (collisioni) o convivere pacificamente, oppure ancora dove possono prosperare (automi a crescita infinita), o morire (configurazioni che scompaiono dopo tot-generazioni).
Dopo aver confuso le idee del lettore che non conosce il gioco della vita, passo a spiegare come funziona lo stesso. Esso si basa su uno spazio costituito da celle, dove ogni cella può essere accesa (viva) o spenta (morta). La configurazione in cui si trova lo spazio funziona da input per la generazione seguente, la quale funzionerà da input per la generazione seguente ancora e così via: esso è Turing-compatibile, infatti è possibile configurare le celle in una maniera particolare costruendo porte logiche e implementare istruzioni.
Le regole che determinano il passaggio da una generazione a un'altra sono essenzialmente due:
  1. Se una cella è morta, essa nasce se ha esattamente 3 celle vive accanto
  2. Se una cella è viva, essa muore se ha un numero di celle accanto diverso da 2 o da 3, per isolamento o sovraffollamento
Queste semplici regole permettono di creare un gran numero di configurazioni, elencate qui appresso.
  • Forme stabili:
    • Blocco
    • Barca
  • Oscillatori:
    • Lampeggiatore
    • Rospo
  • Nav icelle:
    • Aliante
    • Astronave
  • Altro:
    • Cannone di alianti
    • Die hard
    • …

Il software

Il software che ho sviluppato è minimale, usa clutter ma non usa appieno le sue potenzialità (probabilmente avrei potuto sviluppare il gioco anche col solo uso di gtk).
Si presenta con la barra degli strumenti in alto, lo stage al centro, e la barra di stato in basso per il conteggio delle generazioni:
La scena del gioco con un oscillatore e un aliante

La barra degli strumenti:
La barra degli strumenti

contiene i comandi per:
  • Creare una nuova scena
  • Aprire un file
  • Salvare un file
  • Mettere in pausa/riprendere
  • Le impostazioni
  • Modificare il ritardo (in ms) tra una generazione e un'altra
  • Informazioni sul programma
Il supporto per i file è in realtà un modo per salvare delle configurazioni particolari (per esempio salvare una navicella), ma devo ancora perfezionarlo perché non è molto utile (potrebbe essere utile se riuscissi a importare nella scena il contenuto del file in una determinata posizione della scena stessa).

Installazione

Innanzitutto, per ora è disponibile solo per linux. Purtroppo per varie difficoltà nel compilare i sorgenti di clutter e clutter-gtk su windows, non posso andare avanti (fortunatamente il team gtk+ fornisce già i binari compilati per windows).

È possibile installare il software in due modi:
1)Compilando i sorgenti
2)Installando il pacchetto .deb per i sistemi debian/ubuntu


Per compilare i sorgenti è necessario avere installati sul sistemai seguenti pacchetti:
  • pkg-config
  • libgtk-3-dev
  • libclutter-1.0-dev
  • libclutter-gtk-1.0-dev
    quindi basta scaricare ed estrarre in una cartella il contenuto del file lifegame-0.8-src.tar.gz, aprire il terminale, posizionarsi sulla cartella con i file estratti:
cd [percorso-cartella]
e lanciare il comando:
sudo ./install
il quale lancia il file di script “install” che ho preparato il quale compila i sorgenti e installa il software nel sistema. Per i sistemi che hanno GNOME o simili, come ubuntu, è possibile trovarlo nelle applicazioni sotto la categoria “giochi”, altrimenti è possibile lanciarlo da terminale col comando “lifegame”.

Per installare direttamente i binari è possibile scaricare il comodo pacchetto .dev (per i sistemi con pacchettizzazione debian, come Debian stesso e Ubuntu) che risolve automaticamente le dipendenze. Per installare il pacchetto .deb su ubuntu basta aprirlo con un doppio click oppure digitare da terminale:

dpkg --install lifegame-0.8.deb

Download:

martedì 10 dicembre 2013

I comandi più divertenti di linux

Ci sono alcuni comandi (=programmi) che ho scoperto su linux che non sono altro che comandi creati a scopo goliardico, senza alcuna utilità se non quella di strapparci un piccolo sorriso. Possiamo vederli qui appresso, ordinati iniziando da quelli che considerò più divertenti.

1. sl

Il comando ls su linux ("list) serve per elencare i file contenuti in una cartella, senza opzioni elenca i file contenuti nella cartella corrente. Essendo un comando molto usato, può capitare che nella fretta qualcuno scriva "sl" invece che "ls", causando un simpatico effetto: un treno che passa dalla finestra del terminale. 

Normalmente nelle distro non è installato, ma su Debian/Ubuntu per installarlo basta scrivere sul terminale:
sudo apt-get install sl

2. xcowsay

 Questo comando non fa altro che lanciare una applicazione GTK+ che visualizza una mucca che "dice" quello che noi le passiamo come argomento. 

Per installarla lanciamo:
sudo apt-get install xcowsay

3. figlet & toilet

Due programmi analoghi che stampano usando l'ASCII art quello che noi passiamo come argomento. 

Per installarli:
sudo apt-get install figlet
sudo apt-get install toilet

4. oneko

Per i gattofili c'è una simpatica applicazione che in effetti potete lanciare e lasciarla in background. Mostra un gattino che segue sempre il vostro mouse, (in realtà il puntatore del mouse):
Per installarlo:
sudo apt-get install oneko
Mentre per lanciarlo e lasciarlo in background:
oneko &
Poi premete INVIO e scrivete "exit" per uscire dal terminale senza chiudere l'applicazione.

5. moo

Rimanendo sempre in tema mucca, visto che fa molto simpatia, ecco quest'altra chicca: in realtà è un easter-egg di apt-get, sistema di pacchettizzazione di debian. Basta scrivere:
apt-get moo

6. fortune

Questa è una vecchia applicazione per Unix, quindi presente anche in Linux oggi. Mostra un messaggio pseudo-casuale preso da un database di citazioni. Pesa circa 2 megabyte, per installarlo:
sudo apt-get install fortune



domenica 10 novembre 2013

Memory, gioco mnemonico che ho scritto in javascript



Aggiornamento: clicca qui

Ispirandomi al gioco che era presente nel vecchio Nokia 3210, ho creato questo gioco con poche linee di codice in Javascript e qualcosa basilare di CSS e HTML. Il file HTML su cui mi baso (ci sono riferimenti alla tabella, id in particolare, e un tasto per "nuovo gioco"):









<CENTER>
<p style="font-size:x-large;font-style:italic;">Memory: clicca sulle caselle e memorizzale per trovare le coppie, fino a scoprire tutte le caselle.</p>
<a href="#">
<table>
<tr><th id="11" onclick="clickTo(1,1)">*</th><th id="12" onclick="clickTo(1,2)">*</th><th id="13" onclick="clickTo(1,3)">*</th><th id="14" onclick="clickTo(1,4)">*</th></tr>
<tr><th id="21" onclick="clickTo(2,1)">*</th><th id="22" onclick="clickTo(2,2)">*</th><th id="23" onclick="clickTo(2,3)">*</th><th id="24" onclick="clickTo(2,4)">*</th></tr>
<tr><th id="31" onclick="clickTo(3,1)">*</th><th id="32" onclick="clickTo(3,2)">*</th><th id="33" onclick="clickTo(3,3)">*</th><th id="34" onclick="clickTo(3,4)">*</th></tr>
<tr><th id="41" onclick="clickTo(4,1)">*</th><th id="42" onclick="clickTo(4,2)">*</th><th id="43" onclick="clickTo(4,3)">*</th><th id="44" onclick="clickTo(4,4)">*</th></tr>
</table>
<input type="button" value="Nuovo gioco" onclick="nuovoGioco()" style="width:100px;height:50px;">
</a>
</CENTER>
Mentre  il cuore del programma vero e proprio è lo script in javascript, tutto commentato:
<script type="text/javascript">
//Array per contenere i caratteri da scoprire
var array = new Array();
//Array per controllare il flusso del gioco (se sono state aperte due caselle e sono uguale allora scopri, altrimenti nascondi ecc.
var caselle_aperte = new Array();
/*INIZIO*/
nuovoGioco();

/*
*Questa funzione gestisce quando viene cliccata una casella, e decide cosa fare
*a seconda del contenuto di caselle_aperte
*/

function clickTo(riga,colonna){
   //Caso 1: non ci sono caselle aperte, quindi deve scoprirne una sola
   if (caselle_aperte.length==0){
      mostraSimbolo(riga,colonna);
      caselle_aperte[0]=riga.toString()+colonna.toString();
      return;
   }
   //Caso 2: c'è già una casella aperta, scopri la nuova e controlla se sono uguali
   if (caselle_aperte.length==1){
      //se è la stessa ignora ed esci
      if (caselle_aperte[0]==riga.toString()+colonna.toString())
   return;
     //scopri la casella
      mostraSimbolo(riga,colonna);
      //memorizza la nuova casella in caselle_aperte
      caselle_aperte[1]=riga.toString()+colonna.toString();
     //controlla se i simboli sono uguali
      if (getSimboloAt(caselle_aperte[1])==(getSimboloAt(caselle_aperte[0]))){
          document.getElementById(riga.toString()+colonna.toString()).setAttribute('style','background-color:#81F781;');
          document.getElementById(caselle_aperte[0]).setAttribute('style','background-color:#81F781;');
          //cancella il contenuto di caselle_aperte
          caselle_aperte.pop();
          caselle_aperte.pop();
          //Controlla se hai vinto (cioè se tutte le caselle sono state scoperte
          var conta;
          conta=0;
          for (i=1;i<=4;i++)
             for (c=1;c<=4;c++){
                 if (document.getElementById(i.toString()+c.toString()).innerHTML!="*")
                      conta++;
             }
          }
          if (conta==16)
              alert("Hai vinto!");
          return;
      }
   //Caso 3: ci sono già due caselle scoperte e sono entrambe diverse.
   if (caselle_aperte.length==2){
      //Nascondi i simboli
      nascondiSimbolo(caselle_aperte[0]);
      nascondiSimbolo(caselle_aperte[1]);
      //svuota caselle_aperte
      caselle_aperte.pop();
      caselle_aperte.pop();
      return;
   }
}
/*
* Mostra semplicemente il simbolo in riga,colonna
*/
function mostraSimbolo(riga,colonna){
   document.getElementById(riga.toString()+colonna.toString()).innerHTML=array[((riga-1)*4)+colonna-1];
}
/*
*Nasconde semplicemente il simbolo in riga, colonna
*/
function nascondiSimbolo(riga,colonna){
   document.getElementById(riga.toString()+colonna.toString()).innerHTML="*";
}
/*
*Come sopra, ma overload per un solo parametro di tipo stringa(es:"32, riga 3, colonna 2)
*/
function nascondiSimbolo(_stringa){
   stringa = new String(_stringa);
   riga = stringa.charAt(0);
   colonna = stringa.charAt(1);
   document.getElementById(riga.toString()+colonna.toString()).innerHTML="*";
}
/*
*Ritorna il simbolo che c'è memorizzato in riga, colonna
*/
function getSimboloAt(riga,colonna){
  riga--;
  colonna--;
  return array[(riga*4)+colonna];
}
/*
*Come sopra, ma overload per un solo parametro di tipo stringa(es:"32, riga 3, colonna 2)
function getSimboloAt(_stringa){
  stringa = new String(_stringa);
  riga = stringa.charAt(0);
  colonna = stringa.charAt(1);
  riga--;
  colonna--;
  return array[(riga*4)+colonna];
}
/*
*Inizia un nuovo gioco
*/
function nuovoGioco(){
  //creo di nuovo un array
  array  = new Array();
  //idem
  caselle_aperte=new Array();
  //array che contiene i simbolo, per ora solo lettere
  var array_simboli= new Array("a","b","c","d","e","f","g","h");
  var simbolo;
  //Cancello il contenuto delle caselle e metto "*" al posto
  for (i=1;i<=4;i++)
     for(c=1;c<=4;c++) {
        riga = Math.floor(i/4)+1;
        colonna = i-((riga-1)*4);
        document.getElementById(i.toString()+c.toString()).innerHTML="*";
        document.getElementById(i.toString()+c.toString()).setAttribute("style","background-color:white;");
     }
   //Preparo l'array a random
  var pos1,pos2;
  for (i=0;i<8;i++){
   while(array[pos1]!=null)
       pos1=Math.floor((Math.random()*16));
   array[pos1]=array_simboli[i];
   while(array[pos2]!=null)
       pos2=Math.floor((Math.random()*16));
   array[pos2]=array_simboli[i];
   }
   //Correggo bug, secondo il quale "a" viene memorizzata una sola volta mentre l'altra è null
   for (i=0;i<16;i++)
     if (array[i]==null){
         array[i]="a";
         break;
     }
}
E da qui potrete vedere il risultato:

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

sabato 10 dicembre 2011

Minicomposer

NOME: Minicomposer
LINGUAGGIO: Borland Pascal
PIATTAFORMA: DOS
LIBRERIE: Giobe.TPU
PERIODO DI SVILUPPO: Finito il 29/05/2003

Ai tempi questo programma-gioco fu pubblicato (con tanto di scritta rossa in prima pagina)  qui: http://pascal.stormdrop.com/ ,un sito dedicato agli sviluppatori di programmini in Pascal, ma ormai da tempo è "freezato", ovvero l'ultimo aggiornamento è l'aggiunta di questo mio programma.

Si tratta di uno spartano programmino che visualizza una tastiera (priva di diesis, ovvero non suonabili) con la quale possiamo suonare note col mouse e registrarle in un file, e poi quest'ultimo può essere riproducibile con un altro programmino incluso nel pacchetto: PlayMusic.

E' stato il mio primo programma "utile" in assoluto, da quando mi avvicinai alla programmazione col Pascal. Inoltre è l'antenato di quel programma che sviluppai nel 2009 per la piattaforma .NET (Prodigious)

Ringrazio il creatore della libreria Giobe.TPU Ing. Giorgio Ober , webmaster del sito www.giobe2000.it che ha creato una libreria direi essenziale per chi programma(va) in Pascal.

Lo potete scaricare da qui: http://www.pernasoft.net/hosted/pascal/progs/minicomp.zip mentre i sorgenti da qui:  http://www.pernasoft.net/hosted/pascal/sorgenti/minicomp.zip

Nota: questo programma per suonare le note utilizza il beeper interno del sistema, ovvero non le casse (eh...era arduo con Turbo Pascal ed ero alle prime armi). Per esempio nel mio portatile con l'emulatore DOS di windows non si sente nulla. Ho provato invece con DosBox  e funziona perfettamente.
Bye
Alberto