Arte generativa con FreeBASIC

Tutti sanno che nell’ottobre del 1917 ci fu una rivoluzione.

Fu proprio in quel mese, infatti, che uscì il primo numero della rivista Lo stile intorno alla quale si sviluppò il movimento artistico del neoplasticismo. Movimento che nel campo della pittura si basava su principi astratti e geometrici. Proprio per questo si fa riferimento al neoplasticismo anche definendolo astrattismo geometrico.

Tra i fondatori della rivista e del movimento ci fu il pittore Piet Mondrian le cui opere più famose sono composizioni dove compaiono soltanto linee nere orizzontali o verticali che dividono la tela in figure geometriche rettangolari o quadrate, alcune delle quali – la minoranza – sono colorate utilizzando soltanto i colori primari (rosso, giallo e blu). Molte parti, quindi, rimangono bianche.

Ma questo blog non parla di programmazione (coding) basandosi sul linguaggio di programmazione FreeBASIC? Cosa c’entra quindi l’arte?

In realtà esiste un elemento in comune tra queste due forme di espressione umana: la creatività.

Sebbene la creatività interessi molte fasi del ciclo della produzione del software (per esempio quella relativa al problem solving), qui vorrei concentrarmi soltanto su uno specifico campo di applicazione dello sviluppo del codice che è quello della cosiddetta Arte generativa: ovvero scrivere del codice che in maniera autonoma, o semi-autonoma, generi delle opere d’arte.

Generatore di immagini in stile Mondrian

Questo blog ha un taglio molto pratico e quindi vediamo subito insieme un esempio di come creare un piccolo programma in FreeBASIC che generi in maniera casuale – e quindi autonoma – delle immagini che richiamino almeno in parte lo stile di Mondrian.

Algoritmo

Ecco in sintesi i passi del nostro algoritmo:

  • Creare una finestra con l’istruzione screenres.
  • Inserire all’interno di un ciclo do-loop il codice per raccogliere l’input dell’utente attraverso l’istruzione getkey e poi con la struttura di controllo select case discriminare la prosecuzione delle operazioni:
    • In un caso disegnare le figure con l’istruzione line le cui dimensioni e colore sono definiti in maniera aleatoria attraverso l’istruzione rnd che genera un numero pseudo-casuale basato sull’algoritmo Mersenne Twister.
    • Nell’altro uscire dal programma.

Codice sorgente

''project:     fb_mondrian.bas
''license:     GNU GPL v3
''version:     beta
''date:        September 20, 2020
''description: Mondrian style images generator
''author:      Luca Evangelisti
''web:         https://ciucoinformatico.home.blog/

dim as long key ''key entered

dim w as integer ''width of canvas
dim h as integer ''height of canvas

declare sub draw_image(w as integer, h as integer)

''canvas's size
w = 1000
h= 617

screenres w, h, 32
windowtitle("fb_mondrian.bas | Mondrian style images generator")

''color:
'' foreground: black
'' background: white
color(rgb(255,255,255),rgb(0,0,0))
cls

'menu
print "Press <Enter> to generate the image."
print "Press <Esc> to exit the programme."

''seeds the random number generator
randomize

do
  key = getkey

  select case key
    case 13 ''carriage return
      cls
      draw_image(w, h)
    case 27 '' quit
      exit do
    case else ''none
  end select

  ''to avoid unwanted or repeated characters,
  ''this loop works until the inkey buffer is empty
  while inkey <> "": wend
loop

''draws image
sub draw_image(w as integer, h as integer)
  dim free_w as integer '' free width
  dim free_h as integer ''free height
  dim as integer fill_color
  dim as integer x1, y1, x2, y2 = 0
  dim as integer margin = 10
  free_w = w
  free_h = h
  do
    y2 = int(rnd * h) + y1
    do
      x2 = int(rnd * w) + x1
      fill_color = int(rnd * 8) + 1
      select case fill_color
        case 1 ''yellow
          line(x1,y1)-(x2,y2),rgb(255,255,0),BF
        case 2 ''red
          line(x1,y1)-(x2,y2),rgb(255,0,0),BF
        case 3 ''blue
          line(x1,y1)-(x2,y2),rgb(0,0,255),BF
        case 4 to 8 ''white
          line(x1,y1)-(x2,y2),rgb(255,255,255),BF
        case else
      end select
      x1 = x2 + margin
    loop until x2 > w
    x1 = 0
    y1 = y2 + margin
  loop until y2 > h
