Archivi categoria: Getting started

Articoli sulle basi del linguaggio di programmazione Processing.

Array bidimensionali: esercizio alta difficoltà (soluzione)

Ecco la soluzione all’esercizio di difficoltà media proposto nell’articolo Array bidimensionali.

Esercizio

Partendo dalla soluzione all’esercizio di difficoltà media, sareste in grado di modificarlo ulteriormente per fare in modo che la griglia non sia necessariamente 8×8?

Continua la lettura di Array bidimensionali: esercizio alta difficoltà (soluzione)

Array bidimensionali: esercizio media difficoltà (soluzione)

Ecco la soluzione all’esercizio di difficoltà media proposto nell’articolo Array bidimensionali.

Esercizio:

Siete in grado di modificare lo script affinché, ad esempio, modificando i valori all’interno di size() mi venga disegnata sempre una scacchiera di 8×8 che occupi l’intera grandezza della finestra (anche quando non è quadrata)?

Continua la lettura di Array bidimensionali: esercizio media difficoltà (soluzione)

Array bidimensionali in Processing

In programmazione gli Array sono utili per archiviare in modo ordinato una serie di informazioni. Per chi avesse bisogno di un recap, questo è l’articolo in cui ho introdotto il concetto di array e il loro utilizzo in Processing.

A volte può capitare di avere a che fare con informazioni che non possono essere rappresentate in un’unica dimensione. Per questo motivo abbiamo bisogno di una struttura di dati multidimensionale: un array bidimensionale, ovvero un array di array, ci permette di lavorare con facilità su dati di questo tipo.

Per fare un esempio, proviamo a pensare di rappresentare i quadrati neri e bianchi di una scacchiera utilizzando il valore 0 per i primi e 1 per i secondi.

Partiamo da un array monodimensionale:

int[] scacchiera = { 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1 };

Con questo array ho rappresentato tutti e 64 i tasselli ma non è chiara la struttura a righe e colonne tipica della scacchiera. Ecco che, invece, inserendo ciascuna riga in un array separato le cose migliorano sensibilmente:

int[][] scacchiera = { 
 {1, 0, 1, 0, 1, 0, 1, 0},
 {0, 1, 0, 1, 0, 1, 0, 1},
 {1, 0, 1, 0, 1, 0, 1, 0},
 {0, 1, 0, 1, 0, 1, 0, 1},
 {1, 0, 1, 0, 1, 0, 1, 0},
 {0, 1, 0, 1, 0, 1, 0, 1},
 {1, 0, 1, 0, 1, 0, 1, 0},
 {0, 1, 0, 1, 0, 1, 0, 1} };

Abbiamo costruito il nostro primo array bidimensionale.

Dichiarazione e inizializzazione

Le regole da rispettare in merito alla dichiarazione e inizializzazione dell’array bidimensionale rimangono identiche rispetto a quanto già visto in passato: è necessario indicare il tipo di dato, il nome dell’array ed è fondamentale stabilire una grandezza iniziale.

Sempre prendendo come riferimento l’esempio della scacchiera, inizializziamo l’array bidimensionale con 8 valori per le righe e 8 per le colonne:

int[][] scacchiera = new int[8][8];

Array bidimensionali e nested loop

I loop annidati sono il modo migliore per accedere a tutti i dati presenti in un array bidimensionale. Proviamo a disegnare la nostra scacchiera:

/*
 * Array bidimensionali e scacchi
 * Federico Pepe, 19.02.2017
 * http://blog.federicopepe.com/processing
*/

int[][] scacchiera = { 
  {1, 0, 1, 0, 1, 0, 1, 0}, 
  {0, 1, 0, 1, 0, 1, 0, 1}, 
  {1, 0, 1, 0, 1, 0, 1, 0}, 
  {0, 1, 0, 1, 0, 1, 0, 1}, 
  {1, 0, 1, 0, 1, 0, 1, 0}, 
  {0, 1, 0, 1, 0, 1, 0, 1}, 
  {1, 0, 1, 0, 1, 0, 1, 0}, 
  {0, 1, 0, 1, 0, 1, 0, 1} };

void setup() {
  size(480, 480);
  
  int rows = 8;
  int cols = 8;
  
  for (int i = 0; i < rows; i++) {
    for(int j = 0; j < cols; j++) {
      if(scacchiera[i][j] == 0) {
        fill(0);
      } else {
        fill(255);
      }
      rect(i*60, j*60, 60, 60);
    }
  }
}

void draw() {
}

All'inizio del programma troviamo l'array bidimensionale che contiene i valori su cui dobbiamo lavorare. All'interno della funzione setup() abbiamo impostato la grandezza della finestra e abbiamo creato due variabili per avere il numero di righe (rows) e il numero di colonne (cols).

