Ingegneria Informatica, Elettronica, Automazione, I anno
Come detto durante le ultime esercitazioni prima della pausa, questo piccolo gioco può essere usato come base per vari esercizi.
Un esempio di esercizio svolto è l'implementazione dell'attacco da parte dei mostri, che mostra come impiegare utilmente i costrutti switch e static per ottenere un iteratore sui mostri che si trovano nelle caselle adiacenti a quella dove si trova il personaggio giocante.
Alcuni esercizi semplici dal punto di vista concettuale sono i seguenti:
Un altro esercizio, decisamente più complesso, riguarda la determinazione della visibilità (line of sight) fra due posizioni (tipicamente quella del personaggio giocante e la posizione di un mostro o di un oggetto). Si può realizzare questo tipo di funzionalità sfruttando l'algoritmo di Bresenham. Oltre al controllo di visibilità dei mostri e degli oggetti (utile per mostrare nella schermata di gioco solo i mostri e gli oggetti effettivamente in vista, i.e. per la “fog of war”), la line of sight serve anche per determinare l'area esplorata dal personaggio, e mostrare a video solo quella parte del dungeon. Qui trovate l'implementazione dell'algoritmo di Bresenham e delle sue applicazioni principali nel gioco. Il grosso delle modifiche riguarda il file level.c
. Nei prossimi giorni vedremo come applicare lo stesso concetto per consentire ai mostri di individuare e inseguire il personaggio.
Con la funzione line_of_sight
, potete ora realizzare anche altri esercizi semplici, come i seguenti:
Nota: nella terza versione ho inserito anche un supporto generico per le liste, che sarà utile in seguito, ad esempio per realizzare un dungeon multilivello come lista di livelli.
Come promesso, ecco una quarta versione del gioco, con un po' di funzionalità in più. In questa versione, ci concentriamo sulla gestione del movimento dei mostri. Per semplicità, manteniamo separato il caso dei mostri adiacenti al personaggio, e creiamo un nuovo campo turn
nella descrizione di ciascun mostro, che ci consente di determinare se un mostro ha già agito nel turno, oppure no. In questo modo, possiamo creare ora una funzione statica che restituisce uno alla volta tutti i mostri che non hanno ancora agito, e usarla in game.c
nella funzione principale per gestire le loro azioni. Una funzione monster_ai
nel file monsters.c
implementa la scelta dell'azione in una gamma definita da un set enumerativo (enum
). Al momento la scelta è piuttosto semplice: i mostri attaccano il personaggio (o gli si avvicinano se lontani) quando lo vedono, e quando i loro punti ferita sono ancora abbastanza alti. Se invece hanno pochi punti ferita (hits
) tendono a fuggire. Se non vedono il personaggio, si muovono casualmente, o rimangono fermi.
Nel file level.c
ci sono ora molte funzioni relative alla gestione del movimento e delle direzioni, che richiedono l'uso dei tipi monster
e Level
. In realtà, a questo punto level.c
è troppo complesso. Sarebbe opportuno spostare le funzionalità diverse dal semplice caricamento del livello in una coppia di file (.c
e .h
) separati. Questo è lasciato (al momento) come esercizio.
Altri esercizi semplici che potete svolgere sulla base di questa versione del gioco sono i seguenti:
d20()
) con il bonus mind
del mostro per valutare le condizioni del personaggio.speed
del personaggio e dei mostri per determinare chi è più veloce. E.g., se la velocità del personaggio è almeno 2x la velocità del mostro, quest'ultimo agisce solo una volta ogni due turni. Suggerimento: i turni sono visti dal punto di vista del personaggio (che agisce quindi sempre una volta per turno), mentre i mostri possono agire anche più volte (o meno) per turno, a seconda della velocità relativa.
E ultima, visto che le vacanze sono quasi finite. Arrivati alla versione che trovate nella terza parte, risulta evidente che il file level.c
sta assorbendo troppe funzionalità. E' preferibile allora estrarre le parti relative al comportamento dei mostri, aggiungendo una nuova coppia di file enemyactions.c
e enemyactions.h
, tanto più che in level.c
avremo bisogno di ulteriori funzionalità per gestire l'ultima caratteristica primaria che vogliamo aggiungere, ovvero la possibilità di avere un numero illimitato (memoria permettendo) di livelli.
Vediamo allora l'implementazione, che trovate in questo file. Concettualmente, ci sono pochi punti importanti da considerare:
test_level_2.txt
e test_level_2_map.txt
)<
e >
rispettivamente per scale che salgono e scendono.[x,y]:< filename.txt
per rappresentare le scale (in questo caso verso l'alto).stairs_to
che rappresenta la destinazione.list.c
. Notate che il tipo di dato contenuto nella lista non è specificato (void *
), quindi dobbiamo tenerne traccia noi (qui non è un problema, dato che per il momento abbiamo una sola lista). Notate che al passaggio del personaggio da un livello all'altro, il livello non più attivo rimane “congelato”, ma gli effetti delle azioni compiute dal personaggio permangono – tornando nel livello appena lasciato, ad esempio, i mostri uccisi non ricompaiono.
Aggiungiamo anche un caso speciale di scala, quella verso l'esterno. Questa si specifica nel file di configurazione come [x,y]:< exit
, e permette di terminare il gioco. Viene stampata una scritta di commiato, comprensiva del numero di XP raccolti dal personaggio, dopodichè il gioco termina.
Altri esercizi che fanno uso di liste e dei livelli multipli includono:
morgue.txt
) e mostrare alla fine della partita una lista di “high score”, ordinati per XP decrescenti. Potete usare le liste e la funzione sort per ordinare i punteggi, senza mantererli necessariamente ordinati nel file morgue.txt
.level→objs
da array a lista di content
.Nota: qui potete trovare una versione ulteriormente semplificata (rispetto a quella vista a lezione) del gioco, senza oggetti nè mostri. Può essere utile come punto di partenza per implementare in modo diverso oggetti e mostri. Sarebbe possibile semplificare ulteriormente il gioco, eliminando le caratteristiche del personaggio (comunque inutilizzate in questa versione molto semplice). Il codice risultante potete scaricarlo qui.
Raccolgo qui materiali vari che possono risultare utili, ma non sono direttamente connessi con le esercitazioni. Principalmente, riguardano la programmazione in linguaggio assembler per architetture ARM.
Le fonti citate qui sono suggerimenti per chi desidera approfondire alcuni dei temi visti nelle esercitazioni, non materiali del corso.
Materiali su Python.
Data | Argomenti | Materiali |
---|---|---|
13/10 | Algebra di Boole, Rappresentazione dei numeri | |
20/10 | Algebra di Boole, Rappresentazione dei numeri | |
27/10 | Cicli e condizioni | Materiali aggiornati |
06/11 | Array | Materiali |
10/11 | Stringhe, matrici | |
14/11 | Funzioni, Stringhe | Materiali aggiuntivi |
17/11 | Riepilogo pre-compito | Temi d'esame |
01/12 | File | Materiale aggiuntivo |
03/12 | Ricorsione e allocazione dinamica della memoria | Materiale aggiuntivo, Torre di Hanoi |
15/12 | Liste | |
09/01 | Liste e Ricorsione | Materiali aggiornati |
12/01 | Esercizi sui processi | Materiali aggiornati |
Riepilogo/Temi d'esame |
Data | Argomenti | Materiali |
---|---|---|
27/09 | Algebra di Boole, Rappresentazione dei numeri | |
1/10 | Algebra di Boole, Rappresentazione dei numeri | |
12/10 | Cicli e condizioni | |
19/10 | Array | |
26/10 | Stringhe, matrici | |
Funzioni | ||
Stringhe | ||
30/11 | File | |
14/12 | Ricorsione | |
17/12 | Allocazione dinamica della memoria | |
18/12 | Liste e Ricorsione |
Data | Argomenti | Materiali |
---|---|---|
27/09 | Algebra di Boole, Rappresentazione dei numeri | |
14/10 | Linguaggi di programmazione: C, Assembler (1) | Sintesi GNU as e ARM ISA, semplici programmi in assembler ARM |
21/10 | Linguaggio di programmazione C: stringhe e array | esercizi sulle stringhe |
28/10 | Funzioni e Strutture | esercizi su funzioni e strutture |
4/11 | Definizione di nuovi tipi di dato in C | esercizi su funzioni e strutture, schema dell'architettura del programma |
7/11 | Esercizi di preparazione per l'esame | esercizi, soluzioni degli esercizi da corsi online |
8/11 | Esercizi di preparazione per l'esame | esercizi |
25/11 | Puntatori e chiamata di funzioni | introduzione all'assembler e struttura a basso livello di una chiamata di funzione |
12/12 | Ricorsione | |
13/12 | Ricorsione + File | |
16/12 | Ricorsione + Liste | Esercizi |
10/01 | Liste | Esercizi sulle liste |
16/01 | Processi | Esercizi sui processi |
17/01 | Esercizi vari | Esercizio ecopass |
Esercitazione riassuntiva |