end sub

Provate pure liberamente a modificarlo.

Ciao e al prossimo articolo. 🙂

CLUMSY beta version

Good news! 🙂

CLUMSY si arricchisce delle classiche funzionalità taglia (cut), copia (copy) e incolla (paste).

CLUMSY è un semplice editor di testo multipiattaforma per la console (o emulatore di terminale). In ambienti Windows tipicamente si indica la console con il termine “Prompt dei comandi”, mentre in ambienti GNU-Linux con “Terminale”. CLUMSY permette inoltre di compilare direttamente un file sorgente .bas utilizzando il compilatore FreeBASIC, e mandare quindi in esecuzione il file eseguibile. CLUMSY è distribuito sotto i termini della Licenza GNU General Public License versione 3. Licenza che permette di ridistribuire e modificare il codice sorgente liberamente. CLUMSY è scritto in FreeBASIC.

Ci sono però delle limitazioni, neanche tanto piccole. 😉

  • Cut (F6) taglia una sola linea di codice e la elimina per intero (occhio!).
  • Copy (F7) idem con patate: ovvero copia un’intera e sola linea di codice.
  • Paste (F8) incolla la linea di codice precedentemente selezionata con la funzione F6 o F7.

Oltre questo, CLUMSY è un programma vecchio stile, dove l’uso del mouse è stato volutamente escluso (sigh!).

Clumsy è un termine della lingua inglese. Traducendolo in italiano si ottiene l’immagine di una persona maldestra o di un oggetto mal costruito. 🙂

Lo scopo di CLUMSY è però semplicemente quello di testare velocemente piccole porzioni di codice FreeBASIC, giusto per impratichirsi con questo linguaggio di programmazione senza dover perdere troppo tempo con un IDE (Integrated Development Environment) decisamente più serio e potente di cui si avrà bisogno una volta che si dovrà scrivere un programma di una certa complessità.

Potete scaricare i files sorgenti di CLUMSY qui.

Ecco anche un breve video introduttivo a CLUMSY.

Buon coding! 🙂

Vittorio Albertoni, Intramontabile BASIC

Tempo fa, giracchiando qua e là per il Web, mi sono imbattuto sul sito di Vittorio Albertoni ed è stata una piacevole scoperta.

Vittorio è un vero appassionato della tecnologia informatica (information technology) e i suoi articoli hanno il pregio di essere sintetici, chiari e molto accurati.

In particolare volevo segnalarvi l’articolo Intramontabile BASIC che contiene anche un link ad una utilissima Introduzione al linguaggio FreeBASIC (il documento è liberamente scaricabile, stampabile e distribuibile).

Buona lettura… 🙂

FreeBASIC – getkey

Dopo aver visto come si configura la console e aver imparato in un altro articolo ad usare la funzione multikey, oggi vedremo come ottenere il codice ASCII di alcuni pulsanti (keys) della nostra tastiera (keyboard) utilizzando la funzione getkey.

Il codice ASCII viene restituito dalla funzione getkey come valore di tipo long (vedi Tabella dei limiti dei tipi standard).

Vediamo adesso per passi come si può lavorare con questa istruzione:

  • per prima cosa è bene dimensionare una variabile ti tipo long per memorizzare il codice ASCII del pulsante premuto;
  • la funzione getkey va quindi inserita all’interno di un ciclo (per esempio il ciclo do-loop) in modo tale che il programma rimanga costantemente in ascolto dell’input da tastiera dell’utente;
  • a questo punto, all’interno del ciclo do-loop, si inserisce la struttura di controllo select case in modo tale da distinguere i vari codici ASCII che verranno ricevuti.