Utilizzando due loop andiamo a vedere tutti i valori presenti all'interno dell'array: se il valore trovato è uguale a 0 la casella sarà nera, altrimenti bianca. Completiamo il programma andando effettivamente a disegnare la singola casella.

Esercizio di difficoltà media

In questo script ci sono diversi parametri "hard-coded". Siete in grado di modificare lo script affinché, ad esempio, modificando i valori all'interno di size() mi venga disegnata sempre una scacchiera di 8x8 che occupi l'intera grandezza della finestra (anche quando non è quadrata)?

Esercizio di difficoltà alta

Partendo dalla soluzione all'esercizio di difficoltà media, sareste in grado di modificarlo ulteriormente per fare in modo che la griglia non sia necessariamente 8x8?

Fine (del livello base)

Se siete arrivati a leggere fino a questo post, ho una buona notizia da darvi: abbiamo finito gli argomenti base di Processing. Le fondamenta ci sono tutte e, nella pagina riassuntiva dedicata, potete ripercorrere – tutorial dopo tutorial – il percorso iniziato ben otto mesi fa.

Abbiamo finito? Certo che no. Alcuni argomenti che abbiamo già trattato sarebbero da approfondire e ci sono ancora tantissime cose da imparare e io non ho certo intenzione di fermarmi.

Quando ero partito lo scorso luglio avevo pianificato soltanto dieci post/argomenti di cui volevo parlare che, col tempo, si sono evoluti in ben trenta articoli.

Se devo essere sincero, non ho ancora messo nero su bianco quali saranno i prossimi argomenti ma sto valutando una riorganizzazione dei contenuti e della struttura degli articoli in modo da renderli ancora migliori.

Mi piacerebbe anche ricevere dei suggerimenti sui temi che vorreste che venissero trattati o sulla forma che, secondo voi, dovrebbero avere i contenuti futuri. Potete lasciare un commento a questo post oppure scrivermi via e-mail.

Come sempre, vi invito a iscrivervi alla newsletter dedicata al creative coding per rimanere aggiornati sulle prossime novità.

Soluzione al “trova l’errore”

Soluzione: trova l'errore

Prima di concludere il post era rimasto in sospeso l’esercizio “trova l’errore” del post precedente. Non avendo specificato esattamente qual era il problema nascosto nel codice, prima di darvi la soluzione vorrei assicurarmi che fosse tutto chiaro: come mostrato nell’immagine lo sfondo si è colorato di rosso nonostante i due cerchi non fossero sovrapposti uno all’altro.

Ecco che il problema era dovuto a una svista nel codice della funzione isOver all’interno della classe Ball.pde:

if(distance <= (radius+b.radius)/2)

è, infatti, necessario dividere per due la somma dei due raggi perché, benché la variabile si chiami radius, quando creiamo i nostri cerchi nel constructor, utilizziamo radius come fosse un diametro:

ellipse(ellipseX, ellipseY, radius, radius);

Ci eravate arrivati?

Interazione tra oggetti

Dopo la parentesi sugli array (parte 1, parte 2), torniamo a parlare di oggetti. Una domanda che può sorgere spontanea è: possono due oggetti interagire tra loro?

Riprendendo porzioni di codice che già abbiamo usato in precedenza oggi realizzeremo uno sketch in cui sarà presente un’interazione tra due oggetti: cambieremo il colore dello sfondo da nero a rosso quando due cerchi si sovrapporranno uno all’altro.

Analizziamo il problema

Come posso sapere se due cerchi si intersecano? Per crearli in Processing utilizziamo la funzione ellipse() che accetta quattro parametri: posizione x e y del centro più la larghezza e l’altezza. Nel caso in cui questi ultimi due parametri coincidano avrò un cerchio.

Grazie agli studi fatti alle scuole elementari e medie sappiamo che esiste una cosa chiamata raggio che determina la distanza tra il centro e il bordo del cerchio. A questo punto, la soluzione al problema dovrebbe esservi chiara: se la distanza tra il centro del primo cerchio e del secondo è inferiore alla somma dei due raggi, allora i cerchi saranno sovrapposti, altrimenti no.

Se avete dimenticato la geometria, questa immagine dovrebbe esservi d’aiuto per visualizzare quello che ho appena scritto:

Cerchi sovrapposti

Punto di partenza: copia-incolla

Ora che abbiamo individuato il fulcro del nostro programma, faccio copia-incolla del codice dall’esercizio Bouncing Ball, semplificandolo in alcune parti: all’interno della mia classe “Ball” utilizzerò un solo constructor a cui passerò solo il valore relativo al raggio del cerchio. Posizione x e y del centro e velocità di spostamento saranno creati in modo casuale.

