Table of Contents

Esercitazioni del corso di Fondamenti di Informatica

Ingegneria Informatica, Elettronica, Automazione, I anno

Esercitazioni, versione 2019-2020

Applicazioni di esempio

C: roguelike

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:

  1. Muovere in modo casuale i mostri (assumete una velocità di 1 casella per turno)
  2. Visualizzare il contenuto dell'inventario (usate una schermata “nuova”, e ripristinate quella originale in seguito; avvantaggiatevi del fatto che l'inventario contiene al più 25 oggetti diversi)
  3. Raccogliere gli oggetti (comando PICK) e inserirli nell'inventario
  4. Lasciar cadere un oggetto presente nell'inventario (comando DROP)
  5. Mangiare un oggetto edibile per recuperare punti nelle varie abilità (decidete voi quali cibi permettono di recuperare punti in quali abilità)
  6. Usare armi (WIELD) e introdurre i loro effetti nel combattimento corpo a corpo. Suggerimento: armi piccole infliggono 2 (o 1d4 se preferite danni casuali) punti di danno, armi medie 4 o 1d8, armi grandi 6 o 1d12
  7. Inserire nel gioco nuovi oggetti utilizzabili (ad esempio, bacchette magiche o pozioni)
  8. Salvare e ricaricare il gioco
  9. Consentire al giocatore di creare il personaggio invece di crearlo in modo casuale

Parte Seconda

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:

  1. Attacchi a distanza (con arco e frecce o altre armi simili; considerate l'arco un'arma media)
  2. Attacchi con incantesimi (cominciate con un semplice “dardo incantato” che infligge 3 o 1d6 punti ferita)
  3. Ovviamente, potete introdurre mostri con abilità magiche o armi a distanza; questo potrebbe richiedere qualche modifica nelle strutture che descrivono i mostri, o anche no (e.g., assumendo che i mostri infliggano danni a distanza o da incantesimi pari ai normali danni da mischia)

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.

Parte Terza

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:

  1. Rendere più realistica l'intelligenza artificiale dei mostri: ad esempio, i mostri feriti gravemente potrebbero continuare ad attaccare il personaggio se anche questi è gravemente ferito. Usate un tiro casuale (d20()) con il bonus mind del mostro per valutare le condizioni del personaggio.
  2. Al momento i mostri si muovono verso i loro obiettivi solo in linea retta. Gestire il caso in cui c'è un ostacolo, cercando di spostarsi nella direzione migliore (e.g., il mostro vuole andare verso SOUTH, ma c'è un muro o un altro mostro, proverà allora a spostarsi verso SOUTHEAST o SOUTHWEST).
  3. Introdurre la gestione della velocità: usate il rapporto fra le caratteristiche di 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.

Parte Quarta

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:

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:

  1. Mantenere (in un file separato, tradizionalmente chiamato 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.
  2. Trasformare level→objs da array a lista di content.
  3. Inserire nel gioco dei portali. Un portale consente di spostarsi in un altro livello (o in quello corrente), ma non nella medesima posizione (la posizione di arrivo deve quindi essere specificata esplicitamente).
  4. Scrivere una funzione che controlli che la definizione di un insieme di livelli non presenti errori o incoerenze (e.g., se due livelli sono collegati da scale, queste devono essere presenti in entrambi i livelli, e devono essere discendenti nel livello superiore e ascendenti in quello inferiore). La funzione può essere anche utilizzata da un programma separato, la cui funzionalità è solo quella di verificare la correttezza dell'intero dungeon.
  5. Al momento, se il personaggio si trova nella posizione x,y al livello A e scende le scale verso il livello B, si ritrova nella posizione x,y al livello B. Questo potrebbe non essere possibile, in quanto potrebbe esserci un muro o un mostro. Verificare e/o risolvere questo tipo di problema (o spostando la posizione di arrivo, o facendo sì che la posizione di arrivo sia quella della scala).

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.

Documenti utili

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.

Bibliografia

Le fonti citate qui sono suggerimenti per chi desidera approfondire alcuni dei temi visti nelle esercitazioni, non materiali del corso.

Programmi e materiali degli anni precedenti

Materiali su Python.

Programma delle esercitazioni (2015-2016)

1 Rappresentazione dei numeri
2 Algebra di Boole
3 Controllo
4 Array
5 Stringhe e strutture
6 Funzioni, Rappresentazione FP
7 File
8 Introduzione a Python (iPython notebook), A small Python game, traccia della lezione
9 Introduzione a Python, parte seconda (e versione integrata in inglese); esempio web crawler esempio web app

Programma delle esercitazioni (2014)

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

Programma delle esercitazioni (2012)

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

Programma delle esercitazioni (2011)

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