Una volta ricevuto un codice ASCII la memoria tampone della tastiera (keyboard buffer) viene ripulita automaticamente. Se non viene premuto alcun pulsante la funzione getkey rimane in ascolto e il programma si ferma.

Alcuni pulsanti della tastiera, come quelli funzione (F1, F2, ecc.) o quelli di spostamento del cursore, sono rappresentati da due caratteri e quindi occorrerà, per esempio, identificarli con la struttura di controllo if-then-else e poi operare uno spostamento (shift) di 8 bits in modo tale da scartare il carattere che non ci serve. Per farlo useremo l’istruzione shr.

Un sito molto utile dove trovare descritti i vari codici è theasciicode.com.ar

Per vedere un esempio pratico riporto una parte del codice di CLUMSY ripulita dalle istruzioni che al momento non ci servono:

dim as long key ''key entered

''main loop
do
  key = getkey
  if key > 255 then ''cursor keys
    key = key shr 8 ''shifts the bits to the right
    select case key
      case 59 ''F1
        ''...
      case 60 ''F2
        ''...
      case 61 ''F3
        ''...
      case 62 ''F4
        ''...
      case 63 ''F5
        ''...
      case 64 ''F6
        ''...
      case 65 ''F7
        ''...
      case 66 ''F8
        ''...
      case 67 ''F9
        ''...
      case 68 ''F10
        ''not used (problems with Ubuntu terminal)
      case 71 ''home
        ''...
      case 72 ''up
        ''...
      case 73 ''page up
        ''...
      case 79 ''end
        ''...
      case 80 ''down
        ''...
      case 81 ''page down
        ''...
      case 77 ''right
        ''...
      case 75 ''left
        ''...
      case 83 ''delete
        ''...
      case else ''none
    end select
  else ''ASCII extended
    select case key
      case 32 to 126 ''printable characters
        ''...
      case 128 to 255 ''extended ASCII characters
        ''...
      case 8 ''backspace
        ''...
      case 13 ''carriage return
        ''...
      case 27 '' quit
        exit do
      case else
        ''none
    end select
  end if

''to avoid unwanted or repeated characters,
''this loop works until the inkey buffer is empty
  while inkey <> "": wend

loop

Bene. Un altro piccolo passo è stato fatto. 😉

FreeBASIC – configurare la console

Esiste ancora tutto un mondo di applicazioni per console (console application), alcune delle quali si permettono anche il lusso di fare a meno del mouse.

Per esempio ne (the nice editor) è un noto editor di testo per console sviluppato da Sebastiano Vigna dell’Università degli Studi di Milano.

Potrà sembrare una cosa da inguaribili nostalgici – e forse lo è – ma in questo blog trovano posto anche questo tipo di applicazioni.

Seguendo il metodo imparare facendo (learning by doing), approfondiremo passo passo le procedure per la console messe a disposizione dal compilatore FreeBASIC cercando di realizzare un semplice editor di testo per console che avrà quindi una interfaccia utente basata sul solo testo (text-based user interface). Un editor di testo che magari possa anche interagire con il compilatore FreeBASIC in modo tale che possa essere utilizzato anche come semplice IDE (Integrated Development Environment).

Il nostro piccolo progetto si chiamerà CLUMSY che in inglese suona più o meno come oggetto mal costruito. 😉

CLUMSY è ospitato su GitHub in questo repository.

Configurare la console

Come prima cosa dobbiamo informare il compilatore FreeBASIC che abbiamo intenzione di aprire una finestra grafica in modalità console. Per farlo dobbiamo impostare il valore della modalità grafica (parametro mode della istruzione screen) al valore 0 (zero).

Imposteremo poi il colore del primo piano e dello sfondo con l’istruzione color sfruttando la tavolozza dei colori di default.

Faremo uso dell’istruzione cls per pulire lo schermo e rendere attiva l’impostazione dei colori precedentemente scelta.