Mantengo inalterato il metodo display() mentre modifico leggermente il metodo move() nel quale inserisco anche la parte di codice relativa al controllo sui bordi.

Nel programma principale creo e inizializzo due oggetti di tipo Ball chiamati myBall1 e myBall2 e, all’interno di draw() li visualizzo e li faccio muovere:

class Ball {
  // Variabili
  int radius;
  float ellipseX, ellipseY, speedX, speedY;
  // Constructor
  Ball(int _radius) {
    radius = _radius;
    ellipseX = random(width);
    ellipseY = random(height);
    speedX = random(2, 5);
    speedY = random(2, 5);
  }
  // Metodi
  void display() {
    ellipse(ellipseX, ellipseY, radius, radius);
  }
  
  void move() {
    ellipseX = ellipseX + speedX;
    ellipseY = ellipseY + speedY;
    if (ellipseX > width || ellipseX < 0) {
      speedX = speedX * -1;
    }
    if (ellipseY > height || ellipseY < 0) {
      speedY = speedY * -1;
    }
  }
}
Ball myBall1, myBall2;

void setup() {
  size(700, 500);
  myBall1 = new Ball(100);
  myBall2 = new Ball(50);
}

void draw() {
  background(0);
  myBall1.display();
  myBall2.display();
  myBall1.move();
  myBall2.move();
}

Determinare la sovrapposizione

Ora non mi resta che creare un nuovo metodo all'interno della classe per verificare l'effettiva sovrapposizione dei due cerchi. Il primo passo che potremmo pensare di fare è creare una funzione in cui utilizziamo sei parametri (posizione x, y e raggio dei due cerchi) per verificare l'intersezione dei due cerchi.

Dal momento che utilizziamo gli oggetti, possiamo arrivare a una soluzione più semplice: l'oggetto myBall1 interseca myBall2?

Aggiungiamo un metodo alla nostra classe: il tipo di dato che verrà restituito da questo nuovo metodo sarà di tipo booleano (true in caso di sovrapposizione e false in caso contrario) per cui scriviamo quanto segue:

boolean isOver() {

}

Per calcolare la distanza tra due punti utilizziamo la funzione dist() che accetta quattro parametri e restituisce un dato di tipo float:

dist(x1, y1, x2, y2);

Chiaramente i primi due parametri saranno ellipseX ed ellipseY del cerchio che stiamo prendendo in esame. Ma come facciamo a passare i dati del secondo cerchio? Nel secondo post relativo agli oggetti abbiamo visto come passare dei parametri all'interno del costructor, possiamo usare lo stesso sistema per passare delle variabili anche ai metodi.

La cosa davvero interessante è che anziché passare una variabile, passeremo l'intero oggetto:

boolean isOver(Ball b) {
 float distance = dist(ellipseX, ellipseY, b.ellipseX, b.ellipseY);
 if(distance <= (radius+b.radius)) {
  return true;
 } else {
  return false;
 }
}

Ecco il codice completo della classe Ball:

class Ball {
  
  int radius;
  float ellipseX, ellipseY, speedX, speedY;
  
  Ball(int _radius) {
    radius = _radius;
    ellipseX = random(width);
    ellipseY = random(height);
    speedX = random(2, 5);
    speedY = random(2, 5);
  }
  
  void display() {
    ellipse(ellipseX, ellipseY, radius, radius);
  }
  
  void move() {
    ellipseX = ellipseX + speedX;
    ellipseY = ellipseY + speedY;
    if (ellipseX > width || ellipseX < 0) {
      speedX = speedX * -1;
    }
    if (ellipseY > height || ellipseY < 0) {
      speedY = speedY * -1;
    }
  }
  
  boolean isOver(Ball b) {
    float distance = dist(ellipseX, ellipseY, b.ellipseX, b.ellipseY);
    if(distance <= (radius+b.radius)) {
      return true;
    } else {
      return false;
    }
  }
}

Interazione tra due oggetti

Aggiorniamo il codice dello sketch principale aggiungendo all'interno di draw le seguenti righe di codice:

if(myBall2.isOver(myBall1)) {
 background(255, 0, 0);
}

Ed il gioco è fatto.

Trova l'errore

Anziché lasciarvi con un esercizio, questa volta ho un'altra sfida per voi. Il nostro programma funziona ma c'è un piccolo errore che non lo fa funzionare esattamente come ci eravamo prefissati. Sapete individuarlo?

Per aiutarvi allego un'immagine:

Trova l'errore