Initial commit

This commit is contained in:
2026-06-12 20:48:41 +02:00
commit b94191d83d
15 changed files with 131126 additions and 0 deletions
+815
View File
@@ -0,0 +1,815 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
// BEGIN INIZIALIZZAZIONE DELLA MAPPA
// BEGIN STRUTTURE
// Struttura per rappresentare una rotta aerea
typedef struct {
int dest_x, dest_y; // Coordinate destinazione
int cost; // Costo della rotta
} AirRoute;
// Struttura per rappresentare un esagono
typedef struct {
int land_cost; // Costo di uscita via terra (0 = bloccato)
AirRoute air_routes[5]; // Rotte aeree uscenti
int air_route_count; // Numero di rotte aeree
} Hexagon;
// Struttura per la mappa
typedef struct {
int rows, cols; // Dimensioni della matrice
Hexagon** grid; // Matrice
} HexMap;
// Struttura per la coda in change_cost
typedef struct {
int x;
int y;
int dist;
} QueueNode;
// END
void init_map(HexMap* map, int cols, int rows){
// FUNZIONAMENTO:
// Gli passo una struttura HexMap e gli inserisco il numero di colonne e righe.
// La matrice è formata da un "array" di puntatori alle teste delle colonne. Per farlo alloco dinamicamente lo spazio per contenere tutti
// i puntatori agli inizi delle colonne.
// Vado poi a scorrere ogni elemento della matrice ed a inizializarli con i valori land_cost=1 e air_route_count=0.
map -> rows = rows; // Inserisce le dimensioni negli attributi dell'oggetto'
map -> cols = cols;
// Genero la matrice di esagoni
map -> grid = (Hexagon**) malloc(cols * sizeof(Hexagon*)); // Alloco lo spazio del primo livello della matrice
for (int i=0; i<map -> cols; i++){
map -> grid[i] = (Hexagon*) malloc(rows * sizeof(Hexagon)); // Alloco lo spazio del secondo livello della matrice
for (int j=0; j<map -> rows; j++){
map -> grid[i][j].land_cost=1; // Inizializzo i valori degli esagoni
map->grid[i][j].air_route_count = 0;
}
}
fputs("OK\n", stdout);
fflush(stdout);
}
void destroy_map(HexMap* map){
// Va a deallocare la mappa
for (int i = 0; i < map -> cols && map -> grid[i]; i++) {
free(map->grid[i]);
}
free(map->grid);
map->grid = NULL;
map->rows = map->cols = 0;
}
void print_map(HexMap* map){
printf("\n");
for (int i=map->rows-1; i>=0; i--){ // Per ogni riga (se pari la offsetta per ottenere forma esagonale)
if(i%2==1) printf(" ");
for (int j=0; j<map -> cols; j++){ // Per ogni colonna
printf("%d ",map -> grid[j][i].land_cost); // Stampa il costo dell'esagono'
}
printf("\n");
}
}
bool is_valid(HexMap* map, int x, int y){
if(x<0 || x>map->cols-1 || y<0 || y>map->rows-1){
return false;
} else {
return true;
}
}
int hexagons_distance(int x_1, int y_1, int x_2, int y_2){
// Viene usato per calcolare un euristica (approssimata) per A*
// Euristica=stima della distanza (in numero di esagoni ignorando i land_cost) tra due esagoni
// Converte (x, y) in coordinate cubiche (cx, cy, cz)
int cx1 = x_1 - (y_1 - (y_1 & 1)) / 2;
int cz1 = y_1;
int cy1 = -cx1 - cz1;
int cx2 = x_2 - (y_2 - (y_2 & 1)) / 2;
int cz2 = y_2;
int cy2 = -cx2 - cz2;
return fmax(fabs(cx1 - cx2), fmax(fabs(cy1 - cy2), fabs(cz1 - cz2)));
}
void change_cost(HexMap* map, int x, int y, int cost, int radius){
// Controllo che l'esagono sia valido
if(!is_valid(map, x, y) || radius<=0){
fputs("KO\n", stdout);
fflush(stdout);
return;
}
// Per ogni esagono in griglia, controllo se dista meno del raggio e, nel caso, gli calbio il costo
int distanza;
int cost_old_air_route;
for(int i=0; i<map->cols; i++){
for (int j=0; j<map->rows; j++){
distanza=hexagons_distance(x,y,i,j);
if (distanza<radius){
map->grid[i][j].land_cost=map->grid[i][j].land_cost+(float) floor(cost * fmax(0,(float) (radius-distanza)/radius)); // Aggiorna il costo
if (map -> grid[i][j].land_cost>100){ // Faccio il controllo sul costo (può essere compreso tra 0 e 100)
map -> grid[i][j].land_cost=100;
}
if (map -> grid[i][j].land_cost<0){
map -> grid[i][j].land_cost=0;
}
// Aggiorno i costi delle rotte aeree
for (int counter=0; counter<map -> grid[i][j].air_route_count;counter++){
cost_old_air_route=0;
for (int n=0; n<counter;n++){
cost_old_air_route=cost_old_air_route + map->grid[i][j].air_routes[n].cost;
}
map->grid[i][j].air_routes[counter].cost=(int)((cost_old_air_route+map->grid[i][j].land_cost)/(counter+1));
}
}
}
}
fputs("OK\n", stdout);
fflush(stdout);
}
/*
// VECCHIA FUNZIONE
void change_cost(HexMap* map, int x, int y, int cost, int radius){
// FUNZIONAMENTO:
// Inizialmente faccio il controllo su x, y e radius.
// Genero una tabella di valori booleani in cui mi salvo quali esagoni sono già stati modificati.
// Creo una coda in cui metto l'esagono sorgente e ne modifico il costo. Poi aggiungo i 6 esagoni vicini (si differenzia se la riga è pari o dispari).
// Vado a scorrere la tabella per ogni nodo che c'è al suo interno (i primi che incontro sono i 6 vicini del sorgente) e per ogni nodo aggiorno il valore
// e aggiungo i vicini alla coda (se sono all'interno del raggio che voglio e se non sono già stati modificati prima (lo controllo con la tabella booleana)).
if(!is_valid(map, x, y) || radius<=0){
fputs("KO\n", stdout);
fflush(stdout);
return;
}
bool** visited = (bool**) malloc(map -> cols * sizeof(bool*)); // Genero la tabella booleana
for (int i=0; i< map -> cols; i++){
visited[i]= (bool*) malloc(map -> rows * sizeof(bool));
for (int j=0; j< map -> rows; j++){
visited[i][j]=false;
}
}
QueueNode* queue = (QueueNode*) malloc(map -> cols * map -> rows * sizeof(QueueNode)); // Creo la coda di dimensione giusta
int head = 0; // head è la testa della coda (dove estraggo), tail è il culo della coda (dove inserisco)
int tail = 0;
// Inserisco il primo esagono (quello su cui ho chiamato la funzione) all'interno della coda e lo marco nella tabella booleana
queue[tail] = (QueueNode){x, y, 0};
visited[queue[tail].x][queue[tail].y]=true;
tail = tail + 1;
// Creo i due percorsi da ispezionare in base alla riga (pari o dispari)
const int dir_even[6][2] = {
{+1, 0}, { 0, -1}, {+1, -1},
{-1, 0}, {+1, +1}, { 0, +1}
};
const int dir_odd[6][2] = {
{+1, 0}, {-1, -1}, { 0, -1},
{-1, 0}, { 0, +1}, {-1, +1}
};
while (head < tail){ // Fino a quando ho oggetti in coda, faccio sta merda
QueueNode current_node = queue[head]; // Prendo il primo oggetto nella coda
head=head+1;
if (current_node.dist < radius){ // Se la sua distanza dalla sorgente è minore di quella del raggio, gli aggiorno il costo e guardo i vicini
map -> grid[current_node.x][current_node.y].land_cost = map -> grid[current_node.x][current_node.y].land_cost + (float) floor(cost * fmax(0,(float) (radius-current_node.dist)/radius));
if (map -> grid[current_node.x][current_node.y].land_cost>100){ // Faccio il controllo sul costo (può essere compreso tra 0 e 100)
map -> grid[current_node.x][current_node.y].land_cost=100;
}
if (map -> grid[current_node.x][current_node.y].land_cost<0){
map -> grid[current_node.x][current_node.y].land_cost=0;
}
// Scegli le direzioni in base alla parità della riga
const int (*dirs)[2] = (current_node.y % 2 == 0) ? dir_even : dir_odd;
int new_x;
int new_y;
// Esplora i 6 vicini
for (int i = 0; i < 6; i++) {
new_x = current_node.x + dirs[i][0];
new_y = current_node.y + dirs[i][1];
// Controllo limiti mappa e se li rispetta, lo aggiungo in coda
if (is_valid(map, new_x, new_y) && !visited[new_x][new_y]) {
visited[new_x][new_y] = true;
queue[tail] = (QueueNode){new_x, new_y, current_node.dist + 1};
tail = tail + 1;
}
}
}
}
// Dealloco dalla memoria la tabella booleana
for (int i = 0; i < map->cols; i++) {
free(visited[i]);
}
free(visited);
// Dealloco la coda
free(queue);
fputs("OK\n", stdout);
fflush(stdout);
}
*/
void toggle_air_route(HexMap* map, int x_1, int y_1, int x_2, int y_2){
// FUNZIONAMENTO:
// Analizzo l'array delle rotte aeree dell'esagono di partenza (x_1, y_1) e, se la rotta è già presente la elimino (rimuovo dall'array); se invece non è presente
// la aggiungo (inserisco nell'array)
// Controllo che gli esagoni siano validi
if(!is_valid(map, x_1,y_1) || !is_valid(map, x_2,y_2)){
fputs("KO\n", stdout);
fflush(stdout);
return;
}
Hexagon* StartingHexagon = &(map -> grid[x_1][y_1]); // Memorizzo il puntatore all'esagono di partenza
// Controllo di non avere più di 5 rotte aeree (uso il 4 per come è costruito il codice)
if (StartingHexagon -> air_route_count>4){
fputs("KO\n", stdout);
fflush(stdout);
return;
}
for (int i=0; i< StartingHexagon -> air_route_count; i++){ // Per ogni elemento all'interno della lista di rotte aeree dell'esagono di partenza
if ((StartingHexagon -> air_routes[i].dest_x == x_2)&&(StartingHexagon -> air_routes[i].dest_y == y_2)){ // Se la rotta è già presente
for (int j=i; j<StartingHexagon -> air_route_count; j++){ // Lo elimino shiftando gli elementi successivi a sx (così da non avere buchi nell'array)
StartingHexagon -> air_routes[j]=StartingHexagon -> air_routes[j+1];
}
StartingHexagon -> air_route_count = StartingHexagon -> air_route_count - 1; // Diminuisco il contatore di rotte aeree
fputs("OK\n", stdout);
fflush(stdout);
return;
}
}
// Se sono arrivato qua, significa che la rotta non era presente nell'array e quindi la aggiungo
// Calcolo il costo della rotta aerea
int cost;
if (StartingHexagon -> air_route_count==0){
cost = (int) floor(StartingHexagon -> land_cost / ((StartingHexagon -> air_route_count)+1));
} else {
int sum=0;
for (int i=0; i< StartingHexagon -> air_route_count; i++){
sum = sum + (int) StartingHexagon -> air_routes[i].cost;
}
cost = (int) floor((int) (sum+(StartingHexagon -> land_cost)) / ((StartingHexagon -> air_route_count)+1));
}
StartingHexagon -> air_routes[StartingHexagon -> air_route_count] = (AirRoute) {x_2, y_2, cost}; // Aggiungo la tratta all'array
StartingHexagon -> air_route_count = StartingHexagon -> air_route_count + 1; // Aumento il contatore del numero di tratte
fputs("OK\n", stdout);
fflush(stdout);
// DEBUGGING
/*
printf("Tratta aggiunta\n");
printf("La lista di tratte aeree partendo da %d %d:\n", x_1, y_1);
for (int i=0; i< StartingHexagon -> air_route_count; i++){
printf("x:%d y:%d costo:%d\n", StartingHexagon -> air_routes[i].dest_x, StartingHexagon -> air_routes[i].dest_y, StartingHexagon -> air_routes[i].cost);
}
printf("\n\n");
*/
}
// END
// BEGIN HASH-TABLE
// BEGIN STRUTTURE
typedef struct HashNode HashNode;
struct HashNode{
int x,y;
int distance_from_start;
int approx_distance_from_end;
HashNode* next;
};
typedef struct{
int size;
HashNode** buckets;
} HashTable;
// END
HashTable* create_hash_table(int size){
// Riceve la dimensione della tabella esagonale e crea una hash table di dimensioni adeguate
// Trovo la dimensione minima che deve avere la tabella (fattore di carico massimo 1.5)
int min_size = (int)ceil(size / 1.5);
// Numeri primi buoni per dimensioni di hash table
static const int primes[] = {
53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433, 1572869, 3145739,
6291469, 12582917, 25165843, 50331653, 100663319, 201326611
};
// Trova il primo numero primo >= min_size che sarà la dimensione della tabella hash
int hash_length=0;
for (size_t i = 0; i < 23; i++) {
if (primes[i] >= min_size) {
hash_length = primes[i];
break;
}
}
// Allco la struttura principale della hash table e inizializzo i suoi attributi
HashTable* ht = (HashTable*)malloc(sizeof(HashTable));
ht->size = hash_length;
// Alloco l'array di bucket (li inizializzo tutti a NULL)
ht->buckets = (HashNode**) malloc(hash_length * sizeof(HashNode*));
for (int i = 0; i < hash_length; i++) {
ht->buckets[i] = NULL;
}
return ht;
}
void clear_hash_table(HashTable* ht) {
// Ripulisce la tabella dagli HashNode per poterla riutilizzare
// Scorri tutti i bucket
for (int i = 0; i < ht->size; i++) {
HashNode* current = ht->buckets[i];
// Libera tutti i nodi nella catena
while (current != NULL) {
HashNode* next = current->next;
free(current);
current = next;
}
// Reimposta il bucket a NULL
ht->buckets[i] = NULL;
}
}
void destroy_hash_table(HashTable** ht) {
// Distrugge completamente l'hash table
// Ripulisce tutti i nodi nella tabella
clear_hash_table(*ht);
// Pulisce l'array dei bucket, la struttura principale e infine imposta a null il puntatore alla hash table
free((*ht)->buckets);
free(*ht);
*ht = NULL;
}
void print_hash_table (HashTable* ht){
for (int i=0; i<(ht->size); i++){
HashNode* current = ht->buckets[i];
printf("Index[%d]: ", i);
while (current != NULL) {
printf("[x:%d y:%d dist:%d approx:%d]-> ", current -> x, current -> y, current -> distance_from_start, current -> approx_distance_from_end);
current = current->next;
}
printf("\n");
}
}
int calculate_hash(int x, int y, int size){
// Hash function
const int p1 = 7919; // Primo grande
const int p2 = 1237; // Altro primo grande
return ((x * p1) ^ (y * p2)) % size;
}
bool insert_or_update_element (HashTable* ht, int x, int y, int distance_from_start, int approx_distance_from_end){
// Cerca un nodo dalla hash table, se non lo trova lo aggiunge, mentre se lo trova aggiorna (se necessario) la distance_from_start
// Ritorna true se il nodo è stato aggiunto o aggiornato, false altrimenti
int index = calculate_hash(x, y, ht -> size); // Calcolo l'hash
HashNode* current = ht -> buckets[index]; //Scorro la lista fino a quando trovo il nodo o la finisco
while (current != NULL && !(current -> x == x && current -> y == y)){
current = current -> next;
}
if (current == NULL){ // Se non c'è il nodo, lo inserisco
int index = calculate_hash(x, y, ht -> size); // Calcola l'index con la hash function
HashNode* hn = malloc(sizeof(HashNode)); // Crea il nodo con i parametri che sono stati passati alla funzione (l'attributo next lo definisco nell'inserimento in testa)
hn->x = x;
hn->y = y;
hn->distance_from_start = distance_from_start;
hn->approx_distance_from_end = approx_distance_from_end;
hn -> next = ht -> buckets[index]; // Inserisco in testa alla lista
ht -> buckets[index] = hn;
return true;
}
if (current -> distance_from_start > distance_from_start){ // Se c'è già, se posso lo aggiorno
current -> distance_from_start = distance_from_start;
return true;
}
return false;
}
HashNode* search_element (HashTable* ht, int x, int y){
// Cerca un nodo dalla hash table
int index = calculate_hash(x, y, ht -> size); // Calcolo l'hash
HashNode* current = ht -> buckets[index]; //Scorro la lista fino a quando trovo il nodo o la finisco
while (current != NULL && !(current -> x == x && current -> y == y)){
current = current -> next;
}
return current;
}
// END
// BEGIN PATH-FINDING (A*)
// BEGIN STRUTTURE
typedef struct {
int x,y;
} QueueNode_PathFinding;
// END
void enqueue(QueueNode_PathFinding* queue, HashTable* ht, int x, int y, int head, int tail){
HashNode* hn = search_element(ht, x, y);
int node_total_cost = hn->distance_from_start + hn->approx_distance_from_end; // Trovo il costo totale del nodo da inserire
HashNode* temp_node = NULL;
for (int i=head; i<tail; i++) {
temp_node = search_element(ht, queue[i].x, queue[i].y);
int temp_cost = temp_node->distance_from_start + temp_node->approx_distance_from_end; // Trovo il costo totale del nodo i-esimo nella coda
if(node_total_cost < temp_cost){ // Se il nodo da inserire costa meno del nodo i-esimo della coda
for (int j=tail; j>=i; j--){ // Sposto tutti i nodi successivi avanti di uno
queue[j+1]=queue[j];
}
queue[i]=(QueueNode_PathFinding) {x, y}; // Inserisco il nodo nella coda in posizione i
return;
}
}
queue[tail]=(QueueNode_PathFinding) {x, y}; // Se non è stato aggiunto in mezzo alla coda, lo devo aggiungere alla fine
}
int travel_cost(HexMap* map, HashTable* ht, int xp, int yp, int xd, int yd){
// FUNZIONAMENTO:
// È un algoritmo A* in cui tengo sempre una coda ORDINATA che hain testa le coordinate x,y del nodo con minore costo totale, mentre in coda quello con maggior costo totale.
// Il costo totale è dato dalla distanza dal nodo sorgente + euristica (distanza approssimativa in linea d'aria dalla fine).
//
// Inizializzo la coda e ci inserisco il nodo sorgente; una volta fatto questo inserisco i 6 nodi vicini nella hashtable e poi nella coda.
//
// NOTA: la funzione di inserimento dell'hash table inserisce il costo solamente se è minore di quello già presente e riotrna true solo se il nodo viene aggiunto o aggiornato
// (se il costo è maggiore di quello precedente, ritorna false).
//
// NOTA: la funzione di inserimento in coda inserisce i nodi già ordinandoli in base al loro costo.
if (!is_valid(map,xp,yp) || !is_valid(map,xd,yd)){
return -1;
}
QueueNode_PathFinding* queue = malloc(map->rows * map->cols * sizeof(QueueNode_PathFinding)*1000); // Inizializzo la coda, head e tail
int head=0;
int tail=0;
queue[0] = (QueueNode_PathFinding) {xp, yp}; // Inserisco il nodo sorgente in coda
tail++;
insert_or_update_element(ht, xp, yp, 0, hexagons_distance(xp,yp,xd,yd)); // Inserisco il nodo sorgente in hash table
QueueNode_PathFinding current; // Inizializzo current, ossia il primo nodo della coda su cui lavoro (quello con costo minore dato che la coda è ordinata)
HashNode* current_ht; // Inizializzo current_ht, ossia il nodo nell'hash table corrispondente a current
int new_x; // new_x e new_y vengono usate per immagazzinare i 6 nodi vicini
int new_y;
int air_route_count; // In queste due variabili vado a inserire gli attributi delle rotte aeree del nodo corrente
AirRoute* air_routes;
while (head<tail){
// ISPEZIONO IL PRIMO NODO DELLA CODA
current=queue[head]; // Definisco current
head++;
current_ht = search_element(ht, current.x, current.y); // Cerco current nella hash table e il nodo corrispondente lo metto in current_ht (NOTA: current sarà sicuramente nell'hash table)
if (current.x==xd && current.y==yd){ // Se il nodo current è il nodo di destinazione, ritorno il costo impiegato per raggiungerlo (approx_dist sarebbe comunque 0)
int ris = current_ht -> distance_from_start;
free(queue); // Dealloco la coda
clear_hash_table(ht); // Pulisco la hash table
return (ris);
}
if(map->grid[current.x][current.y].land_cost!=0){ // Se current è transitabile
// ISPEZIONI I 6 VICINI
// Creo i due percorsi da ispezionare in base alla riga (pari o dispari)
const int dir_even[6][2] = {
{+1, 0}, { 0, -1}, {+1, -1},
{-1, 0}, {+1, +1}, { 0, +1}
};
const int dir_odd[6][2] = {
{+1, 0}, {-1, -1}, { 0, -1},
{-1, 0}, { 0, +1}, {-1, +1}
};
// Scegli le direzioni in base alla parità della riga
const int (*dirs)[2] = (current.y % 2 == 1) ? dir_even : dir_odd;
// Guardo le coordinate dei 6 nodi vicini (per ogni iterazione, il nodo avrà coordinate new_x, new_y)
for (int i = 0; i < 6; i++) {
new_x = current.x + dirs[i][0];
new_y = current.y + dirs[i][1];
if(is_valid(map, new_x, new_y)){ // Se il nodo è nella mappa
if (insert_or_update_element(ht, new_x, new_y, current_ht->distance_from_start+map->grid[current.x][current.y].land_cost, hexagons_distance(new_x, new_y, xd, yd))){ // Se inserendolo ottengo true
enqueue(queue, ht, new_x, new_y, head, tail); // Lo aggiungo alla coda
tail++;
}
}
}
// ISPEZIONI LE ROTTE AEREE USCENTI DA CURRENT
air_route_count=map->grid[current.x][current.y].air_route_count;
air_routes=map->grid[current.x][current.y].air_routes;
for (int i=0; i<air_route_count; i++){
new_x=air_routes[i].dest_x; // La destinazione la memorizzo in new_x e new_y (come l'inserimento dei 6 nodi vicini')
new_y=air_routes[i].dest_y;
if(is_valid(map, new_x, new_y)){ // Se il nodo è nella mappa
if (insert_or_update_element(ht, new_x, new_y, current_ht->distance_from_start+air_routes[i].cost, hexagons_distance(new_x, new_y, xd, yd))){ // Se inserendolo ottengo true
enqueue(queue, ht, new_x, new_y, head, tail); // Lo aggiungo alla coda
tail++;
}
}
}
}
}
free(queue); // Dealloco la coda
clear_hash_table(ht); // Pulisco la hash table
return -1;
}
// END
int main(){
char testo[30];
int inp_uno, inp_due, inp_tre, inp_quattro;
HexMap map;
HashTable* ht = NULL;
int already_initialized=0;
while (1){
// LEGGO L'INPUT E SE RAGGIUNGO L'EOF FACCIO UNA BREAK
int tmp_input = scanf("%s %d %d %d %d",testo, &inp_uno, &inp_due, &inp_tre, &inp_quattro);
if (tmp_input==-1){
break;
}
if (strcmp(testo, "init")==0){
if (already_initialized==1){
destroy_map(&map);
}
init_map(&map, inp_uno, inp_due);
already_initialized=1;
if (ht!=NULL){
destroy_hash_table(&ht);
}
ht = create_hash_table(inp_uno * inp_due);
}
if (strcmp(testo, "print")==0){
print_map(&map);
}
if (strcmp(testo, "change_cost")==0){
change_cost(&map, inp_uno, inp_due, inp_tre, inp_quattro);
clear_hash_table(ht);
}
if (strcmp(testo, "toggle_air_route")==0){
toggle_air_route(&map, inp_uno, inp_due, inp_tre, inp_quattro);
}
if (strcmp(testo, "travel_cost")==0){
int costo = travel_cost(&map, ht, inp_uno, inp_due, inp_tre, inp_quattro);
fprintf(stdout, "%d\n", costo);
fflush(stdout);
}
}
}