L’istruzione cls può essere fatta seguire da un parametro che può assumere i seguenti tre valori:

  • 0 -> pulisce l’intero schermo;
  • 1 -> pulisce l’area grafica se definita con l’istruzione view (se lo schermo è stato settato in modalità console questa istruzione non ha alcun effetto);
  • 2 -> pulisce l’area di stampa se definita con l’istruzione view print.

Se si utilizza l’istruzione cls senza alcun parametro, nel caso sia stata definita un’area grafica o di stampa, questa verra pulita, altrimenti viene pulito l’intero schermo.

Utilizzeremo poi l’istruzione width in combinazione con le istruzioni hiword e loword, per conoscere rispettivamente il numero massimo di righe e di colonne messe a disposizione di default dalla nostra finestra terminale (o prompt dei comandi in ambiente Windows).

Per posizionare il testo sfrutteremo l’istruzione locate.

Infine, per definire l’area di testo su cui andremo a scrivere utilizzaremo l’istruzione view print specificando la riga iniziale e quella finale.

''project: clumsy | pre-alpha version
''description: text editor for console
''author: Luca Evangelisti
''last update: 3th February 2020
''GitHub: https://github.com/lucaevangelisti/clumsy
''license: https://github.com/lucaevangelisti/clumsy/blob/master/LICENSE

dim as uinteger max_rows, max_cols

screen 0 ''console-mode functionality

max_rows = hiword(width) ''max number of rows
max_cols = loword(width) ''max number of columns

color 7, 0 ''text grey, background black
cls 0 ''clears the entire screen

''footer info
locate max_rows, 1
print "clumsy is a silly text editor for console"; _
      " | pre-alpha version" _
      " | ^H for Help";

''sets the printable area of the console screen
view print 1 to (max_rows - 1)

color 0, 7 ''text black, background grey
cls 2 ''clear only the text viewport

''text
print "Hello, world!";

sleep

Nel prossimo articolo vedremo come ottenere informazioni talla tastiera sfruttando la funzione getkey.

Advent of Code 2019 – Day 2

I due problemi del Day 2 proposti da Eric Wastl su Advent of Code cominciano ad essere veramente impegnativi.

Per risolverli ho utilizzato diversi strumenti messi a disposizione da FreeBASIC:

Il codice sorgente relativo alle due soluzioni è disponibile su GitHub:

Attenzione però!

L’algoritmo utilizzato per scrivere il codice contenuto nel file sorgente 02b.bas contiene una parte che lo rende altamente inefficiente.

Lascio a voi trovarla. 😉

Quando progettiamo i nostri algoritmi non dobbiamo mai dimenticarci di curare la fase di ottimizzazione.

Ed ora… Day 03!

Advent of Code 2019 – Day 1

Il testo completo in inglese lo trovate qui.

Puzzle n.1

In questo primo puzzle Eric, l’ideatore di Advent of Code, ci chiede di calcolare la quantità di carburante necessario per lanciare nello spazio dei moduli spaziali.

Il puzzle è molto semplice: ci viene detto che per calcolare la quantità di carburante per ciascun modulo occorre basarsi sulla sua massa e applicare questa formula:

Dividi la massa per 3, approssima il risultato per difetto e quindi sottrai il valore 2.

I valori delle masse dei moduli ci vengono forniti sotto forma di lista numerica.

Definizione dell’algoritmo

In questo caso i passi del nostro algoritmo sono praticamente già descritti in chiaro nel testo:

  1. Leggi il valore della massa (m) del primo modulo.
  2. Applica la formula data per ricavare la quantità di carburante necessario (f) per il lancio di un singolo modulo.
  3. Somma tutti i valori ottenuti per ottenere il totale del carburante (f_tot) necessario per tutti i moduli.

Scrittura del codice

Prima di iniziare a scrivere il codice, salviamo la lista delle masse in un file di testo che nomineremo input.txt e che salveremo per semplicità nella stessa cartella dove andremo a salvare il nostro file sorgente che possiamo nominare per esempio 01a.bas.

