![]() |
Benvenuto Visitatore(Log In|Registrati)
![]() ![]() |
![]() |
![]()
Messaggio
#126
|
|
![]() ![]() Gruppo: SMod Messaggi: 11.744 Iscritto il: 20 January 06 Da: Bologna Utente Nr.: 2.653 Entropologo part-time BGE AG: Indy3 Arcade: Silpheed Free Roaming: Shadow of the Colossus RTS: Praetorians Playing Magari... SO Windows7 OGI Supporter ![]() |
La funzione di disegno della mappa è una di quelle che ho cambiato più volte mantenendo sempre lo stesso layout, simboli, colori e posizione delle terre, ma con l'obiettivo di velocizzare l'output a schermo.
Il metodo originale è curioso e molto furbo. Per rappresentare la mappa di 22x40 Leon Baradat ha assegnato ad ogni casella una variabile T(Y,X) con valori che vanno da T(1,1) a T(22,40). All'inizio del gioco lui associa poi ad ognona di queste variabili T un numero che corrisponde ad un particolare carattere della tabella ascii che poi stamperà a schermo al momento opportuno col comando PRINT CHR$([0-255]) Ad esempio se la prima casella in alto a sx fosse del Duca Cinturanera, alla variabile verrebbe assegnato il valore 219: T(1,1)=219 di modo che se dovesse stampare quella casella basterebbe il comando: PRINT CHR$(T(Y,X)) ovvero PRINT CHR$(219) questa è la definizione delle variabili ad inizio gioco, ovvero il layout della mappa: CODICE 130 FOR A=1 TO 10:FOR B=1 TO 23:T(A,B)=176:NEXT:NEXT:FOR A=17 TO 22:FOR B=1 TO 40:T(A,B)=178:NEXT:NEXT 140 FOR A=1 TO 5:FOR B=24 TO 40:T(A,B)=8:NEXT:NEXT:FOR A=9 TO 18:FOR B=30 TO 40:T(A,B)=15:NEXT:NEXT 150 FOR A=9 TO 16:FOR B=16 TO 29:T(A,B)=219:NEXT:NEXT:FOR A=11 TO 18:FOR B=1 TO 15:T(A,B)=177:NEXT:NEXT:FOR A=10 TO 12:T(A,15)=219:NEXT:FOR A=1 TO 10:T(11,A)=176:NEXT 160 FOR A=12 TO 15:T(18,A)=178:NEXT:FOR A=7 TO 8:FOR B=24 TO 31:T(A,B)=8:NEXT:NEXT:FOR A=38 TO 40:T(8,A)=15:NEXT:FOR A=24 TO 40:T(6,A)=8:NEXT:FOR A=16 TO 27:T(17,A)=219:NEXT 170 FOR A=7 TO 8:FOR B=32 TO 40:T(A,B)=15:NEXT:NEXT:FOR A=32 TO 40:T(19,A)=15:NEXT:T(7,32)=8:T(17,28)=15:T(17,29)=15:FOR A=36 TO 40:T(20,A)=15:NEXT:FOR A=38 TO 40:T(21,A)=15:NEXT Già il codice è abbastanza efficiente perché usa dei cicli FOR-NEXT per assegnare lo stesso valore a caselle contigue, invece di assegnare il valore ad ogni variabile singolarmente, perché 22x40 sarebbero 880 definizioni. esempio per una linea contigua: FOR A=16 TO 27:T(17,A)=219:NEXT Alle caselle dalla T(17,16) alla T(17,27) assegna il valore 219. esempio per blocco (due cicli FOR nested) FOR A=1 TO 10:FOR B=1 TO 23:T(A,B)=176:NEXT:NEXT Alle caselle dalla T(1,1) alla T(10,23) assegna il valore 176. piccola digressione sui cicli NESTED: il seguente codice non fa nulla se non far passare del tempo ma è COME lo fa che è interessante per capire questa diavoleria dei cicli INNESTATI uno dentro l'altro. Molto comodi in programmazione perché evitano codice e fanno risparmiare tempo sia al programmatore che alla macchina. FOR A=1 TO 10:FOR B=1 TO 8:NEXT:NEXT la macchina comincia a contare dal ciclo più interno per poi uscire verso gli esterni. Nota che possono essrci ben più di due cicli innestati. Comincia ad assegnare ad A il valore 1, poi a B il valore 1, dunque assegna a B il valore 2 e così via fino al valore 8, dopo di che dichiara finito il primo ciclo B (di 8) ed anche il primo ciclo A (di 10). In seguito associa ad A il valore 2, poi a B il valore 1 (sovrascrivendo l'ultimo valore che era 8) e così via fino a B=8 dichiarando concluso anche il secondo ciclo B. Dopo di che continua fino a che non è giunto a A=10 e B=8 e dichiara concluso anche il ciclo A. Per creare il minor numero di comandi Baradat ha disegnato dei rettangoli poi ha aggiunto blocchetti e linee qua e là sovrascrivendo variabili già definite. Io l'ho voluto riscrivere col numero minore di comandi ma senza sovrapposizioni: CODICE 100 FOR A=1 TO 8:FOR B=1 TO 23:T(A,B)=176:NEXT:NEXT:FOR B=1 TO 15:T(9,B)=176:NEXT:FOR B=1 TO 14:T(10,B)=176:NEXT:FOR B=1 TO 10:T(11,B)=176:NEXT 101 FOR B=11 TO 14:T(11,B)=237:NEXT:FOR B=1 TO 14:T(12,B)=237:NEXT:FOR A=13 TO 17:FOR B=1 TO 15:T(A,B)=237:NEXT:NEXT:FOR B=1 TO 11:T(18,B)=237:NEXT 102 FOR A=1 TO 6:FOR B=24 TO 40:T(A,B)=8:NEXT:NEXT:FOR B=24 TO 32:T(7,B)=8:NEXT:FOR B=24 TO 31:T(8,B)=8:NEXT 103 FOR A=33 TO 40:T(7,A)=15:NEXT:FOR A=8 TO 19:FOR B=32 TO 40:T(A,B)=15:NEXT:NEXT:FOR A=9 TO 18:FOR B=30 TO 40:T(A,B)=15:NEXT:NEXT:FOR B=28 TO 29:T(17,B)=15:NEXT:FOR B=36 TO 40:T(20,B)=15:NEXT:FOR B=38 TO 40:T(21,B)=15:NEXT 104 FOR A=9 TO 16:FOR B=16 TO 29:T(A,B)=219:NEXT:NEXT:FOR A=10 TO 12:T(A,15)=219:NEXT:FOR B=16 TO 27:T(17,B)=219:NEXT: 105 FOR B=11 TO 29:T(18,B)=178:NEXT:FOR B=1 TO 31:T(19,B)=178:NEXT:FOR B=1 TO 35:T(20,B)=178:NEXT:FOR B=1 TO 37:T(21,B)=178:NEXT:FOR B=1 TO 40:T(22,B)=178:NEXT:T(1,1)=206 La quantità di codice è maggiore ma è più veloce perché non fa cose superflue. Veniamo ora alla messa a schermo originale della mappa: CODICE 1240 CLS:CL=2:FOR A=1 TO 22:FOR B=1 TO 40:IF T(A,B)=T(A,B-1) THEN 1250 ELSE IF T(A,B)=176 THEN CL=13 ELSE IF T(A,B)=177 THEN CL=11 ELSE IF T(A,B)=178 THEN CL=14 ELSE IF T(A,B)=15 THEN CL=12 ELSE IF T(A,B)=8 THEN CL=15 ELSE CL=2 1245 COLOR CL,0 1250 PRINT CHR$(T(A,B));:NEXT:PRINT:NEXT I due cicli nested non sono altro che l'area della mappa: FOR A=1 TO 22:FOR B=1 TO 40 Dopo di che viene una condizionale IF che associa al volo il colore alla casella. SE T(A,B)=176 ALLORA CL=13, variabile CL che viene passata subito dopo a COLOR CL che in questo caso diventerà COLOR 13 ovvero porpora. Stampa poi CHR$(176) e se non è arrivato alla fine della riga (A,40) allora continua con il ciclo delle B. Quando arriva a T(A,40) e terminato il primo dei 22 cicli A, passa alla A successiva andando a capo come definisce quel PRINT fra i due NEXT. Questa stampa dura circa 2.6 secondi su un 386. Questa invece è la mia ultima iterazione: CODICE 210 FOR A=1 TO 22:FOR B=1 TO 40:IF T(A,B)=T(A,B-1) THEN 213 212 COLOR CF(CINT(T(A,B)/7.9)) 213 ?CHR$(T(A,B));:NEXT:?:NEXT Si può comprimere ancora un pelo togliendo la condizione che fa saltare la definizione del colore nel caso si trovino valori uguali contigui, ma poi rallenta. Su harware 386 le performance sono addirittura peggiori della versione originale. Ad ogni modo questo sarebbe la versione in una sola linea: CODICE 210 FOR A=1 TO 22:FOR B=1 TO 40:COLOR CF(CINT(T(A,B)/7.9)):?CHR$(T(A,B));:NEXT:?:NEXT Stessi due cicli nested ma questa volta definisco il colore con una singola condizione invece di 6. Questo approccio però necessita di una ulteriore definizione ad inizio programma ovvero CF quello che sostanzialmente cambia nel mio codice è questo: COLOR CF(CINT(T(A,B)/7.9)) io voglio che per ogni valore di T(A,B) che mi si presenta corrisponda un determinato numero di modo che quando devo stampare il valore 219 questo sia collegato alla variabile CF del COLOR con il valore 2, COLOR 2 ovvero verde. la definizione è questa: CF(28)=2 CF(22)=13 CF(30)=11 CF(23)=14 CF(02)=12 CF(01)=15 CF(26)=9 perché CF(28), CF(22)... ? Ad inizio programma è necessario definire le dimensioni massime degli ARRAY, in GW BASIC normalmente settati a 10. DIM T(23,41),CF(30) Questa definizione alloca già la memoria necessaria a queste variabili e la riempie di zeri, questo per evitare che durante l'esecuzione il programma non si debba trovare ad immagazzinare un nuovo valore per una variabile e non avere più memoria, il dannato "out of memory" error. NOTA, ogni variabile non definita ha valore ZERO. Se si vuole che una certa variabile cominci con ZERO è inutile definirla, si risparmia così codice e memoria. Qui entra in scena il trucco di dividere per 7.9, un valore non a caso ed è il più alto che mi sono potuto permettere. l'assegnazione delle variabili CF senza questo trucco sarebbe stata: CF(219)=2 (ricordate l'associazione che devo fare 219 - 2?) CF(176)=13 CF(237)=11 CF(178)=14 CF(015)=12 CF(008)=15 CF(206)=9 avrei dovuto quindi definire come dimensione massima dell'ARRAY almeno il valore massimo di CF ovvero 237. Ma ciò avrebbe comportato tanta memoria sprecata (per ogni posizione dell'array il GWBASIC richiede 4Bytes, quindi quasi 1KB a fronte di sole 7 posizioni occupate realmente nell'array). NOTA 1: GW BASIC alloca lo stesso anche tutte le altre variabili mancanti tipo CF(1) o CF(100) anche se non le utilizzi NOTA 2: Essendo 60300Bytes la memoria che può gestire il GW BASIC, Il valore massimo definibile per un array è DIM A(15071) (ma lascerebbe soli 3 Bytes per il programma). Da qui la divisione per 7.9 (il valore più vicino al valore minimo dei valori che mi servono, che è 8). La funzione CINT è come INT ma arrotonda all'INTero più vicino, mentre INT all'intero più basso. Se il risultato è 1,6 INT lo arrotonda a 1, mentre CINT a 2. Ho usato CINT per via del fatto che ho due valori nell'array molto vicini (176 e 178) che se divisi per 8 danno la stessa cifra, mentre con CINT e dividendo per 7.9 danno rispettivamente 22 e 23. Tirando le somme, mi sono dilungato anche troppo, la nuova funzione di stampa della mappa dura ora 1.4 secondi, sempre su stesso hardware 386. Lo si nota anche via PCBASIC. Meno su GWBASIC sotto dosbox, lì è istantaneo. Alla prossima! -------------------- |
|
|
![]()
Messaggio
#127
|
|
![]() ![]() Gruppo: SMod Messaggi: 11.744 Iscritto il: 20 January 06 Da: Bologna Utente Nr.: 2.653 Entropologo part-time BGE AG: Indy3 Arcade: Silpheed Free Roaming: Shadow of the Colossus RTS: Praetorians Playing Magari... SO Windows7 OGI Supporter ![]() |
Oggi un semplice escursus sulla stampa a schermo ripetitiva grazie al ciclo FOR:NEXT.
Per stampare una lista di elementi a schermo sarebbe sufficiente: CODICE 10 PRINT"Contadini" 20 PRINT"Vassalli" 30 PRINT"Nobili" o più semplicemente in una sola riga: CODICE 10 PRINT"Contadini":PRINT"Vassalli":PRINT"Nobili" si avrà a schermo il seguente output: CODICE Contadini Vassalli Nobili Quando gli elementi sono immagazzinati come variabili o costanti si possono stampare così: CODICE 10 P(1)="Contadini":P(2)="Vassalli":P(3)="Nobili" 20 FOR A=1 TO 3:PRINT P(A):NEXT e l'output sarà lo stesso. E' possibile anche stamparli in posizioni predefinite sullo schermo tramite il comando LOCATE Y,X dove Y è la riga (1-24) e X la colonna (1,80): CODICE 10 P(1)="Contadini":P(2)="Vassalli":P(3)="Nobili" 20 FOR A=1 TO 3:LOCATE A+2,1:PRINT P(A):NEXT Verranno stampate sempre all'inizio (,1) delle righe 3, 4 e 5 (A+2). E' possibile stamparle anche sulla stessa linea: CODICE 10 PRINT"Contadini";:PRINT" ";:PRINT"Vassalli";:PRINT" ";:PRINT"Nobili" Contadini Vassalli Nobili Se le stringhe non fossero sempre della stessa lunghezza e le si volesse stampate sempre nella setssa posizione sullo schermo si può ricorrere alla "virgola": CODICE 10 PRINT"Contadini","Vassalli","Nobili" Contadini Vassalli Nobili Se si preferisono posizioni diverse e non fisse come con la virgola, si può usare il più versatile TAB(XX) XX = colonna: CODICE 10 PRINT TAB(10)"Contadini"TAB(30)"Vassalli"TAB(50)"Nobili" Contadini Vassalli Nobili L'unica differenza tra questi comandi è che solo con il LOCATE è possibile mantenere tutto quello che è stato scritto prima a schermo negli spazi, la virgola e il TAB cancellano tutto quello che li divide. Dunque la faccenda si complica se dobbiamo aggiornare variabili in mezzo a caratteri che non volgiamo spariscano, come nel caso del riepilogo in cima allo schermo: CODICE ╔═╦ acri ╦ moggi ╔ fiorini╔═ contadini ╦ vassalli ═╦═ nobili ╦═╦═╦ sol ╦ pop ╦═╗ ╠╦╩ 1385 ╩ 913 ═╩ 50 ╔═╦╩╦ 22 ═╩ 60 ═╩ 30 ═╩ 75 ═╩ 8 ═╩ 22 ═╩╦╩ 53 ═╩ 150 ╚╦╣ ╠╩╦ 75 ╩╦╩ buona ╦╩╦╩╦╩ pacifici ╦ pacifici ╦ pacifici ╦╩╦ ESTATE ╦╩╣ ╚═╩════╩═╩═╩══════════╩═╩═╩═╩════════╩═╩═════════╩═╩═════════╩═╩═╩═╩═══════╩═╩═╝ il morale può avere 5 diversi stati di lunghezza variabile così come i valori numerici che cambiano di continuo. La difficoltà sta nel trovare una formula che come risultato dia le esatte posizioni volute. Nel caso del morale mi servivano a partire da 30 e le successive a distanza di 12: 30, 42, 54 Ho dovuto scomporre i tre numeri in componenti fissi per ridurre la variabilità di A e potergli dare la forma che volevo, CODICE 30 = 1+29 = A*10 +18 +A*2 = 10 + 18 +2 = 30 42 = 2+40 = A*10 +18 +A*2 = 20 + 18 +4 = 42 54 = 3+51 = A*10 +18 +A*2 = 30 + 18 +6 = 54 alla fine questò è il convoluto algoritmo che ho partorito da passare alla colonna del LOCATE (UNA sola formula per TRE posizioni): CODICE FOR A=1 TO 3:LOCATE 3,A*10+A*2+18:PRINT MO(A):NEXT Si può fare in altri modi, ma questo è quello più elegante che ho potuto immaginare. Alla prossima! -------------------- |
|
|
![]()
Messaggio
#128
|
|
![]() ![]() Gruppo: SMod Messaggi: 3.507 Iscritto il: 20 February 12 Utente Nr.: 19.203 BGE Baldur's Gate Fallout 2 Playing Divinity: Original Sin SO Windows7 ![]() |
Abbiamo fatto sapere anche al resto del mondo, finalmente la news sul nostro sito: https://www.oldgamesitalia.net/notizie/mani...deluxe-e-uscito
-------------------- Un ottimista non rimarrà mai piacevolmente sorpreso
elogio di Coit-Murphy del pensiero negativo |
|
|
![]()
Messaggio
#129
|
|
![]() ![]() Gruppo: SMod Messaggi: 11.744 Iscritto il: 20 January 06 Da: Bologna Utente Nr.: 2.653 Entropologo part-time BGE AG: Indy3 Arcade: Silpheed Free Roaming: Shadow of the Colossus RTS: Praetorians Playing Magari... SO Windows7 OGI Supporter ![]() |
In occasione dell'uscita di PC-PASIC 2.0.6 ho aggiornato il pacchetto di Maniero e fixato un paio di vecchi bug e un typo.
CITAZIONE Changelog Maniero v2.51 DELUXE (26/08/2022) - Aggiornato PC-Basic alla versione 2.0.6 - Aggiunto A$=INKEY$ all'interno dei cicli FOR/NEXT dedicati alle animazioni (transizioni, morto bruciato e raccolto) come workaround del bug di PC-BASIC #166 (https://github.com/robhagemans/pcbasic/issues/166). - L'animazione "morto bruciato", se scoperta, non veniva correttamente visualizzata nella sezione "L'arte di Maniero" - Risolto il bug nel riepilogo di fine partita per cui premere "Vedere la mappa finale" incrementava erroneamente la statistica "felicità del popolo" Scaricabile al solito posto, qui. -------------------- |
|
|
![]() ![]() |
Versione Lo-Fi | Oggi è il: 28th January 2023 - 12:14 |