Commodore: 8 bit a caso, ma non troppo

Girovagando sui vari gruppi Facebook dedicati al Commodore 64 ho trovato un link a questa pagina, che riporta uno stralcio di codice interessante, compatibile in realtà con tutti i computer Commodore a 8bit (PET, CBM-II, Vic20, C64, C16, plus/4, C128):

10 x=rnd(-1963):fori=1to81:y=rnd(1):next
20 forj=1to5:printchr$(rnd(1)*16+70);:next
30 printint(rnd(1)*328)-217

Leggendolo, a prima vista sembra estrarre numeri casuali ogni volta, e stamparli in modo diverso. A una prima lettura pare quindi che ogni esecuzione debba dare un risultato differente, ma non è così. Infatti eccone il risultato:

screen-shot-2016-09-14-at-18-42-48

Quasi incredibile, vero? Stampa sempre e solo la stringa “FORUM 64“. Per capire come sia possibile, bisogna sapere come lavora la funzione RND(x), l’unico comando che il Commodore BASIC mette a disposizione per la generazione di numeri casuali. Il parametro x della funzione è un numero reale che può assumere valore negativo, positivo, oppure zero. In ogni caso la funzione restituisce un numero reale che può variare tra 0 e 1, escluso 1.

Prima di spiegare il significato di ognuna di queste possibilità, bisogna capire come funziona la generazione di numeri casuali in un computer, partendo dal presupposto che si tratterà comunque di numeri pseudocasuali e non di veri numeri casuali. Una sequenza di numeri pseudocasuali è una sequenza sempre predeterminata, in cui ogni volta si sceglie un diverso punto da cui partire (chiamato seme, in inglese seed), in modo che ogni volta si otterrà una sequenza differente dalla precedente, perché diverso è il punto di partenza. Il punto di partenza viene tipicamente scelto tramite un valore legato al tempo (per esempio il numero di secondi da cui il computer è acceso, in modo che questo valore cambi a ogni esecuzione).

Un approfondimento divulgativo sull’argomento è possibile leggerlo su QueryOnline, a questo link.

Detto questo, vediamo ora quali sono i possibili valori del parametro della funzione RND:

  • RND(<numero negativo>): il numero negativo rappresenta il seme (o seed) di cui si parlava sopra. Richiamare la funzione tramite un numero negativo significa quindi inizializzare il generatore di numeri pseudocasuali impostando il punto di partenza all’interno della sequenza. Se per esempio all’inizio del programma richiamo sempre la funzione RND(-1), utilizzerò sempre -1 come seed, quindi otterrò in seguito sempre la stessa sequenza di numeri. Con RND(-2) otterrò una sequenza diversa rispetto a prima, ma comunque sempre la stessa e ripetibile. Quindi è importante non solo che si tratti di un numero negativo, è molto importante quale numero sia.
  • RND(<numero positivo>): il numero positivo può essere uno qualsiasi, il risultato non cambia. Il significato è: ritorna il prossimo numero pseudocasuale della sequenza inizializzata tramite il caso precedente. Pertanto dopo aver inizializzato il generatore tramite RND(-n), i valori successivi vanno prelevati utilizzando come parametro un numero positivo qualsiasi. In altre parole, usare RND(1), RND(2) o RND(4383) produrrà lo stesso risultato.
  • RND(0): il numero pseudocasuale generato dipende dallo stato del clock interno del computer. Pertanto il valore prodotto non apparterrà a una sequenza data, ma dipenderà dal momento in cui viene richiamata la funzione.

Esempio di generazione della stessa sequenza

10 print rnd(-1)
20 for i=1 to 10:print rnd(1):next

Sostituendo il -1 con altri numeri negativi a piacere, varierà la sequenza di numeri generati. Provare per credere.

Utilizzo del timer di sistema come seed

La variabile riservata TI contiene il numero di sessantesimi di secondo da cui il computer è acceso. Si può pertanto invertire di segno per utilizzare questo numero come seed inizializzando di conseguenza il generatore di numeri casuali in base al tempo, e generare a ogni esecuzione una sequenza differente:

10 print rnd(-ti)
20 for i=1 to 10:print rnd(1):next

In dialetti BASIC differenti dal Commodore BASIC (GW-BASIC, AmigaBASIC e innumerevoli altri) esiste il comando RANDOMIZE seguito dal numero che rappresenta il seed. In questi casi per misurare il tempo di solito esiste la variabile riservata TIMER, per cui in questi casi il generatore di numeri casuali viene inizializzato con il tipico comando “RANDOMIZE TIMER“, che avrete sicuramente letto innumerevoli volte in altrettanto innumerevoli listati per questi dialetti BASIC.

Spiegazione del listato di partenza

Eccoci quindi a spiegare le tre righe misteriose che producono sempre la scritta “FORUM 64“:

10 x=rnd(-1963):fori=1to81:y=rnd(1):next

Inizializza il generatore di numeri pseudocasuali con il seed-1963“, quindi scorre in avanti di 81 numeri la sequenza, prelevandone uno alla volta i valori con rnd(1).

20 forj=1to5:printchr$(rnd(1)*16+70);:next

Preleva dalla sequenza precedentemente inizializzata 5 valori compresi tra 70 e 85 e ne stampa il corrispettivo carattere in codice ASCII (sono quindi lettere comprese fra F e U)

30 printint(rnd(1)*328)-217

Preleva dalla sequenza un numero compreso tra -217 e 110 e lo stampa.

Essendo il generatore inizializzato sempre con lo stesso seed, la sequenza generata dai vari RND(1) sarà sempre la stessa. Immaginate ora la quantità di tempo spesa dall’autore di questo spezzone di codice per trovare la giusta combinazione tra seed e punto della sequenza in cui la successione di numeri estratti ha il giusto intervallo per poter generare i caratteri FORUM. Dopo di che, inchinatevi a lui, chiunque egli sia 🙂

Link esterni

Francesco Sblendorio

Francesco Sblendorio nasce nel 1977. Nel 1985 fa conoscenza con il mondo dei computer attraverso un Commodore 16: da quel momento la discesa verso il lato oscuro è inesorabile e si trasforma in un geek impenitente. Nell'ambito del retrocomputing ha un sogno: che i vecchi computer non debbano semplicemente sopravvivere (conservando la loro funzionalità), piuttosto devono vivere, attraverso nuovi software sviluppati oggi per i computer di ieri.