''nome file: 01a.bas

dim m as string ''variabile per la lettura della massa
dim as integer f, f_tot ''variabili per il carburante

''reset variabili
m = ""
f = 0
f_tot = 0

''apertura del file
open "input.txt" for input as #1

  line input #1, m ''lettura della prima massa

  ''ciclo per la lettura di tutte le masse e
  ''calcolo del carburante necessario
  do until m = ""
    f = int(val(m) / 3) - 2 ''carburante per un modulo
    f_tot = f_tot + f ''carburante totale
    line input #1, m ''lettura di un'altra massa
  loop

''stampa il risultato del puzzle n.1
print "Totale carburante necessario = "; f_tot

close #1 ''chiusura del file

Ovviamente questo è soltanto un esempio di possibile risoluzione: potremmo infatti decidere di creare un array per memorizzare tutti i valori delle masse, oppure potremmo creare una funzione ad hoc per il calcolo del carburante necessario.

Insomma, nella programmazione esistono sempre più modi di risolvere un problema. E questa è proprio una delle cose che la rendono così interessante.

Puzzle n.2

Il secondo puzzle del Day 1 comincia ad essere un po’ più impegnativo.

Eric ci fa notare che anche il carburante di un modulo è a sua volta una massa, e quindi anche questa massa per essere lanciata nello spazio ha bisogno di carburante. Ma, attenzione! Anche il carburante necessario per la massa del carburante è a sua volta una massa…

Insomma, lo avete capito, qui bisogna escogitare un algoritmo iterativo un po’ più raffinato.

Come tutte le iterazioni, anche questa deve avere una fine, altrimenti sono guai. Nel testo del puzzle n.2 ci viene detto che, applicando la stessa formula che abbiamo visto per il puzzle n.1, una volta che otteniamo una quantità di carburante negativa, allora dobbiamo fermarci.

Definizione dell’algoritmo

L’idea è quella di utilizzare un altro ciclo do-loop all’interno del ciclo principale che itera le masse dei moduli.

In questo nuovo clico interno il valore del carburante necessario dovrà rientrare come input alla formula di calcolo. Nella condizione di controllo del ciclo andremo quindi a verificare per ogni iterazione che il carburante necessario non sia negativo. Infine la variabile del carburante necessario al carburante dovrà incrementarsi soltanto se i valori del carburante calcolato sono positivi.

Scrittura del codice

Riprandendo il codice del puzzle n.1, andiamo ad aggiungere, nella parte dedicata alla dichiarazione delle variabili, una nuova variabile che conterrà la somma del carburante necessario al carburante.

dim as integer f, f_tot, f_tot2 ''variabili per il carburante

Poi andremo ad inserire all’interno del primo ciclo un secondo ciclo dedicato proprio al calcolo del carburante necessario al carburante.

  do until m = ""
    f = int(val(m) / 3) - 2 ''carburante per un modulo
    f_tot = f_tot + f ''carburante totale

    ''puzzle n.2
    do until f <= 0
      f = int(f / 3) - 2
      if f > 0 then f_tot2 = f_tot2 + f
    loop

    line input #1, m ''lettura di un'altra massa
  loop

Ed infine possiamo inserire una riga per la stampa del risultato.

''stampa il risultato del puzzle n.1
print "Puzzle n.1 - Totale carburante necessario = "; f_tot

''stampa il risultato del puzzle n.2
print "Puzzle n.2 - Totale carburante necessario = "; f_tot + f_tot2

Bene. Il Day 1 è fatto! 🙂

FreeBASIC – gestione degli errori

La gestione degli errori è un aspetto molto importante che un programmatore deve conoscere per scrivere del codice affidabile.

Può capitare per esempio di dover aprire un file, ma, per qualche motivo, il file è stato cancellato, spostato o rinominato. In questi casi è bene prevedere un controllo per intercettare l’eventuale errore ed informare così l’utente del problema.

A tale scopo FreeBASIC mette a disposizione la funzione err che ritorna lo specifico codice di errore del problema riscontrato.

In FreeBASIC esiste una lista dei codici di errore che il programmatore può gestire.

Vediamo un semplice esempio:

dim err_code as integer

open "file.txt" for input as #1

err_code = err

select case err_code
  case 0
    close #1 'nessun errore riscontrato
  case 1
    print "Illegal function call"
  case 2
    print "File not found signal"
  case 3
    print "File I/O error"
  case 4
    print "Out of memory"
  case 5
    print "Illegal resume"
  case 6
    print "Out of bounds array access"
  case 7
    print "Null Pointer Access"
  case 8
    print "No privileges"
  case 9
    print "Interrupted signal"
  case 10
    print "Illegal instruction signal"
  case 11
    print "Floating point error signal"
  case 12
    print "Segmentation violation signal"
  case 13
    print "Termination request signal"
  case 14
    print "Abnormal termination signal"
  case 15
    print "Quit request signal"
  case 16
    print "Return without gosub"
  case 17
    print "End of file"
end select

sleep

Abbastanza semplice direi. 😉

In FreeBASIC esistono anche altre istruzioni e procedure che permettono una gestione degli errori avanzata.

A rileggerci al prossimo articolo.

FreeBASIC – libreria dinamica

A differenza della libreria statica che abbiamo visto nel precedente articolo, la libreria dinamica viene caricata soltanto quando il nostro programma è in esecuzione (runtime).

Altra importante caratteristica è che la libreria dinamica può essere condivisa (shared) tra più programmi in esecuzione.

Passiamo subito alla pratica e scriviamo quattro files.

Modulo libreria

''nome del file: funzioni_utili.bas

''funzione per il calcolo dell'area di un quadrato
public function area_quadrato(byval l as integer) _
       as integer
  return l * l
end function

Una volta salvato il file dobbiamo compilarlo specificando al compilatore FreeBASIC che vogliamo creare proprio una libreria dinamica:

fbc -dll [eventuale percorso]\funzioni_utili.bas

Se tutto è andato in porto troveremo nella stessa cartella contenente il file sorgente la nostra libreria.

Sui sistemi operativi Linux l’estensione del file sarà .so, mentre sul sistema operativo Windows l’estensione del file sarà .dll.

A questo punto il file della libreria dinamica va spostato in specifiche cartelle a seconda del sistema operativo:

  • su Linux: /usr/lib
  • su Windows: la cartella di sistema (p.e. C:\Windows\System32)

Interfaccia

Come abbiamo già fatto per rendere facilmente disponibile la libreria statica, anche per la libreria dinamica creiamo la nostra interfaccia (API) sfruttando un file di intestazione:

''nome file: funzioni_utili.bi

#inclib "funzioni_utili" ''include il modulo libreria

''dichiara la funzione
declare function area_quadrato(byval l as integer) _
        as integer

Test

Creiamo adesso due programmi sostanzialmente identici che, una volta mandati in esecuzione, condivideranno la medesima libreria dinamica.

Programma 1

''nome file: test1.bas

#include "funzioni_utili.bi" ''include l'interfaccia

dim a as integer

''stampa l'area del quadrato
do
  input "Test 1: Inserisci la lunghezza del lato: "; a
  print "L'area del quadrato vale: "; area_quadrato(a)
loop until a = 0

Programma 2

''nome file: test2.bas

#include "funzioni_utili.bi" ''include l'interfaccia

dim a as integer

''stampa l'area del quadrato
do
  input "Test 2: Inserisci la lunghezza del lato: "; a
  print "L'area del quadrato vale: "; area_quadrato(a)
loop until a = 0

Bene! Con questo articolo possiamo per ora ritenere la nostra conoscenza dell’uso delle librerie in FreeBASIC sufficiente.

Ricordiamoci però sempre di ben commentare le nostre librerie per aiutare chi volesse utilizzarle a capire velocemente come funzionano.

A rileggerci al prossimo articolo. 🙂